- IFC: revamp binary subtraction and opening generation logic, which now supports 90deg rotated opening proxies. The new version adds extra cleanup steps to prepare the data for processing by clipper. It also has a slightly refactored code base.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1305 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/6/merge
aramis_acg 2012-10-16 19:51:00 +00:00
parent fa1016ddc8
commit 11a22b671b
1 changed files with 175 additions and 97 deletions

View File

@ -1143,7 +1143,6 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField&
void InsertWindowContours(const std::vector< BoundingBox >& bbs, void InsertWindowContours(const std::vector< BoundingBox >& bbs,
const std::vector< std::vector<IfcVector2> >& contours, const std::vector< std::vector<IfcVector2> >& contours,
const std::vector<TempOpening>& openings, const std::vector<TempOpening>& openings,
const std::vector<IfcVector3>& nors,
const IfcMatrix3& minv, const IfcMatrix3& minv,
const IfcVector2& scale, const IfcVector2& scale,
const IfcVector2& offset, const IfcVector2& offset,
@ -1272,7 +1271,9 @@ void InsertWindowContours(const std::vector< BoundingBox >& bbs,
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void MergeContours (const std::vector<IfcVector2>& a, const std::vector<IfcVector2>& b, ClipperLib::ExPolygons& out) void MergeWindowContours (const std::vector<IfcVector2>& a,
const std::vector<IfcVector2>& b,
ClipperLib::ExPolygons& out)
{ {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
ClipperLib::Polygon clip; ClipperLib::Polygon clip;
@ -1301,164 +1302,58 @@ void MergeContours (const std::vector<IfcVector2>& a, const std::vector<IfcVecto
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool TryAddOpenings_Quadrulate(const std::vector<TempOpening>& openings,const std::vector<IfcVector3>& nors, TempMesh& curmesh) void CleanupWindowContours(std::vector< std::vector<IfcVector2> >& contours)
{ {
std::vector<IfcVector3>& out = curmesh.verts; std::vector<IfcVector2> scratch;
// Try to derive a solid base plane within the current surface for use as // use polyclipper to clean up window contours as well
// working coordinate system. try {
const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh); BOOST_FOREACH(std::vector<IfcVector2>& contour, contours) {
const IfcMatrix3 minv = IfcMatrix3(m).Inverse(); ClipperLib::Polygon subject;
const IfcVector3& nor = IfcVector3(m.c1, m.c2, m.c3); ClipperLib::Clipper clipper;
ClipperLib::ExPolygons clipped;
IfcFloat coord = -1; BOOST_FOREACH(const IfcVector2& pip, contour) {
subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) ));
}
std::vector<IfcVector2> contour_flat; clipper.AddPolygon(subject,ClipperLib::ptSubject);
contour_flat.reserve(out.size()); clipper.Execute(ClipperLib::ctUnion,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero);
IfcVector2 vmin, vmax; // this should yield only one polygon or something went wrong
MinMaxChooser<IfcVector2>()(vmin, vmax); if (clipped.size() != 1) {
// Move all points into the new coordinate system, collecting min/max verts on the way // empty polygon? drop the contour altogether
BOOST_FOREACH(IfcVector3& x, out) { if(clipped.empty()) {
const IfcVector3 vv = m * x;
// keep Z offset in the plane coordinate system. Ignoring precision issues
// (which are present, of course), this should be the same value for
// all polygon vertices (assuming the polygon is planar).
// XXX this should be guarded, but we somehow need to pick a suitable
// epsilon
// if(coord != -1.0f) {
// assert(fabs(coord - vv.z) < 1e-3f);
// }
coord = vv.z;
vmin = std::min(IfcVector2(vv.x, vv.y), vmin);
vmax = std::max(IfcVector2(vv.x, vv.y), vmax);
contour_flat.push_back(IfcVector2(vv.x,vv.y));
}
// With the current code in DerivePlaneCoordinateSpace,
// vmin,vmax should always be the 0...1 rectangle (+- numeric inaccuracies)
// but here we won't rely on this.
vmax -= vmin;
BOOST_FOREACH(IfcVector2& vv, contour_flat) {
vv.x = (vv.x - vmin.x) / vmax.x;
vv.y = (vv.y - vmin.y) / vmax.y;
}
// project all points into the coordinate system defined by the p+sv*tu plane
// and compute bounding boxes for them
std::vector< BoundingBox > bbs;
std::vector< std::vector<IfcVector2> > contours;
size_t c = 0;
BOOST_FOREACH(const TempOpening& t,openings) {
const IfcVector3& outernor = nors[c++];
const IfcFloat dot = nor * outernor;
if (fabs(dot)<1.f-1e-6f) {
continue;
}
const std::vector<IfcVector3>& va = t.profileMesh->verts;
if(va.size() <= 2) {
continue;
}
IfcVector2 vpmin,vpmax;
MinMaxChooser<IfcVector2>()(vpmin,vpmax);
std::vector<IfcVector2> contour;
BOOST_FOREACH(const IfcVector3& x, t.profileMesh->verts) {
const IfcVector3 v = m * x;
IfcVector2 vv(v.x, v.y);
// rescale
vv.x = (vv.x - vmin.x) / vmax.x;
vv.y = (vv.y - vmin.y) / vmax.y;
vpmin = std::min(vpmin,vv);
vpmax = std::max(vpmax,vv);
contour.push_back(vv);
}
BoundingBox bb = BoundingBox(vpmin,vpmax);
// see if this BB intersects any other, in which case we could not use the Quadrify()
// algorithm and would revert to Poly2Tri only.
for (std::vector<BoundingBox>::iterator it = bbs.begin(); it != bbs.end();) {
const BoundingBox& ibb = *it;
if (ibb.first.x < bb.second.x && ibb.second.x > bb.first.x &&
ibb.first.y < bb.second.y && ibb.second.y > bb.second.x) {
// take these two contours and try to merge them. If they overlap (which
// should not happen, but in fact happens-in-the-real-world [tm] ),
// resume using a single contour and a single bounding box.
const std::vector<IfcVector2>& other = contours[std::distance(bbs.begin(),it)];
ClipperLib::ExPolygons poly;
MergeContours(contour, other, poly);
if (poly.size() > 1) {
IFCImporter::LogWarn("cannot use quadrify algorithm to generate wall openings due to "
"bounding box overlaps, using poly2tri fallback");
return TryAddOpenings_Poly2Tri(openings, nors, curmesh);
}
else if (poly.size() == 0) {
IFCImporter::LogWarn("ignoring duplicate opening");
contour.clear(); contour.clear();
break;
}
else {
IFCImporter::LogDebug("merging oberlapping openings, this should not happen");
contour.clear();
BOOST_FOREACH(const ClipperLib::IntPoint& point, poly[0].outer) {
contour.push_back( IfcVector2( from_int64(point.X), from_int64(point.Y)));
}
bb.first = std::min(bb.first, ibb.first);
bb.second = std::max(bb.second, ibb.second);
contours.erase(contours.begin() + std::distance(bbs.begin(),it));
it = bbs.erase(it);
continue; continue;
} }
// else: take only the first ...
IFCImporter::LogError("error during polygon clipping, window contour is not convex");
} }
++it;
}
if(contour.size()) { scratch.clear();
contours.push_back(contour); BOOST_FOREACH(const ClipperLib::IntPoint& point, clipped[0].outer) {
bbs.push_back(bb); scratch.push_back( IfcVector2(from_int64(point.X), from_int64(point.Y)));
}
contour.swap(scratch);
} }
} }
catch (const char* sx) {
if (bbs.empty()) { IFCImporter::LogError("error during polygon clipping, window shape may be wrong: (Clipper: "
return false; + std::string(sx) + ")");
} }
}
XYSortedField field; // ------------------------------------------------------------------------------------------------
for (std::vector<BoundingBox>::iterator it = bbs.begin(); it != bbs.end(); ++it) { void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh& curmesh,
if (field.find((*it).first) != field.end()) { const IfcMatrix3& minv,
IFCImporter::LogWarn("constraint failure during generation of wall openings, results may be faulty"); const IfcVector2& vmin,
} const IfcVector2& vmax,
field[(*it).first] = std::distance(bbs.begin(),it); IfcFloat coord,
} const std::vector<IfcVector2>& outflat)
{
std::vector<IfcVector2> outflat;
outflat.reserve(openings.size()*4);
QuadrifyPart(IfcVector2(0.f,0.f),IfcVector2(1.f,1.f),field,bbs,outflat);
ai_assert(!(outflat.size() % 4));
std::vector<IfcVector3> vold; std::vector<IfcVector3> vold;
std::vector<unsigned int> iold; std::vector<unsigned int> iold;
@ -1518,7 +1413,7 @@ bool TryAddOpenings_Quadrulate(const std::vector<TempOpening>& openings,const st
assert(!(cnt % 4)); assert(!(cnt % 4));
} }
catch (const char* sx) { catch (const char* sx) {
IFCImporter::LogError("Ifc: error during polygon clipping, contour line may be wrong: (Clipper: " IFCImporter::LogError("Ifc: error during polygon clipping, wall contour line may be wrong: (Clipper: "
+ std::string(sx) + ")"); + std::string(sx) + ")");
iold.resize(outflat.size()/4,4); iold.resize(outflat.size()/4,4);
@ -1532,8 +1427,187 @@ bool TryAddOpenings_Quadrulate(const std::vector<TempOpening>& openings,const st
// undo the projection, generate output quads // undo the projection, generate output quads
std::swap(vold,curmesh.verts); std::swap(vold,curmesh.verts);
std::swap(iold,curmesh.vertcnt); std::swap(iold,curmesh.vertcnt);
}
InsertWindowContours(bbs,contours,openings, nors,minv,vmax, vmin, coord, curmesh); // ------------------------------------------------------------------------------------------------
bool TryAddOpenings_Quadrulate(const std::vector<TempOpening>& openings,
const std::vector<IfcVector3>& nors,
TempMesh& curmesh)
{
std::vector<IfcVector3>& out = curmesh.verts;
// Try to derive a solid base plane within the current surface for use as
// working coordinate system.
const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh);
const IfcMatrix3 minv = IfcMatrix3(m).Inverse();
const IfcVector3& nor = IfcVector3(m.c1, m.c2, m.c3);
IfcFloat coord = -1;
std::vector<IfcVector2> contour_flat;
contour_flat.reserve(out.size());
IfcVector2 vmin, vmax;
MinMaxChooser<IfcVector2>()(vmin, vmax);
// Move all points into the new coordinate system, collecting min/max verts on the way
BOOST_FOREACH(IfcVector3& x, out) {
const IfcVector3 vv = m * x;
// keep Z offset in the plane coordinate system. Ignoring precision issues
// (which are present, of course), this should be the same value for
// all polygon vertices (assuming the polygon is planar).
// XXX this should be guarded, but we somehow need to pick a suitable
// epsilon
// if(coord != -1.0f) {
// assert(fabs(coord - vv.z) < 1e-3f);
// }
coord = vv.z;
vmin = std::min(IfcVector2(vv.x, vv.y), vmin);
vmax = std::max(IfcVector2(vv.x, vv.y), vmax);
contour_flat.push_back(IfcVector2(vv.x,vv.y));
}
// With the current code in DerivePlaneCoordinateSpace,
// vmin,vmax should always be the 0...1 rectangle (+- numeric inaccuracies)
// but here we won't rely on this.
vmax -= vmin;
BOOST_FOREACH(IfcVector2& vv, contour_flat) {
vv.x = (vv.x - vmin.x) / vmax.x;
vv.y = (vv.y - vmin.y) / vmax.y;
}
// project all points into the coordinate system defined by the p+sv*tu plane
// and compute bounding boxes for them
std::vector< BoundingBox > bbs;
std::vector< std::vector<IfcVector2> > contours;
size_t c = 0;
BOOST_FOREACH(const TempOpening& t,openings) {
const IfcVector3& outernor = nors[c++];
const IfcFloat dot = nor * outernor;
std::vector<IfcVector3> profile_verts = t.profileMesh->verts;
std::vector<unsigned int> profile_vertcnts = t.profileMesh->vertcnt;
if(profile_verts.size() <= 2) {
continue;
}
IfcVector2 vpmin,vpmax;
MinMaxChooser<IfcVector2>()(vpmin,vpmax);
std::vector<IfcVector2> contour;
for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) {
const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^
(profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize();
const IfcFloat dot_face_nor = nor * face_nor;
if (fabs(dot_face_nor) < 0.5) {
vi_total += profile_vertcnts[f];
continue;
}
for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) {
const IfcVector3& x = profile_verts[vi_total];
const IfcVector3 v = m * x;
IfcVector2 vv(v.x, v.y);
// rescale
vv.x = (vv.x - vmin.x) / vmax.x;
vv.y = (vv.y - vmin.y) / vmax.y;
vpmin = std::min(vpmin,vv);
vpmax = std::max(vpmax,vv);
contour.push_back(vv);
}
}
if(contour.size() <= 2) {
continue;
}
BoundingBox bb = BoundingBox(vpmin,vpmax);
// see if this BB intersects any other, in which case we could not use the Quadrify()
// algorithm and would revert to Poly2Tri only.
for (std::vector<BoundingBox>::iterator it = bbs.begin(); it != bbs.end();) {
const BoundingBox& ibb = *it;
if (ibb.first.x < bb.second.x && ibb.second.x > bb.first.x &&
ibb.first.y < bb.second.y && ibb.second.y > bb.second.x) {
// take these two contours and try to merge them. If they overlap (which
// should not happen, but in fact happens-in-the-real-world [tm] ),
// resume using a single contour and a single bounding box.
const std::vector<IfcVector2>& other = contours[std::distance(bbs.begin(),it)];
ClipperLib::ExPolygons poly;
MergeWindowContours(contour, other, poly);
if (poly.size() > 1) {
IFCImporter::LogWarn("cannot use quadrify algorithm to generate wall openings due to "
"bounding box overlaps, using poly2tri fallback method");
return TryAddOpenings_Poly2Tri(openings, nors, curmesh);
}
else if (poly.size() == 0) {
IFCImporter::LogWarn("ignoring duplicate opening");
contour.clear();
break;
}
else {
IFCImporter::LogDebug("merging oberlapping openings, this should not happen");
contour.clear();
BOOST_FOREACH(const ClipperLib::IntPoint& point, poly[0].outer) {
contour.push_back( IfcVector2( from_int64(point.X), from_int64(point.Y)));
}
bb.first = std::min(bb.first, ibb.first);
bb.second = std::max(bb.second, ibb.second);
contours.erase(contours.begin() + std::distance(bbs.begin(),it));
it = bbs.erase(it);
continue;
}
}
++it;
}
if(contour.size()) {
contours.push_back(contour);
bbs.push_back(bb);
}
}
if (bbs.empty()) {
return false;
}
XYSortedField field;
for (std::vector<BoundingBox>::iterator it = bbs.begin(); it != bbs.end(); ++it) {
if (field.find((*it).first) != field.end()) {
IFCImporter::LogWarn("constraint failure during generation of wall openings, results may be faulty");
}
field[(*it).first] = std::distance(bbs.begin(),it);
}
std::vector<IfcVector2> outflat;
outflat.reserve(openings.size()*4);
QuadrifyPart(IfcVector2(0.f,0.f),IfcVector2(1.f,1.f),field,bbs,outflat);
ai_assert(!(outflat.size() % 4));
CleanupOuterContour(contour_flat, curmesh, minv, vmin, vmax, coord, outflat);
CleanupWindowContours(contours);
InsertWindowContours(bbs,contours,openings, minv,vmax, vmin, coord, curmesh);
return true; return true;
} }
@ -1672,6 +1746,9 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, Co
// In this case we don't extrude the surface yet, just keep the profile and transform it correctly // In this case we don't extrude the surface yet, just keep the profile and transform it correctly
if(conv.collect_openings) { if(conv.collect_openings) {
boost::shared_ptr<TempMesh> meshtmp(new TempMesh()); boost::shared_ptr<TempMesh> meshtmp(new TempMesh());
ProcessExtrudedAreaSolid(*solid,*meshtmp,conv);
/*
ProcessProfile(swept.SweptArea,*meshtmp,conv); ProcessProfile(swept.SweptArea,*meshtmp,conv);
IfcMatrix4 m; IfcMatrix4 m;
@ -1679,8 +1756,9 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, Co
meshtmp->Transform(m); meshtmp->Transform(m);
IfcVector3 dir; IfcVector3 dir;
ConvertDirection(dir,solid->ExtrudedDirection); ConvertDirection(dir,solid->ExtrudedDirection); */
conv.collect_openings->push_back(TempOpening(solid, IfcMatrix3(m) * (dir*static_cast<IfcFloat>(solid->Depth)),meshtmp)); conv.collect_openings->push_back(TempOpening(solid,IfcVector3(0,0,0)
/* IfcMatrix3(m) * (dir*static_cast<IfcFloat>(solid->Depth)) */,meshtmp));
return; return;
} }