Sfoglia il codice sorgente

- 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

aramis_acg 14 anni fa
parent
commit
bbd7547fff

File diff suppressed because it is too large
+ 512 - 237
code/IFCLoader.cpp


+ 42 - 9
code/IFCReaderGen.cpp

@@ -654,7 +654,7 @@ namespace {
 ,		SchemaEntry("ifcreldefinesbyproperties",&STEP::ObjectHelper<NotImplemented,0>::Construct )
 ,		SchemaEntry("ifccondition",&STEP::ObjectHelper<IfcCondition,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("ifcrelflowcontrolelements",&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)
 {
 	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;
 }
 // -----------------------------------------------------------------------------------------------------------
@@ -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)
 {
 	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
-	return base;
+	if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcFeatureElement"); }	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
 template <> size_t GenericFill<IfcFeatureElementSubtraction>(const DB& db, const LIST& params, IfcFeatureElementSubtraction* 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
-	return base;
+	if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcFeatureElementSubtraction"); }	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
 template <> size_t GenericFill<IfcOpeningElement>(const DB& db, const LIST& params, IfcOpeningElement* 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
-	return base;
+	if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcOpeningElement"); }	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
 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;
 }
 // -----------------------------------------------------------------------------------------------------------
+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)
 {
 	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)
 {
 	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;
 }
 // -----------------------------------------------------------------------------------------------------------

+ 8 - 1
code/IFCReaderGen.h

@@ -994,7 +994,7 @@ namespace IFC {
 	typedef NotImplemented IfcRelDefinesByProperties; // (not currently used by Assimp)
 	struct IfcCondition;
 	typedef NotImplemented IfcGridAxis; // (not currently used by Assimp)
-	typedef NotImplemented IfcRelVoidsElement; // (not currently used by Assimp)
+	struct IfcRelVoidsElement;
 	struct IfcWindow;
 	typedef NotImplemented IfcRelFlowControlElements; // (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
     struct IfcWindow : IfcBuildingElement, ObjectHelper<IfcWindow,2> { IfcWindow() : Object("IfcWindow") {}
 		Maybe< IfcPositiveLengthMeasure::Out > OverallHeight;
@@ -3980,6 +3986,7 @@ namespace STEP {
 	DECL_CONV_STUB(IfcWorkControl);
 	DECL_CONV_STUB(IfcWorkPlan);
 	DECL_CONV_STUB(IfcCondition);
+	DECL_CONV_STUB(IfcRelVoidsElement);
 	DECL_CONV_STUB(IfcWindow);
 	DECL_CONV_STUB(IfcProtectiveDeviceType);
 	DECL_CONV_STUB(IfcJunctionBoxType);

+ 223 - 0
code/PolyTools.h

@@ -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

+ 25 - 63
code/ProcessHelper.h

@@ -64,6 +64,16 @@ namespace std {
 		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
 	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));
@@ -126,13 +136,13 @@ struct MinMaxChooser;
 
 template <> struct MinMaxChooser<float> {
 	void operator ()(float& min,float& max) {
-		max = -10e10f;
-		min =  10e10f;
+		max = -1e10f;
+		min =  1e10f;
 }};
 template <> struct MinMaxChooser<double> {
 	void operator ()(double& min,double& max) {
-		max = -10e10;
-		min =  10e10;
+		max = -1e10;
+		min =  1e10;
 }};
 template <> struct MinMaxChooser<unsigned int> {
 	void operator ()(unsigned int& min,unsigned int& max) {
@@ -142,19 +152,24 @@ template <> struct MinMaxChooser<unsigned int> {
 
 template <> struct MinMaxChooser<aiVector3D> {
 	void operator ()(aiVector3D& min,aiVector3D& max) {
-		max = aiVector3D(-10e10f,-10e10f,-10e10f);
-		min = aiVector3D( 10e10f, 10e10f, 10e10f);
+		max = aiVector3D(-1e10f,-1e10f,-1e10f);
+		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> {
 	void operator ()(aiColor4D& min,aiColor4D& max) {
-		max = aiColor4D(-10e10f,-10e10f,-10e10f,-10e10f);
-		min = aiColor4D( 10e10f, 10e10f, 10e10f, 10e10f);
+		max = aiColor4D(-1e10f,-1e10f,-1e10f,-1e10f);
+		min = aiColor4D( 1e10f, 1e10f, 1e10f, 1e10f);
 }};
 
 template <> struct MinMaxChooser<aiQuaternion> {
 	void operator ()(aiQuaternion& min,aiQuaternion& max) {
-		max = aiQuaternion(-10e10f,-10e10f,-10e10f,-10e10f);
-		min = aiQuaternion( 10e10f, 10e10f, 10e10f, 10e10f);
+		max = aiQuaternion(-1e10f,-1e10f,-1e10f,-1e10f);
+		min = aiQuaternion( 1e10f, 1e10f, 1e10f, 1e10f);
 }};
 
 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 
  * of two colours. 

+ 148 - 57
code/TriangulateProcess.cpp

@@ -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
  *  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
  */
 
 #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
 #include "TriangulateProcess.h"
 #include "ProcessHelper.h"
+#include "PolyTools.h"
 
 //#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;
 
 // ------------------------------------------------------------------------------------------------
@@ -99,34 +112,6 @@ void TriangulateProcess::Execute( aiScene* pScene)
 	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.
@@ -150,17 +135,18 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 		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
 	unsigned int numOut = 0, max_out = 0;
+	bool get_normals = true;
 	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)	{
 		aiFace& face = pMesh->mFaces[a];
-		if( face.mNumIndices <= 3)
+		if (face.mNumIndices <= 4) {
+			get_normals = false;
+		}
+		if( face.mNumIndices <= 3) {
 			numOut++;
 
+		}	
 		else {
 			numOut += face.mNumIndices-2;
 			max_out = std::max(max_out,face.mNumIndices);
@@ -171,12 +157,21 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 	assert(numOut != pMesh->mNumFaces);
 
 	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;
-	std::vector<aiVector3D> temp_verts(max_out+2); /* temporary storage for vertices */
+	// 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;
+	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
@@ -188,6 +183,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 	aiColor4D* clr = pMesh->mColors[0];
 #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
 	boost::scoped_array<bool> done(new bool[max_out]); 
 	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)	{
@@ -205,12 +205,17 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 		}
 #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 = NULL;
+			continue;
 		} 
 		// quadrilaterals can't have ears. trifanning will always work
 		else if ( face.mNumIndices == 4) {
@@ -225,6 +230,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 			sface.mIndices[0] = face.mIndices[0];
 			sface.mIndices[1] = face.mIndices[2];
 			sface.mIndices[2] = face.mIndices[3];
+
+			face.mIndices = NULL;
+			continue;
 		}
 		else
 		{
@@ -241,12 +249,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 			// Collect all vertices of of the polygon.
 			aiVector3D* verts = pMesh->mVertices;
 			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
 			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) {
 				 for (tmp = 0; tmp < max; ++tmp)
 					 nor_out[idx[tmp]] = n;
@@ -281,6 +289,35 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 				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
 			// complexity of O(n^2) (I think). Can be done in O(n).
@@ -297,12 +334,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 							break;
 						}
 					}
-					const aiVector2D* pnt1 = (const aiVector2D*)&temp_verts[ear], 
-						*pnt0 = (const aiVector2D*)&temp_verts[prev], 
-						*pnt2 = (const aiVector2D*)&temp_verts[next];
+					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 (OnLeftSideOfLine (*pnt0,*pnt2,*pnt1)) {
+					if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) {
 						continue;
 					}
 
@@ -310,7 +347,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 					for ( tmp = 0; tmp < max; ++tmp) {
 
 						// 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
 						// PointInTriangle() I'm guessing that it's actually possible to construct
@@ -324,12 +361,12 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 					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 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
 					// 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?");
+					
+
+#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 */
 					for (tmp = 0; tmp < max-2; ++tmp) {
@@ -346,9 +390,10 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 						if (!nface.mIndices)
 							nface.mIndices = new unsigned int[3];
 
-						nface.mIndices[0] = idx[0];
-						nface.mIndices[1] = idx[tmp+1];
-						nface.mIndices[2] = idx[tmp+2];
+						nface.mIndices[0] = 0;
+						nface.mIndices[1] = tmp+1;
+						nface.mIndices[2] = tmp+2;
+
 					}
 					num = 0;
 					break;
@@ -362,9 +407,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 				}
 
 				// setup indices for the new triangle ...
-				nface.mIndices[0] = idx[prev];
-				nface.mIndices[1] = idx[ear];
-				nface.mIndices[2] = idx[next];
+				nface.mIndices[0] = prev;
+				nface.mIndices[1] = ear;
+				nface.mIndices[2] = next;
 
 				// exclude the ear from most further processing
 				done[ear] = true;
@@ -374,21 +419,67 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 				// We have three indices forming the last 'ear' remaining. Collect them.
 				aiFace& nface = *curOut++;
 				nface.mNumIndices = 3;
-				nface.mIndices = face.mIndices;
+				if (!nface.mIndices) {
+					nface.mIndices = new unsigned int[3];
+				}
 
 				for (tmp = 0; done[tmp]; ++tmp);
-				idx[0] = idx[tmp];
+				nface.mIndices[0] = tmp;
 
 				for (++tmp; done[tmp]; ++tmp);
-				idx[1] = idx[tmp];
+				nface.mIndices[1] = 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
 	delete [] pMesh->mFaces;
 

+ 6 - 0
scripts/IFCImporter/entitylist.txt

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

+ 4 - 0
workspaces/vc9/assimp.vcproj

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

Some files were not shown because too many files changed in this diff