Prototype new triangulation.
parent
4bb2006325
commit
8e4ee11bf3
|
@ -43,18 +43,17 @@ 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 <poly2tri/poly2tri.h>
|
||||
#include <polyclipping/clipper.hpp>
|
||||
#else
|
||||
# include "../contrib/poly2tri/poly2tri/poly2tri.h"
|
||||
#include "../contrib/clipper/clipper.hpp"
|
||||
#include "../contrib/poly2tri/poly2tri/poly2tri.h"
|
||||
#endif
|
||||
|
||||
#include <iterator>
|
||||
|
@ -71,21 +70,17 @@ namespace Assimp {
|
|||
#define from_int64(p) (static_cast<IfcFloat>((p)) / max_ulong64)
|
||||
#define one_vec (IfcVector2(static_cast<IfcFloat>(1.0), static_cast<IfcFloat>(1.0)))
|
||||
|
||||
|
||||
// fallback method to generate wall openings
|
||||
bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening> &openings, const std::vector<IfcVector3> &nors,
|
||||
TempMesh &curmesh);
|
||||
|
||||
|
||||
typedef std::pair< IfcVector2, IfcVector2 > BoundingBox;
|
||||
typedef std::map<IfcVector2,size_t,XYSorter> XYSortedField;
|
||||
|
||||
using BoundingBox = std::pair<IfcVector2, IfcVector2>;
|
||||
using XYSortedField = std::map<IfcVector2, size_t, XYSorter>;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void QuadrifyPart(const IfcVector2 &pmin, const IfcVector2 &pmax, XYSortedField &field,
|
||||
const std::vector<BoundingBox> &bbs,
|
||||
std::vector<IfcVector2>& out)
|
||||
{
|
||||
std::vector<IfcVector2> &out) {
|
||||
if (!(pmin.x - pmax.x) || !(pmin.y - pmax.y)) {
|
||||
return;
|
||||
}
|
||||
|
@ -111,10 +106,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -123,10 +118,10 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField&
|
|||
|
||||
// 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
|
||||
|
@ -157,10 +152,10 @@ 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) {
|
||||
|
@ -173,23 +168,17 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField&
|
|||
}
|
||||
}
|
||||
|
||||
typedef std::vector<IfcVector2> Contour;
|
||||
typedef std::vector<bool> SkipList; // should probably use int for performance reasons
|
||||
using Contour = std::vector<IfcVector2>;
|
||||
using SkipList = std::vector<bool>; // 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();
|
||||
|
@ -204,19 +193,17 @@ struct ProjectedWindowContour
|
|||
}
|
||||
};
|
||||
|
||||
typedef std::vector< ProjectedWindowContour > ContourVector;
|
||||
using ContourVector = std::vector<ProjectedWindowContour>;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool IsDuplicateVertex(const IfcVector2& vv, const std::vector<IfcVector2>& temp_contour)
|
||||
{
|
||||
bool IsDuplicateVertex(const IfcVector2 &vv, const std::vector<IfcVector2> &temp_contour) {
|
||||
// sanity check for duplicate vertices
|
||||
for (const IfcVector2 &cp : temp_contour) {
|
||||
if ((cp - vv).SquareLength() < 1e-5f) {
|
||||
|
@ -228,8 +215,7 @@ bool IsDuplicateVertex(const IfcVector2& vv, const std::vector<IfcVector2>& temp
|
|||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ExtractVerticesFromClipper(const ClipperLib::Polygon &poly, std::vector<IfcVector2> &temp_contour,
|
||||
bool filter_duplicates = false)
|
||||
{
|
||||
bool filter_duplicates = false) {
|
||||
temp_contour.clear();
|
||||
for (const ClipperLib::IntPoint &point : poly) {
|
||||
IfcVector2 vv = IfcVector2(from_int64(point.X), from_int64(point.Y));
|
||||
|
@ -243,8 +229,7 @@ void ExtractVerticesFromClipper(const ClipperLib::Polygon& poly, std::vector<Ifc
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
BoundingBox GetBoundingBox(const ClipperLib::Polygon& poly)
|
||||
{
|
||||
BoundingBox GetBoundingBox2D(const ClipperLib::Polygon &poly) {
|
||||
IfcVector2 newbb_min, newbb_max;
|
||||
MinMaxChooser<IfcVector2>()(newbb_min, newbb_max);
|
||||
|
||||
|
@ -264,8 +249,7 @@ BoundingBox GetBoundingBox(const ClipperLib::Polygon& poly)
|
|||
// ------------------------------------------------------------------------------------------------
|
||||
void InsertWindowContours(const ContourVector &contours,
|
||||
const std::vector<TempOpening> & /*openings*/,
|
||||
TempMesh& curmesh)
|
||||
{
|
||||
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;
|
||||
|
@ -282,10 +266,7 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
verts.insert(contour[n]);
|
||||
}
|
||||
const std::set<IfcVector2, XYSorter>::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
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -310,8 +291,7 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
if (std::fabs(v.x - bb.first.x) < epsilon) {
|
||||
edge.x = bb.first.x;
|
||||
hit = true;
|
||||
}
|
||||
else if (std::fabs(v.x-bb.second.x)<epsilon) {
|
||||
} else if (std::fabs(v.x - bb.second.x) < epsilon) {
|
||||
edge.x = bb.second.x;
|
||||
hit = true;
|
||||
}
|
||||
|
@ -319,8 +299,7 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
if (std::fabs(v.y - bb.first.y) < epsilon) {
|
||||
edge.y = bb.first.y;
|
||||
hit = true;
|
||||
}
|
||||
else if (std::fabs(v.y-bb.second.y)<epsilon) {
|
||||
} else if (std::fabs(v.y - bb.second.y) < epsilon) {
|
||||
edge.y = bb.second.y;
|
||||
hit = true;
|
||||
}
|
||||
|
@ -349,21 +328,18 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
|
||||
if (std::fabs(contour[last_hit].x - bb.first.x) < epsilon) {
|
||||
corner.x = bb.first.x;
|
||||
}
|
||||
else if (std::fabs(contour[last_hit].x-bb.second.x)<epsilon) {
|
||||
} else if (std::fabs(contour[last_hit].x - bb.second.x) < epsilon) {
|
||||
corner.x = bb.second.x;
|
||||
}
|
||||
|
||||
if (std::fabs(contour[last_hit].y - bb.first.y) < epsilon) {
|
||||
corner.y = bb.first.y;
|
||||
}
|
||||
else if (std::fabs(contour[last_hit].y-bb.second.y)<epsilon) {
|
||||
} else if (std::fabs(contour[last_hit].y - bb.second.y) < epsilon) {
|
||||
corner.y = bb.second.y;
|
||||
}
|
||||
|
||||
curmesh.mVerts.push_back(IfcVector3(corner.x, corner.y, 0.0f));
|
||||
}
|
||||
else if (cnt == 1) {
|
||||
} else if (cnt == 1) {
|
||||
// avoid degenerate polygons (also known as lines or points)
|
||||
curmesh.mVerts.erase(curmesh.mVerts.begin() + old, curmesh.mVerts.end());
|
||||
}
|
||||
|
@ -375,8 +351,7 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
if (n == very_first_hit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
very_first_hit = n;
|
||||
}
|
||||
|
||||
|
@ -389,8 +364,7 @@ void InsertWindowContours(const ContourVector& contours,
|
|||
// ------------------------------------------------------------------------------------------------
|
||||
void MergeWindowContours(const std::vector<IfcVector2> &a,
|
||||
const std::vector<IfcVector2> &b,
|
||||
ClipperLib::ExPolygons& out)
|
||||
{
|
||||
ClipperLib::ExPolygons &out) {
|
||||
out.clear();
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
|
@ -423,8 +397,7 @@ void MergeWindowContours (const std::vector<IfcVector2>& a,
|
|||
// Subtract a from b
|
||||
void MakeDisjunctWindowContours(const std::vector<IfcVector2> &a,
|
||||
const std::vector<IfcVector2> &b,
|
||||
ClipperLib::ExPolygons& out)
|
||||
{
|
||||
ClipperLib::ExPolygons &out) {
|
||||
out.clear();
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
|
@ -454,8 +427,7 @@ void MakeDisjunctWindowContours (const std::vector<IfcVector2>& a,
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void CleanupWindowContour(ProjectedWindowContour& window)
|
||||
{
|
||||
void CleanupWindowContour(ProjectedWindowContour &window) {
|
||||
std::vector<IfcVector2> scratch;
|
||||
std::vector<IfcVector2> &contour = window.contour;
|
||||
|
||||
|
@ -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) {
|
||||
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<IfcVector2>& contour_flat, TempMesh& curmesh)
|
||||
{
|
||||
void CleanupOuterContour(const std::vector<IfcVector2> &contour_flat, TempMesh &curmesh) {
|
||||
std::vector<IfcVector3> vold;
|
||||
std::vector<unsigned int> iold;
|
||||
|
||||
|
@ -568,10 +536,8 @@ void CleanupOuterContour(const std::vector<IfcVector2>& 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;
|
||||
}
|
||||
|
@ -586,12 +552,11 @@ typedef std::vector<OpeningRefs > OpeningRefVector;
|
|||
|
||||
typedef std::vector<std::pair<
|
||||
ContourVector::const_iterator,
|
||||
Contour::const_iterator>
|
||||
> ContourRefVector;
|
||||
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<float>();
|
||||
return (std::fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) ||
|
||||
|
@ -605,8 +570,7 @@ bool BoundingBoxesAdjacent(const BoundingBox& bb, const BoundingBox& ibb)
|
|||
// 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)
|
||||
{
|
||||
IfcVector2 &out0, IfcVector2 &out1) {
|
||||
const IfcVector2 n0_to_n1 = n1 - n0;
|
||||
|
||||
const IfcVector2 n0_to_m0 = m0 - n0;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -679,8 +642,7 @@ 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<IfcFloat>(Math::getEpsilon<float>());
|
||||
const BoundingBox &bb = (*current).bb;
|
||||
|
||||
|
@ -737,8 +699,7 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector&
|
|||
|
||||
ncontour.insert(ncontour.begin() + n, isect0);
|
||||
skiplist.insert(skiplist.begin() + n, true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
skiplist[n] = true;
|
||||
}
|
||||
|
||||
|
@ -756,15 +717,13 @@ 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<IfcFloat>(Math::getEpsilon<float>());
|
||||
return std::fabs(vdelta.x * vdelta.y) < dot_point_epsilon;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FindBorderContours(ContourVector::iterator current)
|
||||
{
|
||||
void FindBorderContours(ContourVector::iterator current) {
|
||||
const IfcFloat border_epsilon_upper = static_cast<IfcFloat>(1 - 1e-4);
|
||||
const IfcFloat border_epsilon_lower = static_cast<IfcFloat>(1e-4);
|
||||
|
||||
|
@ -790,14 +749,12 @@ void FindBorderContours(ContourVector::iterator current)
|
|||
if (LikelyBorder(proj_point - last_proj_point)) {
|
||||
skiplist[std::distance(cbegin, cit) - 1] = true;
|
||||
}
|
||||
}
|
||||
else if (cit == cbegin) {
|
||||
} else if (cit == cbegin) {
|
||||
start_on_outer_border = true;
|
||||
}
|
||||
|
||||
outer_border = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
outer_border = false;
|
||||
}
|
||||
|
||||
|
@ -814,16 +771,14 @@ void FindBorderContours(ContourVector::iterator current)
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
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));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void FindLikelyCrossingLines(ContourVector::iterator current)
|
||||
{
|
||||
void FindLikelyCrossingLines(ContourVector::iterator current) {
|
||||
SkipList &skiplist = (*current).skiplist;
|
||||
IfcVector2 last_proj_point;
|
||||
|
||||
|
@ -851,8 +806,7 @@ void FindLikelyCrossingLines(ContourVector::iterator current)
|
|||
size_t CloseWindows(ContourVector &contours,
|
||||
const IfcMatrix4 &minv,
|
||||
OpeningRefVector &contours_to_openings,
|
||||
TempMesh& curmesh)
|
||||
{
|
||||
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
|
||||
|
@ -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,15 +942,13 @@ 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) {
|
||||
|
@ -1015,8 +966,7 @@ size_t CloseWindows(ContourVector& contours,
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh)
|
||||
{
|
||||
void Quadrify(const std::vector<BoundingBox> &bbs, TempMesh &curmesh) {
|
||||
ai_assert(curmesh.IsEmpty());
|
||||
|
||||
std::vector<IfcVector2> quads;
|
||||
|
@ -1042,8 +992,7 @@ void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh)
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Quadrify(const ContourVector& contours, TempMesh& curmesh)
|
||||
{
|
||||
void Quadrify(const ContourVector &contours, TempMesh &curmesh) {
|
||||
std::vector<BoundingBox> bbs;
|
||||
bbs.reserve(contours.size());
|
||||
|
||||
|
@ -1056,8 +1005,7 @@ void Quadrify(const ContourVector& contours, TempMesh& curmesh)
|
|||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2> &out_contour, const TempMesh &in_mesh,
|
||||
bool &ok, IfcVector3& nor_out)
|
||||
{
|
||||
bool &ok, IfcVector3 &nor_out) {
|
||||
const std::vector<IfcVector3> &in_verts = in_mesh.mVerts;
|
||||
ok = true;
|
||||
|
||||
|
@ -1073,7 +1021,6 @@ IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh
|
|||
IfcFloat zcoord = 0;
|
||||
out_contour.reserve(in_verts.size());
|
||||
|
||||
|
||||
IfcVector3 vmin, vmax;
|
||||
MinMaxChooser<IfcVector3>()(vmin, vmax);
|
||||
|
||||
|
@ -1144,8 +1091,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
TempMesh &curmesh,
|
||||
bool check_intersection,
|
||||
bool generate_connection_geometry,
|
||||
const IfcVector3& wall_extrusion_axis)
|
||||
{
|
||||
const IfcVector3 &wall_extrusion_axis) {
|
||||
OpeningRefVector contours_to_openings;
|
||||
|
||||
// Try to derive a solid base plane within the current surface for use as
|
||||
|
@ -1163,14 +1109,12 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
}
|
||||
|
||||
// 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<IfcVector2> temp_contour;
|
||||
std::vector<IfcVector2> temp_contour2;
|
||||
|
||||
std::vector<IfcVector2> temp_contour, temp_contour2;
|
||||
IfcVector3 wall_extrusion_axis_norm = wall_extrusion_axis;
|
||||
wall_extrusion_axis_norm.Normalize();
|
||||
|
||||
|
@ -1181,8 +1125,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
IfcVector3 norm_extrusion_dir = opening.extrusionDir;
|
||||
if (norm_extrusion_dir.SquareLength() > 1e-10) {
|
||||
norm_extrusion_dir.Normalize();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
norm_extrusion_dir = IfcVector3();
|
||||
}
|
||||
|
||||
|
@ -1196,8 +1139,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
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();
|
||||
|
@ -1241,7 +1183,8 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
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();
|
||||
(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) {
|
||||
|
@ -1270,8 +1213,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
if (side_flag) {
|
||||
vpmin = std::min(vpmin, vv);
|
||||
vpmax = std::max(vpmax, vv);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
vpmin2 = std::min(vpmin2, vv);
|
||||
vpmax2 = std::max(vpmax2, vv);
|
||||
}
|
||||
|
@ -1335,7 +1277,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
MakeDisjunctWindowContours(other, temp_contour, poly);
|
||||
if (poly.size() == 1) {
|
||||
|
||||
const BoundingBox newbb = GetBoundingBox(poly[0].outer);
|
||||
const BoundingBox newbb = GetBoundingBox2D(poly[0].outer);
|
||||
if (!BoundingBoxesOverlapping(ibb, newbb)) {
|
||||
// Good guy bounding box
|
||||
bb = newbb;
|
||||
|
@ -1352,13 +1294,11 @@ bool GenerateOpenings(std::vector<TempOpening>& 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);
|
||||
|
||||
|
@ -1427,21 +1367,20 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
|
||||
// Undo the projection and get back to world (or local object) space
|
||||
for (IfcVector3 &v3 : curmesh.mVerts) {
|
||||
v3 = minv * v3;
|
||||
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<TempOpening> &openings, const std::vector<IfcVector3> &nors,
|
||||
TempMesh& curmesh)
|
||||
{
|
||||
TempMesh &curmesh) {
|
||||
IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings");
|
||||
std::vector<IfcVector3> &out = curmesh.mVerts;
|
||||
|
||||
|
@ -1456,8 +1395,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
return false;
|
||||
}
|
||||
|
||||
const IfcMatrix3 minv = IfcMatrix3(m).Inverse();
|
||||
|
||||
const IfcMatrix3 mInverse = IfcMatrix3(m).Inverse();
|
||||
|
||||
IfcFloat coord = -1;
|
||||
|
||||
|
@ -1475,7 +1413,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
// (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) {
|
||||
|
@ -1502,7 +1439,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
ClipperLib::ExPolygons clipped;
|
||||
ClipperLib::Polygons holes_union;
|
||||
|
||||
|
||||
IfcVector3 wall_extrusion;
|
||||
bool first = true;
|
||||
|
||||
|
@ -1597,10 +1533,8 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
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;
|
||||
}
|
||||
|
@ -1629,14 +1563,11 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
// 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) {
|
||||
|
||||
|
@ -1653,10 +1584,8 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& 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;
|
||||
}
|
||||
|
||||
|
@ -1668,11 +1597,10 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
|
||||
const IfcVector2 v = IfcVector2(
|
||||
static_cast<IfcFloat>(tri->GetPoint(i)->x),
|
||||
static_cast<IfcFloat>( tri->GetPoint(i)->y )
|
||||
);
|
||||
static_cast<IfcFloat>(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);
|
||||
}
|
||||
|
@ -1693,9 +1621,8 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // ! IFC
|
||||
} // ! Assimp
|
||||
} // namespace IFC
|
||||
} // namespace Assimp
|
||||
|
||||
#undef to_int64
|
||||
#undef from_int64
|
||||
|
|
|
@ -192,7 +192,7 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &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<IfcVector3> &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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,27 +46,26 @@ 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 <assimp/mesh.h>
|
||||
#include <assimp/material.h>
|
||||
#include <assimp/mesh.h>
|
||||
|
||||
struct aiNode;
|
||||
|
||||
namespace Assimp {
|
||||
namespace IFC {
|
||||
|
||||
typedef double IfcFloat;
|
||||
using IfcFloat = double;
|
||||
|
||||
// IfcFloat-precision math data types
|
||||
typedef aiVector2t<IfcFloat> IfcVector2;
|
||||
typedef aiVector3t<IfcFloat> IfcVector3;
|
||||
typedef aiMatrix4x4t<IfcFloat> IfcMatrix4;
|
||||
typedef aiMatrix3x3t<IfcFloat> IfcMatrix3;
|
||||
typedef aiColor4t<IfcFloat> IfcColor4;
|
||||
|
||||
using IfcVector2 = aiVector2t<IfcFloat>;
|
||||
using IfcVector3 = aiVector3t<IfcFloat>;
|
||||
using IfcMatrix4 = aiMatrix4x4t<IfcFloat>;
|
||||
using IfcMatrix3 = aiMatrix3x3t<IfcFloat>;
|
||||
using IfcColor4 = aiColor4t<IfcFloat>;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Helper for std::for_each to delete all heap-allocated items in a container
|
||||
|
@ -79,8 +77,6 @@ struct delete_fun {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -104,17 +100,14 @@ struct TempMesh {
|
|||
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,34 +122,33 @@ struct TempOpening
|
|||
std::vector<IfcVector3> wallPoints;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
TempOpening()
|
||||
: solid()
|
||||
, extrusionDir()
|
||||
, profileMesh()
|
||||
{
|
||||
TempOpening() :
|
||||
solid(),
|
||||
extrusionDir(),
|
||||
profileMesh() {
|
||||
// empty
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
|
||||
std::shared_ptr<TempMesh> profileMesh,
|
||||
std::shared_ptr<TempMesh> profileMesh2D)
|
||||
: solid(solid)
|
||||
, extrusionDir(extrusionDir)
|
||||
, profileMesh(profileMesh)
|
||||
, profileMesh2D(profileMesh2D)
|
||||
{
|
||||
std::shared_ptr<TempMesh> profileMesh2D) :
|
||||
solid(solid),
|
||||
extrusionDir(extrusionDir),
|
||||
profileMesh(profileMesh),
|
||||
profileMesh2D(profileMesh2D) {
|
||||
// empty
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
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();
|
||||
|
@ -166,22 +158,28 @@ struct TempOpening
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// 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<aiMesh>());
|
||||
|
@ -200,16 +198,19 @@ struct ConversionData
|
|||
std::vector<aiMaterial *> 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) { }
|
||||
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<MeshCacheIndex, std::set<unsigned int> > MeshCache;
|
||||
using MeshCache = std::map<MeshCacheIndex, std::set<unsigned int>>;
|
||||
MeshCache cached_meshes;
|
||||
|
||||
typedef std::map<const IFC::Schema_2x3::IfcSurfaceStyle*, unsigned int> MaterialCache;
|
||||
using MaterialCache = std::map<const IFC::Schema_2x3::IfcSurfaceStyle *, unsigned int>;
|
||||
MaterialCache cached_materials;
|
||||
|
||||
const IFCImporter::Settings &settings;
|
||||
|
@ -226,13 +227,13 @@ struct ConversionData
|
|||
std::set<uint64_t> already_processed;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Binary predicate to compare vectors with a given, quadratic epsilon.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
struct FuzzyVectorCompare {
|
||||
|
||||
FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
|
||||
FuzzyVectorCompare(IfcFloat epsilon) :
|
||||
epsilon(epsilon) {}
|
||||
bool operator()(const IfcVector3 &a, const IfcVector3 &b) {
|
||||
return std::abs((a - b).SquareLength()) < epsilon;
|
||||
}
|
||||
|
@ -240,7 +241,6 @@ struct FuzzyVectorCompare {
|
|||
const IfcFloat epsilon;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Ordering predicate to totally order R^2 vectors first by x and then by y
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -255,8 +255,6 @@ 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);
|
||||
|
@ -272,7 +270,6 @@ void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTra
|
|||
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);
|
||||
|
@ -305,7 +302,6 @@ void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedArea
|
|||
const TempMesh &first_operand,
|
||||
ConversionData &conv);
|
||||
|
||||
|
||||
// IFCOpenings.cpp
|
||||
|
||||
bool GenerateOpenings(std::vector<TempOpening> &openings,
|
||||
|
@ -315,8 +311,6 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
|||
bool generate_connection_geometry,
|
||||
const IfcVector3 &wall_extrusion_axis = IfcVector3(0, 1, 0));
|
||||
|
||||
|
||||
|
||||
// IFCCurve.cpp
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -324,8 +318,8 @@ bool GenerateOpenings(std::vector<TempOpening>& 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<IfcFloat, IfcFloat> ParamRange;
|
||||
using ParamRange = std::pair<IfcFloat, IfcFloat>;
|
||||
|
||||
virtual ~Curve() {}
|
||||
|
||||
|
||||
// check if a curve is closed
|
||||
virtual bool IsClosed() const = 0;
|
||||
|
||||
|
@ -384,23 +377,19 @@ protected:
|
|||
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;
|
||||
using Curve::SampleDiscrete;
|
||||
|
@ -408,7 +397,8 @@ public:
|
|||
|
||||
// IfcProfile.cpp
|
||||
bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace IFC
|
||||
} // namespace Assimp
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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 <typename T>
|
||||
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 T>
|
||||
class TBoundingBox2D {
|
||||
T mMin, mMax;
|
||||
|
||||
TBoundingBox2D( const T &min, const T &max ) :
|
||||
mMin( min ),
|
||||
mMax( max ) {
|
||||
// empty
|
||||
}
|
||||
};
|
||||
|
||||
using BoundingBox2D = TBoundingBox2D<aiVector2D>;
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** 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 <typename T>
|
||||
inline bool OnLeftSideOfLine2D(const T &p0, const T &p1, const T &p2) {
|
||||
return GetArea2D(p0, p2, p1) > 0;
|
||||
}
|
||||
|
||||
/// 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()
|
||||
// -------------------------------------------------------------------------------
|
||||
/** 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 <typename T>
|
||||
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 <typename T>
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** 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 <size_t ofs_x, size_t ofs_y, size_t ofs_z, typename TReal>
|
||||
inline void NewellNormal(aiVector3t<TReal> &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<TReal> &out, size_t num, TReal *x, TReal *y,
|
|||
out = aiVector3t<TReal>(sum_yz, sum_zx, sum_xy);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
inline aiMatrix4x4t<T> DerivePlaneCoordinateSpace(const aiVector3t<T> *vertices, size_t numVertices, bool &ok, aiVector3t<T> &norOut) {
|
||||
const aiVector3t<T> *out = vertices;
|
||||
aiMatrix4x4t<T> m;
|
||||
|
||||
ok = true;
|
||||
|
||||
const size_t s = numVertices;
|
||||
|
||||
const aiVector3t<T> &any_point = out[numVertices - 1u];
|
||||
aiVector3t<T> 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<T> r = (out[idx] - any_point);
|
||||
r.Normalize();
|
||||
|
||||
// Reconstruct orthonormal basis
|
||||
// XXX use Gram Schmidt for increased robustness
|
||||
aiVector3t<T> 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
|
||||
|
|
|
@ -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,11 +43,9 @@ 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 <assimp/Exceptional.h>
|
||||
|
||||
using namespace Assimp;
|
||||
|
@ -61,9 +57,9 @@ 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
|
||||
}
|
||||
|
||||
|
@ -91,8 +87,7 @@ void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
|
|||
// Executes the post processing step on the given imported data.
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -261,8 +255,7 @@ evil_jump_outside:
|
|||
// 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)
|
||||
{
|
||||
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++];
|
||||
|
@ -276,8 +269,7 @@ evil_jump_outside:
|
|||
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;
|
||||
|
|
|
@ -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 <cstdint>
|
||||
#include <memory>
|
||||
|
||||
//#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;
|
||||
|
@ -147,91 +134,7 @@ static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut,
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Triangulates the given mesh.
|
||||
bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
|
||||
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
|
||||
|
||||
if (!pMesh->mPrimitiveTypes) {
|
||||
if (!validateNumIndices(pMesh)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find out how many output faces we'll get
|
||||
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];
|
||||
}
|
||||
|
||||
// the output mesh will contain triangles, but no polys anymore
|
||||
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
||||
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
|
||||
|
||||
aiFace *out = new aiFace[numOut](), *curOut = out;
|
||||
const size_t Capa = max_out + 2;
|
||||
std::vector<aiVector3D> temp_verts3d(max_out + 2); /* temporary storage for vertices */
|
||||
std::vector<aiVector2D> 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;
|
||||
|
||||
// use std::unique_ptr to avoid slow std::vector<bool> specialiations
|
||||
std::unique_ptr<bool[]> done(new bool[max_out]);
|
||||
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++;
|
||||
nface.mNumIndices = face.mNumIndices;
|
||||
nface.mIndices = face.mIndices;
|
||||
|
||||
face.mIndices = nullptr;
|
||||
continue;
|
||||
}
|
||||
// optimized code for quadrilaterals
|
||||
else if (face.mNumIndices == 4) {
|
||||
|
||||
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.
|
||||
|
@ -276,229 +179,162 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
|
|||
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
|
||||
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]];
|
||||
}
|
||||
|
||||
// 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;
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool getContourFromePolyline(aiFace &face, aiMesh *pMesh, std::vector<p2t::Point *> &contour,
|
||||
aiMatrix4x4 &m, aiVector3D &vmin, aiVector3D &vmax, ai_real &zcoord) {
|
||||
aiVector3D normal;
|
||||
bool ok = true;
|
||||
m = DerivePlaneCoordinateSpace<ai_real>(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));
|
||||
}
|
||||
|
||||
// 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);
|
||||
zcoord /= pMesh->mNumVertices;
|
||||
|
||||
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;
|
||||
}
|
||||
} else if (ay > az) { // no y coord. projection to zy
|
||||
ac = 2;
|
||||
bc = 0;
|
||||
inv = n.y;
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Swap projection axes to take the negated projection vector into account
|
||||
if (inv < 0.f) {
|
||||
std::swap(ac, bc);
|
||||
}
|
||||
aiMatrix4x4 mult;
|
||||
mult.a1 = static_cast<ai_real>(1.0) / vmax.x;
|
||||
mult.b2 = static_cast<ai_real>(1.0) / vmax.y;
|
||||
|
||||
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;
|
||||
}
|
||||
mult.a4 = -vmin.x * mult.a1;
|
||||
mult.b4 = -vmin.y * mult.b2;
|
||||
mult.c4 = -zcoord;
|
||||
m = mult * m;
|
||||
|
||||
#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<size_t>(v.x * (POLY_GRID_X - 1)), y = static_cast<size_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
#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->mNumFaces = (unsigned int)(curOut - out); /* not necessarily equal to numOut */
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Triangulates the given mesh.
|
||||
bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
|
||||
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Find out how many output faces we'll get
|
||||
size_t numOut = 0, max_out = 0;
|
||||
bool getNormals = true;
|
||||
calulateNumOutputFaces(pMesh, numOut, max_out, getNormals);
|
||||
if (numOut == pMesh->mNumFaces) {
|
||||
ASSIMP_LOG_DEBUG("Error while generating contour.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// the output mesh will contain triangles, but no polys anymore
|
||||
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
||||
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
|
||||
|
||||
aiFace *out = new aiFace[numOut](), *curOut = out;
|
||||
const size_t Capa = max_out + 2;
|
||||
std::vector<aiVector3D> temp_verts3d(max_out + 2); /* temporary storage for vertices */
|
||||
std::vector<aiVector2D> temp_verts(max_out + 2);
|
||||
|
||||
// Apply vertex colors to represent the face winding?
|
||||
|
||||
const aiVector3D *verts = pMesh->mVertices;
|
||||
|
||||
// use std::unique_ptr to avoid slow std::vector<bool> specialiations
|
||||
std::unique_ptr<bool[]> done(new bool[max_out]);
|
||||
for (unsigned int a = 0; a < pMesh->mNumFaces; a++) {
|
||||
aiFace &face = pMesh->mFaces[a];
|
||||
|
||||
// if it's a simple point,line or triangle: just copy it
|
||||
if (face.mNumIndices <= 3) {
|
||||
aiFace &nface = *curOut++;
|
||||
nface.mNumIndices = face.mNumIndices;
|
||||
nface.mIndices = face.mIndices;
|
||||
|
||||
face.mIndices = nullptr;
|
||||
} else if (face.mNumIndices == 4) {
|
||||
// optimized code for quadrilaterals
|
||||
quad2Triangles(face, verts, curOut);
|
||||
face.mIndices = nullptr;
|
||||
} else {
|
||||
std::vector<p2t::Point *> 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;
|
||||
}
|
||||
p2t::CDT cdt(contour);
|
||||
cdt.Triangulate();
|
||||
const std::vector<p2t::Triangle *> 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<ai_real>(tri->GetPoint(i)->x), static_cast<ai_real>(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;
|
||||
}
|
||||
curOut++;
|
||||
}
|
||||
face.mIndices = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] pMesh->mFaces;
|
||||
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
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Hashing function taken from
|
||||
|
|
|
@ -52,9 +52,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <assimp/StreamReader.h>
|
||||
#include <assimp/ParsingUtils.h>
|
||||
#include <assimp/StreamReader.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Assimp {
|
||||
|
||||
|
@ -79,7 +79,7 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
|
|||
// ------------------------------------------------------------------------------------------------
|
||||
class LineSplitter {
|
||||
public:
|
||||
typedef size_t line_idx;
|
||||
using line_idx = size_t;
|
||||
|
||||
// -----------------------------------------
|
||||
/** construct from existing stream reader
|
||||
|
@ -144,21 +144,19 @@ private:
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -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,8 +203,7 @@ LineSplitter& LineSplitter::operator++() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
AI_FORCE_INLINE
|
||||
LineSplitter &LineSplitter::operator++(int) {
|
||||
AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
|
||||
return ++(*this);
|
||||
}
|
||||
|
||||
|
@ -226,8 +225,7 @@ const char *LineSplitter::operator[] (size_t idx) const {
|
|||
}
|
||||
|
||||
template <size_t N>
|
||||
AI_FORCE_INLINE
|
||||
void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
|
||||
AI_FORCE_INLINE void LineSplitter::get_tokens(const char *(&tokens)[N]) const {
|
||||
const char *s = operator->()->c_str();
|
||||
|
||||
SkipSpaces(&s);
|
||||
|
@ -237,7 +235,8 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
|
|||
}
|
||||
tokens[i] = s;
|
||||
|
||||
for (; *s && !IsSpace(*s); ++s);
|
||||
for (; *s && !IsSpace(*s); ++s)
|
||||
;
|
||||
SkipSpaces(&s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ai_real> aiVector3D;
|
||||
using aiVector3D = aiVector3t<ai_real>;
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -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<TReal> operator - ( const aiVector3t<TReal>& v) {
|
|||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // AI_VECTOR3D_INL_INC
|
||||
|
|
|
@ -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<ai_real>(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<ai_real>(vertices_not_ok, 3, ok, normal);
|
||||
EXPECT_FALSE(ok);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue