# 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-9d2fd5bffc1fpull/1/head
parent
73a7fdf200
commit
d467b5bb27
|
@ -162,6 +162,18 @@ struct ConversionData
|
||||||
std::vector<TempOpening>* collect_openings;
|
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.
|
// 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->mNumFaces = static_cast<unsigned int>(vertcnt.size());
|
||||||
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
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];
|
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];
|
f.mIndices = new unsigned int[f.mNumIndices];
|
||||||
for(unsigned int a = 0; a < f.mNumIndices; ++a) {
|
for(unsigned int a = 0; a < f.mNumIndices; ++a) {
|
||||||
f.mIndices[a] = acc++;
|
f.mIndices[a] = acc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mesh.release();
|
return mesh.release();
|
||||||
|
@ -227,6 +245,66 @@ struct TempMesh
|
||||||
vertcnt.insert(vertcnt.end(),other.vertcnt.begin(),other.vertcnt.end());
|
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);
|
meshout.verts.push_back(tmp);
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
// zero- or one- vertex polyloops simply ignored
|
|
||||||
if (cnt >= 1) {
|
|
||||||
meshout.vertcnt.push_back(cnt);
|
meshout.vertcnt.push_back(cnt);
|
||||||
|
|
||||||
|
// zero- or one- vertex polyloops simply ignored
|
||||||
|
if (meshout.vertcnt.back() > 1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cnt==1) {
|
if (meshout.vertcnt.back()==1) {
|
||||||
meshout.vertcnt.pop_back();
|
meshout.vertcnt.pop_back();
|
||||||
|
meshout.verts.pop_back();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void FixupFaceOrientation(TempMesh& result)
|
void ComputePolygonNormals(const TempMesh& meshout, std::vector<aiVector3D>& normals, bool normalize = true, size_t ofs = 0)
|
||||||
{
|
{
|
||||||
aiVector3D vavg;
|
size_t max_vcount = 0;
|
||||||
BOOST_FOREACH(aiVector3D& v, result.verts) {
|
std::vector<unsigned int>::const_iterator begin=meshout.vertcnt.begin()+ofs, end=meshout.vertcnt.end(), iit;
|
||||||
vavg += v;
|
for(iit = begin; iit != end; ++iit) {
|
||||||
|
max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix face orientation - try at least.
|
std::vector<float> temp((max_vcount+2)*4);
|
||||||
vavg /= static_cast<float>( result.verts.size() );
|
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) {
|
BOOST_FOREACH(unsigned int cnt, result.vertcnt) {
|
||||||
if (cnt>2){
|
if (cnt>2){
|
||||||
const aiVector3D& thisvert = result.verts[c];
|
const aiVector3D& thisvert = result.verts[c];
|
||||||
const aiVector3D normal((thisvert-result.verts[c+1])^(thisvert-result.verts[c+2]));
|
if (normals[ofs]*(thisvert-vavg) < 0) {
|
||||||
if (normal*(thisvert-vavg) < 0) {
|
|
||||||
std::reverse(result.verts.begin()+c,result.verts.begin()+cnt+c);
|
std::reverse(result.verts.begin()+c,result.verts.begin()+cnt+c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c += cnt;
|
c += cnt;
|
||||||
|
++ofs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,8 +933,11 @@ void RecursiveMergeBoundaries(TempMesh& final_result, const TempMesh& in, const
|
||||||
|
|
||||||
ai_assert(best_outer != boundary.verts.size());
|
ai_assert(best_outer != boundary.verts.size());
|
||||||
|
|
||||||
|
|
||||||
// now that we collected all vertex connections to be added, build the output polygon
|
// 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) {
|
for(size_t outer = 0; outer < boundary.verts.size(); ++outer) {
|
||||||
const aiVector3D& o = boundary.verts[outer];
|
const aiVector3D& o = boundary.verts[outer];
|
||||||
out.verts.push_back(o);
|
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
|
// also append a copy of the initial insertion point to be able to continue the outer polygon
|
||||||
out.verts.push_back(o);
|
out.verts.push_back(o);
|
||||||
cnt += *best_iit+2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.vertcnt.push_back(cnt);
|
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
|
// Recursively apply the same algorithm if there are more boundaries to merge. The
|
||||||
// current implementation is relatively inefficient, though.
|
// 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);
|
const size_t dist = std::distance(begin, best_iit);
|
||||||
TempMesh remaining = in;
|
TempMesh remaining = in;
|
||||||
remaining.vertcnt.erase(remaining.vertcnt.begin() + dist);
|
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);
|
normals.erase(normals.begin() + dist);
|
||||||
RecursiveMergeBoundaries(temp,remaining,out,normals,nor_boundary);
|
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, const TempMesh& inmesh, size_t master_bounds = -1)
|
||||||
void MergePolygonBoundaries(TempMesh& result, TempMesh& meshout, size_t master_bounds = -1)
|
|
||||||
{
|
{
|
||||||
// standard case - only one boundary, just copy it to the result vector
|
// standard case - only one boundary, just copy it to the result vector
|
||||||
result.vertcnt.reserve(meshout.vertcnt.size()+result.vertcnt.size());
|
if (inmesh.vertcnt.size() <= 1) {
|
||||||
if (meshout.vertcnt.size() <= 1) {
|
result.Append(inmesh);
|
||||||
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));
|
|
||||||
return;
|
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
|
// 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
|
// the ear cutting algorithm is solid enough to deal with them if we join the inner
|
||||||
// holes with the outer boundaries by dummy connections.
|
// holes with the outer boundaries by dummy connections.
|
||||||
IFCImporter::LogDebug("fixing polygon with holes for triangulation via ear-cutting");
|
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
|
// each hole results in two extra vertices
|
||||||
result.verts.reserve(meshout.verts.size()+meshout.vertcnt.size()*2+result.verts.size());
|
result.verts.reserve(meshout.verts.size()+meshout.vertcnt.size()*2+result.verts.size());
|
||||||
size_t outer_polygon_start = 0;
|
size_t outer_polygon_start = 0;
|
||||||
|
|
||||||
// compute proper normals for all polygons
|
// do not normalize 'normals', we need the original length for computing the polygon area
|
||||||
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);
|
|
||||||
std::vector<aiVector3D> normals;
|
std::vector<aiVector3D> normals;
|
||||||
normals.reserve( meshout.vertcnt.size() );
|
ComputePolygonNormals(meshout,normals,false);
|
||||||
|
|
||||||
// `NewellNormal()` currently has a relatively strange interface and need to
|
// see if one of the polygons is a IfcFaceOuterBound (in which case `master_bounds` is its index).
|
||||||
// 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`).
|
|
||||||
// sadly we can't rely on it, the docs say 'At most one of the bounds shall be of the type IfcFaceOuterBound'
|
// 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) {
|
if (master_bounds != -1) {
|
||||||
outer_polygon = begin + master_bounds;
|
outer_polygon = begin + master_bounds;
|
||||||
outer_polygon_start = std::accumulate(begin,outer_polygon,0);
|
outer_polygon_start = std::accumulate(begin,outer_polygon,0);
|
||||||
BOOST_FOREACH(aiVector3D& n, normals) {
|
area_outer_polygon = normals[master_bounds].SquareLength();
|
||||||
n.Normalize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float area_outer_polygon = 1e-10f;
|
|
||||||
size_t vidx = 0;
|
size_t vidx = 0;
|
||||||
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
|
for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
|
||||||
// find the polygon with the largest area, it must be the outer bound.
|
// find the polygon with the largest area, it must be the outer bound.
|
||||||
aiVector3D& n = normals[std::distance(begin,iit)];
|
aiVector3D& n = normals[std::distance(begin,iit)];
|
||||||
const float area = n.Length();
|
const float area = n.SquareLength();
|
||||||
if (area > area_outer_polygon) {
|
if (area > area_outer_polygon) {
|
||||||
area_outer_polygon = area;
|
area_outer_polygon = area;
|
||||||
outer_polygon = iit;
|
outer_polygon = iit;
|
||||||
outer_polygon_start = vidx;
|
outer_polygon_start = vidx;
|
||||||
}
|
}
|
||||||
|
|
||||||
n /= area;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ai_assert(outer_polygon != meshout.vertcnt.end());
|
ai_assert(outer_polygon != meshout.vertcnt.end());
|
||||||
std::vector<aiVector3D>& in = meshout.verts;
|
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.
|
// see if one or more of the hole has a face that lies directly on an outer bound.
|
||||||
// this happens for doors, for example.
|
// this happens for doors, for example.
|
||||||
vidx = 0;
|
vidx = 0;
|
||||||
for(iit = begin; ; vidx += *iit++) {
|
for(iit = begin; ; vidx += *iit++) {
|
||||||
next_loop:
|
next_loop:
|
||||||
if (iit == meshout.vertcnt.end()) {
|
if (iit == end) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (iit == outer_polygon) {
|
if (iit == outer_polygon) {
|
||||||
|
@ -946,10 +1077,10 @@ next_loop:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const size_t next = (vofs+1)%*iit;
|
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) {
|
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) {
|
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");
|
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();
|
*outer_polygon += tmp.size();
|
||||||
*iit++ = 0;
|
*iit++ = 0;
|
||||||
|
++removed;
|
||||||
goto next_loop;
|
goto next_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( meshout.vertcnt.size() - removed <= 1) {
|
||||||
|
result.Append(meshout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// extract the outer boundary and move it to a separate mesh
|
// extract the outer boundary and move it to a separate mesh
|
||||||
TempMesh boundary;
|
TempMesh boundary;
|
||||||
boundary.vertcnt.resize(1,*outer_polygon);
|
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) {
|
BOOST_FOREACH(const IFC::IfcFace& face, fset.CfsFaces) {
|
||||||
size_t ob = -1, cnt = 0;
|
size_t ob = -1, cnt = 0;
|
||||||
//TempMesh meshout;
|
TempMesh meshout;
|
||||||
BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) {
|
BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) {
|
||||||
|
|
||||||
// XXX implement proper merging for polygonal loops
|
// XXX implement proper merging for polygonal loops
|
||||||
if(const IFC::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) {
|
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>()) {
|
if(bound.ToPtr<IFC::IfcFaceOuterBound>()) {
|
||||||
ob = cnt;
|
ob = cnt;
|
||||||
}
|
}
|
||||||
|
@ -1021,17 +1158,15 @@ void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& res
|
||||||
|
|
||||||
/*if(!IsTrue(bound.Orientation)) {
|
/*if(!IsTrue(bound.Orientation)) {
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
BOOST_FOREACH(unsigned int& i, meshout.vertcnt) {
|
BOOST_FOREACH(unsigned int& c, meshout.vertcnt) {
|
||||||
std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + c);
|
std::reverse(result.verts.begin() + cnt,result.verts.begin() + cnt + c);
|
||||||
cnt += c;
|
cnt += c;
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
MergePolygonBoundaries(result,meshout);
|
||||||
//MergePolygonBoundaries(result,meshout,ob);
|
|
||||||
}
|
}
|
||||||
FixupFaceOrientation(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -1043,6 +1178,7 @@ void ProcessPolyLine(const IFC::IfcPolyline& def, TempMesh& meshout, ConversionD
|
||||||
ConvertCartesianPoint(t,cp);
|
ConvertCartesianPoint(t,cp);
|
||||||
meshout.verts.push_back(t);
|
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)
|
void ProcessClosedProfile(const IFC::IfcArbitraryClosedProfileDef& def, TempMesh& meshout, ConversionData& conv)
|
||||||
{
|
{
|
||||||
if(ProcessCurve(def.OuterCurve,meshout,conv)) {
|
ProcessCurve(def.OuterCurve,meshout,conv);
|
||||||
if(meshout.verts.size()>2 && meshout.verts.front() == meshout.verts.back()) {
|
|
||||||
meshout.verts.pop_back(); // duplicate element, first==last
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -1129,6 +1261,10 @@ bool ProcessProfile(const IFC::IfcProfileDef& prof, TempMesh& meshout, Conversio
|
||||||
IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is " + prof.GetClassName());
|
IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is " + prof.GetClassName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
meshout.RemoveAdjacentDuplicates();
|
||||||
|
if (!meshout.vertcnt.size() || meshout.vertcnt.front() <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1216,8 +1352,6 @@ void ProcessRevolvedAreaSolid(const IFC::IfcRevolvedAreaSolid& solid, TempMesh&
|
||||||
ConvertAxisPlacement(trafo, solid.Position,conv);
|
ConvertAxisPlacement(trafo, solid.Position,conv);
|
||||||
|
|
||||||
result.Transform(trafo);
|
result.Transform(trafo);
|
||||||
|
|
||||||
FixupFaceOrientation(result);
|
|
||||||
IFCImporter::LogDebug("generate mesh procedurally by radial extrusion (IfcRevolvedAreaSolid)");
|
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 size_t s = out.size();
|
||||||
|
|
||||||
const aiVector3D any_point = out[s-4];
|
const aiVector3D any_point = out[s-1];
|
||||||
const aiVector3D nor = ((out[s-3]-any_point)^(out[s-2]-any_point)).Normalize();
|
const aiVector3D nor = ComputePolygonNormal(curmesh); ;
|
||||||
|
|
||||||
bool got_openings = false;
|
bool got_openings = false;
|
||||||
TempMesh res;
|
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;
|
std::vector<aiVector3D>& out = curmesh.verts;
|
||||||
|
|
||||||
|
@ -1716,7 +1850,7 @@ void ProcessExtrudedAreaSolid(const IFC::IfcExtrudedAreaSolid& solid, TempMesh&
|
||||||
std::vector<aiVector3D>& out = curmesh.verts;
|
std::vector<aiVector3D>& out = curmesh.verts;
|
||||||
|
|
||||||
bool (* const gen_openings)(const std::vector<TempOpening>&,const std::vector<aiVector3D>&, TempMesh&) = conv.settings.useCustomTriangulation
|
bool (* const gen_openings)(const std::vector<TempOpening>&,const std::vector<aiVector3D>&, TempMesh&) = conv.settings.useCustomTriangulation
|
||||||
? &TryAddOpenings_Triangulate
|
? &TryAddOpenings_Quadrulate
|
||||||
: &TryAddOpenings;
|
: &TryAddOpenings;
|
||||||
|
|
||||||
size_t sides_with_openings = 0;
|
size_t sides_with_openings = 0;
|
||||||
|
@ -1749,7 +1883,7 @@ void ProcessExtrudedAreaSolid(const IFC::IfcExtrudedAreaSolid& solid, TempMesh&
|
||||||
}
|
}
|
||||||
|
|
||||||
curmesh.vertcnt.push_back(size);
|
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
|
// 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)
|
// 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
|
// 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");
|
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)");
|
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)
|
void ProcessBoolean(const IFC::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv)
|
||||||
{
|
{
|
||||||
|
@ -2088,6 +2210,9 @@ bool ProcessTopologicalItem(const IFC::IfcTopologicalRepresentationItem& topo, s
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meshtmp.RemoveAdjacentDuplicates();
|
||||||
|
FixupFaceOrientation(meshtmp);
|
||||||
|
|
||||||
aiMesh* const mesh = meshtmp.ToMesh();
|
aiMesh* const mesh = meshtmp.ToMesh();
|
||||||
if(mesh) {
|
if(mesh) {
|
||||||
mesh->mMaterialIndex = ProcessMaterials(topo,conv);
|
mesh->mMaterialIndex = ProcessMaterials(topo,conv);
|
||||||
|
@ -2138,8 +2263,10 @@ bool ProcessGeometricItem(const IFC::IfcGeometricRepresentationItem& geo, std::v
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMesh* const mesh = meshtmp.ToMesh();
|
meshtmp.RemoveAdjacentDuplicates();
|
||||||
|
FixupFaceOrientation(meshtmp);
|
||||||
|
|
||||||
|
aiMesh* const mesh = meshtmp.ToMesh();
|
||||||
if(mesh) {
|
if(mesh) {
|
||||||
mesh->mMaterialIndex = ProcessMaterials(geo,conv);
|
mesh->mMaterialIndex = ProcessMaterials(geo,conv);
|
||||||
mesh_indices.push_back(conv.meshes.size());
|
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());
|
std::auto_ptr<aiNode> nd(new aiNode());
|
||||||
nd->mName.Set("IfcMappedItem");
|
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
|
// handle the cartesian operator
|
||||||
aiMatrix4x4 m;
|
aiMatrix4x4 m;
|
||||||
ConvertTransformOperator(m, *mapped.MappingTarget);
|
ConvertTransformOperator(m, *mapped.MappingTarget);
|
||||||
|
@ -2265,28 +2381,39 @@ void ProcessMappedItem(const IFC::IfcMappedItem& mapped, aiNode* nd_src, std::ve
|
||||||
aiMatrix4x4 msrc;
|
aiMatrix4x4 msrc;
|
||||||
ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
|
ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
|
||||||
|
|
||||||
|
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;
|
aiMatrix4x4 minv = msrc;
|
||||||
minv.Inverse();
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conv.apply_openings) {
|
|
||||||
BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
|
BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
|
||||||
open.Transform(minv);
|
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());
|
subnodes_src.push_back(nd.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2430,7 +2557,6 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IFC::IfcProduct& el, Conve
|
||||||
conv.apply_openings = &openings;
|
conv.apply_openings = &openings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ProcessProductRepresentation(el,nd.get(),subnodes,conv);
|
ProcessProductRepresentation(el,nd.get(),subnodes,conv);
|
||||||
conv.apply_openings = conv.collect_openings = NULL;
|
conv.apply_openings = conv.collect_openings = NULL;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue