diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index e15691957..6769fd161 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -43,50 +43,45 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * holes for windows and doors into walls. */ - #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER -#include "IFCUtil.h" #include "Common/PolyTools.h" +#include "IFCUtil.h" #include "PostProcessing/ProcessHelper.h" #ifdef ASSIMP_USE_HUNTER -# include -# include +#include +#include #else -# include "../contrib/poly2tri/poly2tri/poly2tri.h" -# include "../contrib/clipper/clipper.hpp" +#include "../contrib/clipper/clipper.hpp" +#include "../contrib/poly2tri/poly2tri/poly2tri.h" #endif #include namespace Assimp { - namespace IFC { +namespace IFC { - using ClipperLib::ulong64; - // XXX use full -+ range ... - const ClipperLib::long64 max_ulong64 = 1518500249; // clipper.cpp / hiRange var +using ClipperLib::ulong64; +// XXX use full -+ range ... +const ClipperLib::long64 max_ulong64 = 1518500249; // clipper.cpp / hiRange var - //#define to_int64(p) (static_cast( std::max( 0., std::min( static_cast((p)), 1.) ) * max_ulong64 )) -#define to_int64(p) (static_cast(static_cast((p) ) * max_ulong64 )) +//#define to_int64(p) (static_cast( std::max( 0., std::min( static_cast((p)), 1.) ) * max_ulong64 )) +#define to_int64(p) (static_cast(static_cast((p)) * max_ulong64)) #define from_int64(p) (static_cast((p)) / max_ulong64) -#define one_vec (IfcVector2(static_cast(1.0),static_cast(1.0))) +#define one_vec (IfcVector2(static_cast(1.0), static_cast(1.0))) +// fallback method to generate wall openings +bool TryAddOpenings_Poly2Tri(const std::vector &openings, const std::vector &nors, + TempMesh &curmesh); - // fallback method to generate wall openings - bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std::vector& nors, - TempMesh& curmesh); - - -typedef std::pair< IfcVector2, IfcVector2 > BoundingBox; -typedef std::map XYSortedField; - +using BoundingBox = std::pair; +using XYSortedField = std::map; // ------------------------------------------------------------------------------------------------ -void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& field, - const std::vector< BoundingBox >& bbs, - std::vector& out) -{ - if (!(pmin.x-pmax.x) || !(pmin.y-pmax.y)) { +void QuadrifyPart(const IfcVector2 &pmin, const IfcVector2 &pmax, XYSortedField &field, + const std::vector &bbs, + std::vector &out) { + if (!(pmin.x - pmax.x) || !(pmin.y - pmax.y)) { return; } @@ -95,9 +90,9 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& // Search along the x-axis until we find an opening XYSortedField::iterator start = field.begin(); - for(; start != field.end(); ++start) { - const BoundingBox& bb = bbs[(*start).second]; - if(bb.first.x >= pmax.x) { + for (; start != field.end(); ++start) { + const BoundingBox &bb = bbs[(*start).second]; + if (bb.first.x >= pmax.x) { break; } @@ -111,29 +106,29 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& if (!found) { // the rectangle [pmin,pend] is opaque, fill it - out.push_back(pmin); - out.push_back(IfcVector2(pmin.x,pmax.y)); - out.push_back(pmax); - out.push_back(IfcVector2(pmax.x,pmin.y)); + out.emplace_back(pmin); + out.emplace_back(pmin.x, pmax.y); + out.emplace_back(pmax); + out.emplace_back(pmax.x, pmin.y); return; } - xs = std::max(pmin.x,xs); - xe = std::min(pmax.x,xe); + xs = std::max(pmin.x, xs); + xe = std::min(pmax.x, xe); // see if there's an offset to fill at the top of our quad if (xs - pmin.x) { - out.push_back(pmin); - out.push_back(IfcVector2(pmin.x,pmax.y)); - out.push_back(IfcVector2(xs,pmax.y)); - out.push_back(IfcVector2(xs,pmin.y)); + out.emplace_back(pmin); + out.emplace_back(pmin.x, pmax.y); + out.emplace_back(xs, pmax.y); + out.emplace_back(xs, pmin.y); } // search along the y-axis for all openings that overlap xs and our quad IfcFloat ylast = pmin.y; found = false; - for(; start != field.end(); ++start) { - const BoundingBox& bb = bbs[(*start).second]; + for (; start != field.end(); ++start) { + const BoundingBox &bb = bbs[(*start).second]; if (bb.first.x > xs || bb.first.y >= pmax.y) { break; } @@ -141,9 +136,9 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& if (bb.second.y > ylast) { found = true; - const IfcFloat ys = std::max(bb.first.y,pmin.y), ye = std::min(bb.second.y,pmax.y); + const IfcFloat ys = std::max(bb.first.y, pmin.y), ye = std::min(bb.second.y, pmax.y); if (ys - ylast > 0.0f) { - QuadrifyPart( IfcVector2(xs,ylast), IfcVector2(xe,ys) ,field,bbs,out); + QuadrifyPart(IfcVector2(xs, ylast), IfcVector2(xe, ys), field, bbs, out); } // the following are the window vertices @@ -157,39 +152,33 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& } if (!found) { // the rectangle [pmin,pend] is opaque, fill it - out.push_back(IfcVector2(xs,pmin.y)); - out.push_back(IfcVector2(xs,pmax.y)); - out.push_back(IfcVector2(xe,pmax.y)); - out.push_back(IfcVector2(xe,pmin.y)); + out.emplace_back(IfcVector2(xs, pmin.y)); + out.emplace_back(xs, pmax.y); + out.emplace_back(xe, pmax.y); + out.emplace_back(xe, pmin.y); return; } if (ylast < pmax.y) { - QuadrifyPart( IfcVector2(xs,ylast), IfcVector2(xe,pmax.y) ,field,bbs,out); + QuadrifyPart(IfcVector2(xs, ylast), IfcVector2(xe, pmax.y), field, bbs, out); } // now for the whole rest - if (pmax.x-xe) { - QuadrifyPart(IfcVector2(xe,pmin.y), pmax ,field,bbs,out); + if (pmax.x - xe) { + QuadrifyPart(IfcVector2(xe, pmin.y), pmax, field, bbs, out); } } -typedef std::vector Contour; -typedef std::vector SkipList; // should probably use int for performance reasons +using Contour = std::vector; +using SkipList = std::vector; // should probably use int for performance reasons -struct ProjectedWindowContour -{ +struct ProjectedWindowContour { Contour contour; BoundingBox bb; SkipList skiplist; bool is_rectangular; - - ProjectedWindowContour(const Contour& contour, const BoundingBox& bb, bool is_rectangular) - : contour(contour) - , bb(bb) - , is_rectangular(is_rectangular) - {} - + ProjectedWindowContour(const Contour &contour, const BoundingBox &bb, bool is_rectangular) : + contour(contour), bb(bb), is_rectangular(is_rectangular) {} bool IsInvalid() const { return contour.empty(); @@ -200,26 +189,24 @@ struct ProjectedWindowContour } void PrepareSkiplist() { - skiplist.resize(contour.size(),false); + skiplist.resize(contour.size(), false); } }; -typedef std::vector< ProjectedWindowContour > ContourVector; +using ContourVector = std::vector; // ------------------------------------------------------------------------------------------------ -bool BoundingBoxesOverlapping( const BoundingBox &ibb, const BoundingBox &bb ) -{ +bool BoundingBoxesOverlapping(const BoundingBox &ibb, const BoundingBox &bb) { // count the '=' case as non-overlapping but as adjacent to each other return ibb.first.x < bb.second.x && ibb.second.x > bb.first.x && - ibb.first.y < bb.second.y && ibb.second.y > bb.first.y; + ibb.first.y < bb.second.y && ibb.second.y > bb.first.y; } // ------------------------------------------------------------------------------------------------ -bool IsDuplicateVertex(const IfcVector2& vv, const std::vector& temp_contour) -{ +bool IsDuplicateVertex(const IfcVector2 &vv, const std::vector &temp_contour) { // sanity check for duplicate vertices - for(const IfcVector2& cp : temp_contour) { - if ((cp-vv).SquareLength() < 1e-5f) { + for (const IfcVector2 &cp : temp_contour) { + if ((cp - vv).SquareLength() < 1e-5f) { return true; } } @@ -227,14 +214,13 @@ bool IsDuplicateVertex(const IfcVector2& vv, const std::vector& temp } // ------------------------------------------------------------------------------------------------ -void ExtractVerticesFromClipper(const ClipperLib::Polygon& poly, std::vector& temp_contour, - bool filter_duplicates = false) -{ +void ExtractVerticesFromClipper(const ClipperLib::Polygon &poly, std::vector &temp_contour, + bool filter_duplicates = false) { temp_contour.clear(); - for(const ClipperLib::IntPoint& point : poly) { - IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y)); - vv = std::max(vv,IfcVector2()); - vv = std::min(vv,one_vec); + for (const ClipperLib::IntPoint &point : poly) { + IfcVector2 vv = IfcVector2(from_int64(point.X), from_int64(point.Y)); + vv = std::max(vv, IfcVector2()); + vv = std::min(vv, one_vec); if (!filter_duplicates || !IsDuplicateVertex(vv, temp_contour)) { temp_contour.push_back(vv); @@ -243,84 +229,77 @@ void ExtractVerticesFromClipper(const ClipperLib::Polygon& poly, std::vector()(newbb_min, newbb_max); - for(const ClipperLib::IntPoint& point : poly) { - IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y)); + for (const ClipperLib::IntPoint &point : poly) { + IfcVector2 vv = IfcVector2(from_int64(point.X), from_int64(point.Y)); // sanity rounding - vv = std::max(vv,IfcVector2()); - vv = std::min(vv,one_vec); + vv = std::max(vv, IfcVector2()); + vv = std::min(vv, one_vec); - newbb_min = std::min(newbb_min,vv); - newbb_max = std::max(newbb_max,vv); + newbb_min = std::min(newbb_min, vv); + newbb_max = std::max(newbb_max, vv); } return BoundingBox(newbb_min, newbb_max); } // ------------------------------------------------------------------------------------------------ -void InsertWindowContours(const ContourVector& contours, - const std::vector& /*openings*/, - TempMesh& curmesh) -{ +void InsertWindowContours(const ContourVector &contours, + const std::vector & /*openings*/, + TempMesh &curmesh) { // fix windows - we need to insert the real, polygonal shapes into the quadratic holes that we have now - for(size_t i = 0; i < contours.size();++i) { - const BoundingBox& bb = contours[i].bb; - const std::vector& contour = contours[i].contour; - if(contour.empty()) { + for (size_t i = 0; i < contours.size(); ++i) { + const BoundingBox &bb = contours[i].bb; + const std::vector &contour = contours[i].contour; + if (contour.empty()) { continue; } // check if we need to do it at all - many windows just fit perfectly into their quadratic holes, // i.e. their contours *are* already their bounding boxes. if (contour.size() == 4) { - std::set verts; - for(size_t n = 0; n < 4; ++n) { + std::set verts; + for (size_t n = 0; n < 4; ++n) { verts.insert(contour[n]); } - const std::set::const_iterator end = verts.end(); - if (verts.find(bb.first)!=end && verts.find(bb.second)!=end - && verts.find(IfcVector2(bb.first.x,bb.second.y))!=end - && verts.find(IfcVector2(bb.second.x,bb.first.y))!=end - ) { - continue; + const std::set::const_iterator end = verts.end(); + if (verts.find(bb.first) != end && verts.find(bb.second) != end && verts.find(IfcVector2(bb.first.x, bb.second.y)) != end && verts.find(IfcVector2(bb.second.x, bb.first.y)) != end) { + continue; } } - const IfcFloat diag = (bb.first-bb.second).Length(); - const IfcFloat epsilon = diag/1000.f; + const IfcFloat diag = (bb.first - bb.second).Length(); + const IfcFloat epsilon = diag / 1000.f; // walk through all contour points and find those that lie on the BB corner size_t last_hit = (size_t)-1, very_first_hit = (size_t)-1; IfcVector2 edge; - for(size_t n = 0, e=0, size = contour.size();; n=(n+1)%size, ++e) { + for (size_t n = 0, e = 0, size = contour.size();; n = (n + 1) % size, ++e) { // sanity checking - if (e == size*2) { + if (e == size * 2) { IFCImporter::LogError("encountered unexpected topology while generating window contour"); break; } - const IfcVector2& v = contour[n]; + const IfcVector2 &v = contour[n]; bool hit = false; - if (std::fabs(v.x-bb.first.x) n ? size-(last_hit-n) : n-last_hit; - for(size_t a = last_hit, ee = 0; ee <= cnt; a=(a+1)%size, ++ee) { + size_t cnt = last_hit > n ? size - (last_hit - n) : n - last_hit; + for (size_t a = last_hit, ee = 0; ee <= cnt; a = (a + 1) % size, ++ee) { // hack: this is to fix cases where opening contours are self-intersecting. // Clipper doesn't produce such polygons, but as soon as we're back in // our brave new floating-point world, very small distances are consumed // by the maximum available precision, leading to self-intersecting // polygons. This fix makes concave windows fail even worse, but // anyway, fail is fail. - if ((contour[a] - edge).SquareLength() > diag*diag*0.7) { + if ((contour[a] - edge).SquareLength() > diag * diag * 0.7) { continue; } curmesh.mVerts.push_back(IfcVector3(contour[a].x, contour[a].y, 0.0f)); @@ -347,36 +326,32 @@ void InsertWindowContours(const ContourVector& contours, IfcVector2 corner = edge; - if (std::fabs(contour[last_hit].x-bb.first.x)(d)); - std::reverse(curmesh.mVerts.rbegin(),curmesh.mVerts.rbegin()+d); + std::reverse(curmesh.mVerts.rbegin(), curmesh.mVerts.rbegin() + d); } if (n == very_first_hit) { break; } - } - else { + } else { very_first_hit = n; } @@ -387,17 +362,16 @@ void InsertWindowContours(const ContourVector& contours, } // ------------------------------------------------------------------------------------------------ -void MergeWindowContours (const std::vector& a, - const std::vector& b, - ClipperLib::ExPolygons& out) -{ +void MergeWindowContours(const std::vector &a, + const std::vector &b, + ClipperLib::ExPolygons &out) { out.clear(); ClipperLib::Clipper clipper; ClipperLib::Polygon clip; - for(const IfcVector2& pip : a) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : a) { + clip.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (ClipperLib::Orientation(clip)) { @@ -407,8 +381,8 @@ void MergeWindowContours (const std::vector& a, clipper.AddPolygon(clip, ClipperLib::ptSubject); clip.clear(); - for(const IfcVector2& pip : b) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : b) { + clip.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (ClipperLib::Orientation(clip)) { @@ -416,22 +390,21 @@ void MergeWindowContours (const std::vector& a, } clipper.AddPolygon(clip, ClipperLib::ptSubject); - clipper.Execute(ClipperLib::ctUnion, out,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + clipper.Execute(ClipperLib::ctUnion, out, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // ------------------------------------------------------------------------------------------------ // Subtract a from b -void MakeDisjunctWindowContours (const std::vector& a, - const std::vector& b, - ClipperLib::ExPolygons& out) -{ +void MakeDisjunctWindowContours(const std::vector &a, + const std::vector &b, + ClipperLib::ExPolygons &out) { out.clear(); ClipperLib::Clipper clipper; ClipperLib::Polygon clip; - for(const IfcVector2& pip : a) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : a) { + clip.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (ClipperLib::Orientation(clip)) { @@ -441,8 +414,8 @@ void MakeDisjunctWindowContours (const std::vector& a, clipper.AddPolygon(clip, ClipperLib::ptClip); clip.clear(); - for(const IfcVector2& pip : b) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : b) { + clip.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (ClipperLib::Orientation(clip)) { @@ -450,31 +423,30 @@ void MakeDisjunctWindowContours (const std::vector& a, } clipper.AddPolygon(clip, ClipperLib::ptSubject); - clipper.Execute(ClipperLib::ctDifference, out,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + clipper.Execute(ClipperLib::ctDifference, out, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // ------------------------------------------------------------------------------------------------ -void CleanupWindowContour(ProjectedWindowContour& window) -{ +void CleanupWindowContour(ProjectedWindowContour &window) { std::vector scratch; - std::vector& contour = window.contour; + std::vector &contour = window.contour; ClipperLib::Polygon subject; ClipperLib::Clipper clipper; ClipperLib::ExPolygons clipped; - for(const IfcVector2& pip : contour) { - subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : contour) { + subject.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } - clipper.AddPolygon(subject,ClipperLib::ptSubject); - clipper.Execute(ClipperLib::ctUnion,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + clipper.AddPolygon(subject, ClipperLib::ptSubject); + clipper.Execute(ClipperLib::ctUnion, clipped, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // This should yield only one polygon or something went wrong if (clipped.size() != 1) { // Empty polygon? drop the contour altogether - if(clipped.empty()) { + if (clipped.empty()) { IFCImporter::LogError("error during polygon clipping, window contour is degenerate"); window.FlagInvalid(); return; @@ -489,23 +461,19 @@ void CleanupWindowContour(ProjectedWindowContour& window) } // ------------------------------------------------------------------------------------------------ -void CleanupWindowContours(ContourVector& contours) -{ +void CleanupWindowContours(ContourVector &contours) { // Use PolyClipper to clean up window contours try { - for(ProjectedWindowContour& window : contours) { + for (ProjectedWindowContour &window : contours) { CleanupWindowContour(window); } - } - catch (const char* sx) { - IFCImporter::LogError("error during polygon clipping, window shape may be wrong: (Clipper: " - + std::string(sx) + ")"); + } catch (const char *sx) { + IFCImporter::LogError("error during polygon clipping, window shape may be wrong: (Clipper: " + std::string(sx) + ")"); } } // ------------------------------------------------------------------------------------------------ -void CleanupOuterContour(const std::vector& contour_flat, TempMesh& curmesh) -{ +void CleanupOuterContour(const std::vector &contour_flat, TempMesh &curmesh) { std::vector vold; std::vector iold; @@ -521,8 +489,8 @@ void CleanupOuterContour(const std::vector& contour_flat, TempMesh& ClipperLib::Polygon clip; clip.reserve(contour_flat.size()); - for(const IfcVector2& pip : contour_flat) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + for (const IfcVector2 &pip : contour_flat) { + clip.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (!ClipperLib::Orientation(clip)) { @@ -535,31 +503,31 @@ void CleanupOuterContour(const std::vector& contour_flat, TempMesh& subject.reserve(4); size_t index = 0; size_t countdown = 0; - for(const IfcVector3& pip : curmesh.mVerts) { + for (const IfcVector3 &pip : curmesh.mVerts) { if (!countdown) { countdown = curmesh.mVertcnt[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 (--countdown == 0) { if (!ClipperLib::Orientation(subject)) { std::reverse(subject.begin(), subject.end()); } - clipper.AddPolygon(subject,ClipperLib::ptSubject); - clipper.AddPolygon(clip,ClipperLib::ptClip); + clipper.AddPolygon(subject, ClipperLib::ptSubject); + clipper.AddPolygon(clip, ClipperLib::ptClip); - clipper.Execute(ClipperLib::ctIntersection,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + clipper.Execute(ClipperLib::ctIntersection, clipped, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - for(const ClipperLib::ExPolygon& ex : clipped) { + for (const ClipperLib::ExPolygon &ex : clipped) { iold.push_back(static_cast(ex.outer.size())); - for(const ClipperLib::IntPoint& point : ex.outer) { + for (const ClipperLib::IntPoint &point : ex.outer) { vold.push_back(IfcVector3( - from_int64(point.X), - from_int64(point.Y), - 0.0f)); + from_int64(point.X), + from_int64(point.Y), + 0.0f)); } } @@ -568,45 +536,41 @@ void CleanupOuterContour(const std::vector& contour_flat, TempMesh& clipper.Clear(); } } - } - catch (const char* sx) { - IFCImporter::LogError("Ifc: error during polygon clipping, wall contour line may be wrong: (Clipper: " - + std::string(sx) + ")"); + } catch (const char *sx) { + IFCImporter::LogError("Ifc: error during polygon clipping, wall contour line may be wrong: (Clipper: " + std::string(sx) + ")"); return; } // swap data arrays - std::swap(vold,curmesh.mVerts); - std::swap(iold,curmesh.mVertcnt); + std::swap(vold, curmesh.mVerts); + std::swap(iold, curmesh.mVertcnt); } -typedef std::vector OpeningRefs; -typedef std::vector OpeningRefVector; +typedef std::vector OpeningRefs; +typedef std::vector OpeningRefVector; typedef std::vector -> ContourRefVector; + ContourVector::const_iterator, + Contour::const_iterator>> + ContourRefVector; // ------------------------------------------------------------------------------------------------ -bool BoundingBoxesAdjacent(const BoundingBox& bb, const BoundingBox& ibb) -{ +bool BoundingBoxesAdjacent(const BoundingBox &bb, const BoundingBox &ibb) { // TODO: I'm pretty sure there is a much more compact way to check this const IfcFloat epsilon = Math::getEpsilon(); - return (std::fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) || - (std::fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) || - (std::fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) || - (std::fabs(bb.first.y - ibb.second.y) < epsilon && ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x); + return (std::fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) || + (std::fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) || + (std::fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) || + (std::fabs(bb.first.y - ibb.second.y) < epsilon && ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x); } // ------------------------------------------------------------------------------------------------ // Check if m0,m1 intersects n0,n1 assuming same ordering of the points in the line segments // output the intersection points on n0,n1 -bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, - const IfcVector2& m0, const IfcVector2& m1, - IfcVector2& out0, IfcVector2& out1) -{ +bool IntersectingLineSegments(const IfcVector2 &n0, const IfcVector2 &n1, + const IfcVector2 &m0, const IfcVector2 &m1, + IfcVector2 &out0, IfcVector2 &out1) { const IfcVector2 n0_to_n1 = n1 - n0; const IfcVector2 n0_to_m0 = m0 - n0; @@ -619,11 +583,11 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, static const IfcFloat inf = std::numeric_limits::infinity(); - if (!(n0_to_m0.SquareLength() < e*e || std::fabs(n0_to_m0 * n0_to_n1) / (n0_to_m0.Length() * n0_to_n1.Length()) > 1-1e-5 )) { + if (!(n0_to_m0.SquareLength() < e * e || std::fabs(n0_to_m0 * n0_to_n1) / (n0_to_m0.Length() * n0_to_n1.Length()) > 1 - 1e-5)) { return false; } - if (!(n1_to_m1.SquareLength() < e*e || std::fabs(n1_to_m1 * n0_to_n1) / (n1_to_m1.Length() * n0_to_n1.Length()) > 1-1e-5 )) { + if (!(n1_to_m1.SquareLength() < e * e || std::fabs(n1_to_m1 * n0_to_n1) / (n1_to_m1.Length() * n0_to_n1.Length()) > 1 - 1e-5)) { return false; } @@ -635,7 +599,7 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, // the higher absolute difference is big enough as to avoid // divisions by zero, the case 0/0 ~ infinity is detected and // handled separately. - if(std::fabs(n0_to_n1.x) > std::fabs(n0_to_n1.y)) { + if (std::fabs(n0_to_n1.x) > std::fabs(n0_to_n1.y)) { s0 = n0_to_m0.x / n0_to_n1.x; s1 = n0_to_m1.x / n0_to_n1.x; @@ -645,8 +609,7 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, if (std::fabs(s1) == inf && std::fabs(n0_to_m1.x) < smalle) { s1 = 0.; } - } - else { + } else { s0 = n0_to_m0.y / n0_to_n1.y; s1 = n0_to_m1.y / n0_to_n1.y; @@ -659,16 +622,16 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, } if (s1 < s0) { - std::swap(s1,s0); + std::swap(s1, s0); } - s0 = std::max(0.0,s0); - s1 = std::max(0.0,s1); + s0 = std::max(0.0, s0); + s1 = std::max(0.0, s1); - s0 = std::min(1.0,s0); - s1 = std::min(1.0,s1); + s0 = std::min(1.0, s0); + s1 = std::min(1.0, s1); - if (std::fabs(s1-s0) < e) { + if (std::fabs(s1 - s0) < e) { return false; } @@ -679,14 +642,13 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, } // ------------------------------------------------------------------------------------------------ -void FindAdjacentContours(ContourVector::iterator current, const ContourVector& contours) -{ +void FindAdjacentContours(ContourVector::iterator current, const ContourVector &contours) { const IfcFloat sqlen_epsilon = static_cast(Math::getEpsilon()); - const BoundingBox& bb = (*current).bb; + const BoundingBox &bb = (*current).bb; // What is to be done here is to populate the skip lists for the contour // and to add necessary padding points when needed. - SkipList& skiplist = (*current).skiplist; + SkipList &skiplist = (*current).skiplist; // First step to find possible adjacent contours is to check for adjacent bounding // boxes. If the bounding boxes are not adjacent, the contours lines cannot possibly be. @@ -704,7 +666,7 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector& const bool is_me = it == current; - const BoundingBox& ibb = (*it).bb; + const BoundingBox &ibb = (*it).bb; // Assumption: the bounding boxes are pairwise disjoint or identical ai_assert(is_me || !BoundingBoxesOverlapping(bb, ibb)); @@ -716,29 +678,28 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector& // world Ifc files it will not matter since most windows that // are adjacent to each others are rectangular anyway. - Contour& ncontour = (*current).contour; - const Contour& mcontour = (*it).contour; + Contour &ncontour = (*current).contour; + const Contour &mcontour = (*it).contour; for (size_t n = 0; n < ncontour.size(); ++n) { const IfcVector2 n0 = ncontour[n]; - const IfcVector2 n1 = ncontour[(n+1) % ncontour.size()]; + const IfcVector2 n1 = ncontour[(n + 1) % ncontour.size()]; for (size_t m = 0, mend = (is_me ? n : mcontour.size()); m < mend; ++m) { ai_assert(&mcontour != &ncontour || m < n); const IfcVector2 m0 = mcontour[m]; - const IfcVector2 m1 = mcontour[(m+1) % mcontour.size()]; + const IfcVector2 m1 = mcontour[(m + 1) % mcontour.size()]; IfcVector2 isect0, isect1; - if (IntersectingLineSegments(n0,n1, m0, m1, isect0, isect1)) { + if (IntersectingLineSegments(n0, n1, m0, m1, isect0, isect1)) { if ((isect0 - n0).SquareLength() > sqlen_epsilon) { ++n; ncontour.insert(ncontour.begin() + n, isect0); skiplist.insert(skiplist.begin() + n, true); - } - else { + } else { skiplist[n] = true; } @@ -756,48 +717,44 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector& } // ------------------------------------------------------------------------------------------------ -AI_FORCE_INLINE bool LikelyBorder(const IfcVector2& vdelta) -{ +AI_FORCE_INLINE bool LikelyBorder(const IfcVector2 &vdelta) { const IfcFloat dot_point_epsilon = static_cast(Math::getEpsilon()); return std::fabs(vdelta.x * vdelta.y) < dot_point_epsilon; } // ------------------------------------------------------------------------------------------------ -void FindBorderContours(ContourVector::iterator current) -{ - const IfcFloat border_epsilon_upper = static_cast(1-1e-4); +void FindBorderContours(ContourVector::iterator current) { + const IfcFloat border_epsilon_upper = static_cast(1 - 1e-4); const IfcFloat border_epsilon_lower = static_cast(1e-4); bool outer_border = false; bool start_on_outer_border = false; - SkipList& skiplist = (*current).skiplist; + SkipList &skiplist = (*current).skiplist; IfcVector2 last_proj_point; const Contour::const_iterator cbegin = (*current).contour.begin(), cend = (*current).contour.end(); for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) { - const IfcVector2& proj_point = *cit; + const IfcVector2 &proj_point = *cit; // Check if this connection is along the outer boundary of the projection // plane. In such a case we better drop it because such 'edges' should // not have any geometry to close them (think of door openings). if (proj_point.x <= border_epsilon_lower || proj_point.x >= border_epsilon_upper || - proj_point.y <= border_epsilon_lower || proj_point.y >= border_epsilon_upper) { + proj_point.y <= border_epsilon_lower || proj_point.y >= border_epsilon_upper) { - if (outer_border) { - ai_assert(cit != cbegin); - if (LikelyBorder(proj_point - last_proj_point)) { - skiplist[std::distance(cbegin, cit) - 1] = true; - } - } - else if (cit == cbegin) { - start_on_outer_border = true; + if (outer_border) { + ai_assert(cit != cbegin); + if (LikelyBorder(proj_point - last_proj_point)) { + skiplist[std::distance(cbegin, cit) - 1] = true; } + } else if (cit == cbegin) { + start_on_outer_border = true; + } - outer_border = true; - } - else { + outer_border = true; + } else { outer_border = false; } @@ -806,30 +763,28 @@ void FindBorderContours(ContourVector::iterator current) // handle last segment if (outer_border && start_on_outer_border) { - const IfcVector2& proj_point = *cbegin; + const IfcVector2 &proj_point = *cbegin; if (LikelyBorder(proj_point - last_proj_point)) { - skiplist[skiplist.size()-1] = true; + skiplist[skiplist.size() - 1] = true; } } } // ------------------------------------------------------------------------------------------------ -AI_FORCE_INLINE bool LikelyDiagonal(IfcVector2 vdelta) -{ +AI_FORCE_INLINE bool LikelyDiagonal(IfcVector2 vdelta) { vdelta.x = std::fabs(vdelta.x); vdelta.y = std::fabs(vdelta.y); - return (std::fabs(vdelta.x-vdelta.y) < 0.8 * std::max(vdelta.x, vdelta.y)); + return (std::fabs(vdelta.x - vdelta.y) < 0.8 * std::max(vdelta.x, vdelta.y)); } // ------------------------------------------------------------------------------------------------ -void FindLikelyCrossingLines(ContourVector::iterator current) -{ - SkipList& skiplist = (*current).skiplist; +void FindLikelyCrossingLines(ContourVector::iterator current) { + SkipList &skiplist = (*current).skiplist; IfcVector2 last_proj_point; const Contour::const_iterator cbegin = (*current).contour.begin(), cend = (*current).contour.end(); for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) { - const IfcVector2& proj_point = *cit; + const IfcVector2 &proj_point = *cit; if (cit != cbegin) { IfcVector2 vdelta = proj_point - last_proj_point; @@ -843,16 +798,15 @@ void FindLikelyCrossingLines(ContourVector::iterator current) // handle last segment if (LikelyDiagonal(*cbegin - last_proj_point)) { - skiplist[skiplist.size()-1] = true; + skiplist[skiplist.size() - 1] = true; } } // ------------------------------------------------------------------------------------------------ -size_t CloseWindows(ContourVector& contours, - const IfcMatrix4& minv, - OpeningRefVector& contours_to_openings, - TempMesh& curmesh) -{ +size_t CloseWindows(ContourVector &contours, + const IfcMatrix4 &minv, + OpeningRefVector &contours_to_openings, + TempMesh &curmesh) { size_t closed = 0; // For all contour points, check if one of the assigned openings does // already have points assigned to it. In this case, assume this is @@ -868,11 +822,11 @@ size_t CloseWindows(ContourVector& contours, if ((*it).IsInvalid()) { continue; } - OpeningRefs& refs = contours_to_openings[std::distance(contours.begin(), it)]; + OpeningRefs &refs = contours_to_openings[std::distance(contours.begin(), it)]; bool has_other_side = false; - for(const TempOpening* opening : refs) { - if(!opening->wallPoints.empty()) { + for (const TempOpening *opening : refs) { + if (!opening->wallPoints.empty()) { has_other_side = true; break; } @@ -895,7 +849,7 @@ size_t CloseWindows(ContourVector& contours, // be artifacts caused by numerical inaccuracies or other bugs in polyclipper // and our own code. Since rectangular openings are by far the most frequent // case, it is worth filtering for this corner case. - if((*it).is_rectangular) { + if ((*it).is_rectangular) { FindLikelyCrossingLines(it); } @@ -906,21 +860,21 @@ size_t CloseWindows(ContourVector& contours, curmesh.mVerts.reserve(curmesh.mVerts.size() + (*it).contour.size() * 4); curmesh.mVertcnt.reserve(curmesh.mVertcnt.size() + (*it).contour.size()); - bool reverseCountourFaces = false; + bool reverseCountourFaces = false; // compare base poly normal and contour normal to detect if we need to reverse the face winding - if(curmesh.mVertcnt.size() > 0) { - IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); - - std::vector worldSpaceContourVtx(it->contour.size()); - - for(size_t a = 0; a < it->contour.size(); ++a) - worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); - - IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); - - reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; - } + if (curmesh.mVertcnt.size() > 0) { + IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); + + std::vector worldSpaceContourVtx(it->contour.size()); + + for (size_t a = 0; a < it->contour.size(); ++a) + worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); + + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); + + reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; + } // XXX this algorithm is really a bit inefficient - both in terms // of constant factor and of asymptotic runtime. @@ -933,22 +887,22 @@ size_t CloseWindows(ContourVector& contours, bool drop_this_edge = false; for (Contour::const_iterator cit = cbegin; cit != cend; ++cit, drop_this_edge = *skipit++) { - const IfcVector2& proj_point = *cit; + const IfcVector2 &proj_point = *cit; // Locate the closest opposite point. This should be a good heuristic to // connect only the points that are really intended to be connected. IfcFloat best = static_cast(1e10); IfcVector3 bestv; - const IfcVector3 world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f); + const IfcVector3 world_point = minv * IfcVector3(proj_point.x, proj_point.y, 0.0f); - for(const TempOpening* opening : refs) { - for(const IfcVector3& other : opening->wallPoints) { + for (const TempOpening *opening : refs) { + for (const IfcVector3 &other : opening->wallPoints) { const IfcFloat sqdist = (world_point - other).SquareLength(); if (sqdist < best) { // avoid self-connections - if(sqdist < 1e-5) { + if (sqdist < 1e-5) { continue; } @@ -961,8 +915,7 @@ size_t CloseWindows(ContourVector& contours, if (drop_this_edge) { curmesh.mVerts.pop_back(); curmesh.mVerts.pop_back(); - } - else { + } else { curmesh.mVerts.push_back(((cit == cbegin) != reverseCountourFaces) ? world_point : bestv); curmesh.mVerts.push_back(((cit == cbegin) != reverseCountourFaces) ? bestv : world_point); @@ -989,24 +942,22 @@ size_t CloseWindows(ContourVector& contours, curmesh.mVertcnt.pop_back(); curmesh.mVerts.pop_back(); curmesh.mVerts.pop_back(); - } - else { + } else { curmesh.mVerts.push_back(reverseCountourFaces ? start0 : start1); curmesh.mVerts.push_back(reverseCountourFaces ? start1 : start0); } } } - } - else { + } else { const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end(); - for(TempOpening* opening : refs) { + for (TempOpening *opening : refs) { ai_assert(opening->wallPoints.empty()); opening->wallPoints.reserve(opening->wallPoints.capacity() + (*it).contour.size()); for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) { - const IfcVector2& proj_point = *cit; - opening->wallPoints.push_back(minv * IfcVector3(proj_point.x,proj_point.y,0.0f)); + const IfcVector2 &proj_point = *cit; + opening->wallPoints.push_back(minv * IfcVector3(proj_point.x, proj_point.y, 0.0f)); } } } @@ -1015,12 +966,11 @@ size_t CloseWindows(ContourVector& contours, } // ------------------------------------------------------------------------------------------------ -void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh) -{ +void Quadrify(const std::vector &bbs, TempMesh &curmesh) { ai_assert(curmesh.IsEmpty()); std::vector quads; - quads.reserve(bbs.size()*4); + quads.reserve(bbs.size() * 4); // sort openings by x and y axis as a preliminiary to the QuadrifyPart() algorithm XYSortedField field; @@ -1028,26 +978,25 @@ void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh) 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); + field[(*it).first] = std::distance(bbs.begin(), it); } - QuadrifyPart(IfcVector2(),one_vec,field,bbs,quads); + QuadrifyPart(IfcVector2(), one_vec, field, bbs, quads); ai_assert(!(quads.size() % 4)); - curmesh.mVertcnt.resize(quads.size()/4,4); + curmesh.mVertcnt.resize(quads.size() / 4, 4); curmesh.mVerts.reserve(quads.size()); - for(const IfcVector2& v2 : quads) { + for (const IfcVector2 &v2 : quads) { curmesh.mVerts.push_back(IfcVector3(v2.x, v2.y, static_cast(0.0))); } } // ------------------------------------------------------------------------------------------------ -void Quadrify(const ContourVector& contours, TempMesh& curmesh) -{ +void Quadrify(const ContourVector &contours, TempMesh &curmesh) { std::vector bbs; bbs.reserve(contours.size()); - for(const ContourVector::value_type& val : contours) { + for (const ContourVector::value_type &val : contours) { bbs.push_back(val.bb); } @@ -1055,30 +1004,28 @@ void Quadrify(const ContourVector& contours, TempMesh& curmesh) } // ------------------------------------------------------------------------------------------------ -IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh& in_mesh, - bool &ok, IfcVector3& nor_out) -{ - const std::vector& in_verts = in_mesh.mVerts; +IfcMatrix4 ProjectOntoPlane(std::vector &out_contour, const TempMesh &in_mesh, + bool &ok, IfcVector3 &nor_out) { + const std::vector &in_verts = in_mesh.mVerts; ok = true; IfcMatrix4 m = IfcMatrix4(DerivePlaneCoordinateSpace(in_mesh, ok, nor_out)); - if(!ok) { + if (!ok) { return IfcMatrix4(); } #ifdef ASSIMP_BUILD_DEBUG const IfcFloat det = m.Determinant(); - ai_assert(std::fabs(det-1) < 1e-5); + ai_assert(std::fabs(det - 1) < 1e-5); #endif IfcFloat zcoord = 0; out_contour.reserve(in_verts.size()); - IfcVector3 vmin, vmax; MinMaxChooser()(vmin, vmax); // Project all points into the new coordinate system, collect min/max verts on the way - for(const IfcVector3& x : in_verts) { + for (const IfcVector3 &x : in_verts) { 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 @@ -1093,7 +1040,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh vmin = std::min(vv, vmin); vmax = std::max(vv, vmax); - out_contour.push_back(IfcVector2(vv.x,vv.y)); + out_contour.push_back(IfcVector2(vv.x, vv.y)); } zcoord /= in_verts.size(); @@ -1102,13 +1049,13 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh // [0,1] range. This gives us a consistent data range so all epsilons // used below can be constants. vmax -= vmin; - for(IfcVector2& vv : out_contour) { - vv.x = (vv.x - vmin.x) / vmax.x; - vv.y = (vv.y - vmin.y) / vmax.y; + for (IfcVector2 &vv : out_contour) { + vv.x = (vv.x - vmin.x) / vmax.x; + vv.y = (vv.y - vmin.y) / vmax.y; // sanity rounding - vv = std::max(vv,IfcVector2()); - vv = std::min(vv,one_vec); + vv = std::max(vv, IfcVector2()); + vv = std::min(vv, one_vec); } IfcMatrix4 mult; @@ -1123,15 +1070,15 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh // debug code to verify correctness #ifdef ASSIMP_BUILD_DEBUG std::vector out_contour2; - for(const IfcVector3& x : in_verts) { - const IfcVector3& vv = m * x; + for (const IfcVector3 &x : in_verts) { + const IfcVector3 &vv = m * x; - out_contour2.push_back(IfcVector2(vv.x,vv.y)); + out_contour2.push_back(IfcVector2(vv.x, vv.y)); ai_assert(std::fabs(vv.z) < vmax.z + 1e-8); } - for(size_t i = 0; i < out_contour.size(); ++i) { - ai_assert((out_contour[i]-out_contour2[i]).SquareLength() < 1e-6); + for (size_t i = 0; i < out_contour.size(); ++i) { + ai_assert((out_contour[i] - out_contour2[i]).SquareLength() < 1e-6); } #endif @@ -1139,13 +1086,12 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh } // ------------------------------------------------------------------------------------------------ -bool GenerateOpenings(std::vector& openings, - const std::vector& nors, - TempMesh& curmesh, - bool check_intersection, - bool generate_connection_geometry, - const IfcVector3& wall_extrusion_axis) -{ +bool GenerateOpenings(std::vector &openings, + const std::vector &nors, + TempMesh &curmesh, + bool check_intersection, + bool generate_connection_geometry, + const IfcVector3 &wall_extrusion_axis) { OpeningRefVector contours_to_openings; // Try to derive a solid base plane within the current surface for use as @@ -1157,47 +1103,43 @@ bool GenerateOpenings(std::vector& openings, std::vector contour_flat; IfcVector3 nor; - const IfcMatrix4 m = ProjectOntoPlane(contour_flat, curmesh, ok, nor); - if(!ok) { + const IfcMatrix4 m = ProjectOntoPlane(contour_flat, curmesh, ok, nor); + if (!ok) { return false; } // Obtain inverse transform for getting back to world space later on - const IfcMatrix4 minv = IfcMatrix4(m).Inverse(); + const IfcMatrix4 mInverse = IfcMatrix4(m).Inverse(); // Compute bounding boxes for all 2D openings in projection space ContourVector contours; - std::vector temp_contour; - std::vector temp_contour2; - + std::vector temp_contour, temp_contour2; IfcVector3 wall_extrusion_axis_norm = wall_extrusion_axis; wall_extrusion_axis_norm.Normalize(); - for(TempOpening& opening :openings) { + for (TempOpening &opening : openings) { // extrusionDir may be 0,0,0 on case where the opening mesh is not an // IfcExtrudedAreaSolid but something else (i.e. a brep) IfcVector3 norm_extrusion_dir = opening.extrusionDir; if (norm_extrusion_dir.SquareLength() > 1e-10) { norm_extrusion_dir.Normalize(); - } - else { + } else { norm_extrusion_dir = IfcVector3(); } - TempMesh* profile_data = opening.profileMesh.get(); + TempMesh *profile_data = opening.profileMesh.get(); bool is_2d_source = false; if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) { - if(std::fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) { + if (std::fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) { // horizontal extrusion if (std::fabs(norm_extrusion_dir * nor) > 0.9) { profile_data = opening.profileMesh2D.get(); is_2d_source = true; } - } - else { + } else { // vertical extrusion if (std::fabs(norm_extrusion_dir * nor) > 0.9) { profile_data = opening.profileMesh2D.get(); @@ -1207,7 +1149,7 @@ bool GenerateOpenings(std::vector& openings, } std::vector profile_verts = profile_data->mVerts; std::vector profile_vertcnts = profile_data->mVertcnt; - if(profile_verts.size() <= 2) { + if (profile_verts.size() <= 2) { continue; } @@ -1225,23 +1167,24 @@ bool GenerateOpenings(std::vector& openings, // take both - this would likely cause major screwup of vertex // winding, producing errors as late as in CloseWindows()). IfcFloat dmin, dmax; - MinMaxChooser()(dmin,dmax); + MinMaxChooser()(dmin, dmax); temp_contour.clear(); temp_contour2.clear(); - IfcVector2 vpmin,vpmax; - MinMaxChooser()(vpmin,vpmax); + IfcVector2 vpmin, vpmax; + MinMaxChooser()(vpmin, vpmax); - IfcVector2 vpmin2,vpmax2; - MinMaxChooser()(vpmin2,vpmax2); + IfcVector2 vpmin2, vpmax2; + MinMaxChooser()(vpmin2, vpmax2); for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) { bool side_flag = true; if (!is_2d_source) { - const IfcVector3 face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ - (profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize(); + 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 abs_dot_face_nor = std::abs(nor * face_nor); if (abs_dot_face_nor < 0.9) { @@ -1253,30 +1196,29 @@ bool GenerateOpenings(std::vector& openings, } for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) { - const IfcVector3& x = profile_verts[vi_total]; + const IfcVector3 &x = profile_verts[vi_total]; const IfcVector3 v = m * x; IfcVector2 vv(v.x, v.y); //if(check_intersection) { - dmin = std::min(dmin, v.z); - dmax = std::max(dmax, v.z); + dmin = std::min(dmin, v.z); + dmax = std::max(dmax, v.z); //} // sanity rounding - vv = std::max(vv,IfcVector2()); - vv = std::min(vv,one_vec); + vv = std::max(vv, IfcVector2()); + vv = std::min(vv, one_vec); - if(side_flag) { - vpmin = std::min(vpmin,vv); - vpmax = std::max(vpmax,vv); - } - else { - vpmin2 = std::min(vpmin2,vv); - vpmax2 = std::max(vpmax2,vv); + if (side_flag) { + vpmin = std::min(vpmin, vv); + vpmax = std::max(vpmax, vv); + } else { + vpmin2 = std::min(vpmin2, vv); + vpmax2 = std::max(vpmax2, vv); } - std::vector& store = side_flag ? temp_contour : temp_contour2; + std::vector &store = side_flag ? temp_contour : temp_contour2; if (!IsDuplicateVertex(vv, store)) { store.push_back(vv); @@ -1286,8 +1228,8 @@ bool GenerateOpenings(std::vector& openings, if (temp_contour2.size() > 2) { ai_assert(!is_2d_source); - const IfcVector2 area = vpmax-vpmin; - const IfcVector2 area2 = vpmax2-vpmin2; + const IfcVector2 area = vpmax - vpmin; + const IfcVector2 area2 = vpmax2 - vpmin2; if (temp_contour.size() <= 2 || std::fabs(area2.x * area2.y) > std::fabs(area.x * area.y)) { temp_contour.swap(temp_contour2); @@ -1295,30 +1237,30 @@ bool GenerateOpenings(std::vector& openings, vpmin = vpmin2; } } - if(temp_contour.size() <= 2) { + if (temp_contour.size() <= 2) { continue; } // TODO: This epsilon may be too large - const IfcFloat epsilon = std::fabs(dmax-dmin) * 0.0001; - if (!is_2d_source && check_intersection && (0 < dmin-epsilon || 0 > dmax+epsilon)) { + const IfcFloat epsilon = std::fabs(dmax - dmin) * 0.0001; + if (!is_2d_source && check_intersection && (0 < dmin - epsilon || 0 > dmax + epsilon)) { continue; } - BoundingBox bb = BoundingBox(vpmin,vpmax); + BoundingBox bb = BoundingBox(vpmin, vpmax); // Skip over very small openings - these are likely projection errors // (i.e. they don't belong to this side of the wall) - if(std::fabs(vpmax.x - vpmin.x) * std::fabs(vpmax.y - vpmin.y) < static_cast(1e-10)) { + if (std::fabs(vpmax.x - vpmin.x) * std::fabs(vpmax.y - vpmin.y) < static_cast(1e-10)) { continue; } - std::vector joined_openings(1, &opening); + std::vector joined_openings(1, &opening); bool is_rectangle = temp_contour.size() == 4; // See if this BB intersects or is in close adjacency to any other BB we have so far. - for (ContourVector::iterator it = contours.begin(); it != contours.end(); ) { - const BoundingBox& ibb = (*it).bb; + for (ContourVector::iterator it = contours.begin(); it != contours.end();) { + const BoundingBox &ibb = (*it).bb; if (BoundingBoxesOverlapping(ibb, bb)) { @@ -1326,22 +1268,22 @@ bool GenerateOpenings(std::vector& openings, is_rectangle = false; } - const std::vector& other = (*it).contour; + const std::vector &other = (*it).contour; ClipperLib::ExPolygons poly; // First check whether subtracting the old contour (to which ibb belongs) // from the new contour (to which bb belongs) yields an updated bb which // no longer overlaps ibb MakeDisjunctWindowContours(other, temp_contour, poly); - if(poly.size() == 1) { + if (poly.size() == 1) { - const BoundingBox newbb = GetBoundingBox(poly[0].outer); - if (!BoundingBoxesOverlapping(ibb, newbb )) { - // Good guy bounding box - bb = newbb ; + const BoundingBox newbb = GetBoundingBox2D(poly[0].outer); + if (!BoundingBoxesOverlapping(ibb, newbb)) { + // Good guy bounding box + bb = newbb; - ExtractVerticesFromClipper(poly[0].outer, temp_contour, false); - continue; + ExtractVerticesFromClipper(poly[0].outer, temp_contour, false); + continue; } } @@ -1352,13 +1294,11 @@ bool GenerateOpenings(std::vector& openings, if (poly.size() > 1) { return TryAddOpenings_Poly2Tri(openings, nors, curmesh); - } - else if (poly.size() == 0) { + } else if (poly.size() == 0) { IFCImporter::LogWarn("ignoring duplicate opening"); temp_contour.clear(); break; - } - else { + } else { IFCImporter::LogVerboseDebug("merging overlapping openings"); ExtractVerticesFromClipper(poly[0].outer, temp_contour, false); @@ -1368,10 +1308,10 @@ bool GenerateOpenings(std::vector& openings, // Update contour-to-opening tables accordingly if (generate_connection_geometry) { - std::vector& t = contours_to_openings[std::distance(contours.begin(),it)]; + std::vector &t = contours_to_openings[std::distance(contours.begin(), it)]; joined_openings.insert(joined_openings.end(), t.begin(), t.end()); - contours_to_openings.erase(contours_to_openings.begin() + std::distance(contours.begin(),it)); + contours_to_openings.erase(contours_to_openings.begin() + std::distance(contours.begin(), it)); } contours.erase(it); @@ -1386,11 +1326,11 @@ bool GenerateOpenings(std::vector& openings, ++it; } - if(!temp_contour.empty()) { + if (!temp_contour.empty()) { if (generate_connection_geometry) { - contours_to_openings.push_back(std::vector( - joined_openings.begin(), - joined_openings.end())); + contours_to_openings.push_back(std::vector( + joined_openings.begin(), + joined_openings.end())); } contours.push_back(ProjectedWindowContour(temp_contour, bb, is_rectangle)); @@ -1408,7 +1348,7 @@ bool GenerateOpenings(std::vector& openings, // Generate a base subdivision into quads to accommodate the given list // of window bounding boxes. - Quadrify(contours,curmesh); + Quadrify(contours, curmesh); // Run a sanity cleanup pass on the window contours to avoid generating // artifacts during the contour generation phase later on. @@ -1417,7 +1357,7 @@ bool GenerateOpenings(std::vector& openings, // 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(contours,openings, curmesh); + InsertWindowContours(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 @@ -1426,24 +1366,23 @@ bool GenerateOpenings(std::vector& openings, CleanupOuterContour(contour_flat, curmesh); // Undo the projection and get back to world (or local object) space - for(IfcVector3& v3 : curmesh.mVerts) { - v3 = minv * v3; + for (IfcVector3 &v3 : curmesh.mVerts) { + v3 = mInverse * v3; } // Generate window caps to connect the symmetric openings on both sides // of the wall. if (generate_connection_geometry) { - CloseWindows(contours, minv, contours_to_openings, curmesh); + CloseWindows(contours, mInverse, contours_to_openings, curmesh); } return true; } // ------------------------------------------------------------------------------------------------ -bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std::vector& nors, - TempMesh& curmesh) -{ +bool TryAddOpenings_Poly2Tri(const std::vector &openings, const std::vector &nors, + TempMesh &curmesh) { IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings"); - std::vector& out = curmesh.mVerts; + std::vector &out = curmesh.mVerts; bool result = false; @@ -1456,8 +1395,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: return false; } - const IfcMatrix3 minv = IfcMatrix3(m).Inverse(); - + const IfcMatrix3 mInverse = IfcMatrix3(m).Inverse(); IfcFloat coord = -1; @@ -1468,14 +1406,13 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: MinMaxChooser()(vmin, vmax); // Move all points into the new coordinate system, collecting min/max verts on the way - for(IfcVector3& x : out) { + for (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) { @@ -1487,7 +1424,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: 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)); + contour_flat.push_back(IfcVector2(vv.x, vv.y)); } // With the current code in DerivePlaneCoordinateSpace, @@ -1502,7 +1439,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: ClipperLib::ExPolygons clipped; ClipperLib::Polygons holes_union; - IfcVector3 wall_extrusion; bool first = true; @@ -1511,22 +1447,22 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: ClipperLib::Clipper clipper_holes; size_t c = 0; - for(const TempOpening& t :openings) { - const IfcVector3& outernor = nors[c++]; + for (const TempOpening &t : openings) { + const IfcVector3 &outernor = nors[c++]; const IfcFloat dot = nor * outernor; - if (std::fabs(dot)<1.f-1e-6f) { + if (std::fabs(dot) < 1.f - 1e-6f) { continue; } - const std::vector& va = t.profileMesh->mVerts; - if(va.size() <= 2) { + const std::vector &va = t.profileMesh->mVerts; + if (va.size() <= 2) { continue; } std::vector contour; - for(const IfcVector3& xx : t.profileMesh->mVerts) { - IfcVector3 vv = m * xx, vv_extr = m * (xx + t.extrusionDir); + for (const IfcVector3 &xx : t.profileMesh->mVerts) { + IfcVector3 vv = m * xx, vv_extr = m * (xx + t.extrusionDir); const bool is_extruded_side = std::fabs(vv.z - coord) > std::fabs(vv_extr.z - coord); if (first) { @@ -1534,27 +1470,27 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: if (dot > 0.f) { wall_extrusion = t.extrusionDir; if (is_extruded_side) { - wall_extrusion = - wall_extrusion; + wall_extrusion = -wall_extrusion; } } } // XXX should not be necessary - but it is. Why? For precision reasons? vv = is_extruded_side ? vv_extr : vv; - contour.push_back(IfcVector2(vv.x,vv.y)); + contour.push_back(IfcVector2(vv.x, vv.y)); } ClipperLib::Polygon hole; - for(IfcVector2& pip : contour) { - pip.x = (pip.x - vmin.x) / vmax.x; - pip.y = (pip.y - vmin.y) / vmax.y; + for (IfcVector2 &pip : contour) { + pip.x = (pip.x - vmin.x) / vmax.x; + pip.y = (pip.y - vmin.y) / vmax.y; - hole.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + hole.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (!ClipperLib::Orientation(hole)) { std::reverse(hole.begin(), hole.end()); - // assert(ClipperLib::Orientation(hole)); + // assert(ClipperLib::Orientation(hole)); } /*ClipperLib::Polygons pol_temp(1), pol_temp2(1); @@ -1563,12 +1499,12 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0); hole = pol_temp2[0];*/ - clipper_holes.AddPolygon(hole,ClipperLib::ptSubject); + clipper_holes.AddPolygon(hole, ClipperLib::ptSubject); } - clipper_holes.Execute(ClipperLib::ctUnion,holes_union, - ClipperLib::pftNonZero, - ClipperLib::pftNonZero); + clipper_holes.Execute(ClipperLib::ctUnion, holes_union, + ClipperLib::pftNonZero, + ClipperLib::pftNonZero); if (holes_union.empty()) { return false; @@ -1578,29 +1514,27 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: // to obtain the final polygon to feed into the triangulator. { ClipperLib::Polygon poly; - for(IfcVector2& pip : contour_flat) { - pip.x = (pip.x - vmin.x) / vmax.x; - pip.y = (pip.y - vmin.y) / vmax.y; + for (IfcVector2 &pip : contour_flat) { + pip.x = (pip.x - vmin.x) / vmax.x; + pip.y = (pip.y - vmin.y) / vmax.y; - poly.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + poly.push_back(ClipperLib::IntPoint(to_int64(pip.x), to_int64(pip.y))); } if (ClipperLib::Orientation(poly)) { std::reverse(poly.begin(), poly.end()); } clipper_holes.Clear(); - clipper_holes.AddPolygon(poly,ClipperLib::ptSubject); + clipper_holes.AddPolygon(poly, ClipperLib::ptSubject); - clipper_holes.AddPolygons(holes_union,ClipperLib::ptClip); - clipper_holes.Execute(ClipperLib::ctDifference,clipped, - ClipperLib::pftNonZero, - ClipperLib::pftNonZero); + clipper_holes.AddPolygons(holes_union, ClipperLib::ptClip); + clipper_holes.Execute(ClipperLib::ctDifference, clipped, + ClipperLib::pftNonZero, + ClipperLib::pftNonZero); } - } - catch (const char* sx) { - IFCImporter::LogError("Ifc: error during polygon clipping, skipping openings for this face: (Clipper: " - + std::string(sx) + ")"); + } catch (const char *sx) { + IFCImporter::LogError("Ifc: error during polygon clipping, skipping openings for this face: (Clipper: " + std::string(sx) + ")"); return false; } @@ -1611,40 +1545,37 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: old_verts.swap(curmesh.mVerts); old_vertcnt.swap(curmesh.mVertcnt); - std::vector< std::vector > contours; - for(ClipperLib::ExPolygon& clip : clipped) { + std::vector> contours; + for (ClipperLib::ExPolygon &clip : clipped) { contours.clear(); // Build the outer polygon contour line for feeding into poly2tri - std::vector contour_points; - for(ClipperLib::IntPoint& point : clip.outer) { - contour_points.push_back( new p2t::Point(from_int64(point.X), from_int64(point.Y)) ); + std::vector contour_points; + for (ClipperLib::IntPoint &point : clip.outer) { + contour_points.push_back(new p2t::Point(from_int64(point.X), from_int64(point.Y))); } - p2t::CDT* cdt ; + p2t::CDT *cdt; try { // Note: this relies on custom modifications in poly2tri to raise runtime_error's // instead if assertions. These failures are not debug only, they can actually // happen in production use if the input data is broken. An assertion would be // inappropriate. cdt = new p2t::CDT(contour_points); - } - catch(const std::exception& e) { - IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " - + std::string(e.what()) + ")"); + } catch (const std::exception &e) { + IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " + std::string(e.what()) + ")"); continue; } - // Build the poly2tri inner contours for all holes we got from ClipperLib - for(ClipperLib::Polygon& opening : clip.holes) { + for (ClipperLib::Polygon &opening : clip.holes) { - contours.push_back(std::vector()); - std::vector& contour = contours.back(); + contours.push_back(std::vector()); + std::vector &contour = contours.back(); - for(ClipperLib::IntPoint& point : opening) { - contour.push_back( new p2t::Point(from_int64(point.X), from_int64(point.Y)) ); + for (ClipperLib::IntPoint &point : opening) { + contour.push_back(new p2t::Point(from_int64(point.X), from_int64(point.Y))); } cdt->AddHole(contour); @@ -1653,26 +1584,23 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: try { // Note: See above cdt->Triangulate(); - } - catch(const std::exception& e) { - IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " - + std::string(e.what()) + ")"); + } catch (const std::exception &e) { + IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " + std::string(e.what()) + ")"); continue; } - const std::vector tris = cdt->GetTriangles(); + const std::vector tris = cdt->GetTriangles(); // Collect the triangles we just produced - for(p2t::Triangle* tri : tris) { - for(int i = 0; i < 3; ++i) { + for (p2t::Triangle *tri : tris) { + for (int i = 0; i < 3; ++i) { const IfcVector2 v = IfcVector2( - static_cast( tri->GetPoint(i)->x ), - static_cast( tri->GetPoint(i)->y ) - ); + static_cast(tri->GetPoint(i)->x), + static_cast(tri->GetPoint(i)->y)); ai_assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0); - const IfcVector3 v3 = minv * IfcVector3(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y,coord) ; + const IfcVector3 v3 = mInverse * IfcVector3(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y, coord); curmesh.mVerts.push_back(v3); } @@ -1684,8 +1612,8 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: if (!result) { // revert -- it's a shame, but better than nothing - curmesh.mVerts.insert(curmesh.mVerts.end(),old_verts.begin(), old_verts.end()); - curmesh.mVertcnt.insert(curmesh.mVertcnt.end(),old_vertcnt.begin(), old_vertcnt.end()); + curmesh.mVerts.insert(curmesh.mVerts.end(), old_verts.begin(), old_verts.end()); + curmesh.mVertcnt.insert(curmesh.mVertcnt.end(), old_vertcnt.begin(), old_vertcnt.end()); IFCImporter::LogError("Ifc: revert, could not generate openings for this wall"); } @@ -1693,9 +1621,8 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: return result; } - - } // ! IFC -} // ! Assimp +} // namespace IFC +} // namespace Assimp #undef to_int64 #undef from_int64 diff --git a/code/AssetLib/IFC/IFCUtil.cpp b/code/AssetLib/IFC/IFCUtil.cpp index ffd0d680e..723dd6dcc 100644 --- a/code/AssetLib/IFC/IFCUtil.cpp +++ b/code/AssetLib/IFC/IFCUtil.cpp @@ -192,7 +192,7 @@ void TempMesh::ComputePolygonNormals(std::vector &normals, bool norm size_t vidx = std::accumulate(mVertcnt.begin(), begin, 0); for (iit = begin; iit != end; vidx += *iit++) { if (!*iit) { - normals.push_back(IfcVector3()); + normals.emplace_back(); continue; } for (size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) { @@ -206,7 +206,7 @@ void TempMesh::ComputePolygonNormals(std::vector &normals, bool norm ++cnt; } - normals.push_back(IfcVector3()); + normals.emplace_back(); NewellNormal<4, 4, 4>(normals.back(), *iit, &temp[0], &temp[1], &temp[2], Capa); } diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index 2b7fba3b2..99f6ef15c 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -47,40 +46,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_IFCUTIL_H #define INCLUDED_IFCUTIL_H -#include "AssetLib/IFC/IFCReaderGen_2x3.h" #include "AssetLib/IFC/IFCLoader.h" +#include "AssetLib/IFC/IFCReaderGen_2x3.h" #include "AssetLib/Step/STEPFile.h" -#include #include +#include struct aiNode; namespace Assimp { namespace IFC { - typedef double IfcFloat; - - // IfcFloat-precision math data types - typedef aiVector2t IfcVector2; - typedef aiVector3t IfcVector3; - typedef aiMatrix4x4t IfcMatrix4; - typedef aiMatrix3x3t IfcMatrix3; - typedef aiColor4t IfcColor4; +using IfcFloat = double; +// IfcFloat-precision math data types +using IfcVector2 = aiVector2t; +using IfcVector3 = aiVector3t; +using IfcMatrix4 = aiMatrix4x4t; +using IfcMatrix3 = aiMatrix3x3t; +using IfcColor4 = aiColor4t; // ------------------------------------------------------------------------------------------------ // Helper for std::for_each to delete all heap-allocated items in a container // ------------------------------------------------------------------------------------------------ -template +template struct delete_fun { - void operator()(T* del) { + void operator()(T *del) { delete del; } }; - - // ------------------------------------------------------------------------------------------------ // Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons. // ------------------------------------------------------------------------------------------------ @@ -89,32 +85,29 @@ struct TempMesh { std::vector mVertcnt; // utilities - aiMesh* ToMesh(); + aiMesh *ToMesh(); void Clear(); - void Transform(const IfcMatrix4& mat); + void Transform(const IfcMatrix4 &mat); IfcVector3 Center() const; - void Append(const TempMesh& other); + void Append(const TempMesh &other); bool IsEmpty() const; void RemoveAdjacentDuplicates(); void RemoveDegenerates(); void FixupFaceOrientation(); - static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true); + static IfcVector3 ComputePolygonNormal(const IfcVector3 *vtcs, size_t cnt, bool normalize = true); IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const; - void ComputePolygonNormals(std::vector& normals, bool normalize = true, size_t ofs = 0) const; - void Swap(TempMesh& other); + void ComputePolygonNormals(std::vector &normals, bool normalize = true, size_t ofs = 0) const; + void Swap(TempMesh &other); }; -inline -bool TempMesh::IsEmpty() const { +inline bool TempMesh::IsEmpty() const { return mVerts.empty() && mVertcnt.empty(); } - // ------------------------------------------------------------------------------------------------ // Temporary representation of an opening in a wall or a floor // ------------------------------------------------------------------------------------------------ -struct TempOpening -{ +struct TempOpening { const IFC::Schema_2x3::IfcSolidModel *solid; IfcVector3 extrusionDir; @@ -129,90 +122,98 @@ struct TempOpening std::vector wallPoints; // ------------------------------------------------------------------------------ - TempOpening() - : solid() - , extrusionDir() - , profileMesh() - { + TempOpening() : + solid(), + extrusionDir(), + profileMesh() { + // empty } // ------------------------------------------------------------------------------ - TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, - std::shared_ptr profileMesh, - std::shared_ptr profileMesh2D) - : solid(solid) - , extrusionDir(extrusionDir) - , profileMesh(profileMesh) - , profileMesh2D(profileMesh2D) - { + TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, + std::shared_ptr profileMesh, + std::shared_ptr profileMesh2D) : + solid(solid), + extrusionDir(extrusionDir), + profileMesh(profileMesh), + profileMesh2D(profileMesh2D) { + // empty } // ------------------------------------------------------------------------------ - void Transform(const IfcMatrix4& mat); // defined later since TempMesh is not complete yet - - + void Transform(const IfcMatrix4 &mat); // defined later since TempMesh is not complete yet // ------------------------------------------------------------------------------ // Helper to sort openings by distance from a given base point struct DistanceSorter { - DistanceSorter(const IfcVector3& base) : base(base) {} + DistanceSorter(const IfcVector3 &base) : + base(base) {} - bool operator () (const TempOpening& a, const TempOpening& b) const { - return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength(); + bool operator()(const TempOpening &a, const TempOpening &b) const { + return (a.profileMesh->Center() - base).SquareLength() < (b.profileMesh->Center() - base).SquareLength(); } IfcVector3 base; }; }; - // ------------------------------------------------------------------------------------------------ // Intermediate data storage during conversion. Keeps everything and a bit more. // ------------------------------------------------------------------------------------------------ -struct ConversionData -{ - ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings) - : len_scale(1.0) - , angle_scale(-1.0) - , db(db) - , proj(proj) - , out(out) - , settings(settings) - , apply_openings() - , collect_openings() - {} +struct ConversionData { + ConversionData(const STEP::DB &db, const IFC::Schema_2x3::IfcProject &proj, aiScene *out, const IFCImporter::Settings &settings) : + len_scale(1.0), + angle_scale(-1.0), + plane_angle_in_radians(true), + db(db), + proj(proj), + out(out), + wcs(), + meshes(), + materials(), + cached_meshes(), + cached_materials(), + settings(settings), + apply_openings(nullptr), + collect_openings(nullptr), + already_processed() { + // empty + } ~ConversionData() { - std::for_each(meshes.begin(),meshes.end(),delete_fun()); - std::for_each(materials.begin(),materials.end(),delete_fun()); + std::for_each(meshes.begin(), meshes.end(), delete_fun()); + std::for_each(materials.begin(), materials.end(), delete_fun()); } IfcFloat len_scale, angle_scale; bool plane_angle_in_radians; - const STEP::DB& db; - const IFC::Schema_2x3::IfcProject& proj; - aiScene* out; + const STEP::DB &db; + const IFC::Schema_2x3::IfcProject &proj; + aiScene *out; IfcMatrix4 wcs; - std::vector meshes; - std::vector materials; + std::vector meshes; + std::vector materials; struct MeshCacheIndex { - const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex; - MeshCacheIndex() : item(nullptr), matindex(0) { } - MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { } - bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; } - bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); } + const IFC::Schema_2x3::IfcRepresentationItem *item; + unsigned int matindex; + MeshCacheIndex() : + item(nullptr), matindex(0) {} + MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem *i, unsigned int mi) : + item(i), matindex(mi) {} + bool operator==(const MeshCacheIndex &o) const { return item == o.item && matindex == o.matindex; } + bool operator<(const MeshCacheIndex &o) const { return item < o.item || (item == o.item && matindex < o.matindex); } }; - typedef std::map > MeshCache; + using MeshCache = std::map>; MeshCache cached_meshes; - typedef std::map MaterialCache; + using MaterialCache = std::map; MaterialCache cached_materials; - const IFCImporter::Settings& settings; + const IFCImporter::Settings &settings; // Intermediate arrays used to resolve openings in walls: only one of them // can be given at a time. apply_openings if present if the current element @@ -220,34 +221,33 @@ struct ConversionData // collect_openings is present only if the current element is an // IfcOpeningElement, for which all the geometry needs to be preserved // for later processing by a parent, which is a wall. - std::vector* apply_openings; - std::vector* collect_openings; + std::vector *apply_openings; + std::vector *collect_openings; std::set already_processed; }; - // ------------------------------------------------------------------------------------------------ // Binary predicate to compare vectors with a given, quadratic epsilon. // ------------------------------------------------------------------------------------------------ struct FuzzyVectorCompare { - FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {} - bool operator()(const IfcVector3& a, const IfcVector3& b) { - return std::abs((a-b).SquareLength()) < epsilon; + FuzzyVectorCompare(IfcFloat epsilon) : + epsilon(epsilon) {} + bool operator()(const IfcVector3 &a, const IfcVector3 &b) { + return std::abs((a - b).SquareLength()) < epsilon; } const IfcFloat epsilon; }; - // ------------------------------------------------------------------------------------------------ // Ordering predicate to totally order R^2 vectors first by x and then by y // ------------------------------------------------------------------------------------------------ struct XYSorter { // sort first by X coordinates, then by Y coordinates - bool operator () (const IfcVector2&a, const IfcVector2& b) const { + bool operator()(const IfcVector2 &a, const IfcVector2 &b) const { if (a.x == b.x) { return a.y < b.y; } @@ -255,67 +255,61 @@ struct XYSorter { } }; - - // conversion routines for common IFC entities, implemented in IFCUtil.cpp -void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in); -void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base); -void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in); -void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in); -void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in); -void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z); -void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in); -void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in); -void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& in); -void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv); -void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op); -bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in); -IfcFloat ConvertSIPrefix(const std::string& prefix); - +void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourRgb &in); +void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourOrFactor &in, ConversionData &conv, const aiColor4D *base); +void ConvertCartesianPoint(IfcVector3 &out, const Schema_2x3::IfcCartesianPoint &in); +void ConvertDirection(IfcVector3 &out, const Schema_2x3::IfcDirection &in); +void ConvertVector(IfcVector3 &out, const Schema_2x3::IfcVector &in); +void AssignMatrixAxes(IfcMatrix4 &out, const IfcVector3 &x, const IfcVector3 &y, const IfcVector3 &z); +void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement3D &in); +void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement2D &in); +void ConvertAxisPlacement(IfcVector3 &axis, IfcVector3 &pos, const IFC::Schema_2x3::IfcAxis1Placement &in); +void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement &in, ConversionData &conv); +void ConvertTransformOperator(IfcMatrix4 &out, const Schema_2x3::IfcCartesianTransformationOperator &op); +bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN &in); +IfcFloat ConvertSIPrefix(const std::string &prefix); // IFCProfile.cpp -bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv); -bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, ConversionData& conv); +bool ProcessProfile(const Schema_2x3::IfcProfileDef &prof, TempMesh &meshout, ConversionData &conv); +bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv); // IFCMaterial.cpp -unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat); +unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData &conv, bool forceDefaultMat); // IFCGeometry.cpp -IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut); -bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set& mesh_indices, ConversionData& conv); -void AssignAddedMeshes(std::set& mesh_indices,aiNode* nd,ConversionData& /*conv*/); +IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh &curmesh, bool &ok, IfcVector3 &norOut); +bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem &item, unsigned int matid, std::set &mesh_indices, ConversionData &conv); +void AssignAddedMeshes(std::set &mesh_indices, aiNode *nd, ConversionData & /*conv*/); -void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout, - ConversionData& conv); +void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid &swept, TempMesh &meshout, + ConversionData &conv); -void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result, - ConversionData& conv, bool collect_openings); +void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid &solid, TempMesh &result, + ConversionData &conv, bool collect_openings); // IFCBoolean.cpp -void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv); -void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result, - const TempMesh& first_operand, - ConversionData& conv); - -void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace* hs, TempMesh& result, - const TempMesh& first_operand, - ConversionData& conv); -void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid* as, TempMesh& result, - const TempMesh& first_operand, - ConversionData& conv); +void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &result, ConversionData &conv); +void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid *hs, TempMesh &result, + const TempMesh &first_operand, + ConversionData &conv); +void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace *hs, TempMesh &result, + const TempMesh &first_operand, + ConversionData &conv); +void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid *as, TempMesh &result, + const TempMesh &first_operand, + ConversionData &conv); // IFCOpenings.cpp -bool GenerateOpenings(std::vector& openings, - const std::vector& nors, - TempMesh& curmesh, - bool check_intersection, - bool generate_connection_geometry, - const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0)); - - +bool GenerateOpenings(std::vector &openings, + const std::vector &nors, + TempMesh &curmesh, + bool check_intersection, + bool generate_connection_geometry, + const IfcVector3 &wall_extrusion_axis = IfcVector3(0, 1, 0)); // IFCCurve.cpp @@ -324,8 +318,8 @@ bool GenerateOpenings(std::vector& openings, // ------------------------------------------------------------------------------------------------ class CurveError { public: - CurveError(const std::string& s) - : mStr(s) { + CurveError(const std::string &s) : + mStr(s) { // empty } @@ -338,18 +332,17 @@ public: // ------------------------------------------------------------------------------------------------ class Curve { protected: - Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv) - : base_entity(base_entity) - , conv(conv) { + Curve(const Schema_2x3::IfcCurve &base_entity, ConversionData &conv) : + base_entity(base_entity), + conv(conv) { // empty } public: - typedef std::pair ParamRange; + using ParamRange = std::pair; virtual ~Curve() {} - // check if a curve is closed virtual bool IsClosed() const = 0; @@ -359,56 +352,53 @@ public: // try to match a point on the curve to a given parameter // for self-intersecting curves, the result is not ambiguous and // it is undefined which parameter is returned. - virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const; + virtual bool ReverseEval(const IfcVector3 &val, IfcFloat ¶mOut) const; // get the range of the curve (both inclusive). // +inf and -inf are valid return values, the curve is not bounded in such a case. - virtual std::pair GetParametricRange() const = 0; + virtual std::pair GetParametricRange() const = 0; IfcFloat GetParametricRangeDelta() const; // estimate the number of sample points that this curve will require - virtual size_t EstimateSampleCount(IfcFloat start,IfcFloat end) const; + virtual size_t EstimateSampleCount(IfcFloat start, IfcFloat end) const; // intelligently sample the curve based on the current settings // and append the result to the mesh - virtual void SampleDiscrete(TempMesh& out,IfcFloat start,IfcFloat end) const; + virtual void SampleDiscrete(TempMesh &out, IfcFloat start, IfcFloat end) const; #ifdef ASSIMP_BUILD_DEBUG // check if a particular parameter value lies within the well-defined range bool InRange(IfcFloat) const; #endif - static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv); + static Curve *Convert(const IFC::Schema_2x3::IfcCurve &, ConversionData &conv); protected: - const Schema_2x3::IfcCurve& base_entity; - ConversionData& conv; + const Schema_2x3::IfcCurve &base_entity; + ConversionData &conv; }; - // -------------------------------------------------------------------------------- // A BoundedCurve always holds the invariant that GetParametricRange() // never returns infinite values. // -------------------------------------------------------------------------------- class BoundedCurve : public Curve { public: - BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv) - : Curve(entity,conv) - {} + BoundedCurve(const Schema_2x3::IfcBoundedCurve &entity, ConversionData &conv) : + Curve(entity, conv) {} public: - - bool IsClosed() const; + bool IsClosed() const override; public: - // sample the entire curve - void SampleDiscrete(TempMesh& out) const; + void SampleDiscrete(TempMesh &out) const; using Curve::SampleDiscrete; }; // IfcProfile.cpp -bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, ConversionData& conv); -} -} +bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv); + +} // namespace IFC +} // namespace Assimp #endif diff --git a/code/Common/PolyTools.h b/code/Common/PolyTools.h index 35e88ef9a..f9c5bd6cb 100644 --- a/code/Common/PolyTools.h +++ b/code/Common/PolyTools.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -51,134 +50,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -// ------------------------------------------------------------------------------- -/** Compute the signed area of a triangle. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline double GetArea2D(const T &v1, const T &v2, const T &v3) { - return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y)); -} +template +class TBoundingBox2D { + T mMin, mMax; -// ------------------------------------------------------------------------------- -/** Test if a given point p2 is on the left side of the line formed by p0-p1. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline bool OnLeftSideOfLine2D(const T &p0, const T &p1, const T &p2) { - return GetArea2D(p0, p2, p1) > 0; -} - -// ------------------------------------------------------------------------------- -/** Test if a given point is inside a given triangle in R2. - * The function accepts an unconstrained template parameter for use with - * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ -template -inline bool PointInTriangle2D(const T &p0, const T &p1, const T &p2, const T &pp) { - // Point in triangle test using baryzentric coordinates - const aiVector2D v0 = p1 - p0; - const aiVector2D v1 = p2 - p0; - const aiVector2D v2 = pp - p0; - - double dot00 = v0 * v0; - double dot01 = v0 * v1; - double dot02 = v0 * v2; - double dot11 = v1 * v1; - double dot12 = v1 * v2; - - const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); - dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom; - dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom; - - return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1); -} - -// ------------------------------------------------------------------------------- -/** Check whether the winding order of a given polygon is counter-clockwise. - * The function accepts an unconstrained template parameter, but is intended - * to be used only with aiVector2D and aiVector3D (z axis is ignored, only - * x and y are taken into account). - * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++ - */ -template -inline bool IsCCW(T *in, size_t npoints) { - double aa, bb, cc, b, c, theta; - double convex_turn; - double convex_sum = 0; - - ai_assert(npoints >= 3); - - for (size_t i = 0; i < npoints - 2; i++) { - aa = ((in[i + 2].x - in[i].x) * (in[i + 2].x - in[i].x)) + - ((-in[i + 2].y + in[i].y) * (-in[i + 2].y + in[i].y)); - - bb = ((in[i + 1].x - in[i].x) * (in[i + 1].x - in[i].x)) + - ((-in[i + 1].y + in[i].y) * (-in[i + 1].y + in[i].y)); - - cc = ((in[i + 2].x - in[i + 1].x) * - (in[i + 2].x - in[i + 1].x)) + - ((-in[i + 2].y + in[i + 1].y) * - (-in[i + 2].y + in[i + 1].y)); - - b = std::sqrt(bb); - c = std::sqrt(cc); - theta = std::acos((bb + cc - aa) / (2 * b * c)); - - if (OnLeftSideOfLine2D(in[i], in[i + 2], in[i + 1])) { - // if (convex(in[i].x, in[i].y, - // in[i+1].x, in[i+1].y, - // in[i+2].x, in[i+2].y)) { - convex_turn = AI_MATH_PI_F - theta; - convex_sum += convex_turn; - } else { - convex_sum -= AI_MATH_PI_F - theta; - } + TBoundingBox2D( const T &min, const T &max ) : + mMin( min ), + mMax( max ) { + // empty } - aa = ((in[1].x - in[npoints - 2].x) * - (in[1].x - in[npoints - 2].x)) + - ((-in[1].y + in[npoints - 2].y) * - (-in[1].y + in[npoints - 2].y)); +}; - bb = ((in[0].x - in[npoints - 2].x) * - (in[0].x - in[npoints - 2].x)) + - ((-in[0].y + in[npoints - 2].y) * - (-in[0].y + in[npoints - 2].y)); - - cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) + - ((-in[1].y + in[0].y) * (-in[1].y + in[0].y)); - - b = std::sqrt(bb); - c = std::sqrt(cc); - theta = std::acos((bb + cc - aa) / (2 * b * c)); - - //if (convex(in[npoints-2].x, in[npoints-2].y, - // in[0].x, in[0].y, - // in[1].x, in[1].y)) { - if (OnLeftSideOfLine2D(in[npoints - 2], in[1], in[0])) { - convex_turn = AI_MATH_PI_F - theta; - convex_sum += convex_turn; - } else { - convex_sum -= AI_MATH_PI_F - theta; - } - - return convex_sum >= (2 * AI_MATH_PI_F); -} +using BoundingBox2D = TBoundingBox2D; // ------------------------------------------------------------------------------- -/** Compute the normal of an arbitrary polygon in R3. - * - * The code is based on Newell's formula, that is a polygons normal is the ratio - * of its area when projected onto the three coordinate axes. - * - * @param out Receives the output normal - * @param num Number of input vertices - * @param x X data source. x[ofs_x*n] is the n'th element. - * @param y Y data source. y[ofs_y*n] is the y'th element - * @param z Z data source. z[ofs_z*n] is the z'th element - * - * @note The data arrays must have storage for at least num+2 elements. Using - * this method is much faster than the 'other' NewellNormal() - */ +/// Compute the normal of an arbitrary polygon in R3. +/// +/// The code is based on Newell's formula, that is a polygons normal is the ratio +/// of its area when projected onto the three coordinate axes. +/// +/// @param out Receives the output normal +/// @param num Number of input vertices +/// @param x X data source. x[ofs_x*n] is the n'th element. +/// @param y Y data source. y[ofs_y*n] is the y'th element +/// @param z Z data source. z[ofs_z*n] is the z'th element +/// +/// @note The data arrays must have storage for at least num+2 elements. Using +/// this method is much faster than the 'other' NewellNormal() +// ------------------------------------------------------------------------------- template inline void NewellNormal(aiVector3t &out, size_t num, TReal *x, TReal *y, TReal *z, size_t bufferSize) { ai_assert(bufferSize > num); @@ -223,6 +122,69 @@ inline void NewellNormal(aiVector3t &out, size_t num, TReal *x, TReal *y, out = aiVector3t(sum_yz, sum_zx, sum_xy); } +// ------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- +template +inline aiMatrix4x4t DerivePlaneCoordinateSpace(const aiVector3t *vertices, size_t numVertices, bool &ok, aiVector3t &norOut) { + const aiVector3t *out = vertices; + aiMatrix4x4t m; + + ok = true; + + const size_t s = numVertices; + + const aiVector3t &any_point = out[numVertices - 1u]; + aiVector3t nor; + + // The input polygon is arbitrarily shaped, therefore we might need some tries + // until we find a suitable normal. Note that Newell's algorithm would give + // a more robust result, but this variant also gives us a suitable first + // axis for the 2D coordinate space on the polygon plane, exploiting the + // fact that the input polygon is nearly always a quad. + bool done = false; + size_t idx = 0; + for (size_t i = 0; !done && i < s - 2; done || ++i) { + idx = i; + for (size_t j = i + 1; j < s - 1; ++j) { + nor = -((out[i] - any_point) ^ (out[j] - any_point)); + if (std::fabs(nor.Length()) > 1e-8f) { + done = true; + break; + } + } + } + + if (!done) { + ok = false; + return m; + } + + nor.Normalize(); + norOut = nor; + + aiVector3t r = (out[idx] - any_point); + r.Normalize(); + + // Reconstruct orthonormal basis + // XXX use Gram Schmidt for increased robustness + aiVector3t u = r ^ nor; + u.Normalize(); + + m.a1 = r.x; + m.a2 = r.y; + m.a3 = r.z; + + m.b1 = u.x; + m.b2 = u.y; + m.b3 = u.z; + + m.c1 = -nor.x; + m.c2 = -nor.y; + m.c3 = -nor.z; + + return m; +} + } // namespace Assimp #endif diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index e5defdeb0..17dfaca4a 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -45,25 +43,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the FindDegenerates post-process step. */ - - // internal headers -#include "ProcessHelper.h" #include "FindDegenerates.h" +#include "ProcessHelper.h" #include using namespace Assimp; //remove mesh at position 'index' from the scene -static void removeMesh(aiScene* pScene, unsigned const index); +static void removeMesh(aiScene *pScene, unsigned const index); //correct node indices to meshes and remove references to deleted mesh -static void updateSceneGraph(aiNode* pNode, unsigned const index); +static void updateSceneGraph(aiNode *pNode, unsigned const index); // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -FindDegeneratesProcess::FindDegeneratesProcess() -: mConfigRemoveDegenerates( false ) -, mConfigCheckAreaOfTriangle( false ){ +FindDegeneratesProcess::FindDegeneratesProcess() : + mConfigRemoveDegenerates(false), + mConfigCheckAreaOfTriangle(false) { // empty } @@ -75,24 +71,23 @@ FindDegeneratesProcess::~FindDegeneratesProcess() { // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. -bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { +bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const { return 0 != (pFlags & aiProcess_FindDegenerates); } // ------------------------------------------------------------------------------------------------ // Setup import configuration -void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { +void FindDegeneratesProcess::SetupProperties(const Importer *pImp) { // Get the current value of AI_CONFIG_PP_FD_REMOVE - mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); - mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); + mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0)); + mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA)); } // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. -void FindDegeneratesProcess::Execute( aiScene* pScene) { +void FindDegeneratesProcess::Execute(aiScene *pScene) { ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { //Do not process point cloud, ExecuteOnMesh works only with faces data if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { removeMesh(pScene, i); @@ -102,12 +97,12 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); } -static void removeMesh(aiScene* pScene, unsigned const index) { +static void removeMesh(aiScene *pScene, unsigned const index) { //we start at index and copy the pointers one position forward //save the mesh pointer to delete it later auto delete_me = pScene->mMeshes[index]; for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { - pScene->mMeshes[i] = pScene->mMeshes[i+1]; + pScene->mMeshes[i] = pScene->mMeshes[i + 1]; } pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr; --(pScene->mNumMeshes); @@ -117,15 +112,15 @@ static void removeMesh(aiScene* pScene, unsigned const index) { updateSceneGraph(pScene->mRootNode, index); } -static void updateSceneGraph(aiNode* pNode, unsigned const index) { +static void updateSceneGraph(aiNode *pNode, unsigned const index) { for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { if (pNode->mMeshes[i] > index) { --(pNode->mMeshes[i]); continue; } if (pNode->mMeshes[i] == index) { - for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { - pNode->mMeshes[j] = pNode->mMeshes[j+1]; + for (unsigned j = i; j < pNode->mNumMeshes - 1; ++j) { + pNode->mMeshes[j] = pNode->mMeshes[j + 1]; } --(pNode->mNumMeshes); --i; @@ -138,50 +133,50 @@ static void updateSceneGraph(aiNode* pNode, unsigned const index) { } } -static ai_real heron( ai_real a, ai_real b, ai_real c ) { +static ai_real heron(ai_real a, ai_real b, ai_real c) { ai_real s = (a + b + c) / 2; - ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); + ai_real area = pow((s * (s - a) * (s - b) * (s - c)), (ai_real)0.5); return area; } -static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { - const ai_real lx = ( vB.x - vA.x ); - const ai_real ly = ( vB.y - vA.y ); - const ai_real lz = ( vB.z - vA.z ); - ai_real a = lx*lx + ly*ly + lz*lz; - ai_real d = pow( a, (ai_real)0.5 ); +static ai_real distance3D(const aiVector3D &vA, aiVector3D &vB) { + const ai_real lx = (vB.x - vA.x); + const ai_real ly = (vB.y - vA.y); + const ai_real lz = (vB.z - vA.z); + ai_real a = lx * lx + ly * ly + lz * lz; + ai_real d = pow(a, (ai_real)0.5); return d; } -static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { +static ai_real calculateAreaOfTriangle(const aiFace &face, aiMesh *mesh) { ai_real area = 0; - aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); - aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); - aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); + aiVector3D vA(mesh->mVertices[face.mIndices[0]]); + aiVector3D vB(mesh->mVertices[face.mIndices[1]]); + aiVector3D vC(mesh->mVertices[face.mIndices[2]]); - ai_real a( distance3D( vA, vB ) ); - ai_real b( distance3D( vB, vC ) ); - ai_real c( distance3D( vC, vA ) ); - area = heron( a, b, c ); + ai_real a(distance3D(vA, vB)); + ai_real b(distance3D(vB, vC)); + ai_real c(distance3D(vC, vA)); + area = heron(a, b, c); return area; } // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported mesh -bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { +bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) { mesh->mPrimitiveTypes = 0; std::vector remove_me; if (mConfigRemoveDegenerates) { - remove_me.resize( mesh->mNumFaces, false ); + remove_me.resize(mesh->mNumFaces, false); } unsigned int deg = 0, limit; - for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { - aiFace& face = mesh->mFaces[a]; + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + aiFace &face = mesh->mFaces[a]; bool first = true; // check whether the face contains degenerated entries @@ -191,43 +186,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { // double points may not come directly after another. limit = face.mNumIndices; if (face.mNumIndices > 4) { - limit = std::min( limit, i+2 ); + limit = std::min(limit, i + 2); } - for (unsigned int t = i+1; t < limit; ++t) { - if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { + for (unsigned int t = i + 1; t < limit; ++t) { + if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) { // we have found a matching vertex position // remove the corresponding index from the array --face.mNumIndices; --limit; for (unsigned int m = t; m < face.mNumIndices; ++m) { - face.mIndices[ m ] = face.mIndices[ m+1 ]; + face.mIndices[m] = face.mIndices[m + 1]; } --t; // NOTE: we set the removed vertex index to an unique value // to make sure the developer gets notified when his // application attempts to access this data. - face.mIndices[ face.mNumIndices ] = 0xdeadbeef; + face.mIndices[face.mNumIndices] = 0xdeadbeef; - if(first) { + if (first) { ++deg; first = false; } - if ( mConfigRemoveDegenerates ) { - remove_me[ a ] = true; + if (mConfigRemoveDegenerates) { + remove_me[a] = true; goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! } } } - if ( mConfigCheckAreaOfTriangle ) { - if ( face.mNumIndices == 3 ) { - ai_real area = calculateAreaOfTriangle( face, mesh ); - if ( area < 1e-6 ) { - if ( mConfigRemoveDegenerates ) { - remove_me[ a ] = true; + if (mConfigCheckAreaOfTriangle) { + if (face.mNumIndices == 3) { + ai_real area = calculateAreaOfTriangle(face, mesh); + if (area < 1e-6) { + if (mConfigRemoveDegenerates) { + remove_me[a] = true; ++deg; goto evil_jump_outside; } @@ -239,8 +234,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { } // We need to update the primitive flags array of the mesh. - switch (face.mNumIndices) - { + switch (face.mNumIndices) { case 1u: mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; break; @@ -254,30 +248,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; break; }; -evil_jump_outside: + evil_jump_outside: continue; } // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import if (mConfigRemoveDegenerates && deg) { unsigned int n = 0; - for (unsigned int a = 0; a < mesh->mNumFaces; ++a) - { - aiFace& face_src = mesh->mFaces[a]; + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { + aiFace &face_src = mesh->mFaces[a]; if (!remove_me[a]) { - aiFace& face_dest = mesh->mFaces[n++]; + aiFace &face_dest = mesh->mFaces[n++]; // Do a manual copy, keep the index array face_dest.mNumIndices = face_src.mNumIndices; - face_dest.mIndices = face_src.mIndices; + face_dest.mIndices = face_src.mIndices; if (&face_src != &face_dest) { // clear source face_src.mNumIndices = 0; face_src.mIndices = nullptr; } - } - else { + } else { // Otherwise delete it if we don't need this face delete[] face_src.mIndices; face_src.mIndices = nullptr; @@ -295,7 +287,7 @@ evil_jump_outside: } if (deg && !DefaultLogger::isNullLogger()) { - ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); + ASSIMP_LOG_WARN_F("Found ", deg, " degenerated primitives"); } return false; } diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index fc55d975a..2987705f6 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -47,34 +47,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * The triangulation algorithm will handle concave or convex polygons. * Self-intersecting or non-planar polygons are not rejected, but * they're probably not triangulated correctly. - * - * DEBUG SWITCHES - do not enable any of them in release builds: - * - * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - * - generates vertex colors to represent the face winding order. - * the first vertex of a polygon becomes red, the last blue. - * AI_BUILD_TRIANGULATE_DEBUG_POLYS - * - dump all polygons and their triangulation sequences to - * a file */ + #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS #include "PostProcessing/TriangulateProcess.h" #include "Common/PolyTools.h" #include "PostProcessing/ProcessHelper.h" +#include "contrib/poly2tri/poly2tri/poly2tri.h" + #include #include -//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING -#define AI_BUILD_TRIANGULATE_DEBUG_POLYS - -#define POLY_GRID_Y 40 -#define POLY_GRID_X 70 -#define POLY_GRID_XPAD 20 -#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" - -using namespace Assimp; +namespace Assimp { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer @@ -128,6 +114,7 @@ static bool validateNumIndices(aiMesh *mesh) { return bNeed; } +// ------------------------------------------------------------------------------------------------ static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, bool &getNormals) { numOut = maxOut = 0; getNormals = true; @@ -146,6 +133,113 @@ static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, } } +// ------------------------------------------------------------------------------------------------ +static void quad2Triangles(const aiFace &face, const aiVector3D *verts, aiFace *curOut) { + // quads can have at maximum one concave vertex. Determine + // this vertex (if it exists) and start tri-fanning from + // it. + unsigned int start_vertex = 0; + for (unsigned int i = 0; i < 4; ++i) { + const aiVector3D &v0 = verts[face.mIndices[(i + 3) % 4]]; + const aiVector3D &v1 = verts[face.mIndices[(i + 2) % 4]]; + const aiVector3D &v2 = verts[face.mIndices[(i + 1) % 4]]; + + const aiVector3D &v = verts[face.mIndices[i]]; + + aiVector3D left = (v0 - v); + aiVector3D diag = (v1 - v); + aiVector3D right = (v2 - v); + + left.Normalize(); + diag.Normalize(); + right.Normalize(); + + const float angle = std::acos(left * diag) + std::acos(right * diag); + if (angle > AI_MATH_PI_F) { + // this is the concave point + start_vertex = i; + break; + } + } + + const unsigned int temp[] = { face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3] }; + + aiFace &nface = *curOut++; + nface.mNumIndices = 3; + nface.mIndices = face.mIndices; + + nface.mIndices[0] = temp[start_vertex]; + nface.mIndices[1] = temp[(start_vertex + 1) % 4]; + nface.mIndices[2] = temp[(start_vertex + 2) % 4]; + + aiFace &sface = *curOut++; + sface.mNumIndices = 3; + sface.mIndices = new unsigned int[3]; + + sface.mIndices[0] = temp[start_vertex]; + sface.mIndices[1] = temp[(start_vertex + 2) % 4]; + sface.mIndices[2] = temp[(start_vertex + 3) % 4]; +} + +// ------------------------------------------------------------------------------------------------ +bool getContourFromePolyline(aiFace &face, aiMesh *pMesh, std::vector &contour, + aiMatrix4x4 &m, aiVector3D &vmin, aiVector3D &vmax, ai_real &zcoord) { + aiVector3D normal; + bool ok = true; + m = DerivePlaneCoordinateSpace(pMesh->mVertices, pMesh->mNumVertices, ok, normal); + if (!ok) { + false; + } + for (unsigned int i = 0; i < face.mNumIndices; ++i) { + unsigned int index = face.mIndices[i]; + + const aiVector3D vv = m * pMesh->mVertices[index]; + // 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(std::fabs(coord - vv.z) < 1e-3f); + // } + zcoord += vv.z; + vmin = std::min(vv, vmin); + vmax = std::max(vv, vmax); + + contour.push_back(new p2t::Point(vv.x, vv.y)); + } + + zcoord /= pMesh->mNumVertices; + + // Further improve the projection by mapping the entire working set into + // [0,1] range. This gives us a consistent data range so all epsilons + // used below can be constants. + vmax -= vmin; + const aiVector2D one_vec(1, 1); + + for (p2t::Point* &vv : contour) { + vv->x = (vv->x - vmin.x) / vmax.x; + vv->y = (vv->y - vmin.y) / vmax.y; + + // sanity rounding + aiVector2D cur_vv((ai_real) vv->x, (ai_real)vv->y); + cur_vv = std::max(cur_vv, aiVector2D()); + cur_vv = std::min(cur_vv, one_vec); + } + + aiMatrix4x4 mult; + mult.a1 = static_cast(1.0) / vmax.x; + mult.b2 = static_cast(1.0) / vmax.y; + + mult.a4 = -vmin.x * mult.a1; + mult.b4 = -vmin.y * mult.b2; + mult.c4 = -zcoord; + m = mult * m; + + return true; +} + // ------------------------------------------------------------------------------------------------ // Triangulates the given mesh. bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { @@ -153,9 +247,11 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { if (!pMesh->mPrimitiveTypes) { if (!validateNumIndices(pMesh)) { + ASSIMP_LOG_DEBUG("Error while validating number of indices."); return false; } } else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { + ASSIMP_LOG_DEBUG("???!"); return false; } @@ -163,17 +259,9 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { size_t numOut = 0, max_out = 0; bool getNormals = true; calulateNumOutputFaces(pMesh, numOut, max_out, getNormals); - - // Just another check whether aiMesh::mPrimitiveTypes is correct - ai_assert(numOut != pMesh->mNumFaces); - - aiVector3D *nor_out = nullptr; - - // if we don't have normals yet, but expect them to be a cheap side - // product of triangulation anyway, allocate storage for them. - if (!pMesh->mNormals && getNormals) { - // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals - // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + if (numOut == pMesh->mNumFaces) { + ASSIMP_LOG_DEBUG("Error while generating contour."); + return false; } // the output mesh will contain triangles, but no polys anymore @@ -186,18 +274,6 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { std::vector temp_verts(max_out + 2); // Apply vertex colors to represent the face winding? -#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - if (!pMesh->mColors[0]) - pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; - else - new (pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; - - aiColor4D *clr = pMesh->mColors[0]; -#endif - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - FILE *fout = fopen(POLY_OUTPUT_FILE, "a"); -#endif const aiVector3D *verts = pMesh->mVertices; @@ -206,20 +282,6 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { for (unsigned int a = 0; a < pMesh->mNumFaces; a++) { aiFace &face = pMesh->mFaces[a]; - unsigned int *idx = face.mIndices; - int num = (int)face.mNumIndices, ear = 0, tmp, prev = num - 1, next = 0, max = num; - - // Apply vertex colors to represent the face winding? -#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING - for (unsigned int i = 0; i < face.mNumIndices; ++i) { - aiColor4D &c = clr[idx[i]]; - c.r = (i + 1) / (float)max; - c.b = 1.f - c.r; - } -#endif - - aiFace *const last_face = curOut; - // if it's a simple point,line or triangle: just copy it if (face.mNumIndices <= 3) { aiFace &nface = *curOut++; @@ -227,278 +289,52 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) { nface.mIndices = face.mIndices; face.mIndices = nullptr; - continue; - } - // optimized code for quadrilaterals - else if (face.mNumIndices == 4) { - - // quads can have at maximum one concave vertex. Determine - // this vertex (if it exists) and start tri-fanning from - // it. - unsigned int start_vertex = 0; - for (unsigned int i = 0; i < 4; ++i) { - const aiVector3D &v0 = verts[face.mIndices[(i + 3) % 4]]; - const aiVector3D &v1 = verts[face.mIndices[(i + 2) % 4]]; - const aiVector3D &v2 = verts[face.mIndices[(i + 1) % 4]]; - - const aiVector3D &v = verts[face.mIndices[i]]; - - aiVector3D left = (v0 - v); - aiVector3D diag = (v1 - v); - aiVector3D right = (v2 - v); - - left.Normalize(); - diag.Normalize(); - right.Normalize(); - - const float angle = std::acos(left * diag) + std::acos(right * diag); - if (angle > AI_MATH_PI_F) { - // this is the concave point - start_vertex = i; - break; - } - } - - const unsigned int temp[] = { face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3] }; - - aiFace &nface = *curOut++; - nface.mNumIndices = 3; - nface.mIndices = face.mIndices; - - nface.mIndices[0] = temp[start_vertex]; - nface.mIndices[1] = temp[(start_vertex + 1) % 4]; - nface.mIndices[2] = temp[(start_vertex + 2) % 4]; - - aiFace &sface = *curOut++; - sface.mNumIndices = 3; - sface.mIndices = new unsigned int[3]; - - sface.mIndices[0] = temp[start_vertex]; - sface.mIndices[1] = temp[(start_vertex + 2) % 4]; - sface.mIndices[2] = temp[(start_vertex + 3) % 4]; - - // prevent double deletion of the indices field + } else if (face.mNumIndices == 4) { + // optimized code for quadrilaterals + quad2Triangles(face, verts, curOut); face.mIndices = nullptr; - continue; } else { - // A polygon with more than 3 vertices can be either concave or convex. - // Usually everything we're getting is convex and we could easily - // triangulate by tri-fanning. However, LightWave is probably the only - // modeling suite to make extensive use of highly concave, monster polygons ... - // so we need to apply the full 'ear cutting' algorithm to get it right. - - // RERQUIREMENT: polygon is expected to be simple and *nearly* planar. - // We project it onto a plane to get a 2d triangle. - - // Collect all vertices of of the polygon. - for (tmp = 0; tmp < max; ++tmp) { - temp_verts3d[tmp] = verts[idx[tmp]]; + std::vector contour; + aiMatrix4x4 m; + aiVector3D vmin, vmax; + ai_real zcoord = -1; + if (!getContourFromePolyline(face, pMesh, contour, m, vmin, vmax, zcoord)) { + ASSIMP_LOG_DEBUG("Error while generating contour."); + continue; } - - // Get Newell-Normal of the polygon. Store it for future use if it's a polygon-only mesh - aiVector3D n; - NewellNormal<3, 3, 3>(n, max, &temp_verts3d.front().x, &temp_verts3d.front().y, &temp_verts3d.front().z, Capa); - if (nor_out) { - for (tmp = 0; tmp < max; ++tmp) - nor_out[idx[tmp]] = n; - } - - // Select largest normal coordinate to ignore for projection - const float ax = (n.x > 0 ? n.x : -n.x); - const float ay = (n.y > 0 ? n.y : -n.y); - const float az = (n.z > 0 ? n.z : -n.z); - - unsigned int ac = 0, bc = 1; // no z coord. projection to xy - float inv = n.z; - if (ax > ay) { - if (ax > az) { // no x coord. projection to yz - ac = 1; - bc = 2; - inv = n.x; + p2t::CDT cdt(contour); + cdt.Triangulate(); + const std::vector tris = cdt.GetTriangles(); + const aiMatrix4x4 matInv = m.Inverse(); + for (p2t::Triangle *tri : tris) { + curOut->mNumIndices = 3; + curOut->mIndices = new unsigned int[curOut->mNumIndices]; + for (int i = 0; i < 3; ++i) { + const aiVector2D v = aiVector2D(static_cast(tri->GetPoint(i)->x), static_cast(tri->GetPoint(i)->y)); +// ai_assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0); + const aiVector3D v3 = matInv * aiVector3D(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y, zcoord); + temp_verts3d.emplace_back(v3); + curOut->mIndices[i] = (unsigned int) temp_verts3d.size()-1; } - } else if (ay > az) { // no y coord. projection to zy - ac = 2; - bc = 0; - inv = n.y; - } - - // Swap projection axes to take the negated projection vector into account - if (inv < 0.f) { - std::swap(ac, bc); - } - - for (tmp = 0; tmp < max; ++tmp) { - temp_verts[tmp].x = verts[idx[tmp]][ac]; - temp_verts[tmp].y = verts[idx[tmp]][bc]; - done[tmp] = false; - } - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - // plot the plane onto which we mapped the polygon to a 2D ASCII pic - aiVector2D bmin, bmax; - ArrayBounds(&temp_verts[0], max, bmin, bmax); - - char grid[POLY_GRID_Y][POLY_GRID_X + POLY_GRID_XPAD]; - std::fill_n((char *)grid, POLY_GRID_Y * (POLY_GRID_X + POLY_GRID_XPAD), ' '); - - for (int i = 0; i < max; ++i) { - const aiVector2D &v = (temp_verts[i] - bmin) / (bmax - bmin); - const size_t x = static_cast(v.x * (POLY_GRID_X - 1)), y = static_cast(v.y * (POLY_GRID_Y - 1)); - char *loc = grid[y] + x; - if (grid[y][x] != ' ') { - for (; *loc != ' '; ++loc) - ; - *loc++ = '_'; - } - *(loc + ::ai_snprintf(loc, POLY_GRID_XPAD, "%i", i)) = ' '; - } - - for (size_t y = 0; y < POLY_GRID_Y; ++y) { - grid[y][POLY_GRID_X + POLY_GRID_XPAD - 1] = '\0'; - fprintf(fout, "%s\n", grid[y]); - } - - fprintf(fout, "\ntriangulation sequence: "); -#endif - - // - // FIXME: currently this is the slow O(kn) variant with a worst case - // complexity of O(n^2) (I think). Can be done in O(n). - while (num > 3) { - - // Find the next ear of the polygon - int num_found = 0; - for (ear = next;; prev = ear, ear = next) { - - // break after we looped two times without a positive match - for (next = ear + 1; done[(next >= max ? next = 0 : next)]; ++next) - ; - if (next < ear) { - if (++num_found == 2) { - break; - } - } - const aiVector2D *pnt1 = &temp_verts[ear], - *pnt0 = &temp_verts[prev], - *pnt2 = &temp_verts[next]; - - // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. - if (OnLeftSideOfLine2D(*pnt0, *pnt2, *pnt1)) { - continue; - } - - // and no other point may be contained in this triangle - for (tmp = 0; tmp < max; ++tmp) { - - // We need to compare the actual values because it's possible that multiple indexes in - // the polygon are referring to the same position. concave_polygon.obj is a sample - // - // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in - // PointInTriangle() I'm guessing that it's actually possible to construct - // input data that would cause us to end up with no ears. The problem is, - // which epsilon? If we chose a too large value, we'd get wrong results - const aiVector2D &vtmp = temp_verts[tmp]; - if (vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0, *pnt1, *pnt2, vtmp)) { - break; - } - } - if (tmp != max) { - continue; - } - - // this vertex is an ear - break; - } - if (num_found == 2) { - - // Due to the 'two ear theorem', every simple polygon with more than three points must - // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet. - // - - // Instead we're continuing with the standard tri-fanning algorithm which we'd - // use if we had only convex polygons. That's life. - ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - fprintf(fout, "critical error here, no ear found! "); -#endif - num = 0; - break; - } - - aiFace &nface = *curOut++; - nface.mNumIndices = 3; - - if (!nface.mIndices) { - nface.mIndices = new unsigned int[3]; - } - - // setup indices for the new triangle ... - nface.mIndices[0] = prev; - nface.mIndices[1] = ear; - nface.mIndices[2] = next; - - // exclude the ear from most further processing - done[ear] = true; - --num; - } - if (num > 0) { - // We have three indices forming the last 'ear' remaining. Collect them. - aiFace &nface = *curOut++; - nface.mNumIndices = 3; - if (!nface.mIndices) { - nface.mIndices = new unsigned int[3]; - } - - for (tmp = 0; done[tmp]; ++tmp) - ; - nface.mIndices[0] = tmp; - - for (++tmp; done[tmp]; ++tmp) - ; - nface.mIndices[1] = tmp; - - for (++tmp; done[tmp]; ++tmp) - ; - nface.mIndices[2] = tmp; + curOut++; } + face.mIndices = nullptr; } - -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - - for (aiFace *f = last_face; f != curOut; ++f) { - unsigned int *i = f->mIndices; - fprintf(fout, " (%i %i %i)", i[0], i[1], i[2]); - } - - fprintf(fout, "\n*********************************************************************\n"); - fflush(fout); - -#endif - - for (aiFace *f = last_face; f != curOut;) { - unsigned int *i = f->mIndices; - i[0] = idx[i[0]]; - i[1] = idx[i[1]]; - i[2] = idx[i[2]]; - ++f; - } - - delete[] face.mIndices; - face.mIndices = nullptr; } -#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS - fclose(fout); -#endif - - // kill the old faces delete[] pMesh->mFaces; - - // ... and store the new ones pMesh->mFaces = out; + pMesh->mNumVertices = (unsigned int)temp_verts3d.size(); + delete[] pMesh->mVertices; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + for (size_t i = 0; i < temp_verts3d.size(); ++i) { + pMesh->mVertices[i] = temp_verts3d[i]; + } pMesh->mNumFaces = (unsigned int)(curOut - out); /* not necessarily equal to numOut */ + return true; } +} // namespace Assimp + #endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS diff --git a/doc/AssimpDoc_Html/AssimpDoc.chm b/doc/AssimpDoc_Html/AssimpDoc.chm index 26bbb28ed..1d676e62e 100644 Binary files a/doc/AssimpDoc_Html/AssimpDoc.chm and b/doc/AssimpDoc_Html/AssimpDoc.chm differ diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index b510d8c37..b3786f73b 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,5 +1,8 @@ find_package( Doxygen REQUIRED ) +set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) +set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx) + set( HTML_OUTPUT "AssimpDoc_Html" CACHE STRING "Output directory for generated HTML documentation. Defaults to AssimpDoc_Html." ) # Enable Microsoft CHM help style only on Windows diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ffe39f9f7..2362a0f6f 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -1484,7 +1484,7 @@ MAN_LINKS = NO # generate an XML file that captures the structure of # the code including all documentation. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be diff --git a/include/assimp/Hash.h b/include/assimp/Hash.h index 7c360b474..f7d0c7ecd 100644 --- a/include/assimp/Hash.h +++ b/include/assimp/Hash.h @@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#include -#include +#include +#include // ------------------------------------------------------------------------------------------------ // Hashing function taken from @@ -74,8 +74,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ------------------------------------------------------------------------------------------------ inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) { -uint32_t tmp; -int rem; + uint32_t tmp; + int rem; if (!data) return 0; if (!len)len = (uint32_t)::strlen(data); diff --git a/include/assimp/LineSplitter.h b/include/assimp/LineSplitter.h index 6dfeac7eb..78a669b5d 100644 --- a/include/assimp/LineSplitter.h +++ b/include/assimp/LineSplitter.h @@ -49,12 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_LINE_SPLITTER_H #ifdef __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif -#include -#include #include +#include +#include namespace Assimp { @@ -79,37 +79,37 @@ for(LineSplitter splitter(stream);splitter;++splitter) { // ------------------------------------------------------------------------------------------------ class LineSplitter { public: - typedef size_t line_idx; + using line_idx = size_t; // ----------------------------------------- /** construct from existing stream reader note: trim is *always* assumed true if skyp_empty_lines==true */ - LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true); + LineSplitter(StreamReaderLE &stream, bool skip_empty_lines = true, bool trim = true); ~LineSplitter(); // ----------------------------------------- /** pseudo-iterator increment */ - LineSplitter& operator++(); + LineSplitter &operator++(); // ----------------------------------------- - LineSplitter& operator++(int); + LineSplitter &operator++(int); // ----------------------------------------- /** get a pointer to the beginning of a particular token */ - const char* operator[] (size_t idx) const; + const char *operator[](size_t idx) const; // ----------------------------------------- /** extract the start positions of N tokens from the current line*/ template - void get_tokens(const char* (&tokens)[N]) const; + void get_tokens(const char *(&tokens)[N]) const; // ----------------------------------------- /** member access */ - const std::string* operator -> () const; + const std::string *operator->() const; - std::string operator* () const; + std::string operator*() const; // ----------------------------------------- /** boolean context */ @@ -123,47 +123,45 @@ public: // ----------------------------------------- /** access the underlying stream object */ - StreamReaderLE& get_stream(); + StreamReaderLE &get_stream(); // ----------------------------------------- /** !strcmp((*this)->substr(0,strlen(check)),check) */ - bool match_start(const char* check); + bool match_start(const char *check); // ----------------------------------------- /** swallow the next call to ++, return the previous value. */ void swallow_next_increment(); - LineSplitter( const LineSplitter & ) = delete; + LineSplitter(const LineSplitter &) = delete; LineSplitter(LineSplitter &&) = delete; - LineSplitter &operator = ( const LineSplitter & ) = delete; + LineSplitter &operator=(const LineSplitter &) = delete; private: line_idx mIdx; std::string mCur; - StreamReaderLE& mStream; + StreamReaderLE &mStream; bool mSwallow, mSkip_empty_lines, mTrim; }; -AI_FORCE_INLINE -LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) -: mIdx(0) -, mCur() -, mStream(stream) -, mSwallow() -, mSkip_empty_lines(skip_empty_lines) -, mTrim(trim) { +AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE &stream, bool skip_empty_lines, bool trim) : + mIdx(0), + mCur(), + mStream(stream), + mSwallow(), + mSkip_empty_lines(skip_empty_lines), + mTrim(trim) { mCur.reserve(1024); operator++(); mIdx = 0; } -AI_FORCE_INLINE -LineSplitter::~LineSplitter() { +AI_FORCE_INLINE LineSplitter::~LineSplitter() { // empty } AI_FORCE_INLINE -LineSplitter& LineSplitter::operator++() { +LineSplitter &LineSplitter::operator++() { if (mSwallow) { mSwallow = false; return *this; @@ -178,7 +176,8 @@ LineSplitter& LineSplitter::operator++() { while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) { if (s == '\n' || s == '\r') { if (mSkip_empty_lines) { - while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n')); + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n')) + ; if (mStream.GetRemainingSize()) { mStream.IncPtr(-1); } @@ -188,7 +187,8 @@ LineSplitter& LineSplitter::operator++() { mStream.IncPtr(-1); } if (mTrim) { - while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t')); + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t')) + ; if (mStream.GetRemainingSize()) { mStream.IncPtr(-1); } @@ -203,14 +203,13 @@ LineSplitter& LineSplitter::operator++() { return *this; } -AI_FORCE_INLINE -LineSplitter &LineSplitter::operator++(int) { +AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) { return ++(*this); } AI_FORCE_INLINE -const char *LineSplitter::operator[] (size_t idx) const { - const char* s = operator->()->c_str(); +const char *LineSplitter::operator[](size_t idx) const { + const char *s = operator->()->c_str(); SkipSpaces(&s); for (size_t i = 0; i < idx; ++i) { @@ -226,9 +225,8 @@ const char *LineSplitter::operator[] (size_t idx) const { } template -AI_FORCE_INLINE -void LineSplitter::get_tokens(const char* (&tokens)[N]) const { - const char* s = operator->()->c_str(); +AI_FORCE_INLINE void LineSplitter::get_tokens(const char *(&tokens)[N]) const { + const char *s = operator->()->c_str(); SkipSpaces(&s); for (size_t i = 0; i < N; ++i) { @@ -237,18 +235,19 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const { } tokens[i] = s; - for (; *s && !IsSpace(*s); ++s); + for (; *s && !IsSpace(*s); ++s) + ; SkipSpaces(&s); } } AI_FORCE_INLINE -const std::string* LineSplitter::operator -> () const { +const std::string *LineSplitter::operator->() const { return &mCur; } AI_FORCE_INLINE -std::string LineSplitter::operator* () const { +std::string LineSplitter::operator*() const { return mCur; } @@ -273,7 +272,7 @@ StreamReaderLE &LineSplitter::get_stream() { } AI_FORCE_INLINE -bool LineSplitter::match_start(const char* check) { +bool LineSplitter::match_start(const char *check) { const size_t len = ::strlen(check); return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index 8d3c82cf8..67bf9aa90 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -119,7 +119,7 @@ public: /** @brief Normalize the vector with extra check for zero vectors */ aiVector3t& NormalizeSafe(); - /** @brief Componentwise multiplication of two vectors + /** @brief Component-wise multiplication of two vectors * * Note that vec*vec yields the dot product. * @param o Second factor */ @@ -129,7 +129,7 @@ public: }; -typedef aiVector3t aiVector3D; +using aiVector3D = aiVector3t; #else diff --git a/include/assimp/vector3.inl b/include/assimp/vector3.inl index 2765115a2..2d018d14e 100644 --- a/include/assimp/vector3.inl +++ b/include/assimp/vector3.inl @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -306,4 +304,5 @@ aiVector3t operator - ( const aiVector3t& v) { // ------------------------------------------------------------------------------------------------ #endif // __cplusplus + #endif // AI_VECTOR3D_INL_INC diff --git a/test/unit/Common/utPolyTools.cpp b/test/unit/Common/utPolyTools.cpp index 0a3e34151..2932095e2 100644 --- a/test/unit/Common/utPolyTools.cpp +++ b/test/unit/Common/utPolyTools.cpp @@ -65,3 +65,28 @@ TEST_F( utPolyTools, NewellNormalTest ) { z[0] = z[1] = z[2] = z[3] = 0; NewellNormal<3, 3, 3>(out, 4, x, y, z, Capa); } + +TEST_F(utPolyTools, DerivePlaneCoordinateSpaceTest) { + const aiVector3D vertices_ok[3] = { + aiVector3D(-1, -1, 0), + aiVector3D(0, 1, 0), + aiVector3D(1, -1, 0) + + }; + aiVector3D normal; + bool ok = true; + aiMatrix4x4 m_ok = DerivePlaneCoordinateSpace(vertices_ok, 3, ok, normal); + EXPECT_TRUE(ok); + EXPECT_FLOAT_EQ(normal.x, 0.0f); + EXPECT_FLOAT_EQ(normal.y, 0.0f); + EXPECT_FLOAT_EQ(normal.z, 1.0f); + + const aiVector3D vertices_not_ok[3] = { + aiVector3D(-1, -1, 0), + aiVector3D(-1, -1, 0), + aiVector3D(-1, -1, 0) + + }; + aiMatrix4x4 m_not_ok = DerivePlaneCoordinateSpace(vertices_not_ok, 3, ok, normal); + EXPECT_FALSE(ok); +} diff --git a/test/unit/utTriangulate.cpp b/test/unit/utTriangulate.cpp index a95331250..5109195e9 100644 --- a/test/unit/utTriangulate.cpp +++ b/test/unit/utTriangulate.cpp @@ -49,8 +49,8 @@ using namespace Assimp; class TriangulateProcessTest : public ::testing::Test { public: - virtual void SetUp(); - virtual void TearDown(); + void SetUp() override; + void TearDown() override; protected: aiMesh *pcMesh; @@ -132,6 +132,6 @@ TEST_F(TriangulateProcessTest, testTriangulation) { } } - // we should have no valid normal vectors now necause we aren't a pure polygon mesh + // we should have no valid normal vectors now because we aren't a pure polygon mesh EXPECT_TRUE(pcMesh->mNormals == NULL); }