# improve robustness of IFC loader: filter out duplicate points, adjacent colinear edges, silently ignore zero-vertex polygons. fix various small bugs and improve numeric stability of some code segments.

- workaround: silently drop very small nested polygon boundaries to avoid cases where a large polygon brep (i.e. a slab) has extremely holes that are so extremely small in comparison to the dimensions of the outer polygon that the resulting numeric accuracies make the triangulation fail. I guess the ultimate solution would be a delauney triangulator with extremely high numeric stability, however this is difficult to achieve.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1009 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2011-05-29 19:16:52 +00:00
parent 73a7fdf200
commit d467b5bb27
1 changed files with 251 additions and 125 deletions

View File

@ -162,6 +162,18 @@ struct ConversionData
std::vector<TempOpening>* collect_openings;
};
// ------------------------------------------------------------------------------------------------
struct FuzzyVectorCompare {
FuzzyVectorCompare(float epsilon) : epsilon(epsilon) {}
bool operator()(const aiVector3D& a, const aiVector3D& b) {
return fabs((a-b).SquareLength()) < epsilon;
}
const float epsilon;
};
// ------------------------------------------------------------------------------------------------
// Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
// ------------------------------------------------------------------------------------------------
@ -189,14 +201,20 @@ struct TempMesh
mesh->mNumFaces = static_cast<unsigned int>(vertcnt.size());
mesh->mFaces = new aiFace[mesh->mNumFaces];
for(unsigned int i = 0, acc = 0; i < mesh->mNumFaces; ++i) {
for(unsigned int i = 0,n=0, acc = 0; i < mesh->mNumFaces; ++n) {
aiFace& f = mesh->mFaces[i];
if (!vertcnt[n]) {
--mesh->mNumFaces;
continue;
}
f.mNumIndices = vertcnt[i];
f.mNumIndices = vertcnt[n];
f.mIndices = new unsigned int[f.mNumIndices];
for(unsigned int a = 0; a < f.mNumIndices; ++a) {
f.mIndices[a] = acc++;
}
++i;
}
return mesh.release();
@ -227,6 +245,66 @@ struct TempMesh
vertcnt.insert(vertcnt.end(),other.vertcnt.begin(),other.vertcnt.end());
}
// ------------------------------------------------------------------------------
void RemoveAdjacentDuplicates() {
bool drop = false;
std::vector<aiVector3D>::iterator base = verts.begin();
BOOST_FOREACH(unsigned int& cnt, vertcnt) {
if (cnt < 2){
base += cnt;
continue;
}
aiVector3D vmin,vmax;
ArrayBounds(&*base, cnt ,vmin,vmax);
const float epsilon = (vmax-vmin).SquareLength() / 1e9f, dotepsilon = 1e-7;
//// look for vertices that lie directly on the line between their predecessor and their
//// successor and replace them with either of them.
//for(size_t i = 0; i < cnt; ++i) {
// aiVector3D& v1 = *(base+i), &v0 = *(base+(i?i-1:cnt-1)), &v2 = *(base+(i+1)%cnt);
// const aiVector3D& d0 = (v1-v0), &d1 = (v2-v1);
// const float l0 = d0.SquareLength(), l1 = d1.SquareLength();
// if (!l0 || !l1) {
// continue;
// }
// const float d = (d0/sqrt(l0))*(d1/sqrt(l1));
// if ( d >= 1.f-dotepsilon ) {
// v1 = v0;
// }
// else if ( d0*d1 < -1.f+dotepsilon ) {
// v2 = v1;
// continue;
// }
//}
// drop any identical, adjacent vertices. this pass will collect the dropouts
// of the previous pass as a side-effect.
FuzzyVectorCompare fz(epsilon);
std::vector<aiVector3D>::iterator end = base+cnt, e = std::unique( base, end, fz );
if (e != end) {
cnt -= static_cast<unsigned int>(std::distance(e, end));
verts.erase(e,end);
drop = true;
}
// check front and back vertices for this polygon
if (cnt > 1 && fz(*base,*(base+cnt-1))) {
verts.erase(base+ --cnt);
drop = true;
}
// removing adjacent duplicates shouldn't erase everything :-)
ai_assert(cnt>0);
base += cnt;
}
if(drop) {
IFCImporter::LogDebug("removed duplicate vertices");
}
}
};
@ -724,39 +802,98 @@ bool ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, Conversion
meshout.verts.push_back(tmp);
++cnt;
}
meshout.vertcnt.push_back(cnt);
// zero- or one- vertex polyloops simply ignored
if (cnt >= 1) {
meshout.vertcnt.push_back(cnt);
if (meshout.vertcnt.back() > 1) {
return true;
}
if (cnt==1) {
if (meshout.vertcnt.back()==1) {
meshout.vertcnt.pop_back();
meshout.verts.pop_back();
}
return false;
}
// ------------------------------------------------------------------------------------------------
void FixupFaceOrientation(TempMesh& result)
void ComputePolygonNormals(const TempMesh& meshout, std::vector<aiVector3D>& normals, bool normalize = true, size_t ofs = 0)
{
aiVector3D vavg;
BOOST_FOREACH(aiVector3D& v, result.verts) {
vavg += v;
size_t max_vcount = 0;
std::vector<unsigned int>::const_iterator begin=meshout.vertcnt.begin()+ofs, end=meshout.vertcnt.end(), iit;
for(iit = begin; iit != end; ++iit) {
max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
}
// fix face orientation - try at least.
vavg /= static_cast<float>( result.verts.size() );
std::vector<float> temp((max_vcount+2)*4);
normals.reserve( normals.size() + meshout.vertcnt.size()-ofs );
size_t c = 0;
// `NewellNormal()` currently has a relatively strange interface and need to
// re-structure things a bit to meet them.
size_t vidx = std::accumulate(meshout.vertcnt.begin(),begin,0);
for(iit = begin; iit != end; vidx += *iit++) {
if (!*iit) {
normals.push_back(aiVector3D());
continue;
}
for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
const aiVector3D& v = meshout.verts[vidx+vofs];
temp[cnt++] = v.x;
temp[cnt++] = v.y;
temp[cnt++] = v.z;
#ifdef _DEBUG
temp[cnt] = std::numeric_limits<float>::quiet_NaN();
#endif
++cnt;
}
normals.push_back(aiVector3D());
NewellNormal<4,4,4>(normals.back(),*iit,&temp[0],&temp[1],&temp[2]);
}
if(normalize) {
BOOST_FOREACH(aiVector3D& n, normals) {
n.Normalize();
}
}
}
// ------------------------------------------------------------------------------------------------
// Compute the normal of the last polygon in the given mesh
aiVector3D ComputePolygonNormal(const TempMesh& inmesh, bool normalize = true)
{
size_t total = inmesh.vertcnt.back(), vidx = inmesh.verts.size() - total;
std::vector<float> temp((total+2)*3);
for(size_t vofs = 0, cnt = 0; vofs < total; ++vofs) {
const aiVector3D& v = inmesh.verts[vidx+vofs];
temp[cnt++] = v.x;
temp[cnt++] = v.y;
temp[cnt++] = v.z;
}
aiVector3D nor;
NewellNormal<3,3,3>(nor,total,&temp[0],&temp[1],&temp[2]);
return normalize ? nor.Normalize() : nor;
}
// ------------------------------------------------------------------------------------------------
void FixupFaceOrientation(TempMesh& result)
{
const aiVector3D vavg = result.Center();
std::vector<aiVector3D> normals;
ComputePolygonNormals(result,normals);
size_t c = 0, ofs = 0;
BOOST_FOREACH(unsigned int cnt, result.vertcnt) {
if (cnt>2){
const aiVector3D& thisvert = result.verts[c];
const aiVector3D normal((thisvert-result.verts[c+1])^(thisvert-result.verts[c+2]));
if (normal*(thisvert-vavg) < 0) {
if (normals[ofs]*(thisvert-vavg) < 0) {
std::reverse(result.verts.begin()+c,result.verts.begin()+cnt+c);
}
}
c += cnt;
++ofs;
}
}
@ -796,8 +933,11 @@ void RecursiveMergeBoundaries(TempMesh& final_result, const TempMesh& in, const
ai_assert(best_outer != boundary.verts.size());
// now that we collected all vertex connections to be added, build the output polygon
size_t cnt = boundary.verts.size();
const size_t cnt = boundary.verts.size() + *best_iit+2;
out.verts.reserve(cnt);
for(size_t outer = 0; outer < boundary.verts.size(); ++outer) {
const aiVector3D& o = boundary.verts[outer];
out.verts.push_back(o);
@ -821,12 +961,12 @@ void RecursiveMergeBoundaries(TempMesh& final_result, const TempMesh& in, const
// also append a copy of the initial insertion point to be able to continue the outer polygon
out.verts.push_back(o);
cnt += *best_iit+2;
}
}
out.vertcnt.push_back(cnt);
ai_assert(out.verts.size() == cnt);
if (in.vertcnt.size() > 1) {
if (in.vertcnt.size()-std::count(begin,end,0) > 1) {
// Recursively apply the same algorithm if there are more boundaries to merge. The
// current implementation is relatively inefficient, though.
@ -836,7 +976,7 @@ void RecursiveMergeBoundaries(TempMesh& final_result, const TempMesh& in, const
const size_t dist = std::distance(begin, best_iit);
TempMesh remaining = in;
remaining.vertcnt.erase(remaining.vertcnt.begin() + dist);
remaining.verts.erase(remaining.verts.begin()+best_ofs,remaining.verts.begin()+best_ofs+*best_iit);
remaining.verts.erase(remaining.verts.begin()+best_vidx_start,remaining.verts.begin()+best_vidx_start+*best_iit);
normals.erase(normals.begin() + dist);
RecursiveMergeBoundaries(temp,remaining,out,normals,nor_boundary);
@ -847,94 +987,85 @@ void RecursiveMergeBoundaries(TempMesh& final_result, const TempMesh& in, const
}
// ------------------------------------------------------------------------------------------------
// Note: meshout may be modified even though the merged polygon is copied to `result`!
void MergePolygonBoundaries(TempMesh& result, TempMesh& meshout, size_t master_bounds = -1)
void MergePolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t master_bounds = -1)
{
// standard case - only one boundary, just copy it to the result vector
result.vertcnt.reserve(meshout.vertcnt.size()+result.vertcnt.size());
if (meshout.vertcnt.size() <= 1) {
result.verts.reserve(meshout.verts.size()+result.verts.size());
std::copy(meshout.verts.begin(),meshout.verts.end(),std::back_inserter(result.verts));
std::copy(meshout.vertcnt.begin(),meshout.vertcnt.end(),std::back_inserter(result.vertcnt));
if (inmesh.vertcnt.size() <= 1) {
result.Append(inmesh);
return;
}
result.vertcnt.reserve(inmesh.vertcnt.size()+result.vertcnt.size());
// XXX get rid of the extra copy if possible
TempMesh meshout = inmesh;
// handle polygons with holes. Our built in triangulation won't handle them as is, but
// the ear cutting algorithm is solid enough to deal with them if we join the inner
// holes with the outer boundaries by dummy connections.
IFCImporter::LogDebug("fixing polygon with holes for triangulation via ear-cutting");
std::vector<unsigned int>::iterator outer_polygon = meshout.vertcnt.end(), begin=meshout.vertcnt.begin(), end=outer_polygon, iit;
// each hole results in two extra vertices
result.verts.reserve(meshout.verts.size()+meshout.vertcnt.size()*2+result.verts.size());
size_t outer_polygon_start = 0;
// compute proper normals for all polygons
size_t max_vcount = 0;
std::vector<unsigned int>::iterator outer_polygon = meshout.vertcnt.end(), begin=meshout.vertcnt.begin(), end=outer_polygon, iit;
for(iit = begin; iit != meshout.vertcnt.end(); ++iit) {
ai_assert(*iit);
max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
}
std::vector<float> temp((max_vcount+2)*4);
// do not normalize 'normals', we need the original length for computing the polygon area
std::vector<aiVector3D> normals;
normals.reserve( meshout.vertcnt.size() );
ComputePolygonNormals(meshout,normals,false);
// `NewellNormal()` currently has a relatively strange interface and need to
// re-structure things a bit to meet them.
size_t vidx = 0;
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
const aiVector3D& v = meshout.verts[vidx+vofs];
temp[cnt++] = v.x;
temp[cnt++] = v.y;
temp[cnt++] = v.z;
#ifdef _DEBUG
temp[cnt] = std::numeric_limits<float>::quiet_NaN();
#endif
++cnt;
}
normals.push_back(aiVector3D());
NewellNormal<4,4,4>(normals.back(),*iit,&temp[0],&temp[1],&temp[2]);
}
// see if one of the polygons is a IfcFaceOuterBound (in which case master_bounds is not `-1`).
// see if one of the polygons is a IfcFaceOuterBound (in which case `master_bounds` is its index).
// sadly we can't rely on it, the docs say 'At most one of the bounds shall be of the type IfcFaceOuterBound'
float area_outer_polygon = 1e-10f;
if (master_bounds != -1) {
outer_polygon = begin + master_bounds;
outer_polygon_start = std::accumulate(begin,outer_polygon,0);
BOOST_FOREACH(aiVector3D& n, normals) {
n.Normalize();
}
area_outer_polygon = normals[master_bounds].SquareLength();
}
else {
float area_outer_polygon = 1e-10f;
size_t vidx = 0;
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
// find the polygon with the largest area, it must be the outer bound.
aiVector3D& n = normals[std::distance(begin,iit)];
const float area = n.Length();
const float area = n.SquareLength();
if (area > area_outer_polygon) {
area_outer_polygon = area;
outer_polygon = iit;
outer_polygon_start = vidx;
}
n /= area;
}
}
ai_assert(outer_polygon != meshout.vertcnt.end());
std::vector<aiVector3D>& in = meshout.verts;
// skip over extremely small boundaries - this is a workaround to fix cases
// in which the number of holes is so extremely large that the
// triangulation code fails.
size_t vidx = 0, removed = 0, index = 0;
const float treshold = area_outer_polygon * 0.000001f;
for(iit = begin; iit != end ;++index) {
const float sqlen = normals[index].SquareLength();
if (sqlen < treshold) {
std::vector<aiVector3D>::iterator inbase = in.begin()+vidx;
in.erase(inbase,inbase+*iit);
*iit++ = 0;
outer_polygon_start -= outer_polygon_start>vidx ? *iit : 0;
++removed;
}
else {
normals[index] /= sqrt(sqlen);
vidx += *iit++;
}
}
// see if one or more of the hole has a face that lies directly on an outer bound.
// this happens for doors, for example.
vidx = 0;
for(iit = begin; ; vidx += *iit++) {
next_loop:
if (iit == meshout.vertcnt.end()) {
if (iit == end) {
break;
}
if (iit == outer_polygon) {
@ -946,10 +1077,10 @@ next_loop:
continue;
}
const size_t next = (vofs+1)%*iit;
const aiVector3D& v = in[vidx+vofs], vnext = in[vidx+next],vd = (vnext-v).Normalize();
const aiVector3D& v = in[vidx+vofs], &vnext = in[vidx+next],&vd = (vnext-v).Normalize();
for(size_t outer = 0; outer < *outer_polygon; ++outer) {
const aiVector3D& o = in[outer_polygon_start+outer], onext = in[outer_polygon_start+(outer+1)%*outer_polygon],od = (onext-o).Normalize();
const aiVector3D& o = in[outer_polygon_start+outer], &onext = in[outer_polygon_start+(outer+1)%*outer_polygon], &od = (onext-o).Normalize();
if (fabs(vd * od) > 1.f-1e-6f && (onext-v).Normalize() * vd > 1.f-1e-6f && (onext-v)*(o-v) < 0) {
IFCImporter::LogDebug("got an inner hole that lies partly on the outer polygonal boundary, merging them to a single contour");
@ -972,12 +1103,18 @@ next_loop:
*outer_polygon += tmp.size();
*iit++ = 0;
++removed;
goto next_loop;
}
}
}
}
if ( meshout.vertcnt.size() - removed <= 1) {
result.Append(meshout);
return;
}
// extract the outer boundary and move it to a separate mesh
TempMesh boundary;
boundary.vertcnt.resize(1,*outer_polygon);
@ -1002,12 +1139,12 @@ void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& res
{
BOOST_FOREACH(const IFC::IfcFace& face, fset.CfsFaces) {
size_t ob = -1, cnt = 0;
//TempMesh meshout;
TempMesh meshout;
BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) {
// XXX implement proper merging for polygonal loops
if(const IFC::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) {
if(ProcessPolyloop(*polyloop, result,conv)) {
if(ProcessPolyloop(*polyloop, meshout,conv)) {
if(bound.ToPtr<IFC::IfcFaceOuterBound>()) {
ob = cnt;
}
@ -1021,17 +1158,15 @@ void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& res
/*if(!IsTrue(bound.Orientation)) {
size_t c = 0;
BOOST_FOREACH(unsigned int& i, meshout.vertcnt) {
std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + c);
BOOST_FOREACH(unsigned int& c, meshout.vertcnt) {
std::reverse(result.verts.begin() + cnt,result.verts.begin() + cnt + c);
cnt += c;
}
}*/
}
//MergePolygonBoundaries(result,meshout,ob);
MergePolygonBoundaries(result,meshout);
}
FixupFaceOrientation(result);
}
// ------------------------------------------------------------------------------------------------
@ -1043,6 +1178,7 @@ void ProcessPolyLine(const IFC::IfcPolyline& def, TempMesh& meshout, ConversionD
ConvertCartesianPoint(t,cp);
meshout.verts.push_back(t);
}
meshout.vertcnt.push_back(meshout.verts.size());
}
// ------------------------------------------------------------------------------------------------
@ -1061,11 +1197,7 @@ bool ProcessCurve(const IFC::IfcCurve& curve, TempMesh& meshout, ConversionData
// ------------------------------------------------------------------------------------------------
void ProcessClosedProfile(const IFC::IfcArbitraryClosedProfileDef& def, TempMesh& meshout, ConversionData& conv)
{
if(ProcessCurve(def.OuterCurve,meshout,conv)) {
if(meshout.verts.size()>2 && meshout.verts.front() == meshout.verts.back()) {
meshout.verts.pop_back(); // duplicate element, first==last
}
}
ProcessCurve(def.OuterCurve,meshout,conv);
}
// ------------------------------------------------------------------------------------------------
@ -1129,6 +1261,10 @@ bool ProcessProfile(const IFC::IfcProfileDef& prof, TempMesh& meshout, Conversio
IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is " + prof.GetClassName());
return false;
}
meshout.RemoveAdjacentDuplicates();
if (!meshout.vertcnt.size() || meshout.vertcnt.front() <= 1) {
return false;
}
return true;
}
@ -1216,8 +1352,6 @@ void ProcessRevolvedAreaSolid(const IFC::IfcRevolvedAreaSolid& solid, TempMesh&
ConvertAxisPlacement(trafo, solid.Position,conv);
result.Transform(trafo);
FixupFaceOrientation(result);
IFCImporter::LogDebug("generate mesh procedurally by radial extrusion (IfcRevolvedAreaSolid)");
}
@ -1229,8 +1363,8 @@ bool TryAddOpenings(const std::vector<TempOpening>& openings,const std::vector<a
const size_t s = out.size();
const aiVector3D any_point = out[s-4];
const aiVector3D nor = ((out[s-3]-any_point)^(out[s-2]-any_point)).Normalize();
const aiVector3D any_point = out[s-1];
const aiVector3D nor = ComputePolygonNormal(curmesh); ;
bool got_openings = false;
TempMesh res;
@ -1527,7 +1661,7 @@ void InsertWindowContours(const std::vector< BoundingBox >& bbs,const std::vecto
}
// ------------------------------------------------------------------------------------------------
bool TryAddOpenings_Triangulate(const std::vector<TempOpening>& openings,const std::vector<aiVector3D>& nors, TempMesh& curmesh)
bool TryAddOpenings_Quadrulate(const std::vector<TempOpening>& openings,const std::vector<aiVector3D>& nors, TempMesh& curmesh)
{
std::vector<aiVector3D>& out = curmesh.verts;
@ -1716,7 +1850,7 @@ void ProcessExtrudedAreaSolid(const IFC::IfcExtrudedAreaSolid& solid, TempMesh&
std::vector<aiVector3D>& out = curmesh.verts;
bool (* const gen_openings)(const std::vector<TempOpening>&,const std::vector<aiVector3D>&, TempMesh&) = conv.settings.useCustomTriangulation
? &TryAddOpenings_Triangulate
? &TryAddOpenings_Quadrulate
: &TryAddOpenings;
size_t sides_with_openings = 0;
@ -1749,7 +1883,7 @@ void ProcessExtrudedAreaSolid(const IFC::IfcExtrudedAreaSolid& solid, TempMesh&
}
curmesh.vertcnt.push_back(size);
if(conv.apply_openings) {
if(conv.apply_openings && size > 2) {
// XXX here we are forced to use the un-triangulated version of TryAddOpening, with
// all the problems it causes. The reason is that vertical walls (ehm, floors)
// can have an arbitrary outer shape, so the usual approach of projecting
@ -1789,7 +1923,6 @@ void ProcessExtrudedAreaSolid(const IFC::IfcExtrudedAreaSolid& solid, TempMesh&
IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
}
FixupFaceOrientation(result);
IFCImporter::LogDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)");
}
@ -1823,17 +1956,6 @@ void ProcessSweptAreaSolid(const IFC::IfcSweptAreaSolid& swept, TempMesh& meshou
}
}
// ------------------------------------------------------------------------------------------------
struct FuzzyVectorCompare {
FuzzyVectorCompare(float epsilon) : epsilon(epsilon) {}
bool operator()(const aiVector3D& a, const aiVector3D& b) {
return fabs((a-b).SquareLength()) < epsilon;
}
const float epsilon;
};
// ------------------------------------------------------------------------------------------------
void ProcessBoolean(const IFC::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv)
{
@ -2088,6 +2210,9 @@ bool ProcessTopologicalItem(const IFC::IfcTopologicalRepresentationItem& topo, s
return false;
}
meshtmp.RemoveAdjacentDuplicates();
FixupFaceOrientation(meshtmp);
aiMesh* const mesh = meshtmp.ToMesh();
if(mesh) {
mesh->mMaterialIndex = ProcessMaterials(topo,conv);
@ -2138,8 +2263,10 @@ bool ProcessGeometricItem(const IFC::IfcGeometricRepresentationItem& geo, std::v
return false;
}
aiMesh* const mesh = meshtmp.ToMesh();
meshtmp.RemoveAdjacentDuplicates();
FixupFaceOrientation(meshtmp);
aiMesh* const mesh = meshtmp.ToMesh();
if(mesh) {
mesh->mMaterialIndex = ProcessMaterials(geo,conv);
mesh_indices.push_back(conv.meshes.size());
@ -2247,17 +2374,6 @@ void ProcessMappedItem(const IFC::IfcMappedItem& mapped, aiNode* nd_src, std::ve
std::auto_ptr<aiNode> nd(new aiNode());
nd->mName.Set("IfcMappedItem");
std::vector<unsigned int> meshes;
const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0;
const IFC::IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
BOOST_FOREACH(const IFC::IfcRepresentationItem& item, repr.Items) {
if(!ProcessRepresentationItem(item,meshes,conv)) {
IFCImporter::LogWarn("skipping unknown mapped entity, type is " + item.GetClassName());
}
}
AssignAddedMeshes(meshes,nd.get(),conv);
// handle the cartesian operator
aiMatrix4x4 m;
ConvertTransformOperator(m, *mapped.MappingTarget);
@ -2265,28 +2381,39 @@ void ProcessMappedItem(const IFC::IfcMappedItem& mapped, aiNode* nd_src, std::ve
aiMatrix4x4 msrc;
ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
aiMatrix4x4 minv = msrc;
minv.Inverse();
minv = m*msrc;
if (conv.collect_openings) {
// if this pass serves us only to collect opening geometry,
// make sure we transform the TempMesh's which we need to
// preserve as well.
if(const size_t diff = conv.collect_openings->size() - old_openings) {
for(size_t i = 0; i < diff; ++i) {
(*conv.collect_openings)[old_openings+i].Transform(minv);
}
}
}
msrc = m*msrc;
std::vector<unsigned int> meshes;
const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0;
if (conv.apply_openings) {
aiMatrix4x4 minv = msrc;
minv.Inverse();
BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
open.Transform(minv);
}
}
nd->mTransformation = nd_src->mTransformation * minv;
const IFC::IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
BOOST_FOREACH(const IFC::IfcRepresentationItem& item, repr.Items) {
if(!ProcessRepresentationItem(item,meshes,conv)) {
IFCImporter::LogWarn("skipping unknown mapped entity, type is " + item.GetClassName());
}
}
AssignAddedMeshes(meshes,nd.get(),conv);
if (conv.collect_openings) {
// if this pass serves us only to collect opening geometry,
// make sure we transform the TempMesh's which we need to
// preserve as well.
if(const size_t diff = conv.collect_openings->size() - old_openings) {
for(size_t i = 0; i < diff; ++i) {
(*conv.collect_openings)[old_openings+i].Transform(msrc);
}
}
}
nd->mTransformation = nd_src->mTransformation * msrc;
subnodes_src.push_back(nd.release());
}
@ -2430,7 +2557,6 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IFC::IfcProduct& el, Conve
conv.apply_openings = &openings;
}
ProcessProductRepresentation(el,nd.get(),subnodes,conv);
conv.apply_openings = conv.collect_openings = NULL;