Jelajahi Sumber

- IFC: implement automatic conversion from polygons with holes to polygons that consist of only one piece and are thus applicable to triangulation by ear-cutting. This solves many of the broken windows that would fall victim to z-fighting in earlier revisions.
- IFC: reduce logging overhead
- Move parts of IFC and BLENDs logging code to a shared implementation.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@994 67173fc5-114c-0410-ac8e-9d2fd5bffc1f

aramis_acg 14 tahun lalu
induk
melakukan
c55509132b
7 mengubah file dengan 297 tambahan dan 88 penghapusan
  1. 1 27
      code/BlenderLoader.cpp
  2. 3 11
      code/BlenderLoader.h
  3. 2 0
      code/CMakeLists.txt
  4. 157 36
      code/IFCLoader.cpp
  5. 4 14
      code/IFCLoader.h
  6. 126 0
      code/LogAux.h
  7. 4 0
      workspaces/vc9/assimp.vcproj

+ 1 - 27
code/BlenderLoader.cpp

@@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderModifier.h"
 
 #include "StreamReader.h"
-#include "TinyFormatter.h"
 #include "MemoryIOWrapper.h"
 
 // zlib is needed for compressed blend files 
@@ -68,7 +67,7 @@ using namespace Assimp;
 using namespace Assimp::Blender;
 using namespace Assimp::Formatter;
 
-
+const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
 static const aiLoaderDesc blenderDesc = {
 	"Blender 3D Importer \nhttp://www.blender3d.org",
 	"Assimp Team",
@@ -976,30 +975,5 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers
 	return node.dismiss();
 }
 
