- Ifc: revamped Quadrify() version now correctly clips overlapping non-rectangular windows.
git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1327 67173fc5-114c-0410-ac8e-9d2fd5bffc1fpull/6/merge
parent
ccf629bd19
commit
7484dd379c
|
@ -959,7 +959,6 @@ typedef std::vector< std::vector<IfcVector2> > ContourVector;
|
||||||
void InsertWindowContours(const std::vector< BoundingBox >& bbs,
|
void InsertWindowContours(const std::vector< BoundingBox >& bbs,
|
||||||
const ContourVector& contours,
|
const ContourVector& contours,
|
||||||
const std::vector<TempOpening>& openings,
|
const std::vector<TempOpening>& openings,
|
||||||
const IfcMatrix4& minv,
|
|
||||||
TempMesh& curmesh)
|
TempMesh& curmesh)
|
||||||
{
|
{
|
||||||
ai_assert(contours.size() == bbs.size());
|
ai_assert(contours.size() == bbs.size());
|
||||||
|
@ -1038,8 +1037,7 @@ void InsertWindowContours(const std::vector< BoundingBox >& bbs,
|
||||||
if ((contour[a] - edge).SquareLength() > diag*diag*0.7) {
|
if ((contour[a] - edge).SquareLength() > diag*diag*0.7) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const IfcVector3 v3 = minv * IfcVector3(contour[a].x, contour[a].y, 0.0f);
|
curmesh.verts.push_back(IfcVector3(contour[a].x, contour[a].y, 0.0f));
|
||||||
curmesh.verts.push_back(v3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge != contour[last_hit]) {
|
if (edge != contour[last_hit]) {
|
||||||
|
@ -1060,8 +1058,7 @@ void InsertWindowContours(const std::vector< BoundingBox >& bbs,
|
||||||
corner.y = bb.second.y;
|
corner.y = bb.second.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IfcVector3 v3 = minv * IfcVector3(corner.x, corner.y, 0.0f);
|
curmesh.verts.push_back(IfcVector3(corner.x, corner.y, 0.0f));
|
||||||
curmesh.verts.push_back(v3);
|
|
||||||
}
|
}
|
||||||
else if (cnt == 1) {
|
else if (cnt == 1) {
|
||||||
// avoid degenerate polygons (also known as lines or points)
|
// avoid degenerate polygons (also known as lines or points)
|
||||||
|
@ -1167,15 +1164,13 @@ void CleanupWindowContours(ContourVector& contours)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh& curmesh,
|
void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh& curmesh)
|
||||||
const IfcMatrix4& minv,
|
|
||||||
const std::vector<IfcVector2>& outflat)
|
|
||||||
{
|
{
|
||||||
std::vector<IfcVector3> vold;
|
std::vector<IfcVector3> vold;
|
||||||
std::vector<unsigned int> iold;
|
std::vector<unsigned int> iold;
|
||||||
|
|
||||||
vold.reserve(outflat.size());
|
vold.reserve(curmesh.verts.size());
|
||||||
iold.reserve(outflat.size() / 4);
|
iold.reserve(curmesh.vertcnt.size());
|
||||||
|
|
||||||
// Fix the outer contour using polyclipper
|
// Fix the outer contour using polyclipper
|
||||||
try {
|
try {
|
||||||
|
@ -1194,14 +1189,21 @@ void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh&
|
||||||
std::reverse(clip.begin(), clip.end());
|
std::reverse(clip.begin(), clip.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to run polyclipper on every single quad -- we can't run it one all
|
// We need to run polyclipper on every single polygon -- we can't run it one all
|
||||||
// of them at once or it would merge them all together which would undo all
|
// of them at once or it would merge them all together which would undo all
|
||||||
// previous steps
|
// previous steps
|
||||||
subject.reserve(4);
|
subject.reserve(4);
|
||||||
size_t cnt = 0;
|
size_t index = 0;
|
||||||
BOOST_FOREACH(const IfcVector2& pip, outflat) {
|
size_t countdown = 0;
|
||||||
|
BOOST_FOREACH(const IfcVector3& pip, curmesh.verts) {
|
||||||
|
if (!countdown) {
|
||||||
|
countdown = curmesh.vertcnt[index++];
|
||||||
|
if (!countdown) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) ));
|
subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) ));
|
||||||
if (!(++cnt % 4)) {
|
if (--countdown == 0) {
|
||||||
if (!ClipperLib::Orientation(subject)) {
|
if (!ClipperLib::Orientation(subject)) {
|
||||||
std::reverse(subject.begin(), subject.end());
|
std::reverse(subject.begin(), subject.end());
|
||||||
}
|
}
|
||||||
|
@ -1214,7 +1216,7 @@ void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh&
|
||||||
BOOST_FOREACH(const ClipperLib::ExPolygon& ex, clipped) {
|
BOOST_FOREACH(const ClipperLib::ExPolygon& ex, clipped) {
|
||||||
iold.push_back(ex.outer.size());
|
iold.push_back(ex.outer.size());
|
||||||
BOOST_FOREACH(const ClipperLib::IntPoint& point, ex.outer) {
|
BOOST_FOREACH(const ClipperLib::IntPoint& point, ex.outer) {
|
||||||
vold.push_back( minv * IfcVector3(
|
vold.push_back(IfcVector3(
|
||||||
from_int64(point.X),
|
from_int64(point.X),
|
||||||
from_int64(point.Y),
|
from_int64(point.Y),
|
||||||
0.0f));
|
0.0f));
|
||||||
|
@ -1226,19 +1228,12 @@ void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh&
|
||||||
clipper.Clear();
|
clipper.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!(cnt % 4));
|
|
||||||
}
|
}
|
||||||
catch (const char* sx) {
|
catch (const char* sx) {
|
||||||
IFCImporter::LogError("Ifc: error during polygon clipping, wall 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);
|
return;
|
||||||
|
|
||||||
BOOST_FOREACH(const IfcVector2& vproj, outflat) {
|
|
||||||
const IfcVector3 v3 = minv * IfcVector3(vproj.x, vproj.y, static_cast<IfcFloat>(0.0));
|
|
||||||
vold.push_back(v3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swap data arrays
|
// swap data arrays
|
||||||
|
@ -1336,6 +1331,35 @@ void CloseWindows(const ContourVector& contours, const IfcMatrix4& minv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh)
|
||||||
|
{
|
||||||
|
ai_assert(curmesh.IsEmpty());
|
||||||
|
|
||||||
|
std::vector<IfcVector2> quads;
|
||||||
|
quads.reserve(bbs.size()*4);
|
||||||
|
|
||||||
|
// sort openings by x and y axis as a preliminiary to the QuadrifyPart() algorithm
|
||||||
|
XYSortedField field;
|
||||||
|
for (std::vector<BoundingBox>::const_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
QuadrifyPart(IfcVector2(),IfcVector2(static_cast<IfcFloat>(1.0),static_cast<IfcFloat>(1.0)),
|
||||||
|
field,bbs,quads);
|
||||||
|
|
||||||
|
ai_assert(!(quads.size() % 4));
|
||||||
|
|
||||||
|
curmesh.vertcnt.resize(quads.size()/4,4);
|
||||||
|
curmesh.verts.reserve(quads.size());
|
||||||
|
BOOST_FOREACH(const IfcVector2& v2, quads) {
|
||||||
|
curmesh.verts.push_back(IfcVector3(v2.x, v2.y, static_cast<IfcFloat>(0.0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
const std::vector<IfcVector3>& nors,
|
const std::vector<IfcVector3>& nors,
|
||||||
|
@ -1363,7 +1387,7 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
IfcVector2 vmin, vmax;
|
IfcVector2 vmin, vmax;
|
||||||
MinMaxChooser<IfcVector2>()(vmin, vmax);
|
MinMaxChooser<IfcVector2>()(vmin, vmax);
|
||||||
|
|
||||||
// Move all points into the new coordinate system, collecting min/max verts on the way
|
// Project all points into the new coordinate system, collect min/max verts on the way
|
||||||
BOOST_FOREACH(IfcVector3& x, out) {
|
BOOST_FOREACH(IfcVector3& x, out) {
|
||||||
const IfcVector3& vv = m * x;
|
const IfcVector3& vv = m * x;
|
||||||
// keep Z offset in the plane coordinate system. Ignoring precision issues
|
// keep Z offset in the plane coordinate system. Ignoring precision issues
|
||||||
|
@ -1403,13 +1427,15 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
mult.c4 = -coord;
|
mult.c4 = -coord;
|
||||||
m = mult * m;
|
m = mult * m;
|
||||||
|
|
||||||
// Obtain inverse transform for getting back
|
// Obtain inverse transform for getting back to world space later on
|
||||||
const IfcMatrix4& minv = IfcMatrix4(m).Inverse();
|
const IfcMatrix4& minv = IfcMatrix4(m).Inverse();
|
||||||
|
|
||||||
// Compute bounding boxes for the projections of all openings
|
// Compute bounding boxes for all 2D openings in projection space
|
||||||
std::vector< BoundingBox > bbs;
|
std::vector< BoundingBox > bbs;
|
||||||
ContourVector contours;
|
ContourVector contours;
|
||||||
|
|
||||||
|
std::vector<IfcVector2> temp_contour;
|
||||||
|
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
BOOST_FOREACH(TempOpening& opening,openings) {
|
BOOST_FOREACH(TempOpening& opening,openings) {
|
||||||
std::vector<IfcVector3> profile_verts = opening.profileMesh->verts;
|
std::vector<IfcVector3> profile_verts = opening.profileMesh->verts;
|
||||||
|
@ -1427,11 +1453,11 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
// whether the meshes do actually intersect the base surface plane.
|
// whether the meshes do actually intersect the base surface plane.
|
||||||
// This is done by recording minimum and maximum values for the
|
// This is done by recording minimum and maximum values for the
|
||||||
// d component of the plane equation for all polys and checking
|
// d component of the plane equation for all polys and checking
|
||||||
// against the surface d.
|
// against surface d.
|
||||||
IfcFloat dmin, dmax;
|
IfcFloat dmin, dmax;
|
||||||
MinMaxChooser<IfcFloat>()(dmin,dmax);
|
MinMaxChooser<IfcFloat>()(dmin,dmax);
|
||||||
|
|
||||||
std::vector<IfcVector2> contour;
|
temp_contour.clear();
|
||||||
for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) {
|
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]) ^
|
const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^
|
||||||
(profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize();
|
(profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize();
|
||||||
|
@ -1462,20 +1488,20 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
vpmax = std::max(vpmax,vv);
|
vpmax = std::max(vpmax,vv);
|
||||||
|
|
||||||
// usually there won't be too many elements so the linear time check is ok
|
// usually there won't be too many elements so the linear time check is ok
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (std::vector<IfcVector2>::const_iterator it = contour.begin(); it != contour.end(); ++it) {
|
BOOST_FOREACH(const IfcVector2& cp, temp_contour) {
|
||||||
if (((*it)-vv).SquareLength() < 1e-5f) {
|
if ((cp-vv).SquareLength() < 1e-5f) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!found) {
|
if(!found) {
|
||||||
contour.push_back(vv);
|
temp_contour.push_back(vv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(contour.size() <= 2) {
|
if(temp_contour.size() <= 2) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1508,8 +1534,10 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
const std::vector<IfcVector2>& other = contours[std::distance(bbs.begin(),it)];
|
const std::vector<IfcVector2>& other = contours[std::distance(bbs.begin(),it)];
|
||||||
|
|
||||||
ClipperLib::ExPolygons poly;
|
ClipperLib::ExPolygons poly;
|
||||||
MergeWindowContours(contour, other, poly);
|
MergeWindowContours(temp_contour, other, poly);
|
||||||
|
|
||||||
|
// TODO: Commented because it causes more visible artifacts than
|
||||||
|
// it solves.
|
||||||
if (false && poly.size() > 1) {
|
if (false && poly.size() > 1) {
|
||||||
IFCImporter::LogWarn("cannot use quadrify algorithm to generate wall openings due to "
|
IFCImporter::LogWarn("cannot use quadrify algorithm to generate wall openings due to "
|
||||||
"bounding box overlaps, using poly2tri fallback method");
|
"bounding box overlaps, using poly2tri fallback method");
|
||||||
|
@ -1517,19 +1545,19 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
}
|
}
|
||||||
else if (poly.size() == 0) {
|
else if (poly.size() == 0) {
|
||||||
IFCImporter::LogWarn("ignoring duplicate opening");
|
IFCImporter::LogWarn("ignoring duplicate opening");
|
||||||
contour.clear();
|
temp_contour.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
IFCImporter::LogDebug("merging overlapping openings");
|
IFCImporter::LogDebug("merging overlapping openings");
|
||||||
|
|
||||||
contour.clear();
|
temp_contour.clear();
|
||||||
BOOST_FOREACH(const ClipperLib::IntPoint& point, poly[0].outer) {
|
BOOST_FOREACH(const ClipperLib::IntPoint& point, poly[0].outer) {
|
||||||
IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y));
|
IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y));
|
||||||
vv = std::max(vv,IfcVector2());
|
vv = std::max(vv,IfcVector2());
|
||||||
vv = std::min(vv,one_vec);
|
vv = std::min(vv,one_vec);
|
||||||
|
|
||||||
contour.push_back( vv );
|
temp_contour.push_back( vv );
|
||||||
}
|
}
|
||||||
|
|
||||||
bb.first = std::min(bb.first, ibb.first);
|
bb.first = std::min(bb.first, ibb.first);
|
||||||
|
@ -1549,37 +1577,50 @@ bool TryAddOpenings_Quadrulate(std::vector<TempOpening>& openings,
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!contour.empty()) {
|
if(!temp_contour.empty()) {
|
||||||
contours_to_openings.push_back(std::vector<TempOpening*>(
|
contours_to_openings.push_back(std::vector<TempOpening*>(
|
||||||
joined_openings.begin(),
|
joined_openings.begin(),
|
||||||
joined_openings.end()));
|
joined_openings.end()));
|
||||||
|
|
||||||
contours.push_back(contour);
|
contours.push_back(temp_contour);
|
||||||
bbs.push_back(bb);
|
bbs.push_back(bb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we still have any openings left - it may well be that this is
|
||||||
|
// not the cause, for example if all the opening candidates don't intersect
|
||||||
|
// this surface or point into a direction perpendicular to it.
|
||||||
if (bbs.empty()) {
|
if (bbs.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
XYSortedField field;
|
curmesh.Clear();
|
||||||
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;
|
// Generate a base subdivision into quads to accommodate the given list
|
||||||
outflat.reserve(openings.size()*4);
|
// of window bounding boxes.
|
||||||
QuadrifyPart(IfcVector2(0.f,0.f),IfcVector2(1.f,1.f),field,bbs,outflat);
|
Quadrify(bbs,curmesh);
|
||||||
ai_assert(!(outflat.size() % 4));
|
|
||||||
|
|
||||||
CleanupOuterContour(contour_flat, curmesh, minv,outflat);
|
// Run a sanity cleanup pass on the window contours to avoid generating
|
||||||
|
// artifacts during the contour generation phase later on.
|
||||||
CleanupWindowContours(contours);
|
CleanupWindowContours(contours);
|
||||||
InsertWindowContours(bbs,contours,openings, minv,curmesh);
|
|
||||||
|
// Previously we reduced all windows to rectangular AABBs in projection
|
||||||
|
// space, now it is time to fill the gaps between the BBs and the real
|
||||||
|
// window openings.
|
||||||
|
InsertWindowContours(bbs,contours,openings, curmesh);
|
||||||
|
|
||||||
|
// Clip the entire outer contour of our current result against the real
|
||||||
|
// outer contour of the surface. This is necessary because the result
|
||||||
|
// of the Quadrify() algorithm is always a square area spanning
|
||||||
|
// over [0,1]^2 (i.e. entire projection space).
|
||||||
|
CleanupOuterContour(contour_flat, curmesh);
|
||||||
|
|
||||||
|
// Undo the projection and get back to world (or local object) space
|
||||||
|
BOOST_FOREACH(IfcVector3& v3, curmesh.verts) {
|
||||||
|
v3 = minv * v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
// This should connect the window openings on both sides of the wall,
|
// This should connect the window openings on both sides of the wall,
|
||||||
// but it produces lots of artifacts which are not resolved yet.
|
// but it produces lots of artifacts which are not resolved yet.
|
||||||
// Most of all, it makes all cases in which adjacent openings are
|
// Most of all, it makes all cases in which adjacent openings are
|
||||||
|
|
|
@ -184,6 +184,10 @@ struct TempMesh
|
||||||
IfcVector3 Center() const;
|
IfcVector3 Center() const;
|
||||||
void Append(const TempMesh& other);
|
void Append(const TempMesh& other);
|
||||||
|
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return verts.empty() && vertcnt.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void RemoveAdjacentDuplicates();
|
void RemoveAdjacentDuplicates();
|
||||||
void RemoveDegenerates();
|
void RemoveDegenerates();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue