- Further work on IFC, fix transformations, support non-uniform transformations, optimize loading, use recursive algorithm to resolve holes in polygons, implement CSG logic to generate wall openings. The latter is currently disabled.

- Triangulation step now automatically drops polygons with an area of zero.
- Add debug preprocessor switch to dump all triangulations to a separate file.
- Refactoring, collect some polygon related functions in a separate header, PolyTools.h


git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1002 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2011-05-13 00:52:50 +00:00
parent e23767a170
commit bbd7547fff
8 changed files with 1065 additions and 391 deletions

File diff suppressed because it is too large Load Diff

View File

@ -654,7 +654,7 @@ namespace {
, SchemaEntry("ifcreldefinesbyproperties",&STEP::ObjectHelper<NotImplemented,0>::Construct ) , SchemaEntry("ifcreldefinesbyproperties",&STEP::ObjectHelper<NotImplemented,0>::Construct )
, SchemaEntry("ifccondition",&STEP::ObjectHelper<IfcCondition,0>::Construct ) , SchemaEntry("ifccondition",&STEP::ObjectHelper<IfcCondition,0>::Construct )
, SchemaEntry("ifcgridaxis",&STEP::ObjectHelper<NotImplemented,0>::Construct ) , SchemaEntry("ifcgridaxis",&STEP::ObjectHelper<NotImplemented,0>::Construct )
, SchemaEntry("ifcrelvoidselement",&STEP::ObjectHelper<NotImplemented,0>::Construct ) , SchemaEntry("ifcrelvoidselement",&STEP::ObjectHelper<IfcRelVoidsElement,2>::Construct )
, SchemaEntry("ifcwindow",&STEP::ObjectHelper<IfcWindow,2>::Construct ) , SchemaEntry("ifcwindow",&STEP::ObjectHelper<IfcWindow,2>::Construct )
, SchemaEntry("ifcrelflowcontrolelements",&STEP::ObjectHelper<NotImplemented,0>::Construct ) , SchemaEntry("ifcrelflowcontrolelements",&STEP::ObjectHelper<NotImplemented,0>::Construct )
, SchemaEntry("ifcrelconnectsporttoelement",&STEP::ObjectHelper<NotImplemented,0>::Construct ) , SchemaEntry("ifcrelconnectsporttoelement",&STEP::ObjectHelper<NotImplemented,0>::Construct )
@ -1329,7 +1329,16 @@ template <> size_t GenericFill<IfcHalfSpaceSolid>(const DB& db, const LIST& para
template <> size_t GenericFill<IfcPolygonalBoundedHalfSpace>(const DB& db, const LIST& params, IfcPolygonalBoundedHalfSpace* in) template <> size_t GenericFill<IfcPolygonalBoundedHalfSpace>(const DB& db, const LIST& params, IfcPolygonalBoundedHalfSpace* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcHalfSpaceSolid*>(in)); size_t base = GenericFill(db,params,static_cast<IfcHalfSpaceSolid*>(in));
// this data structure is not used yet, so there is no code generated to fill its members if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument
const DataType* arg = params[base++];
try { GenericConvert( in->Position, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); }
} while(0);
do { // convert the 'PolygonalBoundary' argument
const DataType* arg = params[base++];
try { GenericConvert( in->PolygonalBoundary, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); }
} while(0);
return base; return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
@ -1427,22 +1436,19 @@ template <> size_t GenericFill<IfcBooleanResult>(const DB& db, const LIST& param
template <> size_t GenericFill<IfcFeatureElement>(const DB& db, const LIST& params, IfcFeatureElement* in) template <> size_t GenericFill<IfcFeatureElement>(const DB& db, const LIST& params, IfcFeatureElement* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcElement*>(in)); size_t base = GenericFill(db,params,static_cast<IfcElement*>(in));
// this data structure is not used yet, so there is no code generated to fill its members if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcFeatureElement"); } return base;
return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<IfcFeatureElementSubtraction>(const DB& db, const LIST& params, IfcFeatureElementSubtraction* in) template <> size_t GenericFill<IfcFeatureElementSubtraction>(const DB& db, const LIST& params, IfcFeatureElementSubtraction* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcFeatureElement*>(in)); size_t base = GenericFill(db,params,static_cast<IfcFeatureElement*>(in));
// this data structure is not used yet, so there is no code generated to fill its members if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcFeatureElementSubtraction"); } return base;
return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<IfcOpeningElement>(const DB& db, const LIST& params, IfcOpeningElement* in) template <> size_t GenericFill<IfcOpeningElement>(const DB& db, const LIST& params, IfcOpeningElement* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcFeatureElementSubtraction*>(in)); size_t base = GenericFill(db,params,static_cast<IfcFeatureElementSubtraction*>(in));
// this data structure is not used yet, so there is no code generated to fill its members if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcOpeningElement"); } return base;
return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<IfcConditionCriterion>(const DB& db, const LIST& params, IfcConditionCriterion* in) template <> size_t GenericFill<IfcConditionCriterion>(const DB& db, const LIST& params, IfcConditionCriterion* in)
@ -2635,6 +2641,22 @@ template <> size_t GenericFill<IfcCondition>(const DB& db, const LIST& params, I
return base; return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<IfcRelVoidsElement>(const DB& db, const LIST& params, IfcRelVoidsElement* in)
{
size_t base = GenericFill(db,params,static_cast<IfcRelConnects*>(in));
if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument
const DataType* arg = params[base++];
try { GenericConvert( in->RelatingBuildingElement, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); }
} while(0);
do { // convert the 'RelatedOpeningElement' argument
const DataType* arg = params[base++];
try { GenericConvert( in->RelatedOpeningElement, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); }
} while(0);
return base;
}
// -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<IfcWindow>(const DB& db, const LIST& params, IfcWindow* in) template <> size_t GenericFill<IfcWindow>(const DB& db, const LIST& params, IfcWindow* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcBuildingElement*>(in)); size_t base = GenericFill(db,params,static_cast<IfcBuildingElement*>(in));
@ -2814,7 +2836,18 @@ template <> size_t GenericFill<IfcCartesianTransformationOperator3D>(const DB& d
template <> size_t GenericFill<IfcCartesianTransformationOperator3DnonUniform>(const DB& db, const LIST& params, IfcCartesianTransformationOperator3DnonUniform* in) template <> size_t GenericFill<IfcCartesianTransformationOperator3DnonUniform>(const DB& db, const LIST& params, IfcCartesianTransformationOperator3DnonUniform* in)
{ {
size_t base = GenericFill(db,params,static_cast<IfcCartesianTransformationOperator3D*>(in)); size_t base = GenericFill(db,params,static_cast<IfcCartesianTransformationOperator3D*>(in));
// this data structure is not used yet, so there is no code generated to fill its members if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument
const DataType* arg = params[base++];
if (dynamic_cast<const UNSET*>(&*arg)) break;
try { GenericConvert( in->Scale2, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); }
} while(0);
do { // convert the 'Scale3' argument
const DataType* arg = params[base++];
if (dynamic_cast<const UNSET*>(&*arg)) break;
try { GenericConvert( in->Scale3, *arg, db ); break; }
catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); }
} while(0);
return base; return base;
} }
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------