-// ------------------------------------------------------------------------------------------------
-/*static*/ void BlenderImporter::ThrowException(const std::string& msg)
-{
-	throw DeadlyImportError("BLEND: "+msg);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void BlenderImporter::LogWarn(const Formatter::format& message)	{
-	DefaultLogger::get()->warn(std::string("BLEND: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void BlenderImporter::LogError(const Formatter::format& message)	{
-	DefaultLogger::get()->error(std::string("BLEND: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void BlenderImporter::LogInfo(const Formatter::format& message)	{
-	DefaultLogger::get()->info(std::string("BLEND: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void BlenderImporter::LogDebug(const Formatter::format& message)	{
-	DefaultLogger::get()->debug(std::string("BLEND: ")+=message);
-}
 
 #endif

+ 3 - 11
code/BlenderLoader.h

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_BLEND_LOADER_H
 
 #include "BaseImporter.h"
+#include "LogAux.h"
+
 namespace Assimp	{
 	
 	// TinyFormatter.h
@@ -116,7 +118,7 @@ struct aiLoaderDesc
  *  call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
  *  conversion from intermediate format to aiScene. */
 // -------------------------------------------------------------------------------------------
-class BlenderImporter : public BaseImporter
+class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter>
 {
 	friend class Importer;
 
@@ -240,16 +242,6 @@ private: // static stuff, mostly logging and error reporting.
 		const char* type
 	);
 
-	// -------------------------------------------------------------------
-	/** Prepend 'BLEND: ' and throw msg.*/
-	static void ThrowException(const std::string& msg);
-
-	// -------------------------------------------------------------------
-	/** @defgroup blog Prepend 'BLEND: ' and write @c message to log.*/
-	static void LogWarn  (const Formatter::format& message); //! @ingroup blog
-	static void LogError (const Formatter::format& message); //! @ingroup blog
-	static void LogInfo  (const Formatter::format& message); //! @ingroup blog
-	static void LogDebug (const Formatter::format& message); //! @ingroup blog
 
 private:
 

+ 2 - 0
code/CMakeLists.txt

@@ -128,6 +128,7 @@ SOURCE_GROUP( Common FILES
 	LineSplitter.h
 	TinyFormatter.h
 	Profiler.h
+	LogAux.h
 )
 
 SOURCE_GROUP( 3DS FILES
@@ -746,6 +747,7 @@ ADD_LIBRARY( assimp SHARED
 	BlenderModifier.h
 	BlenderModifier.cpp
 	Profiler.h
+	LogAux.h
 	NDOLoader.cpp
 	NDOLoader.h
 	DeboneProcess.cpp

+ 157 - 36
code/IFCLoader.cpp

@@ -50,14 +50,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "IFCReaderGen.h"
 
 #include "StreamReader.h"
-#include "TinyFormatter.h"
 #include "MemoryIOWrapper.h"
+#include "ProcessHelper.h"
+
+#include <boost/tuple/tuple.hpp>
 
 using namespace Assimp;
 using namespace Assimp::Formatter;
-
 namespace EXPRESS = STEP::EXPRESS;
 
+const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
+
 
 /* DO NOT REMOVE this comment block. The genentitylist.sh script
  * just looks for names adhering to the IFC :: IfcSomething naming scheme
@@ -564,9 +567,9 @@ void ConvertTransformOperator(aiMatrix4x4& out, const IFC::IfcCartesianTransform
 }
 
 // ------------------------------------------------------------------------------------------------
-void ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& conv)
+bool ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, ConversionData& conv)
 {
-	unsigned int cnt = 0;
+	size_t cnt = 0;
 	BOOST_FOREACH(const IFC::IfcCartesianPoint& c, loop.Polygon) {
 		aiVector3D tmp;
 		ConvertCartesianPoint(tmp,c);
@@ -574,17 +577,34 @@ void ProcessPolyloop(const IFC::IfcPolyLoop& loop, TempMesh& meshout, Conversion
 		meshout.verts.push_back(tmp);
 		++cnt;
 	}
-	meshout.vertcnt.push_back(cnt);
+	// zero- or one- vertex polyloops simply ignored
+	if (cnt >= 1) { 
+		meshout.vertcnt.push_back(cnt);
+		return true;
+	}
+	
+	if (cnt==1) {
+		meshout.vertcnt.pop_back();
+	}
+	return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& meshout, ConversionData& conv)
+void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& result, ConversionData& conv)
 {
 	BOOST_FOREACH(const IFC::IfcFace& face, fset.CfsFaces) {
+		TempMesh meshout;
+
+		size_t ob = face.Bounds.size(), cnt = 0;
 		BOOST_FOREACH(const IFC::IfcFaceBound& bound, face.Bounds) {
 			
-			if(const IFC::IfcPolyLoop* polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) {
-				ProcessPolyloop(*polyloop, meshout, conv);
+			if(const IFC::IfcPolyLoop* const polyloop = bound.Bound->ToPtr<IFC::IfcPolyLoop>()) {
+				if(ProcessPolyloop(*polyloop, meshout, conv)) {
+					if(bound.ToPtr<IFC::IfcFaceOuterBound>()) {
+						ob = cnt;
+					}
+					++cnt;
+				}
 			}
 			else {
 				IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName());
@@ -592,14 +612,140 @@ void ProcessConnectedFaceSet(const IFC::IfcConnectedFaceSet& fset, TempMesh& mes
 			}
 
 			if(!IsTrue(bound.Orientation)) {
-				unsigned int cnt = 0;
+				size_t c = 0;
 				BOOST_FOREACH(unsigned int& i, meshout.vertcnt) {
+					std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + c);
+					cnt += c;
+				}
+			}
+			
+		}
+
+		result.vertcnt.reserve(meshout.vertcnt.size()+result.vertcnt.size());
+		if (meshout.vertcnt.size() <= 1) {
+			result.verts.reserve(meshout.verts.size()+result.verts.size());
+
+			std::copy(meshout.verts.begin(),meshout.verts.end(),std::back_inserter(result.verts));
+			std::copy(meshout.vertcnt.begin(),meshout.vertcnt.end(),std::back_inserter(result.vertcnt));
+			continue;
+		}
+
+		IFCImporter::LogDebug("fixing polygon with holes for triangulation via ear-cutting");
+
+		// each hole results in two extra vertices
+		result.verts.reserve(meshout.verts.size()+cnt*2+result.verts.size());
+
+		// handle polygons with holes. our built in triangulation won't handle them as is, but
+		// the ear cutting algorithm is solid enough to deal with them if we join the inner
+		// holes with the outer boundaries by dummy connections.
+		size_t outer_polygon_start = 0;
+		
+		// see if one of the polygons is a IfcFaceOuterBound - treats this as the outer boundary.
+		// sadly we can't rely on it, the docs say 'At most one of the bounds shall be of the type IfcFaceOuterBound' 
+		std::vector<unsigned int>::iterator outer_polygon = meshout.vertcnt.end(), begin=meshout.vertcnt.begin(),  iit;
+		if (ob < face.Bounds.size()) {
+			outer_polygon = begin + ob;
+			outer_polygon_start = std::accumulate(begin,outer_polygon,0);
+		}
+		else {
+			float area_outer_polygon = 1e-10f;
+
+			// find the polygon with the largest area, it must be the outer bound. 
+			size_t max_vcount = 0;
+			for(iit = begin; iit != meshout.vertcnt.end(); ++iit) {
+				ai_assert(*iit);
+				max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
+			}
+			std::vector<float> temp((max_vcount+2)*4);
+			size_t vidx = 0;
+			for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
+
+				for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
+					const aiVector3D& v = meshout.verts[vidx+vofs];
+					temp[cnt++] = v.x;
+					temp[cnt++] = v.y;
+					temp[cnt++] = v.z;
+#ifdef _DEBUG
+					temp[cnt] = std::numeric_limits<float>::quiet_NaN();
+#endif
+					++cnt;
+				}
 				
-					std::reverse(meshout.verts.begin() + cnt,meshout.verts.begin() + cnt + i);
-					cnt += i;
+				aiVector3D nor;
+				NewellNormal<4,4,4>(nor,*iit,&temp[0],&temp[1],&temp[2]);
+				const float area = nor.SquareLength();
+
+				if (area > area_outer_polygon) {
+					area_outer_polygon = area;
+					outer_polygon = iit;
+					outer_polygon_start = vidx;
 				}
 			}
 		}
+
+		ai_assert(outer_polygon != meshout.vertcnt.end());	
+
+		typedef boost::tuple<unsigned int, unsigned int, unsigned int> InsertionPoint;
+		std::vector< InsertionPoint > insertions(*outer_polygon,boost::make_tuple(0u,0u,0u));
+
+		// iterate through all other polyloops and find points in the outer polyloop that are close
+		size_t vidx = 0;
+		for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) {
+			if (iit == outer_polygon) {
+				continue;
+			}
+
+			size_t best_ofs,best_outer;
+			float best_dist = 1e10;
+			for(size_t vofs = 0; vofs < *iit; ++vofs) {
+				const aiVector3D& v = meshout.verts[vidx+vofs];
+
+				for(size_t outer = 0; outer < *outer_polygon; ++outer) {
+					if (insertions[outer].get<0>()) {
+						continue;
+					}
+					const aiVector3D& o = meshout.verts[outer_polygon_start+outer];
+					const float d = (o-v).SquareLength();
+										
+					if (d < best_dist) {
+						best_dist = d;
+						best_ofs = vofs;
+						best_outer = outer;
+					}
+				}		
+			}
+		
+			// we will later insert a hidden connection line right after the closest point in the outer polygon
+			insertions[best_outer] = boost::make_tuple(*iit,vidx,best_ofs);
+		}
+
+		// now that we collected all vertex connections to be added, build the output polygon
+		cnt = *outer_polygon;
+		for(size_t outer = 0; outer < *outer_polygon; ++outer) {
+			const aiVector3D& o = meshout.verts[outer_polygon_start+outer];
+			result.verts.push_back(o);
+
+			const InsertionPoint& ins = insertions[outer];
+			if (!ins.get<0>()) {
+				continue;
+			}
+
+			for(size_t i = ins.get<2>(); i < ins.get<0>(); ++i) {
+				result.verts.push_back(meshout.verts[ins.get<1>() + i]);
+			}
+			for(size_t i = 0; i < ins.get<2>(); ++i) {
+				result.verts.push_back(meshout.verts[ins.get<1>() + i]);
+			}
+
+			// we need the first vertex of the inner polygon twice as we return to the
+			// outer loop through the very same connection through which we got there.
+			result.verts.push_back(meshout.verts[ins.get<1>() + ins.get<2>()]);
+
+			// also append a copy of the initial insertion point to be able to continue the outer polygon
+			result.verts.push_back(o);
+			cnt += ins.get<0>()+2;
+		}
+		result.vertcnt.push_back(cnt);
 	}
 }
 
@@ -1262,31 +1408,6 @@ void MakeTreeRelative(ConversionData& conv)
 
 } // !anon
 
-// ------------------------------------------------------------------------------------------------
-/*static*/ void IFCImporter::ThrowException(const std::string& msg)
-{
-	throw DeadlyImportError("IFC: "+msg);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void IFCImporter::LogWarn(const Formatter::format& message)	{
-	DefaultLogger::get()->warn(std::string("IFC: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void IFCImporter::LogError(const Formatter::format& message)	{
-	DefaultLogger::get()->error(std::string("IFC: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void IFCImporter::LogInfo(const Formatter::format& message)	{
-	DefaultLogger::get()->info(std::string("IFC: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ void IFCImporter::LogDebug(const Formatter::format& message)	{
-	DefaultLogger::get()->debug(std::string("IFC: ")+=message);
-}
 
 
 #endif

+ 4 - 14
code/IFCLoader.h

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_IFC_LOADER_H
 
 #include "BaseImporter.h"
+#include "LogAux.h"
+
 namespace Assimp	{
 	
 	// TinyFormatter.h
@@ -65,7 +67,7 @@ namespace Assimp	{
  See http://en.wikipedia.org/wiki/Industry_Foundation_Classes
 */
 // -------------------------------------------------------------------------------------------
-class IFCImporter : public BaseImporter
+class IFCImporter : public BaseImporter, public LogFunctions<IFCImporter>
 {
 	friend class Importer;
 
@@ -117,20 +119,8 @@ public:
 		bool skipSpaceRepresentations;
 		bool skipCurveRepresentations;
 	};
-
-public:
 	
-	// -------------------------------------------------------------------
-	/** Prepend 'IFC: ' and throw msg.*/
-	static void ThrowException(const std::string& msg);
-
-	// -------------------------------------------------------------------
-	/** @defgroup blog Prepend 'IFC: ' and write @c message to log.*/
-	static void LogWarn  (const Formatter::format& message); //! @ingroup blog
-	static void LogError (const Formatter::format& message); //! @ingroup blog
-	static void LogInfo  (const Formatter::format& message); //! @ingroup blog
-	static void LogDebug (const Formatter::format& message); //! @ingroup blog
-
+	
 private:
 
 	Settings settings;

+ 126 - 0
code/LogAux.h

@@ -0,0 +1,126 @@
+/*
+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  LogAux.h
+ *  @brief Common logging usage patterns for importer implementations
+ */
+#ifndef INCLUDED_AI_LOGAUX_H
+#define INCLUDED_AI_LOGAUX_H
+
+#include "TinyFormatter.h"
+
+namespace Assimp {
+
+template <class TDeriving>
+class LogFunctions 
+{
+
+public:
+
+	// ------------------------------------------------------------------------------------------------
+	static void ThrowException(const std::string& msg)
+	{
+		throw DeadlyImportError(log_prefix+msg);
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogWarn(const Formatter::format& message)	{
+		if (!DefaultLogger::isNullLogger()) {
+			DefaultLogger::get()->warn(log_prefix+(std::string)message);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogError(const Formatter::format& message)	{
+		if (!DefaultLogger::isNullLogger()) {
+			DefaultLogger::get()->error(log_prefix+(std::string)message);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogInfo(const Formatter::format& message)	{
+		if (!DefaultLogger::isNullLogger()) {
+			DefaultLogger::get()->info(log_prefix+(std::string)message);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogDebug(const Formatter::format& message)	{
+		if (!DefaultLogger::isNullLogger()) {
+			DefaultLogger::get()->debug(log_prefix+(std::string)message);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogWarn  (const char* message) {
+		if (!DefaultLogger::isNullLogger()) {
+			LogWarn(Formatter::format(message));
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogError  (const char* message) {
+		if (!DefaultLogger::isNullLogger()) {
+			LogError(Formatter::format(message));
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogInfo  (const char* message) {
+		if (!DefaultLogger::isNullLogger()) {
+			LogInfo(Formatter::format(message));
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	static void LogDebug  (const char* message) {
+		if (!DefaultLogger::isNullLogger()) {
+			LogDebug(Formatter::format(message));
+		}
+	}
+
+private:
+
+	static const std::string log_prefix;
+
+};
+
+} // ! Assimp
+#endif

+ 4 - 0
workspaces/vc9/assimp.vcproj

@@ -2406,6 +2406,10 @@
 					RelativePath="..\..\code\LineSplitter.h"
 					>
 				</File>
+				<File
+					RelativePath="..\..\code\LogAux.h"
+					>
+				</File>
 				<File
 					RelativePath="..\..\code\MakeVerboseFormat.cpp"
 					>