Prototype new triangulation.

issue_1044
Kim Kulling 2021-01-13 22:43:46 +01:00
parent 4bb2006325
commit 8e4ee11bf3
15 changed files with 904 additions and 1171 deletions

File diff suppressed because it is too large Load Diff

View File

@ -192,7 +192,7 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &normals, bool norm
size_t vidx = std::accumulate(mVertcnt.begin(), begin, 0); size_t vidx = std::accumulate(mVertcnt.begin(), begin, 0);
for (iit = begin; iit != end; vidx += *iit++) { for (iit = begin; iit != end; vidx += *iit++) {
if (!*iit) { if (!*iit) {
normals.push_back(IfcVector3()); normals.emplace_back();
continue; continue;
} }
for (size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) { for (size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
@ -206,7 +206,7 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3> &normals, bool norm
++cnt; ++cnt;
} }
normals.push_back(IfcVector3()); normals.emplace_back();
NewellNormal<4, 4, 4>(normals.back(), *iit, &temp[0], &temp[1], &temp[2], Capa); NewellNormal<4, 4, 4>(normals.back(), *iit, &temp[0], &temp[1], &temp[2], Capa);
} }

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team Copyright (c) 2006-2020, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, 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 #ifndef INCLUDED_IFCUTIL_H
#define INCLUDED_IFCUTIL_H #define INCLUDED_IFCUTIL_H
#include "AssetLib/IFC/IFCReaderGen_2x3.h"
#include "AssetLib/IFC/IFCLoader.h" #include "AssetLib/IFC/IFCLoader.h"
#include "AssetLib/IFC/IFCReaderGen_2x3.h"
#include "AssetLib/Step/STEPFile.h" #include "AssetLib/Step/STEPFile.h"
#include <assimp/mesh.h>
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/mesh.h>
struct aiNode; struct aiNode;
namespace Assimp { namespace Assimp {
namespace IFC { 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;
// IfcFloat-precision math data types
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 // Helper for std::for_each to delete all heap-allocated items in a container
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template<typename T> template <typename T>
struct delete_fun { struct delete_fun {
void operator()(T* del) { void operator()(T *del) {
delete del; delete del;
} }
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons. // Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -89,32 +85,29 @@ struct TempMesh {
std::vector<unsigned int> mVertcnt; std::vector<unsigned int> mVertcnt;
// utilities // utilities
aiMesh* ToMesh(); aiMesh *ToMesh();
void Clear(); void Clear();
void Transform(const IfcMatrix4& mat); void Transform(const IfcMatrix4 &mat);
IfcVector3 Center() const; IfcVector3 Center() const;
void Append(const TempMesh& other); void Append(const TempMesh &other);
bool IsEmpty() const; bool IsEmpty() const;
void RemoveAdjacentDuplicates(); void RemoveAdjacentDuplicates();
void RemoveDegenerates(); void RemoveDegenerates();
void FixupFaceOrientation(); 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; IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const; void ComputePolygonNormals(std::vector<IfcVector3> &normals, bool normalize = true, size_t ofs = 0) const;
void Swap(TempMesh& other); void Swap(TempMesh &other);
}; };
inline inline bool TempMesh::IsEmpty() const {
bool TempMesh::IsEmpty() const {
return mVerts.empty() && mVertcnt.empty(); return mVerts.empty() && mVertcnt.empty();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Temporary representation of an opening in a wall or a floor // Temporary representation of an opening in a wall or a floor
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
struct TempOpening struct TempOpening {
{
const IFC::Schema_2x3::IfcSolidModel *solid; const IFC::Schema_2x3::IfcSolidModel *solid;
IfcVector3 extrusionDir; IfcVector3 extrusionDir;
@ -129,90 +122,98 @@ struct TempOpening
std::vector<IfcVector3> wallPoints; std::vector<IfcVector3> wallPoints;
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
TempOpening() TempOpening() :
: solid() solid(),
, extrusionDir() extrusionDir(),
, profileMesh() profileMesh() {
{ // empty
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
std::shared_ptr<TempMesh> profileMesh, std::shared_ptr<TempMesh> profileMesh,
std::shared_ptr<TempMesh> profileMesh2D) std::shared_ptr<TempMesh> profileMesh2D) :
: solid(solid) solid(solid),
, extrusionDir(extrusionDir) extrusionDir(extrusionDir),
, profileMesh(profileMesh) profileMesh(profileMesh),
, profileMesh2D(profileMesh2D) 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 // Helper to sort openings by distance from a given base point
struct DistanceSorter { struct DistanceSorter {
DistanceSorter(const IfcVector3& base) : base(base) {} DistanceSorter(const IfcVector3 &base) :
base(base) {}
bool operator () (const TempOpening& a, const TempOpening& b) const { bool operator()(const TempOpening &a, const TempOpening &b) const {
return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength(); return (a.profileMesh->Center() - base).SquareLength() < (b.profileMesh->Center() - base).SquareLength();
} }
IfcVector3 base; IfcVector3 base;
}; };
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Intermediate data storage during conversion. Keeps everything and a bit more. // Intermediate data storage during conversion. Keeps everything and a bit more.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
struct ConversionData struct ConversionData {
{ ConversionData(const STEP::DB &db, const IFC::Schema_2x3::IfcProject &proj, aiScene *out, const IFCImporter::Settings &settings) :
ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings) len_scale(1.0),
: len_scale(1.0) angle_scale(-1.0),
, angle_scale(-1.0) plane_angle_in_radians(true),
, db(db) db(db),
, proj(proj) proj(proj),
, out(out) out(out),
, settings(settings) wcs(),
, apply_openings() meshes(),
, collect_openings() materials(),
{} cached_meshes(),
cached_materials(),
settings(settings),
apply_openings(nullptr),
collect_openings(nullptr),
already_processed() {
// empty
}
~ConversionData() { ~ConversionData() {
std::for_each(meshes.begin(),meshes.end(),delete_fun<aiMesh>()); std::for_each(meshes.begin(), meshes.end(), delete_fun<aiMesh>());
std::for_each(materials.begin(),materials.end(),delete_fun<aiMaterial>()); std::for_each(materials.begin(), materials.end(), delete_fun<aiMaterial>());
} }
IfcFloat len_scale, angle_scale; IfcFloat len_scale, angle_scale;
bool plane_angle_in_radians; bool plane_angle_in_radians;
const STEP::DB& db; const STEP::DB &db;
const IFC::Schema_2x3::IfcProject& proj; const IFC::Schema_2x3::IfcProject &proj;
aiScene* out; aiScene *out;
IfcMatrix4 wcs; IfcMatrix4 wcs;
std::vector<aiMesh*> meshes; std::vector<aiMesh *> meshes;
std::vector<aiMaterial*> materials; std::vector<aiMaterial *> materials;
struct MeshCacheIndex { struct MeshCacheIndex {
const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex; const IFC::Schema_2x3::IfcRepresentationItem *item;
MeshCacheIndex() : item(nullptr), matindex(0) { } unsigned int matindex;
MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { } MeshCacheIndex() :
bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; } item(nullptr), matindex(0) {}
bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); } 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; 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; MaterialCache cached_materials;
const IFCImporter::Settings& settings; const IFCImporter::Settings &settings;
// Intermediate arrays used to resolve openings in walls: only one of them // 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 // 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 // collect_openings is present only if the current element is an
// IfcOpeningElement, for which all the geometry needs to be preserved // IfcOpeningElement, for which all the geometry needs to be preserved
// for later processing by a parent, which is a wall. // for later processing by a parent, which is a wall.
std::vector<TempOpening>* apply_openings; std::vector<TempOpening> *apply_openings;
std::vector<TempOpening>* collect_openings; std::vector<TempOpening> *collect_openings;
std::set<uint64_t> already_processed; std::set<uint64_t> already_processed;
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Binary predicate to compare vectors with a given, quadratic epsilon. // Binary predicate to compare vectors with a given, quadratic epsilon.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
struct FuzzyVectorCompare { struct FuzzyVectorCompare {
FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {} FuzzyVectorCompare(IfcFloat epsilon) :
bool operator()(const IfcVector3& a, const IfcVector3& b) { epsilon(epsilon) {}
return std::abs((a-b).SquareLength()) < epsilon; bool operator()(const IfcVector3 &a, const IfcVector3 &b) {
return std::abs((a - b).SquareLength()) < epsilon;
} }
const IfcFloat epsilon; const IfcFloat epsilon;
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Ordering predicate to totally order R^2 vectors first by x and then by y // Ordering predicate to totally order R^2 vectors first by x and then by y
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
struct XYSorter { struct XYSorter {
// sort first by X coordinates, then by Y coordinates // 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) { if (a.x == b.x) {
return a.y < b.y; return a.y < b.y;
} }
@ -255,67 +255,61 @@ struct XYSorter {
} }
}; };
// conversion routines for common IFC entities, implemented in IFCUtil.cpp // 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::IfcColourRgb &in);
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base); void ConvertColor(aiColor4D &out, const Schema_2x3::IfcColourOrFactor &in, ConversionData &conv, const aiColor4D *base);
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in); void ConvertCartesianPoint(IfcVector3 &out, const Schema_2x3::IfcCartesianPoint &in);
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in); void ConvertDirection(IfcVector3 &out, const Schema_2x3::IfcDirection &in);
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in); void ConvertVector(IfcVector3 &out, const Schema_2x3::IfcVector &in);
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z); 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::IfcAxis2Placement3D &in);
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in); void ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement2D &in);
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& 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 ConvertAxisPlacement(IfcMatrix4 &out, const Schema_2x3::IfcAxis2Placement &in, ConversionData &conv);
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op); void ConvertTransformOperator(IfcMatrix4 &out, const Schema_2x3::IfcCartesianTransformationOperator &op);
bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in); bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN &in);
IfcFloat ConvertSIPrefix(const std::string& prefix); IfcFloat ConvertSIPrefix(const std::string &prefix);
// IFCProfile.cpp // IFCProfile.cpp
bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, 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); bool ProcessCurve(const Schema_2x3::IfcCurve &curve, TempMesh &meshout, ConversionData &conv);
// IFCMaterial.cpp // 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 // IFCGeometry.cpp
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut); IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh &curmesh, bool &ok, IfcVector3 &norOut);
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set<unsigned int>& mesh_indices, ConversionData& conv); bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem &item, unsigned int matid, std::set<unsigned int> &mesh_indices, ConversionData &conv);
void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/); void AssignAddedMeshes(std::set<unsigned int> &mesh_indices, aiNode *nd, ConversionData & /*conv*/);
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout, void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid &swept, TempMesh &meshout,
ConversionData& conv); ConversionData &conv);
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result, void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid &solid, TempMesh &result,
ConversionData& conv, bool collect_openings); ConversionData &conv, bool collect_openings);
// IFCBoolean.cpp // IFCBoolean.cpp
void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv); void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &result, ConversionData &conv);
void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result, void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid *hs, TempMesh &result,
const TempMesh& first_operand, const TempMesh &first_operand,
ConversionData& conv); 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 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 // IFCOpenings.cpp
bool GenerateOpenings(std::vector<TempOpening>& openings, bool GenerateOpenings(std::vector<TempOpening> &openings,
const std::vector<IfcVector3>& nors, const std::vector<IfcVector3> &nors,
TempMesh& curmesh, TempMesh &curmesh,
bool check_intersection, bool check_intersection,
bool generate_connection_geometry, bool generate_connection_geometry,
const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0)); const IfcVector3 &wall_extrusion_axis = IfcVector3(0, 1, 0));
// IFCCurve.cpp // IFCCurve.cpp
@ -324,8 +318,8 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
class CurveError { class CurveError {
public: public:
CurveError(const std::string& s) CurveError(const std::string &s) :
: mStr(s) { mStr(s) {
// empty // empty
} }
@ -338,18 +332,17 @@ public:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
class Curve { class Curve {
protected: protected:
Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv) Curve(const Schema_2x3::IfcCurve &base_entity, ConversionData &conv) :
: base_entity(base_entity) base_entity(base_entity),
, conv(conv) { conv(conv) {
// empty // empty
} }
public: public:
typedef std::pair<IfcFloat, IfcFloat> ParamRange; using ParamRange = std::pair<IfcFloat, IfcFloat>;
virtual ~Curve() {} virtual ~Curve() {}
// check if a curve is closed // check if a curve is closed
virtual bool IsClosed() const = 0; virtual bool IsClosed() const = 0;
@ -359,56 +352,53 @@ public:
// try to match a point on the curve to a given parameter // try to match a point on the curve to a given parameter
// for self-intersecting curves, the result is not ambiguous and // for self-intersecting curves, the result is not ambiguous and
// it is undefined which parameter is returned. // it is undefined which parameter is returned.
virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const; virtual bool ReverseEval(const IfcVector3 &val, IfcFloat &paramOut) const;
// get the range of the curve (both inclusive). // get the range of the curve (both inclusive).
// +inf and -inf are valid return values, the curve is not bounded in such a case. // +inf and -inf are valid return values, the curve is not bounded in such a case.
virtual std::pair<IfcFloat,IfcFloat> GetParametricRange() const = 0; virtual std::pair<IfcFloat, IfcFloat> GetParametricRange() const = 0;
IfcFloat GetParametricRangeDelta() const; IfcFloat GetParametricRangeDelta() const;
// estimate the number of sample points that this curve will require // 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 // intelligently sample the curve based on the current settings
// and append the result to the mesh // 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 #ifdef ASSIMP_BUILD_DEBUG
// check if a particular parameter value lies within the well-defined range // check if a particular parameter value lies within the well-defined range
bool InRange(IfcFloat) const; bool InRange(IfcFloat) const;
#endif #endif
static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv); static Curve *Convert(const IFC::Schema_2x3::IfcCurve &, ConversionData &conv);
protected: protected:
const Schema_2x3::IfcCurve& base_entity; const Schema_2x3::IfcCurve &base_entity;
ConversionData& conv; ConversionData &conv;
}; };
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// A BoundedCurve always holds the invariant that GetParametricRange() // A BoundedCurve always holds the invariant that GetParametricRange()
// never returns infinite values. // never returns infinite values.
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
class BoundedCurve : public Curve { class BoundedCurve : public Curve {
public: public:
BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv) BoundedCurve(const Schema_2x3::IfcBoundedCurve &entity, ConversionData &conv) :
: Curve(entity,conv) Curve(entity, conv) {}
{}
public: public:
bool IsClosed() const override;
bool IsClosed() const;
public: public:
// sample the entire curve // sample the entire curve
void SampleDiscrete(TempMesh& out) const; void SampleDiscrete(TempMesh &out) const;
using Curve::SampleDiscrete; using Curve::SampleDiscrete;
}; };
// IfcProfile.cpp // 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 #endif

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team Copyright (c) 2006-2020, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, 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 { namespace Assimp {
// ------------------------------------------------------------------------------- template<class T>
/** Compute the signed area of a triangle. class TBoundingBox2D {
* The function accepts an unconstrained template parameter for use with T mMin, mMax;
* 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));
}
// ------------------------------------------------------------------------------- TBoundingBox2D( const T &min, const T &max ) :
/** Test if a given point p2 is on the left side of the line formed by p0-p1. mMin( min ),
* The function accepts an unconstrained template parameter for use with mMax( max ) {
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ // empty
template <typename T>
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 <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) * using BoundingBox2D = TBoundingBox2D<aiVector2D>;
(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. /// 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 /// 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. /// of its area when projected onto the three coordinate axes.
* ///
* @param out Receives the output normal /// @param out Receives the output normal
* @param num Number of input vertices /// @param num Number of input vertices
* @param x X data source. x[ofs_x*n] is the n'th element. /// @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 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 /// @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 /// @note The data arrays must have storage for at least num+2 elements. Using
* this method is much faster than the 'other' NewellNormal() /// this method is much faster than the 'other' NewellNormal()
*/ // -------------------------------------------------------------------------------
template <size_t ofs_x, size_t ofs_y, size_t ofs_z, typename TReal> 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) { inline void NewellNormal(aiVector3t<TReal> &out, size_t num, TReal *x, TReal *y, TReal *z, size_t bufferSize) {
ai_assert(bufferSize > num); 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); 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 } // namespace Assimp
#endif #endif

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team Copyright (c) 2006-2020, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, 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. * @brief Implementation of the FindDegenerates post-process step.
*/ */
// internal headers // internal headers
#include "ProcessHelper.h"
#include "FindDegenerates.h" #include "FindDegenerates.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
using namespace Assimp; using namespace Assimp;
//remove mesh at position 'index' from the scene //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 //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 // Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess() FindDegeneratesProcess::FindDegeneratesProcess() :
: mConfigRemoveDegenerates( false ) mConfigRemoveDegenerates(false),
, mConfigCheckAreaOfTriangle( false ){ mConfigCheckAreaOfTriangle(false) {
// empty // empty
} }
@ -75,24 +71,23 @@ FindDegeneratesProcess::~FindDegeneratesProcess() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // 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); return 0 != (pFlags & aiProcess_FindDegenerates);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup import configuration // 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 // Get the current value of AI_CONFIG_PP_FD_REMOVE
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // 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"); 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 //Do not process point cloud, ExecuteOnMesh works only with faces data
if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
removeMesh(pScene, i); removeMesh(pScene, i);
@ -102,12 +97,12 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); 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 //we start at index and copy the pointers one position forward
//save the mesh pointer to delete it later //save the mesh pointer to delete it later
auto delete_me = pScene->mMeshes[index]; auto delete_me = pScene->mMeshes[index];
for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { 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->mMeshes[pScene->mNumMeshes - 1] = nullptr;
--(pScene->mNumMeshes); --(pScene->mNumMeshes);
@ -117,15 +112,15 @@ static void removeMesh(aiScene* pScene, unsigned const index) {
updateSceneGraph(pScene->mRootNode, 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) { for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
if (pNode->mMeshes[i] > index) { if (pNode->mMeshes[i] > index) {
--(pNode->mMeshes[i]); --(pNode->mMeshes[i]);
continue; continue;
} }
if (pNode->mMeshes[i] == index) { if (pNode->mMeshes[i] == index) {
for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { for (unsigned j = i; j < pNode->mNumMeshes - 1; ++j) {
pNode->mMeshes[j] = pNode->mMeshes[j+1]; pNode->mMeshes[j] = pNode->mMeshes[j + 1];
} }
--(pNode->mNumMeshes); --(pNode->mNumMeshes);
--i; --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 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; return area;
} }
static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) { static ai_real distance3D(const aiVector3D &vA, aiVector3D &vB) {
const ai_real lx = ( vB.x - vA.x ); const ai_real lx = (vB.x - vA.x);
const ai_real ly = ( vB.y - vA.y ); const ai_real ly = (vB.y - vA.y);
const ai_real lz = ( vB.z - vA.z ); const ai_real lz = (vB.z - vA.z);
ai_real a = lx*lx + ly*ly + lz*lz; ai_real a = lx * lx + ly * ly + lz * lz;
ai_real d = pow( a, (ai_real)0.5 ); ai_real d = pow(a, (ai_real)0.5);
return d; return d;
} }
static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { static ai_real calculateAreaOfTriangle(const aiFace &face, aiMesh *mesh) {
ai_real area = 0; ai_real area = 0;
aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); aiVector3D vA(mesh->mVertices[face.mIndices[0]]);
aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); aiVector3D vB(mesh->mVertices[face.mIndices[1]]);
aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); aiVector3D vC(mesh->mVertices[face.mIndices[2]]);
ai_real a( distance3D( vA, vB ) ); ai_real a(distance3D(vA, vB));
ai_real b( distance3D( vB, vC ) ); ai_real b(distance3D(vB, vC));
ai_real c( distance3D( vC, vA ) ); ai_real c(distance3D(vC, vA));
area = heron( a, b, c ); area = heron(a, b, c);
return area; return area;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh // Executes the post processing step on the given imported mesh
bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
mesh->mPrimitiveTypes = 0; mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me; std::vector<bool> remove_me;
if (mConfigRemoveDegenerates) { if (mConfigRemoveDegenerates) {
remove_me.resize( mesh->mNumFaces, false ); remove_me.resize(mesh->mNumFaces, false);
} }
unsigned int deg = 0, limit; unsigned int deg = 0, limit;
for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace& face = mesh->mFaces[a]; aiFace &face = mesh->mFaces[a];
bool first = true; bool first = true;
// check whether the face contains degenerated entries // check whether the face contains degenerated entries
@ -191,43 +186,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
// double points may not come directly after another. // double points may not come directly after another.
limit = face.mNumIndices; limit = face.mNumIndices;
if (face.mNumIndices > 4) { 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) { for (unsigned int t = i + 1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
// we have found a matching vertex position // we have found a matching vertex position
// remove the corresponding index from the array // remove the corresponding index from the array
--face.mNumIndices; --face.mNumIndices;
--limit; --limit;
for (unsigned int m = t; m < face.mNumIndices; ++m) { for (unsigned int m = t; m < face.mNumIndices; ++m) {
face.mIndices[ m ] = face.mIndices[ m+1 ]; face.mIndices[m] = face.mIndices[m + 1];
} }
--t; --t;
// NOTE: we set the removed vertex index to an unique value // NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his // to make sure the developer gets notified when his
// application attempts to access this data. // application attempts to access this data.
face.mIndices[ face.mNumIndices ] = 0xdeadbeef; face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first) { if (first) {
++deg; ++deg;
first = false; first = false;
} }
if ( mConfigRemoveDegenerates ) { if (mConfigRemoveDegenerates) {
remove_me[ a ] = true; remove_me[a] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
} }
} }
} }
if ( mConfigCheckAreaOfTriangle ) { if (mConfigCheckAreaOfTriangle) {
if ( face.mNumIndices == 3 ) { if (face.mNumIndices == 3) {
ai_real area = calculateAreaOfTriangle( face, mesh ); ai_real area = calculateAreaOfTriangle(face, mesh);
if ( area < 1e-6 ) { if (area < 1e-6) {
if ( mConfigRemoveDegenerates ) { if (mConfigRemoveDegenerates) {
remove_me[ a ] = true; remove_me[a] = true;
++deg; ++deg;
goto evil_jump_outside; goto evil_jump_outside;
} }
@ -239,8 +234,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
} }
// We need to update the primitive flags array of the mesh. // We need to update the primitive flags array of the mesh.
switch (face.mNumIndices) switch (face.mNumIndices) {
{
case 1u: case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break; break;
@ -254,18 +248,17 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break; break;
}; };
evil_jump_outside: evil_jump_outside:
continue; continue;
} }
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (mConfigRemoveDegenerates && deg) { if (mConfigRemoveDegenerates && deg) {
unsigned int n = 0; 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];
aiFace& face_src = mesh->mFaces[a];
if (!remove_me[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 // Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices; face_dest.mNumIndices = face_src.mNumIndices;
@ -276,8 +269,7 @@ evil_jump_outside:
face_src.mNumIndices = 0; face_src.mNumIndices = 0;
face_src.mIndices = nullptr; face_src.mIndices = nullptr;
} }
} } else {
else {
// Otherwise delete it if we don't need this face // Otherwise delete it if we don't need this face
delete[] face_src.mIndices; delete[] face_src.mIndices;
face_src.mIndices = nullptr; face_src.mIndices = nullptr;
@ -295,7 +287,7 @@ evil_jump_outside:
} }
if (deg && !DefaultLogger::isNullLogger()) { if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); ASSIMP_LOG_WARN_F("Found ", deg, " degenerated primitives");
} }
return false; return false;
} }