View File

@ -994,7 +994,7 @@ namespace IFC {
typedef NotImplemented IfcRelDefinesByProperties; // (not currently used by Assimp) typedef NotImplemented IfcRelDefinesByProperties; // (not currently used by Assimp)
struct IfcCondition; struct IfcCondition;
typedef NotImplemented IfcGridAxis; // (not currently used by Assimp) typedef NotImplemented IfcGridAxis; // (not currently used by Assimp)
typedef NotImplemented IfcRelVoidsElement; // (not currently used by Assimp) struct IfcRelVoidsElement;
struct IfcWindow; struct IfcWindow;
typedef NotImplemented IfcRelFlowControlElements; // (not currently used by Assimp) typedef NotImplemented IfcRelFlowControlElements; // (not currently used by Assimp)
typedef NotImplemented IfcRelConnectsPortToElement; // (not currently used by Assimp) typedef NotImplemented IfcRelConnectsPortToElement; // (not currently used by Assimp)
@ -2376,6 +2376,12 @@ namespace IFC {
}; };
// C++ wrapper for IfcRelVoidsElement
struct IfcRelVoidsElement : IfcRelConnects, ObjectHelper<IfcRelVoidsElement,2> { IfcRelVoidsElement() : Object("IfcRelVoidsElement") {}
Lazy< IfcElement > RelatingBuildingElement;
Lazy< IfcFeatureElementSubtraction > RelatedOpeningElement;
};
// C++ wrapper for IfcWindow // C++ wrapper for IfcWindow
struct IfcWindow : IfcBuildingElement, ObjectHelper<IfcWindow,2> { IfcWindow() : Object("IfcWindow") {} struct IfcWindow : IfcBuildingElement, ObjectHelper<IfcWindow,2> { IfcWindow() : Object("IfcWindow") {}
Maybe< IfcPositiveLengthMeasure::Out > OverallHeight; Maybe< IfcPositiveLengthMeasure::Out > OverallHeight;
@ -3980,6 +3986,7 @@ namespace STEP {
DECL_CONV_STUB(IfcWorkControl); DECL_CONV_STUB(IfcWorkControl);
DECL_CONV_STUB(IfcWorkPlan); DECL_CONV_STUB(IfcWorkPlan);
DECL_CONV_STUB(IfcCondition); DECL_CONV_STUB(IfcCondition);
DECL_CONV_STUB(IfcRelVoidsElement);
DECL_CONV_STUB(IfcWindow); DECL_CONV_STUB(IfcWindow);
DECL_CONV_STUB(IfcProtectiveDeviceType); DECL_CONV_STUB(IfcProtectiveDeviceType);
DECL_CONV_STUB(IfcJunctionBoxType); DECL_CONV_STUB(IfcJunctionBoxType);

223
code/PolyTools.h 100644
View File

@ -0,0 +1,223 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2010, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file PolyTools.h, various utilities for our dealings with arbitrary polygons */
#ifndef AI_POLYTOOLS_H_INCLUDED
#define AI_POLYTOOLS_H_INCLUDED
namespace Assimp {
// -------------------------------------------------------------------------------
/** 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 ( (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y) ) > 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;
float dot00 = v0 * v0;
float dot01 = v0 * v1;
float dot02 = v0 * v2;
float dot11 = v1 * v1;
float dot12 = v1 * v2;
const float 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);
}
// -------------------------------------------------------------------------------
/** 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 float GetArea2D(const T& v1, const T& v2, const T& v3)
{
return 0.5f * (v1.x * (v3.y - v2.y) + v2.x * (v1.y - v3.y) + v3.x * (v2.y - v1.y));
}
// -------------------------------------------------------------------------------
/** 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;
for (int 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 = sqrt(bb);
c = sqrt(cc);
theta = 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 = sqrt(bb);
c = sqrt(cc);
theta = 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 <int ofs_x, int ofs_y, int ofs_z>
inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* z)
{
// Duplicate the first two vertices at the end
x[(num+0)*ofs_x] = x[0];
x[(num+1)*ofs_x] = x[ofs_x];
y[(num+0)*ofs_y] = y[0];
y[(num+1)*ofs_y] = y[ofs_y];
z[(num+0)*ofs_z] = z[0];
z[(num+1)*ofs_z] = z[ofs_z];
float sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
float *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
float *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
float *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
for (int tmp=0; tmp < num; tmp++) {
sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
xptr += ofs_x;
xlow += ofs_x;
xhigh += ofs_x;
yptr += ofs_y;
ylow += ofs_y;
yhigh += ofs_y;
zptr += ofs_z;
zlow += ofs_z;
zhigh += ofs_z;
}
out = aiVector3D(sum_yz,sum_zx,sum_xy);
}
} // ! Assimp
#endif

View File

@ -64,6 +64,16 @@ namespace std {
return ::aiVector3D (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z)); return ::aiVector3D (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
} }
// std::min for aiVector2D
inline ::aiVector2D min (const ::aiVector2D& a, const ::aiVector2D& b) {
return ::aiVector2D (min(a.x,b.x),min(a.y,b.y));
}
// std::max for aiVector2D
inline ::aiVector2D max (const ::aiVector2D& a, const ::aiVector2D& b) {
return ::aiVector2D (max(a.x,b.x),max(a.y,b.y));
}
// std::min for aiColor4D // std::min for aiColor4D
inline ::aiColor4D min (const ::aiColor4D& a, const ::aiColor4D& b) { inline ::aiColor4D min (const ::aiColor4D& a, const ::aiColor4D& b) {
return ::aiColor4D (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a)); return ::aiColor4D (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
@ -126,13 +136,13 @@ struct MinMaxChooser;
template <> struct MinMaxChooser<float> { template <> struct MinMaxChooser<float> {
void operator ()(float& min,float& max) { void operator ()(float& min,float& max) {
max = -10e10f; max = -1e10f;
min = 10e10f; min = 1e10f;
}}; }};
template <> struct MinMaxChooser<double> { template <> struct MinMaxChooser<double> {
void operator ()(double& min,double& max) { void operator ()(double& min,double& max) {
max = -10e10; max = -1e10;
min = 10e10; min = 1e10;
}}; }};
template <> struct MinMaxChooser<unsigned int> { template <> struct MinMaxChooser<unsigned int> {
void operator ()(unsigned int& min,unsigned int& max) { void operator ()(unsigned int& min,unsigned int& max) {
@ -142,19 +152,24 @@ template <> struct MinMaxChooser<unsigned int> {
template <> struct MinMaxChooser<aiVector3D> { template <> struct MinMaxChooser<aiVector3D> {
void operator ()(aiVector3D& min,aiVector3D& max) { void operator ()(aiVector3D& min,aiVector3D& max) {
max = aiVector3D(-10e10f,-10e10f,-10e10f); max = aiVector3D(-1e10f,-1e10f,-1e10f);
min = aiVector3D( 10e10f, 10e10f, 10e10f); min = aiVector3D( 1e10f, 1e10f, 1e10f);
}}; }};
template <> struct MinMaxChooser<aiVector2D> {
void operator ()(aiVector2D& min,aiVector2D& max) {
max = aiVector2D(-1e10f,-1e10f);
min = aiVector2D( 1e10f, 1e10f);
}};
template <> struct MinMaxChooser<aiColor4D> { template <> struct MinMaxChooser<aiColor4D> {
void operator ()(aiColor4D& min,aiColor4D& max) { void operator ()(aiColor4D& min,aiColor4D& max) {
max = aiColor4D(-10e10f,-10e10f,-10e10f,-10e10f); max = aiColor4D(-1e10f,-1e10f,-1e10f,-1e10f);
min = aiColor4D( 10e10f, 10e10f, 10e10f, 10e10f); min = aiColor4D( 1e10f, 1e10f, 1e10f, 1e10f);
}}; }};
template <> struct MinMaxChooser<aiQuaternion> { template <> struct MinMaxChooser<aiQuaternion> {
void operator ()(aiQuaternion& min,aiQuaternion& max) { void operator ()(aiQuaternion& min,aiQuaternion& max) {
max = aiQuaternion(-10e10f,-10e10f,-10e10f,-10e10f); max = aiQuaternion(-1e10f,-1e10f,-1e10f,-1e10f);
min = aiQuaternion( 10e10f, 10e10f, 10e10f, 10e10f); min = aiQuaternion( 1e10f, 1e10f, 1e10f, 1e10f);
}}; }};
template <> struct MinMaxChooser<aiVectorKey> { template <> struct MinMaxChooser<aiVectorKey> {
@ -192,59 +207,6 @@ inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
} }
// -------------------------------------------------------------------------------
/** @brief Compute the newell normal of a polygon regardless of its shape
*
* @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 <int ofs_x, int ofs_y, int ofs_z>
inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* z)
{
// Duplicate the first two vertices at the end
x[(num+0)*ofs_x] = x[0];
x[(num+1)*ofs_x] = x[ofs_x];
y[(num+0)*ofs_y] = y[0];
y[(num+1)*ofs_y] = y[ofs_y];
z[(num+0)*ofs_z] = z[0];
z[(num+1)*ofs_z] = z[ofs_z];
float sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
float *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
float *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
float *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
for (int tmp=0; tmp < num; tmp++) {
sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
xptr += ofs_x;
xlow += ofs_x;
xhigh += ofs_x;
yptr += ofs_y;
ylow += ofs_y;
yhigh += ofs_y;
zptr += ofs_z;
zlow += ofs_z;
zhigh += ofs_z;
}
out = aiVector3D(sum_yz,sum_zx,sum_xy);
}
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** Little helper function to calculate the quadratic difference /** Little helper function to calculate the quadratic difference
* of two colours. * of two colours.

View File

@ -48,9 +48,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 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 * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
* - generates vertex colors to represent the face winding order. * - generates vertex colors to represent the face winding order.
* the first vertex of a polygon becomes red, the last blue. * 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
*/ */
#include "AssimpPCH.h" #include "AssimpPCH.h"
@ -58,8 +63,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
#include "TriangulateProcess.h" #include "TriangulateProcess.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include "PolyTools.h"
//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING //#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; using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -99,34 +112,6 @@ void TriangulateProcess::Execute( aiScene* pScene)
else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to be done."); else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to be done.");
} }
// ------------------------------------------------------------------------------------------------
// Test whether a point p2 is on the left side of the line formed by p0-p1
inline bool OnLeftSideOfLine(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2)
{
return ( (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y) ) > 0;
}
// ------------------------------------------------------------------------------------------------
// Test whether a point is inside a given triangle in R2
inline bool PointInTriangle2D(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2, const aiVector2D& pp)
{
// Point in triangle test using baryzentric coordinates
const aiVector2D v0 = p1 - p0;
const aiVector2D v1 = p2 - p0;
const aiVector2D v2 = pp - p0;
float dot00 = v0 * v0;
float dot01 = v0 * v1;
float dot02 = v0 * v2;
float dot11 = v1 * v1;
float dot12 = v1 * v2;
const float 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);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Triangulates the given mesh. // Triangulates the given mesh.
@ -150,17 +135,18 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
return false; return false;
} }
// the output mesh will contain triangles, but no polys anymore
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
// Find out how many output faces we'll get // Find out how many output faces we'll get
unsigned int numOut = 0, max_out = 0; unsigned int numOut = 0, max_out = 0;
bool get_normals = true;
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
aiFace& face = pMesh->mFaces[a]; aiFace& face = pMesh->mFaces[a];
if( face.mNumIndices <= 3) if (face.mNumIndices <= 4) {
get_normals = false;
}
if( face.mNumIndices <= 3) {
numOut++; numOut++;
}
else { else {
numOut += face.mNumIndices-2; numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices); max_out = std::max(max_out,face.mNumIndices);
@ -171,12 +157,21 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
assert(numOut != pMesh->mNumFaces); assert(numOut != pMesh->mNumFaces);
aiVector3D* nor_out = NULL; aiVector3D* nor_out = NULL;
if (!pMesh->mNormals && pMesh->mPrimitiveTypes == aiPrimitiveType_POLYGON) {
nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; // 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 && get_normals) {
// 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];
} }
aiFace* out = new aiFace[numOut], *curOut = out; // the output mesh will contain triangles, but no polys anymore
std::vector<aiVector3D> temp_verts(max_out+2); /* temporary storage for vertices */ pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
aiFace* out = new aiFace[numOut](), *curOut = out;
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? // Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING #ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
@ -188,6 +183,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
aiColor4D* clr = pMesh->mColors[0]; aiColor4D* clr = pMesh->mColors[0];
#endif #endif
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
FILE* fout = fopen(POLY_OUTPUT_FILE,"a");
#endif
// use boost::scoped_array to avoid slow std::vector<bool> specialiations // use boost::scoped_array to avoid slow std::vector<bool> specialiations
boost::scoped_array<bool> done(new bool[max_out]); boost::scoped_array<bool> done(new bool[max_out]);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
@ -205,12 +205,17 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
} }
#endif #endif
aiFace* const last_face = curOut;
// if it's a simple point,line or triangle: just copy it // if it's a simple point,line or triangle: just copy it
if( face.mNumIndices <= 3) if( face.mNumIndices <= 3)
{ {
aiFace& nface = *curOut++; aiFace& nface = *curOut++;
nface.mNumIndices = face.mNumIndices; nface.mNumIndices = face.mNumIndices;
nface.mIndices = face.mIndices; nface.mIndices = face.mIndices;
face.mIndices = NULL;
continue;
} }
// quadrilaterals can't have ears. trifanning will always work // quadrilaterals can't have ears. trifanning will always work
else if ( face.mNumIndices == 4) { else if ( face.mNumIndices == 4) {
@ -225,6 +230,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
sface.mIndices[0] = face.mIndices[0]; sface.mIndices[0] = face.mIndices[0];
sface.mIndices[1] = face.mIndices[2]; sface.mIndices[1] = face.mIndices[2];
sface.mIndices[2] = face.mIndices[3]; sface.mIndices[2] = face.mIndices[3];
face.mIndices = NULL;
continue;
} }
else else
{ {
@ -241,12 +249,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// Collect all vertices of of the polygon. // Collect all vertices of of the polygon.
aiVector3D* verts = pMesh->mVertices; aiVector3D* verts = pMesh->mVertices;
for (tmp = 0; tmp < max; ++tmp) { for (tmp = 0; tmp < max; ++tmp) {
temp_verts[tmp] = verts[idx[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 // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh
aiVector3D n; aiVector3D n;
NewellNormal<3,3,3>(n,max,&temp_verts.front().x,&temp_verts.front().y,&temp_verts.front().z); NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z);
if (nor_out) { if (nor_out) {
for (tmp = 0; tmp < max; ++tmp) for (tmp = 0; tmp < max; ++tmp)
nor_out[idx[tmp]] = n; nor_out[idx[tmp]] = n;
@ -281,6 +289,35 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
done[tmp] = false; 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 (size_t 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+sprintf(loc,"%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 // 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). // complexity of O(n^2) (I think). Can be done in O(n).
@ -297,12 +334,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
break; break;
} }
} }
const aiVector2D* pnt1 = (const aiVector2D*)&temp_verts[ear], const aiVector2D* pnt1 = &temp_verts[ear],
*pnt0 = (const aiVector2D*)&temp_verts[prev], *pnt0 = &temp_verts[prev],
*pnt2 = (const aiVector2D*)&temp_verts[next]; *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. // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
if (OnLeftSideOfLine (*pnt0,*pnt2,*pnt1)) { if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) {
continue; continue;
} }
@ -310,7 +347,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
for ( tmp = 0; tmp < max; ++tmp) { for ( tmp = 0; tmp < max; ++tmp) {
// We need to compare the actual values because it's possible that multiple indexes in // We need to compare the actual values because it's possible that multiple indexes in
// the polygon are refering to the same position. concave_polygon.obj is a sample // the polygon are referring to the same position. concave_polygon.obj is a sample
// //
// FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
// PointInTriangle() I'm guessing that it's actually possible to construct // PointInTriangle() I'm guessing that it's actually possible to construct
@ -324,12 +361,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
if (tmp != max) { if (tmp != max) {
continue; continue;
} }
// this vertex is an ear // this vertex is an ear
break; break;
} }
if (num_found == 2) { if (num_found == 2) {
// Due to the 'two ear theorem', every simple polygon with more than three points must // Due to the 'two ear theorem', every simple polygon with more than three points must
// have 2 'ears'. Here's definitely someting wrong ... but we don't give up yet. // have 2 'ears'. Here's definitely someting wrong ... but we don't give up yet.
// //
@ -337,6 +374,13 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// Instead we're continuting with the standard trifanning algorithm which we'd // Instead we're continuting with the standard trifanning algorithm which we'd
// use if we had only convex polygons. That's life. // use if we had only convex polygons. That's life.
DefaultLogger::get()->error("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); DefaultLogger::get()->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;
curOut -= (max-num); /* undo all previous work */ curOut -= (max-num); /* undo all previous work */
for (tmp = 0; tmp < max-2; ++tmp) { for (tmp = 0; tmp < max-2; ++tmp) {
@ -346,9 +390,10 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
if (!nface.mIndices) if (!nface.mIndices)
nface.mIndices = new unsigned int[3]; nface.mIndices = new unsigned int[3];
nface.mIndices[0] = idx[0]; nface.mIndices[0] = 0;
nface.mIndices[1] = idx[tmp+1]; nface.mIndices[1] = tmp+1;
nface.mIndices[2] = idx[tmp+2]; nface.mIndices[2] = tmp+2;
} }
num = 0; num = 0;
break; break;
@ -362,9 +407,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
} }
// setup indices for the new triangle ... // setup indices for the new triangle ...
nface.mIndices[0] = idx[prev]; nface.mIndices[0] = prev;
nface.mIndices[1] = idx[ear]; nface.mIndices[1] = ear;
nface.mIndices[2] = idx[next]; nface.mIndices[2] = next;
// exclude the ear from most further processing // exclude the ear from most further processing
done[ear] = true; done[ear] = true;
@ -374,21 +419,67 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// We have three indices forming the last 'ear' remaining. Collect them. // We have three indices forming the last 'ear' remaining. Collect them.
aiFace& nface = *curOut++; aiFace& nface = *curOut++;
nface.mNumIndices = 3; nface.mNumIndices = 3;
nface.mIndices = face.mIndices; if (!nface.mIndices) {
nface.mIndices = new unsigned int[3];
}
for (tmp = 0; done[tmp]; ++tmp); for (tmp = 0; done[tmp]; ++tmp);
idx[0] = idx[tmp]; nface.mIndices[0] = tmp;
for (++tmp; done[tmp]; ++tmp); for (++tmp; done[tmp]; ++tmp);
idx[1] = idx[tmp]; nface.mIndices[1] = tmp;
for (++tmp; done[tmp]; ++tmp); for (++tmp; done[tmp]; ++tmp);
idx[2] = idx[tmp]; nface.mIndices[2] = tmp;
} }
} }
face.mIndices = NULL; /* prevent unintended deletion of our awesome results. would be a pity */
#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;
// drop dumb 0-area triangles
if (fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
DefaultLogger::get()->debug("Dropping triangle with area 0");
--curOut;
delete[] f->mIndices;
f->mIndices = NULL;
for(aiFace* ff = f; ff != curOut; ++ff) {
ff->mNumIndices = (ff+1)->mNumIndices;
ff->mIndices = (ff+1)->mIndices;
(ff+1)->mIndices = NULL;
}
continue;
}
i[0] = idx[i[0]];
i[1] = idx[i[1]];
i[2] = idx[i[2]];
++f;
}
delete[] face.mIndices;
face.mIndices = NULL;
} }
#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
fclose(fout);
#endif
// kill the old faces // kill the old faces
delete [] pMesh->mFaces; delete [] pMesh->mFaces;

View File

@ -8,6 +8,12 @@
# code generator. Also, the names of all used entities need to be present # code generator. Also, the names of all used entities need to be present
# in the source code for this to work. # in the source code for this to work.
IfcCartesianTransformationOperator3DnonUniform
IfcFeatureElementSubtraction
IfcRelVoidsElement
IfcOpeningElement
# IfcRelFillsElement
IfcPolygonalBoundedHalfSpace
IfcPlane IfcPlane
IfcHalfSpaceSolid IfcHalfSpaceSolid
IfcAxis1Placement IfcAxis1Placement

View File

@ -2518,6 +2518,10 @@
RelativePath="..\..\code\ParsingUtils.h" RelativePath="..\..\code\ParsingUtils.h"
> >
</File> </File>
<File
RelativePath="..\..\code\PolyTools.h"
>
</File>
<File <File
RelativePath="..\..\code\Profiler.h" RelativePath="..\..\code\Profiler.h"
> >