View File

@ -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. * The triangulation algorithm will handle concave or convex polygons.
* Self-intersecting or non-planar polygons are not rejected, but * Self-intersecting or non-planar polygons are not rejected, but
* they're probably not triangulated correctly. * 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 #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
#include "PostProcessing/TriangulateProcess.h" #include "PostProcessing/TriangulateProcess.h"
#include "Common/PolyTools.h" #include "Common/PolyTools.h"
#include "PostProcessing/ProcessHelper.h" #include "PostProcessing/ProcessHelper.h"
#include "contrib/poly2tri/poly2tri/poly2tri.h"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING namespace Assimp {
#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;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
@ -128,6 +114,7 @@ static bool validateNumIndices(aiMesh *mesh) {
return bNeed; return bNeed;
} }
// ------------------------------------------------------------------------------------------------
static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, bool &getNormals) { static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut, bool &getNormals) {
numOut = maxOut = 0; numOut = maxOut = 0;
getNormals = true; getNormals = true;
@ -147,91 +134,7 @@ static void calulateNumOutputFaces(aiMesh *mesh, size_t &numOut, size_t &maxOut,
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Triangulates the given mesh. static void quad2Triangles(const aiFace &face, const aiVector3D *verts, aiFace *curOut) {
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) {
// quads can have at maximum one concave vertex. Determine // quads can have at maximum one concave vertex. Determine
// this vertex (if it exists) and start tri-fanning from // this vertex (if it exists) and start tri-fanning from
// it. // it.
@ -276,229 +179,162 @@ bool TriangulateProcess::TriangulateMesh(aiMesh *pMesh) {
sface.mIndices[0] = temp[start_vertex]; sface.mIndices[0] = temp[start_vertex];
sface.mIndices[1] = temp[(start_vertex + 2) % 4]; sface.mIndices[1] = temp[(start_vertex + 2) % 4];
sface.mIndices[2] = temp[(start_vertex + 3) % 4]; sface.mIndices[2] = temp[(start_vertex + 3) % 4];
}
// prevent double deletion of the indices field // ------------------------------------------------------------------------------------------------
face.mIndices = nullptr; bool getContourFromePolyline(aiFace &face, aiMesh *pMesh, std::vector<p2t::Point *> &contour,
continue; aiMatrix4x4 &m, aiVector3D &vmin, aiVector3D &vmax, ai_real &zcoord) {
} else { aiVector3D normal;
// A polygon with more than 3 vertices can be either concave or convex. bool ok = true;
// Usually everything we're getting is convex and we could easily m = DerivePlaneCoordinateSpace<ai_real>(pMesh->mVertices, pMesh->mNumVertices, ok, normal);
// triangulate by tri-fanning. However, LightWave is probably the only if (!ok) {
// modeling suite to make extensive use of highly concave, monster polygons ... false;
// so we need to apply the full 'ear cutting' algorithm to get it right. }
for (unsigned int i = 0; i < face.mNumIndices; ++i) {
unsigned int index = face.mIndices[i];
// RERQUIREMENT: polygon is expected to be simple and *nearly* planar. const aiVector3D vv = m * pMesh->mVertices[index];
// We project it onto a plane to get a 2d triangle. // 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).
// Collect all vertices of of the polygon. // XXX this should be guarded, but we somehow need to pick a suitable
for (tmp = 0; tmp < max; ++tmp) { // epsilon
temp_verts3d[tmp] = verts[idx[tmp]]; // 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));
} }
// Get Newell-Normal of the polygon. Store it for future use if it's a polygon-only mesh zcoord /= pMesh->mNumVertices;
aiVector3D n;
NewellNormal<3, 3, 3>(n, max, &temp_verts3d.front().x, &temp_verts3d.front().y, &temp_verts3d.front().z, Capa); // Further improve the projection by mapping the entire working set into
if (nor_out) { // [0,1] range. This gives us a consistent data range so all epsilons
for (tmp = 0; tmp < max; ++tmp) // used below can be constants.
nor_out[idx[tmp]] = n; 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);
} }
// Select largest normal coordinate to ignore for projection aiMatrix4x4 mult;
const float ax = (n.x > 0 ? n.x : -n.x); mult.a1 = static_cast<ai_real>(1.0) / vmax.x;
const float ay = (n.y > 0 ? n.y : -n.y); mult.b2 = static_cast<ai_real>(1.0) / vmax.y;
const float az = (n.z > 0 ? n.z : -n.z);
unsigned int ac = 0, bc = 1; // no z coord. projection to xy mult.a4 = -vmin.x * mult.a1;
float inv = n.z; mult.b4 = -vmin.y * mult.b2;
if (ax > ay) { mult.c4 = -zcoord;
if (ax > az) { // no x coord. projection to yz m = mult * m;
ac = 1;
bc = 2;
inv = n.x;
}
} 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<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; 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 #endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS

Binary file not shown.

View File

@ -1,5 +1,8 @@
find_package( Doxygen REQUIRED ) 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." ) 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 # Enable Microsoft CHM help style only on Windows

View File

@ -1484,7 +1484,7 @@ MAN_LINKS = NO
# generate an XML file that captures the structure of # generate an XML file that captures the structure of
# the code including all documentation. # 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. # 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 # If a relative path is entered the value of OUTPUT_DIRECTORY will be

View File

@ -47,8 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pragma GCC system_header # pragma GCC system_header
#endif #endif
#include <stdint.h> #include <cstdint>
#include <string.h> #include <cstring>
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Hashing function taken from // 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) { inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) {
uint32_t tmp; uint32_t tmp;
int rem; int rem;
if (!data) return 0; if (!data) return 0;
if (!len)len = (uint32_t)::strlen(data); if (!len)len = (uint32_t)::strlen(data);

View File

@ -49,12 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_LINE_SPLITTER_H #define INCLUDED_LINE_SPLITTER_H
#ifdef __GNUC__ #ifdef __GNUC__
# pragma GCC system_header #pragma GCC system_header
#endif #endif
#include <stdexcept>
#include <assimp/StreamReader.h>
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include <assimp/StreamReader.h>
#include <stdexcept>
namespace Assimp { namespace Assimp {
@ -79,37 +79,37 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
class LineSplitter { class LineSplitter {
public: public:
typedef size_t line_idx; using line_idx = size_t;
// ----------------------------------------- // -----------------------------------------
/** construct from existing stream reader /** construct from existing stream reader
note: trim is *always* assumed true if skyp_empty_lines==true 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(); ~LineSplitter();
// ----------------------------------------- // -----------------------------------------
/** pseudo-iterator increment */ /** pseudo-iterator increment */
LineSplitter& operator++(); LineSplitter &operator++();
// ----------------------------------------- // -----------------------------------------
LineSplitter& operator++(int); LineSplitter &operator++(int);
// ----------------------------------------- // -----------------------------------------
/** get a pointer to the beginning of a particular token */ /** 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*/ /** extract the start positions of N tokens from the current line*/
template <size_t N> template <size_t N>
void get_tokens(const char* (&tokens)[N]) const; void get_tokens(const char *(&tokens)[N]) const;
// ----------------------------------------- // -----------------------------------------
/** member access */ /** member access */
const std::string* operator -> () const; const std::string *operator->() const;
std::string operator* () const; std::string operator*() const;
// ----------------------------------------- // -----------------------------------------
/** boolean context */ /** boolean context */
@ -123,47 +123,45 @@ public:
// ----------------------------------------- // -----------------------------------------
/** access the underlying stream object */ /** access the underlying stream object */
StreamReaderLE& get_stream(); StreamReaderLE &get_stream();
// ----------------------------------------- // -----------------------------------------
/** !strcmp((*this)->substr(0,strlen(check)),check) */ /** !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. */ /** swallow the next call to ++, return the previous value. */
void swallow_next_increment(); void swallow_next_increment();
LineSplitter( const LineSplitter & ) = delete; LineSplitter(const LineSplitter &) = delete;
LineSplitter(LineSplitter &&) = delete; LineSplitter(LineSplitter &&) = delete;
LineSplitter &operator = ( const LineSplitter & ) = delete; LineSplitter &operator=(const LineSplitter &) = delete;
private: private:
line_idx mIdx; line_idx mIdx;
std::string mCur; std::string mCur;
StreamReaderLE& mStream; StreamReaderLE &mStream;
bool mSwallow, mSkip_empty_lines, mTrim; bool mSwallow, mSkip_empty_lines, mTrim;
}; };
AI_FORCE_INLINE AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE &stream, bool skip_empty_lines, bool trim) :
LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) mIdx(0),
: mIdx(0) mCur(),
, mCur() mStream(stream),
, mStream(stream) mSwallow(),
, mSwallow() mSkip_empty_lines(skip_empty_lines),
, mSkip_empty_lines(skip_empty_lines) mTrim(trim) {
, mTrim(trim) {
mCur.reserve(1024); mCur.reserve(1024);
operator++(); operator++();
mIdx = 0; mIdx = 0;
} }
AI_FORCE_INLINE AI_FORCE_INLINE LineSplitter::~LineSplitter() {
LineSplitter::~LineSplitter() {
// empty // empty
} }
AI_FORCE_INLINE AI_FORCE_INLINE
LineSplitter& LineSplitter::operator++() { LineSplitter &LineSplitter::operator++() {
if (mSwallow) { if (mSwallow) {
mSwallow = false; mSwallow = false;
return *this; return *this;
@ -178,7 +176,8 @@ LineSplitter& LineSplitter::operator++() {
while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) { while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
if (s == '\n' || s == '\r') { if (s == '\n' || s == '\r') {
if (mSkip_empty_lines) { 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()) { if (mStream.GetRemainingSize()) {
mStream.IncPtr(-1); mStream.IncPtr(-1);
} }
@ -188,7 +187,8 @@ LineSplitter& LineSplitter::operator++() {
mStream.IncPtr(-1); mStream.IncPtr(-1);
} }
if (mTrim) { if (mTrim) {
while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t')); while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'))
;
if (mStream.GetRemainingSize()) { if (mStream.GetRemainingSize()) {
mStream.IncPtr(-1); mStream.IncPtr(-1);
} }
@ -203,14 +203,13 @@ LineSplitter& LineSplitter::operator++() {
return *this; return *this;
} }
AI_FORCE_INLINE AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) {
LineSplitter &LineSplitter::operator++(int) {
return ++(*this); return ++(*this);
} }
AI_FORCE_INLINE AI_FORCE_INLINE
const char *LineSplitter::operator[] (size_t idx) const { const char *LineSplitter::operator[](size_t idx) const {
const char* s = operator->()->c_str(); const char *s = operator->()->c_str();
SkipSpaces(&s); SkipSpaces(&s);
for (size_t i = 0; i < idx; ++i) { for (size_t i = 0; i < idx; ++i) {
@ -226,9 +225,8 @@ const char *LineSplitter::operator[] (size_t idx) const {
} }
template <size_t N> template <size_t N>
AI_FORCE_INLINE AI_FORCE_INLINE void LineSplitter::get_tokens(const char *(&tokens)[N]) const {
void LineSplitter::get_tokens(const char* (&tokens)[N]) const { const char *s = operator->()->c_str();
const char* s = operator->()->c_str();
SkipSpaces(&s); SkipSpaces(&s);
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N; ++i) {
@ -237,18 +235,19 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
} }
tokens[i] = s; tokens[i] = s;
for (; *s && !IsSpace(*s); ++s); for (; *s && !IsSpace(*s); ++s)
;
SkipSpaces(&s); SkipSpaces(&s);
} }
} }
AI_FORCE_INLINE AI_FORCE_INLINE
const std::string* LineSplitter::operator -> () const { const std::string *LineSplitter::operator->() const {
return &mCur; return &mCur;
} }
AI_FORCE_INLINE AI_FORCE_INLINE
std::string LineSplitter::operator* () const { std::string LineSplitter::operator*() const {
return mCur; return mCur;
} }
@ -273,7 +272,7 @@ StreamReaderLE &LineSplitter::get_stream() {
} }
AI_FORCE_INLINE AI_FORCE_INLINE
bool LineSplitter::match_start(const char* check) { bool LineSplitter::match_start(const char *check) {
const size_t len = ::strlen(check); const size_t len = ::strlen(check);
return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); return len <= mCur.length() && std::equal(check, check + len, mCur.begin());

View File

@ -119,7 +119,7 @@ public:
/** @brief Normalize the vector with extra check for zero vectors */ /** @brief Normalize the vector with extra check for zero vectors */
aiVector3t& NormalizeSafe(); aiVector3t& NormalizeSafe();
/** @brief Componentwise multiplication of two vectors /** @brief Component-wise multiplication of two vectors
* *
* Note that vec*vec yields the dot product. * Note that vec*vec yields the dot product.
* @param o Second factor */ * @param o Second factor */
@ -129,7 +129,7 @@ public:
}; };
typedef aiVector3t<ai_real> aiVector3D; using aiVector3D = aiVector3t<ai_real>;
#else #else

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2020, assimp team Copyright (c) 2006-2020, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, 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 // __cplusplus
#endif // AI_VECTOR3D_INL_INC #endif // AI_VECTOR3D_INL_INC

View File

@ -65,3 +65,28 @@ TEST_F( utPolyTools, NewellNormalTest ) {
z[0] = z[1] = z[2] = z[3] = 0; z[0] = z[1] = z[2] = z[3] = 0;
NewellNormal<3, 3, 3>(out, 4, x, y, z, Capa); 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);
}

View File

@ -49,8 +49,8 @@ using namespace Assimp;
class TriangulateProcessTest : public ::testing::Test { class TriangulateProcessTest : public ::testing::Test {
public: public:
virtual void SetUp(); void SetUp() override;
virtual void TearDown(); void TearDown() override;
protected: protected:
aiMesh *pcMesh; 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); EXPECT_TRUE(pcMesh->mNormals == NULL);
} }