Explorar o código

Merge remote-tracking branch 'origin/master' into opengex_support

Signed-off-by: Kim Kulling <[email protected]>

Conflicts:
	code/ParsingUtils.h
Kim Kulling %!s(int64=10) %!d(string=hai) anos
pai
achega
2717d51f7e
Modificáronse 100 ficheiros con 2460 adicións e 824 borrados
  1. 3 0
      .gitignore
  2. 4 0
      .travis.yml
  3. 47 1
      CMakeLists.txt
  4. 5 0
      CREDITS
  5. 7 1
      assimp-config.cmake.in
  6. 1 1
      code/3DSConverter.cpp
  7. 3 3
      code/3DSExporter.cpp
  8. 7 0
      code/3DSLoader.cpp
  9. 2 2
      code/ACLoader.cpp
  10. 1 1
      code/ASEParser.cpp
  11. 1 1
      code/ASEParser.h
  12. 6 6
      code/AssbinExporter.cpp
  13. 1 1
      code/AssxmlExporter.cpp
  14. 2 2
      code/B3DImporter.cpp
  15. 2 2
      code/B3DImporter.h
  16. 1 1
      code/BVHLoader.cpp
  17. 1 1
      code/BVHLoader.h
  18. 17 1
      code/BlenderIntermediate.h
  19. 10 7
      code/BlenderLoader.cpp
  20. 2 0
      code/BlobIOSystem.h
  21. 641 0
      code/C4DImporter.cpp
  22. 120 0
      code/C4DImporter.h
  23. 26 1
      code/CMakeLists.txt
  24. 1 1
      code/COBLoader.cpp
  25. 1 1
      code/COBLoader.h
  26. 1 1
      code/CalcTangentsProcess.cpp
  27. 2 2
      code/ColladaExporter.cpp
  28. 1 0
      code/ColladaHelper.h
  29. 2 2
      code/ColladaLoader.cpp
  30. 42 8
      code/ColladaParser.cpp
  31. 1 1
      code/ColladaParser.h
  32. 1 1
      code/ComputeUVMappingProcess.cpp
  33. 128 15
      code/Exporter.cpp
  34. 2 1
      code/FBXBinaryTokenizer.cpp
  35. 4 1
      code/FBXConverter.cpp
  36. 137 137
      code/FBXDocument.h
  37. 4 2
      code/FBXParser.cpp
  38. 2 1
      code/FBXTokenizer.cpp
  39. 2 2
      code/FindDegenerates.cpp
  40. 1 1
      code/FindInvalidDataProcess.cpp
  41. 1 1
      code/GenVertexNormalsProcess.cpp
  42. 13 0
      code/GenericProperty.h
  43. 437 334
      code/IFCBoolean.cpp
  44. 2 2
      code/IFCCurve.cpp
  45. 33 16
      code/IFCGeometry.cpp
  46. 16 7
      code/IFCLoader.cpp
  47. 48 22
      code/IFCMaterial.cpp
  48. 17 30
      code/IFCOpenings.cpp
  49. 152 24
      code/IFCUtil.cpp
  50. 17 7
      code/IFCUtil.h
  51. 8 0
      code/ImporterRegistry.cpp
  52. 1 1
      code/ImproveCacheLocality.cpp
  53. 8 0
      code/LWOBLoader.cpp
  54. 1 1
      code/LWOFileData.h
  55. 10 5
      code/LWOLoader.cpp
  56. 1 1
      code/MD2Loader.cpp
  57. 1 1
      code/MD5Parser.cpp
  58. 1 1
      code/MD5Parser.h
  59. 1 1
      code/NFFLoader.cpp
  60. 7 0
      code/OFFLoader.cpp
  61. 1 1
      code/ObjExporter.cpp
  62. 2 1
      code/OgreXmlSerializer.cpp
  63. 1 1
      code/OptimizeMeshes.cpp
  64. 40 35
      code/ParsingUtils.h
  65. 2 2
      code/PlyExporter.cpp
  66. 45 28
      code/PlyLoader.cpp
  67. 3 3
      code/PlyParser.cpp
  68. 1 1
      code/Q3DLoader.cpp
  69. 2 2
      code/RemoveVCProcess.cpp
  70. 2 2
      code/STLExporter.cpp
  71. 8 2
      code/STLLoader.cpp
  72. 1 1
      code/SmoothingGroups.inl
  73. 1 1
      code/SortByPTypeProcess.cpp
  74. 3 3
      code/StringComparison.h
  75. 3 0
      code/Subdivision.cpp
  76. 1 1
      code/TerragenLoader.cpp
  77. 2 2
      code/TextureTransform.cpp
  78. 28 9
      code/XFileExporter.cpp
  79. 8 2
      code/XFileExporter.h
  80. 5 1
      code/XFileParser.cpp
  81. 1 1
      code/XFileParser.h
  82. 1 1
      code/irrXMLWrapper.h
  83. 5 2
      contrib/zlib/CMakeLists.txt
  84. 1 1
      include/assimp/DefaultLogger.hpp
  85. 187 11
      include/assimp/Exporter.hpp
  86. 1 1
      include/assimp/IOStream.hpp
  87. 1 1
      include/assimp/IOSystem.hpp
  88. 3 3
      include/assimp/Importer.hpp
  89. 1 1
      include/assimp/LogStream.hpp
  90. 1 1
      include/assimp/NullLogger.hpp
  91. 3 3
      include/assimp/ProgressHandler.hpp
  92. 1 1
      include/assimp/ai_assert.h
  93. 2 5
      include/assimp/cexport.h
  94. 1 1
      include/assimp/cfileio.h
  95. 45 11
      include/assimp/cimport.h
  96. 1 1
      include/assimp/color4.h
  97. 1 1
      include/assimp/color4.inl
  98. 24 12
      include/assimp/config.h
  99. 3 3
      include/assimp/defs.h
  100. 1 2
      include/assimp/importerdesc.h

+ 3 - 0
.gitignore

@@ -51,3 +51,6 @@ test/gtest/src/gtest-stamp/Debug/gtest-build
 *.lib
 test/gtest/src/gtest-stamp/Debug/
 tools/assimp_view/assimp_viewer.vcxproj.user
+
+# Unix editor backups
+*~

+ 4 - 0
.travis.yml

@@ -1,6 +1,10 @@
 before_install:
   - sudo apt-get install cmake python3
 
+branches:
+  only:
+    - master
+
 env:
     matrix:
     - LINUX=1 TRAVIS_NO_EXPORT=YES

+ 47 - 1
CMakeLists.txt

@@ -110,7 +110,7 @@ IF ( ASSIMP_ENABLE_BOOST_WORKAROUND )
     MESSAGE( STATUS "Building a non-boost version of Assimp." )
 ELSE ( ASSIMP_ENABLE_BOOST_WORKAROUND )
     SET( Boost_DETAILED_FAILURE_MSG ON )
-    SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0" "1.55" "1.55.0" "1.56" "1.56.0" "1.57" "1.57.0" )
+    SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0" "1.55" "1.55.0" "1.56" "1.56.0" "1.57" "1.57.0" "1.58" "1.58.0" )
     FIND_PACKAGE( Boost )
     IF ( NOT Boost_FOUND )
         MESSAGE( FATAL_ERROR
@@ -189,6 +189,52 @@ ENDIF ( ASSIMP_BUILD_COMPILER STREQUAL "")
 
 MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
 
+
+SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL
+	"Build the C4D importer, which relies on the non-free Melange SDK." 
+)
+
+IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+	IF ( MSVC )
+		SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/includes")
+		
+		# pick the correct prebuilt library
+		IF(MSVC11)
+			SET(C4D_LIB_POSTFIX "_2012md")
+		ELSEIF(MSVC10)
+			SET(C4D_LIB_POSTFIX "_2010md")
+		ELSEIF(MSVC90)
+			SET(C4D_LIB_POSTFIX "_2008md")
+		ELSE()
+			MESSAGE( FATAL_ERROR 
+				"C4D is currently only supported with MSVC 9, 10, 11"
+			)
+		ENDIF()
+		
+		IF(CMAKE_CL_64)
+			SET(C4D_LIB_ARCH_POSTFIX "_x64")
+		ELSE()
+			SET(C4D_LIB_ARCH_POSTFIX "")
+		ENDIF()
+		
+		SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/lib/WIN")
+		
+		SET(C4D_DEBUG_LIBRARY "${C4D_LIB_BASE_PATH}/debug/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib")
+		SET(C4D_RELEASE_LIBRARY "${C4D_LIB_BASE_PATH}/release/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib")
+		
+		# winsock and winmm are necessary dependencies of melange (this is undocumented, but true.)
+		SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib)
+	ELSE ()
+		MESSAGE( FATAL_ERROR 
+			"C4D is currently only available on Windows with melange SDK installed in contrib/Melange"
+		)
+	ENDIF ( MSVC )
+else (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+	ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
+ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+
+
+
 ADD_SUBDIRECTORY( code/ )
 option ( ASSIMP_BUILD_ASSIMP_TOOLS
     "If the supplementary tools for Assimp are built in addition to the library."

+ 5 - 0
CREDITS

@@ -151,3 +151,8 @@ Ogre Binary format support
 
 - Filip Wasil, Tieto Poland Sp. z o.o.
 Android JNI asset extraction support
+
+- Richard Steffen
+Contributed X File exporter
+Contributed ExportProperties interface
+

+ 7 - 1
assimp-config.cmake.in

@@ -23,8 +23,14 @@ if( MSVC )
     set(MSVC_PREFIX "vc80")
   elseif( MSVC90 )
     set(MSVC_PREFIX "vc90")
-  else()
+  elseif( MSVC10 )
     set(MSVC_PREFIX "vc100")
+  elseif( MSVC11 )
+    set(MSVC_PREFIX "vc110")
+  elseif( MSVC12 )
+    set(MSVC_PREFIX "vc120")
+  else()
+    set(MSVC_PREFIX "vc130")
   endif()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" FORCE)
 else()

+ 1 - 1
code/3DSConverter.cpp

@@ -396,7 +396,7 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
 			}
 			for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
 			{
-				register unsigned int index = aiSplit[p][q];
+				unsigned int index = aiSplit[p][q];
 				aiFace& face = meshOut->mFaces[q];
 
 				face.mIndices = new unsigned int[3];

+ 3 - 3
code/3DSExporter.cpp

@@ -144,7 +144,7 @@ namespace {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
-void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	boost::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
 	if(!outfile) {
@@ -188,8 +188,8 @@ Discreet3DSExporter:: Discreet3DSExporter(boost::shared_ptr<IOStream> outfile, c
 
 	{
 		ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH);
-		WriteMeshes();
 		WriteMaterials();
+		WriteMeshes();
 
 		{
 			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE);
@@ -294,7 +294,7 @@ void Discreet3DSExporter::WriteMaterials()
 			WriteColor(color);
 		}
 
-		aiShadingMode shading_mode;
+		aiShadingMode shading_mode = aiShadingMode_Flat;
 		if (mat.Get(AI_MATKEY_SHADING_MODEL, shading_mode) == AI_SUCCESS) {
 			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
 

+ 7 - 0
code/3DSLoader.cpp

@@ -175,6 +175,10 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
 	// file.
 	for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
 		 end = mScene->mMeshes.end(); i != end;++i)	{
+		if ((*i).mFaces.size() > 0 && (*i).mPositions.size() == 0)	{
+			delete mScene;
+			throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile);
+		}
 		CheckIndices(*i);
 		MakeUnique  (*i);
 		ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
@@ -944,6 +948,9 @@ void Discreet3DSImporter::ParseFaceChunk()
 		// This is the list of smoothing groups - a bitfield for every face. 
 		// Up to 32 smoothing groups assigned to a single face.
 		unsigned int num = chunkSize/4, m = 0;
+		if (num > mMesh.mFaces.size())	{
+			throw DeadlyImportError("3DS: More smoothing groups than faces");
+		}
 		for (std::vector<D3DS::Face>::iterator i =  mMesh.mFaces.begin(); m != num;++i, ++m)	{
 			// nth bit is set for nth smoothing group
 			(*i).iSmoothGroup = stream->GetI4();

+ 2 - 2
code/ACLoader.cpp

@@ -489,7 +489,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
 
 			for (it = object.surfaces.begin(); it != end; ++it)
 			{
-				register unsigned int idx = (*it).mat;
+				unsigned int idx = (*it).mat;
 				if (idx >= needMat.size())
 				{
 					DefaultLogger::get()->error("AC3D: material index is out of range");
@@ -617,7 +617,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
 							it2  = (*it).entries.begin();
 
 							// either a closed or an unclosed line
-							register unsigned int tmp = (unsigned int)(*it).entries.size();
+							unsigned int tmp = (unsigned int)(*it).entries.size();
 							if (0x2 == type)--tmp;
 							for (unsigned int m = 0; m < tmp;++m)
 							{

+ 1 - 1
code/ASEParser.cpp

@@ -168,7 +168,7 @@ void Parser::LogInfo(const char* szWarn)
 }
 
 // ------------------------------------------------------------------------------------------------
-void Parser::LogError(const char* szWarn)
+AI_WONT_RETURN void Parser::LogError(const char* szWarn)
 {
 	ai_assert(NULL != szWarn);
 

+ 1 - 1
code/ASEParser.h

@@ -602,7 +602,7 @@ private:
 	// -------------------------------------------------------------------
 	//! Output an error to the logger
 	//! \param szWarn Error message
-	void LogError(const char* szWarn);
+	AI_WONT_RETURN void LogError(const char* szWarn) AI_WONT_RETURN_SUFFIX;
 
 	// -------------------------------------------------------------------
 	//! Parse a string, enclosed in double quotation marks

+ 6 - 6
code/AssbinExporter.cpp

@@ -273,13 +273,13 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
 			if (buffer) delete[] buffer;
 		}
 
-		void * GetBufferPointer() { return buffer; };
+		void * GetBufferPointer() { return buffer; }
 
 		// -------------------------------------------------------------------
-		virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { return 0; };
-		virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { return aiReturn_FAILURE; };
-		virtual size_t Tell() const { return cursor; };
-		virtual void Flush() { };
+		virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { return 0; }
+		virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { return aiReturn_FAILURE; }
+		virtual size_t Tell() const { return cursor; }
+		virtual void Flush() { }
 
 		virtual size_t FileSize() const
 		{
@@ -754,7 +754,7 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
 		}
 	};
 
-void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	AssbinExport exporter;
 	exporter.WriteBinaryDump( pFile, pIOSystem, pScene );

+ 1 - 1
code/AssxmlExporter.cpp

@@ -621,7 +621,7 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened)
 
 } // end of namespace AssxmlExport
 
-void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	IOStream * out = pIOSystem->Open( pFile, "wt" );
 	if (!out) return;

+ 2 - 2
code/B3DImporter.cpp

@@ -127,12 +127,12 @@ void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
 }
 
 // ------------------------------------------------------------------------------------------------
-void B3DImporter::Oops(){
+AI_WONT_RETURN void B3DImporter::Oops(){
 	throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" );
 }
 
 // ------------------------------------------------------------------------------------------------
-void B3DImporter::Fail( string str ){
+AI_WONT_RETURN void B3DImporter::Fail( string str ){
 #ifdef DEBUG_B3D
 	cout<<"Error in B3D file data: "<<str<<endl;
 #endif

+ 2 - 2
code/B3DImporter.h

@@ -87,8 +87,8 @@ private:
 		float weights[4];
 	};
 
-	void Oops();
-	void Fail( std::string str );
+	AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX;
+	AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX;
 
 	void ReadTEXS();
 	void ReadBRUS();

+ 1 - 1
code/BVHLoader.cpp

@@ -413,7 +413,7 @@ float BVHLoader::GetNextTokenAsFloat()
 
 // ------------------------------------------------------------------------------------------------
 // Aborts the file reading with an exception
-void BVHLoader::ThrowException( const std::string& pError)
+AI_WONT_RETURN void BVHLoader::ThrowException( const std::string& pError)
 {
 	throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError));
 }

+ 1 - 1
code/BVHLoader.h

@@ -134,7 +134,7 @@ protected:
 	float GetNextTokenAsFloat();
 
 	/** Aborts the file reading with an exception */
-	void ThrowException( const std::string& pError);
+	AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX;
 
 	/** Constructs an animation for the motion data and stores it in the given scene */
 	void CreateAnimation( aiScene* pScene);

+ 17 - 1
code/BlenderIntermediate.h

@@ -118,6 +118,16 @@ namespace Blender {
 #ifdef _MSC_VER
 #	pragma warning(disable:4351)
 #endif
+
+	struct ObjectCompare {
+		bool operator() (const Object* left, const Object* right) const {
+			return strcmp(left->id.name, right->id.name) == -1;
+		}
+	};
+
+	// When keeping objects in sets, sort them by their name.
+	typedef std::set<const Object*, ObjectCompare> ObjectSet;
+
 	// --------------------------------------------------------------------
 	/** ConversionData acts as intermediate storage location for
 	 *  the various ConvertXXX routines in BlenderImporter.*/
@@ -130,7 +140,13 @@ namespace Blender {
 			, db(db)
 		{}
 
-		std::set<const Object*> objects;
+		struct ObjectCompare {
+			bool operator() (const Object* left, const Object* right) const {
+				return strcmp(left->id.name, right->id.name) == -1;
+			}
+		};
+
+		ObjectSet objects;
 
 		TempArray <std::vector, aiMesh> meshes;
 		TempArray <std::vector, aiCamera> cameras;

+ 10 - 7
code/BlenderLoader.cpp

@@ -559,24 +559,26 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data)
 		if (mesh->mMaterialIndex == static_cast<unsigned int>( -1 )) {
 
 			if (index == static_cast<unsigned int>( -1 )) {
-
-				// ok, we need to add a dedicated default material for some poor material-less meshes
+				// Setup a default material.
 				boost::shared_ptr<Material> p(new Material());
+				ai_assert(::strlen(AI_DEFAULT_MATERIAL_NAME) < sizeof(p->id.name)-2);
 				strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME );
 
+				// Note: MSVC11 does not zero-initialize Material here, although it should.
+				// Thus all relevant fields should be explicitly initialized. We cannot add
+				// a default constructor to Material since the DNA codegen does not support
+				// parsing it.
 				p->r = p->g = p->b = 0.6f;
 				p->specr = p->specg = p->specb = 0.6f;
 				p->ambr = p->ambg = p->ambb = 0.0f;
 				p->mirr = p->mirg = p->mirb = 0.0f;
 				p->emit = 0.f;
 				p->alpha = 0.f;
-
-				// XXX add more / or add default c'tor to Material
+				p->har = 0;
 
 				index = static_cast<unsigned int>( conv_data.materials_raw.size() );
 				conv_data.materials_raw.push_back(p);
-
-				LogInfo("Adding default material ...");
+				LogInfo("Adding default material");
 			}
 			mesh->mMaterialIndex = index;
 		}
@@ -591,6 +593,7 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data)
 	
 		aiMaterial* mout = new aiMaterial();
 		conv_data.materials->push_back(mout);
+		// For any new material field handled here, the default material above must be updated with an appropriate default value.
 
 		// set material name
 		aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA'
@@ -1044,7 +1047,7 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
 aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform)
 {
 	std::deque<const Object*> children;
-	for(std::set<const Object*>::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
+	for(ObjectSet::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
 		const Object* object = *it;
 		if (object->parent == obj) {
 			children.push_back(object);

+ 2 - 0
code/BlobIOSystem.h

@@ -120,9 +120,11 @@ public:
 		{
 		case aiOrigin_CUR:
 			cursor += pOffset;
+			break;
 
 		case aiOrigin_END:
 			cursor = file_size - pOffset;
+			break;
 
 		case aiOrigin_SET:
 			cursor = pOffset;

+ 641 - 0
code/C4DImporter.cpp

@@ -0,0 +1,641 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp 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 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  C4DImporter.cpp
+ *  @brief Implementation of the Cinema4D importer class.
+ */
+#include "AssimpPCH.h"
+
+// no #ifdefing here, Cinema4D support is carried out in a branch of assimp
+// where it is turned on in the CMake settings. 
+
+#ifndef _MSC_VER
+#	error C4D support is currently MSVC only
+#endif 
+
+#include "C4DImporter.h"
+#include "TinyFormatter.h"
+
+#if defined(_M_X64) || defined(__amd64__)
+#	define __C4D_64BIT
+#endif
+
+#define __PC 
+#include "c4d_file.h"
+#include "default_alien_overloads.h"
+
+using namespace _melange_;
+
+// overload this function and fill in your own unique data
+void GetWriterInfo(LONG &id, String &appname)
+{
+	id = 2424226; 
+	appname = "Open Asset Import Library";
+}
+
+using namespace Assimp;
+using namespace Assimp::Formatter;
+
+namespace Assimp {
+	template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
+}
+
+static const aiImporterDesc desc = {
+	"Cinema4D Importer",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	0,
+	0,
+	"c4d"
+};
+
+
+// ------------------------------------------------------------------------------------------------
+C4DImporter::C4DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+C4DImporter::~C4DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+	const std::string& extension = GetExtension(pFile);
+	if (extension == "c4d") {
+		return true;
+	}
+
+	else if ((!extension.length() || checkSig) && pIOHandler)	{
+		// TODO
+	}
+	return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc* C4DImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void C4DImporter::SetupProperties(const Importer* /*pImp*/)
+{
+	// nothing to be done for the moment
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void C4DImporter::InternReadFile( const std::string& pFile, 
+	aiScene* pScene, IOSystem* pIOHandler)
+{
+	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+	if( file.get() == NULL) {
+		ThrowException("failed to open file " + pFile);
+	}
+
+	const size_t file_size = file->FileSize();
+
+	std::vector<uint8_t> mBuffer(file_size);
+	file->Read(&mBuffer[0], 1, file_size);
+	
+	Filename f;
+	f.SetMemoryReadMode(&mBuffer[0], file_size);
+
+	// open document first
+	BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
+	if(doc == NULL) {
+		ThrowException("failed to read document " + pFile);
+	}
+
+	pScene->mRootNode = new aiNode("<C4DRoot>");
+
+	// first convert all materials
+	ReadMaterials(doc->GetFirstMaterial());
+
+	// process C4D scenegraph recursively
+	try {
+		RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
+	}
+	catch(...) {
+		BOOST_FOREACH(aiMesh* mesh, meshes) {
+			delete mesh;
+		}
+		BaseDocument::Free(doc);
+		throw;
+	}
+	BaseDocument::Free(doc);
+
+	// copy meshes over
+	pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
+	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
+	std::copy(meshes.begin(), meshes.end(), pScene->mMeshes);
+
+	// copy materials over, adding a default material if necessary
+	unsigned int mat_count = static_cast<unsigned int>(materials.size());
+	BOOST_FOREACH(aiMesh* mesh, meshes) {
+		ai_assert(mesh->mMaterialIndex <= mat_count);
+		if(mesh->mMaterialIndex >= mat_count) {
+			++mat_count;
+
+			ScopeGuard<aiMaterial> def_material(new aiMaterial());
+			const aiString name(AI_DEFAULT_MATERIAL_NAME);
+			def_material->AddProperty(&name, AI_MATKEY_NAME);
+
+			materials.push_back(def_material.dismiss());
+			break;
+		}
+	}
+
+	pScene->mNumMaterials = static_cast<unsigned int>(materials.size());
+	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
+	std::copy(materials.begin(), materials.end(), pScene->mMaterials);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+bool C4DImporter::ReadShader(aiMaterial* out, _melange_::BaseShader* shader) 
+{
+	// based on Melange sample code (C4DImportExport.cpp)
+	while(shader) {
+		if(shader->GetType() == Xlayer) {
+			BaseContainer* container = shader->GetDataInstance();
+			GeData blend = container->GetData(SLA_LAYER_BLEND);
+			iBlendDataType* blend_list = reinterpret_cast<iBlendDataType*>(blend.GetCustomDataType(CUSTOMDATA_BLEND_LIST));
+			if (!blend_list)
+			{
+				LogWarn("ignoring XLayer shader: no blend list given");
+				continue;
+			}
+
+			LayerShaderLayer *lsl = dynamic_cast<LayerShaderLayer*>(blend_list->m_BlendLayers.GetObject(0));
+
+			// Ignore the actual layer blending - models for real-time rendering should not
+			// use them in a non-trivial way. Just try to find textures that we can apply
+			// to the model.
+			while (lsl)
+			{
+				if (lsl->GetType() == TypeFolder)
+				{
+					BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl); 
+					LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
+
+					while (subLsl)
+					{
+						if (subLsl->GetType() == TypeShader) {
+							BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl); 
+							if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
+								return true;
+							}
+						}
+
+						subLsl = subLsl->GetNext();
+					}
+				}
+				else if (lsl->GetType() == TypeShader) {
+					BlendShader* const shader = dynamic_cast<BlendShader*>(lsl); 
+					if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
+						return true;
+					}
+				}
+
+				lsl = lsl->GetNext();	
+			}
+		}
+		else if ( shader->GetType() == Xbitmap )
+		{
+			aiString path;
+			shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
+			path.length = ::strlen(path.data);
+			out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
+			return true;
+		}
+		else {
+			LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
+		}
+		shader = shader->GetNext();
+	}
+	return false;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void C4DImporter::ReadMaterials(_melange_::BaseMaterial* mat)
+{
+	// based on Melange sample code
+	while (mat)
+	{
+		const String& name = mat->GetName();
+		if (mat->GetType() == Mmaterial)
+		{
+			aiMaterial* out = new aiMaterial();
+			material_mapping[mat] = static_cast<unsigned int>(materials.size());
+			materials.push_back(out);
+
+			aiString ai_name;
+			name.GetCString(ai_name.data, MAXLEN-1);
+			ai_name.length = ::strlen(ai_name.data);
+			out->AddProperty(&ai_name, AI_MATKEY_NAME);
+
+			Material& m = dynamic_cast<Material&>(*mat);
+
+			if (m.GetChannelState(CHANNEL_COLOR))
+			{
+				GeData data;
+				mat->GetParameter(MATERIAL_COLOR_COLOR, data);
+				Vector color = data.GetVector();
+				mat->GetParameter(MATERIAL_COLOR_BRIGHTNESS, data);
+				const Real brightness = data.GetReal();
+
+				color *= brightness;
+
+				aiVector3D v;
+				v.x = color.x;
+				v.y = color.y;
+				v.z = color.z;
+				out->AddProperty(&v, 1, AI_MATKEY_COLOR_DIFFUSE);
+			}
+
+			BaseShader* const shader = m.GetShader(MATERIAL_COLOR_SHADER);
+			if(shader) {
+				ReadShader(out, shader);
+			}
+		}
+		else
+		{
+			LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
+		}
+		mat = mat->GetNext();
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
+{
+	ai_assert(parent != NULL);
+	std::vector<aiNode*> nodes;
+
+	// based on Melange sample code
+	while (object)
+	{
+		const String& name = object->GetName();
+		const LONG type = object->GetType(); 
+		const Matrix& ml = object->GetMl(); 
+
+		aiString string;
+		name.GetCString(string.data, MAXLEN-1);
+		string.length = ::strlen(string.data);
+		aiNode* const nd = new aiNode();
+
+		nd->mParent = parent;
+		nd->mName = string;
+
+		nd->mTransformation.a1 = ml.v1.x;
+		nd->mTransformation.b1 = ml.v1.y;
+		nd->mTransformation.c1 = ml.v1.z;
+
+		nd->mTransformation.a2 = ml.v2.x;
+		nd->mTransformation.b2 = ml.v2.y;
+		nd->mTransformation.c2 = ml.v2.z;
+
+		nd->mTransformation.a3 = ml.v3.x;
+		nd->mTransformation.b3 = ml.v3.y;
+		nd->mTransformation.c3 = ml.v3.z;
+
+		nd->mTransformation.a4 = ml.off.x;
+		nd->mTransformation.b4 = ml.off.y;
+		nd->mTransformation.c4 = ml.off.z;
+
+		nodes.push_back(nd);
+		
+		GeData data; 
+		if (type == Ocamera)
+		{
+			object->GetParameter(CAMERAOBJECT_FOV, data);
+			// TODO: read camera
+		}
+		else if (type == Olight)
+		{
+			// TODO: read light
+		}
+		else if (type == Opolygon)
+		{
+			aiMesh* const mesh = ReadMesh(object);
+			if(mesh != NULL) {
+				nd->mNumMeshes = 1;
+				nd->mMeshes = new unsigned int[1];
+				nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
+				meshes.push_back(mesh);
+			}
+		}
+		else {
+			LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
+		}
+		
+		RecurseHierarchy(object->GetDown(), nd);
+		object = object->GetNext();
+	}
+
+	// copy nodes over to parent
+	parent->mNumChildren = static_cast<unsigned int>(nodes.size());
+	parent->mChildren = new aiNode*[parent->mNumChildren]();
+	std::copy(nodes.begin(), nodes.end(), parent->mChildren);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+aiMesh* C4DImporter::ReadMesh(BaseObject* object)
+{
+	assert(object != NULL && object->GetType() == Opolygon);
+
+	// based on Melange sample code
+	PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
+	ai_assert(polyObject != NULL);
+
+	const LONG pointCount = polyObject->GetPointCount();
+	const LONG polyCount = polyObject->GetPolygonCount();
+	if(!polyObject || !pointCount) {
+		LogWarn("ignoring mesh with zero vertices or faces");
+		return NULL;
+	}
+
+	const Vector* points = polyObject->GetPointR();
+	ai_assert(points != NULL);
+
+	const CPolygon* polys = polyObject->GetPolygonR();
+	ai_assert(polys != NULL);
+
+	ScopeGuard<aiMesh> mesh(new aiMesh());
+	mesh->mNumFaces = static_cast<unsigned int>(polyCount);
+	aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces]();
+
+	mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+	mesh->mMaterialIndex = 0;
+
+	unsigned int vcount = 0;
+
+	// first count vertices
+	for (LONG i = 0; i < polyCount; i++)
+	{
+		vcount += 3;
+		
+		// TODO: do we also need to handle lines or points with similar checks?
+		if (polys[i].c != polys[i].d)
+		{
+			mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+			++vcount;
+		}
+	}
+
+	ai_assert(vcount > 0);
+
+	mesh->mNumVertices = vcount;
+	aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+	aiVector3D* normals, *uvs, *tangents, *bitangents;
+	unsigned int n = 0;
+
+	// check if there are normals, tangents or UVW coordinates
+	BaseTag* tag = object->GetTag(Tnormal);
+	NormalTag* normals_src = NULL;
+	if(tag) {
+		normals_src = dynamic_cast<NormalTag*>(tag);
+		normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
+	}
+
+	tag = object->GetTag(Ttangent);
+	TangentTag* tangents_src = NULL;
+	if(tag) {
+		tangents_src = dynamic_cast<TangentTag*>(tag);
+		tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
+		bitangents = mesh->mBitangents = new aiVector3D[mesh->mNumVertices]();
+	}
+
+	tag = object->GetTag(Tuvw);
+	UVWTag* uvs_src = NULL;
+	if(tag) {
+		uvs_src = dynamic_cast<UVWTag*>(tag);
+		uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
+	}
+
+	// copy vertices and extra channels over and populate faces
+	for (LONG i = 0; i < polyCount; ++i, ++face)
+	{
+		ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
+		const Vector& pointA = points[polys[i].a];
+		verts->x = pointA.x;
+		verts->y = pointA.y;
+		verts->z = pointA.z;
+		++verts;
+
+		ai_assert(polys[i].b < pointCount && polys[i].b >= 0);
+		const Vector& pointB = points[polys[i].b];
+		verts->x = pointB.x;
+		verts->y = pointB.y;
+		verts->z = pointB.z;
+		++verts;
+
+		ai_assert(polys[i].c < pointCount && polys[i].c >= 0);
+		const Vector& pointC = points[polys[i].c];
+		verts->x = pointC.x;
+		verts->y = pointC.y;
+		verts->z = pointC.z;
+		++verts;
+
+		// TODO: do we also need to handle lines or points with similar checks?
+		if (polys[i].c != polys[i].d)
+		{
+			ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
+
+			face->mNumIndices = 4;
+			mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+			const Vector& pointD = points[polys[i].d];
+			verts->x = pointD.x;
+			verts->y = pointD.y;
+			verts->z = pointD.z;
+			++verts;
+		}
+		else {
+			face->mNumIndices = 3;
+		}
+		face->mIndices = new unsigned int[face->mNumIndices];
+		for(unsigned int j = 0; j < face->mNumIndices; ++j) {
+			face->mIndices[j] = n++;
+		}
+
+		// copy normals
+		if (normals_src) {
+			if(i >= normals_src->GetNormalCount()) {
+				LogError("unexpected number of normals, ignoring");
+			}
+			else {
+				const NormalStruct& nor = normals_src->GetNormals(i);
+				normals->x = nor.a.x;
+				normals->y = nor.a.y;
+				normals->z = nor.a.z;
+				++normals;
+
+				normals->x = nor.b.x;
+				normals->y = nor.b.y;
+				normals->z = nor.b.z;
+				++normals;
+
+				normals->x = nor.c.x;
+				normals->y = nor.c.y;
+				normals->z = nor.c.z;
+				++normals;
+
+				if(face->mNumIndices == 4) {
+					normals->x = nor.d.x;
+					normals->y = nor.d.y;
+					normals->z = nor.d.z;
+					++normals;
+				}
+			}
+		}
+
+		// copy tangents and bitangents
+		if (tangents_src) {
+			
+			for(unsigned int k = 0; k < face->mNumIndices; ++k) {
+				LONG l;
+				switch(k) {
+				case 0:
+					l = polys[i].a;
+					break;
+				case 1:
+					l = polys[i].b;
+					break;
+				case 2:
+					l = polys[i].c;
+					break;
+				case 3:
+					l = polys[i].d;
+					break;
+				default:
+					ai_assert(false);
+				}
+				if(l >= tangents_src->GetDataCount()) {
+					LogError("unexpected number of tangents, ignoring");
+					break;
+				}
+
+				Tangent tan = tangents_src->GetDataR()[l];
+				tangents->x = tan.vl.x;
+				tangents->y = tan.vl.y;
+				tangents->z = tan.vl.z;
+				++tangents;
+
+				bitangents->x = tan.vr.x;
+				bitangents->y = tan.vr.y;
+				bitangents->z = tan.vr.z;
+				++bitangents;
+			}
+		}
+
+		// copy UVs
+		if (uvs_src) {
+			if(i >= uvs_src->GetDataCount()) {
+				LogError("unexpected number of UV coordinates, ignoring");
+			}
+			else {
+				UVWStruct uvw;
+				uvs_src->Get(uvs_src->GetDataAddressR(),i,uvw);
+
+				uvs->x = uvw.a.x;
+				uvs->y = 1.0f-uvw.a.y;
+				uvs->z = uvw.a.z;
+				++uvs;
+
+				uvs->x = uvw.b.x;
+				uvs->y = 1.0f-uvw.b.y;
+				uvs->z = uvw.b.z;
+				++uvs;
+
+				uvs->x = uvw.c.x;
+				uvs->y = 1.0f-uvw.c.y;
+				uvs->z = uvw.c.z;
+				++uvs;
+
+				if(face->mNumIndices == 4) {
+					uvs->x = uvw.d.x;
+					uvs->y = 1.0f-uvw.d.y;
+					uvs->z = uvw.d.z;
+					++uvs;
+				}
+			}
+		}
+	}
+
+	mesh->mMaterialIndex = ResolveMaterial(polyObject);
+	return mesh.dismiss();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
+{
+	ai_assert(obj != NULL);
+
+	const unsigned int mat_count = static_cast<unsigned int>(materials.size());
+
+	BaseTag* tag = obj->GetTag(Ttexture);
+	if(tag == NULL) {
+		return mat_count;
+	}
+
+	TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
+
+	BaseMaterial* const mat = ttag.GetMaterial();
+	assert(mat != NULL);
+
+	const MaterialMap::const_iterator it = material_mapping.find(mat);
+	if(it == material_mapping.end()) {
+		return mat_count;
+	}
+
+	ai_assert((*it).second < mat_count);
+	return (*it).second;
+}

+ 120 - 0
code/C4DImporter.h

@@ -0,0 +1,120 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp 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 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  C4DImporter.h
+ *  @brief Declaration of the Cinema4D (*.c4d) importer class.
+ */
+#ifndef INCLUDED_AI_CINEMA_4D_LOADER_H
+#define INCLUDED_AI_CINEMA_4D_LOADER_H
+
+#include "BaseImporter.h"
+#include "LogAux.h"
+
+#include <set>
+
+struct aiImporterDesc;
+
+namespace _melange_ {
+	class BaseObject; // c4d_file.h
+	class PolygonObject;
+	class BaseMaterial;
+	class BaseShader;
+}
+
+namespace Assimp	{
+
+	// TinyFormatter.h
+	namespace Formatter {
+		template <typename T,typename TR, typename A> class basic_formatter;
+		typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
+	}
+
+// -------------------------------------------------------------------------------------------
+/** Importer class to load Cinema4D files using the Melange library to be obtained from
+ *  www.plugincafe.com
+ *
+ *  Note that Melange is not free software. */
+// -------------------------------------------------------------------------------------------
+class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter>
+{
+public:
+
+	C4DImporter();
+	~C4DImporter();
+
+
+public:
+
+	// --------------------
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+		bool checkSig) const;
+
+protected:
+
+	// --------------------
+	const aiImporterDesc* GetInfo () const;
+
+	// --------------------
+	void SetupProperties(const Importer* pImp);
+
+	// --------------------
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+private:
+
+	void ReadMaterials(_melange_::BaseMaterial* mat);
+	void RecurseHierarchy(_melange_::BaseObject* object, aiNode* parent);
+	aiMesh* ReadMesh(_melange_::BaseObject* object);
+	unsigned int ResolveMaterial(_melange_::PolygonObject* obj);
+
+	bool ReadShader(aiMaterial* out, _melange_::BaseShader* shader);
+
+	std::vector<aiMesh*> meshes;
+	std::vector<aiMaterial*> materials;
+
+	typedef std::map<_melange_::BaseMaterial*, unsigned int> MaterialMap;
+	MaterialMap material_mapping;
+
+}; // !class C4DImporter
+
+} // end of namespace Assimp
+#endif // INCLUDED_AI_CINEMA_4D_LOADER_H
+

+ 26 - 1
code/CMakeLists.txt

@@ -148,6 +148,14 @@ SET( Common_SRCS
 )
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 
+IF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
+	SET( C4D_SRCS
+		C4DImporter.cpp
+		C4DImporter.h
+	)
+	SOURCE_GROUP( C4D FILES ${C4D_SRCS})
+ENDIF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
+
 SET( 3DS_SRCS
 	3DSConverter.cpp
 	3DSHelper.h
@@ -723,6 +731,11 @@ SET( assimp_src
 	AssimpPCH.cpp
 )
 
+IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+	SET( assimp_src ${assimp_src} ${C4D_SRCS})
+	INCLUDE_DIRECTORIES(${C4D_INCLUDES})
+ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+
 #ADD_MSVC_PRECOMPILED_HEADER("AssimpPCH.h" "AssimpPCH.cpp" assimp_src)
 
 ADD_LIBRARY( assimp ${assimp_src} )
@@ -735,6 +748,12 @@ if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
 	target_link_libraries(assimp android_jniiosystem)
 endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
 
+IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+	TARGET_LINK_LIBRARIES(assimp optimized ${C4D_RELEASE_LIBRARY})
+	TARGET_LINK_LIBRARIES(assimp debug ${C4D_DEBUG_LIBRARY})
+	TARGET_LINK_LIBRARIES(assimp ${C4D_EXTRA_LIBRARIES})
+ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+
 if( MSVC )
   # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
   if( MSVC70 OR MSVC71 )
@@ -743,8 +762,14 @@ if( MSVC )
     set(MSVC_PREFIX "vc80")
   elseif( MSVC90 )
     set(MSVC_PREFIX "vc90")
-  else()
+  elseif( MSVC10 )
     set(MSVC_PREFIX "vc100")
+  elseif( MSVC11 )
+    set(MSVC_PREFIX "vc110")
+  elseif( MSVC12 )
+    set(MSVC_PREFIX "vc120")
+  else()
+    set(MSVC_PREFIX "vc130")
   endif()
   set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" FORCE)
 endif()

+ 1 - 1
code/COBLoader.cpp

@@ -127,7 +127,7 @@ void COBImporter::SetupProperties(const Importer* /*pImp*/)
 }
 
 // ------------------------------------------------------------------------------------------------
-/*static*/ void COBImporter::ThrowException(const std::string& msg)
+/*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string& msg)
 {
 	throw DeadlyImportError("COB: "+msg);
 }

+ 1 - 1
code/COBLoader.h

@@ -95,7 +95,7 @@ private:
 
 	// -------------------------------------------------------------------
 	/** Prepend 'COB: ' and throw msg.*/
-	static void ThrowException(const std::string& msg);
+	AI_WONT_RETURN static void ThrowException(const std::string& msg) AI_WONT_RETURN_SUFFIX;
 
 	// -------------------------------------------------------------------
 	/** @brief Read from an ascii scene/object file

+ 1 - 1
code/CalcTangentsProcess.cpp

@@ -167,7 +167,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 			// their tangent vectors are set to qnan.
 			for (unsigned int i = 0; i < face.mNumIndices;++i)
 			{
-				register unsigned int idx = face.mIndices[i];
+				unsigned int idx = face.mIndices[i];
 				vertexDone  [idx] = true;
 				meshTang    [idx] = aiVector3D(qnan);
 				meshBitang  [idx] = aiVector3D(qnan);

+ 2 - 2
code/ColladaExporter.cpp

@@ -59,7 +59,7 @@ namespace Assimp
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	std::string path = "";
 	std::string file = pFile;
@@ -452,7 +452,7 @@ void ColladaExporter::WriteMaterials()
 	  }
 	}
 
-	aiShadingMode shading;
+	aiShadingMode shading = aiShadingMode_Flat;
 	materials[a].shading_model = "phong";
 	if(mat->Get( AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) {
 		if(shading == aiShadingMode_Phong) {

+ 1 - 0
code/ColladaHelper.h

@@ -395,6 +395,7 @@ struct Controller
 /** A collada material. Pretty much the only member is a reference to an effect. */
 struct Material
 {
+	std::string mName;
 	std::string mEffect;
 };
 

+ 2 - 2
code/ColladaLoader.cpp

@@ -176,7 +176,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
 				 0,  0,  1,  0,
 				 0, -1,  0,  0,
 				 0,  0,  0,  1);
-        }
+		}
 	// store all meshes
 	StoreSceneMeshes( pScene);
 
@@ -1379,7 +1379,7 @@ void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* /*pScene*/)
 
 		// create material
 		aiMaterial* mat = new aiMaterial;
-		aiString name( matIt->first);
+		aiString name( material.mName.empty() ? matIt->first : material.mName );
 		mat->AddProperty(&name,AI_MATKEY_NAME);
 
 		// store the material

+ 42 - 8
code/ColladaParser.cpp

@@ -60,7 +60,7 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
 {
 	mRootNode = NULL;
 	mUnitSize = 1.0f;
-	mUpDirection = UP_Z;
+	mUpDirection = UP_Y;
 
 	// We assume the newest file format by default
 	mFormat = FV_1_5_n;
@@ -225,10 +225,10 @@ void ColladaParser::ReadAssetInfo()
 				const char* content = GetTextContent();
 				if( strncmp( content, "X_UP", 4) == 0)
 					mUpDirection = UP_X;
-				else if( strncmp( content, "Y_UP", 4) == 0)
-					mUpDirection = UP_Y;
-				else
+				else if( strncmp( content, "Z_UP", 4) == 0)
 					mUpDirection = UP_Z;
+				else
+					mUpDirection = UP_Y;
 
 				// check element end
 				TestClosing( "up_axis");
@@ -817,6 +817,7 @@ void ColladaParser::ReadMaterialLibrary()
 	if( mReader->isEmptyElement())
 		return;
 
+	std::map<std::string, int> names;
 	while( mReader->read())
 	{
 		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
@@ -827,8 +828,32 @@ void ColladaParser::ReadMaterialLibrary()
 				int attrID = GetAttribute( "id");
 				std::string id = mReader->getAttributeValue( attrID);
 
+				std::string name;
+				int attrName = TestAttribute("name");
+				if (attrName >= 0)
+					name = mReader->getAttributeValue( attrName);
+
 				// create an entry and store it in the library under its ID
-				ReadMaterial(mMaterialLibrary[id] = Material());
+				mMaterialLibrary[id] = Material();
+
+				if( !name.empty())
+				{
+					std::map<std::string, int>::iterator it = names.find( name);
+					if( it != names.end())
+					{
+						std::ostringstream strStream;
+						strStream << ++it->second;
+						name.append( " " + strStream.str());
+					}
+					else
+					{
+						names[name] = 0;
+					}
+
+					mMaterialLibrary[id].mName = name;
+				}
+
+				ReadMaterial( mMaterialLibrary[id]);
 			} else
 			{
 				// ignore the rest
@@ -1385,6 +1410,9 @@ void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
 				if( attrTex >= 0 )
 	  				pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
 				//SkipElement();
+
+				// as we've read texture, the color needs to be 1,1,1,1
+				pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
 			}
 			else if( IsElement( "technique"))
 			{
@@ -1936,6 +1964,10 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 					// now here the actual fun starts - these are the indices to construct the mesh data from
 					actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
 				}
+			}
+			else if (IsElement("extra"))
+			{
+				SkipElement("extra");
 			} else
 			{
 				ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName));
@@ -1950,9 +1982,11 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 		}
 	}
 
-	// small sanity check
-	if (primType != Prim_TriFans && primType != Prim_TriStrips)
+#ifdef ASSIMP_BUILD_DEBUG  
+	if (primType != Prim_TriFans && primType != Prim_TriStrips) {
 		ai_assert(actualPrimitives == numPrimitives);
+	}
+#endif
 
 	// only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
 	subgroup.mNumFaces = actualPrimitives;
@@ -2659,7 +2693,7 @@ void ColladaParser::ReadScene()
 
 // ------------------------------------------------------------------------------------------------
 // Aborts the file reading with an exception
-void ColladaParser::ThrowException( const std::string& pError) const
+AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const
 {
 	throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError));
 }

+ 1 - 1
code/ColladaParser.h

@@ -212,7 +212,7 @@ protected:
 
 protected:
 	/** Aborts the file reading with an exception */
-	void ThrowException( const std::string& pError) const;
+	AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX;
 
 	/** Skips all data until the end node of the current element */
 	void SkipElement();

+ 1 - 1
code/ComputeUVMappingProcess.cpp

@@ -454,7 +454,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
 						for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
 						{
 							aiMesh* mesh = pScene->mMeshes[m];
-							unsigned int outIdx;
+							unsigned int outIdx = 0;
 							if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX ||
 								!mesh->mNumVertices)
 							{

+ 128 - 15
code/Exporter.cpp

@@ -72,16 +72,16 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
 // ------------------------------------------------------------------------------------------------
 // Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype
 // do not use const, because some exporter need to convert the scene temporary
-void ExportSceneCollada(const char*,IOSystem*, const aiScene*);
-void ExportSceneXFile(const char*,IOSystem*, const aiScene*); 
-void ExportSceneObj(const char*,IOSystem*, const aiScene*);
-void ExportSceneSTL(const char*,IOSystem*, const aiScene*);
-void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*);
-void ExportScenePly(const char*,IOSystem*, const aiScene*);
-void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*);
-void ExportScene3DS(const char*, IOSystem*, const aiScene*);
-void ExportSceneAssbin(const char*, IOSystem*, const aiScene*);
-void ExportSceneAssxml(const char*, IOSystem*, const aiScene*);
+void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*); 
+void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 
 // ------------------------------------------------------------------------------------------------
 // global array of all export formats which Assimp supports in its current build
@@ -91,7 +91,7 @@ Exporter::ExportFormatEntry gExporters[] =
 	Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada),
 #endif
 
-#ifndef ASSIMP_BUILD_NO_FXILE_EXPORTER
+#ifndef ASSIMP_BUILD_NO_XFILE_EXPORTER
 	Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
 		aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs),
 #endif
@@ -226,7 +226,7 @@ bool Exporter :: IsDefaultIOHandler() const
 
 
 // ------------------------------------------------------------------------------------------------
-const aiExportDataBlob* Exporter :: ExportToBlob(  const aiScene* pScene, const char* pFormatId, unsigned int )
+const aiExportDataBlob* Exporter :: ExportToBlob(  const aiScene* pScene, const char* pFormatId, unsigned int, const ExportProperties* pProperties)
 {
 	if (pimpl->blob) {
 		delete pimpl->blob;
@@ -282,7 +282,7 @@ bool IsVerboseFormat(const aiScene* pScene)
 
 
 // ------------------------------------------------------------------------------------------------
-aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing )
+aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing, const ExportProperties* pProperties)
 {
 	ASSIMP_BEGIN_EXCEPTION_REGION();
 
@@ -290,7 +290,7 @@ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const
 	// format. They will likely not be aware that there is a flag in the scene to indicate
 	// this, however. To avoid surprises and bug reports, we check for duplicates in
 	// meshes upfront.
-	const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);
+	const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);	
 
 	pimpl->mError = "";
 	for (size_t i = 0; i < pimpl->mExporters.size(); ++i) {
@@ -397,7 +397,8 @@ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const
 					proc.Execute(scenecopy.get());
 				}
 
-				exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get());
+				ExportProperties emptyProperties;  // Never pass NULL ExportProperties so Exporters don't have to worry.
+				exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
 			}
 			catch (DeadlyExportError& err) {
 				pimpl->mError = err.what();
@@ -492,4 +493,116 @@ void Exporter :: UnregisterExporter(const char* id)
 	}
 }
 
+ExportProperties :: ExportProperties() {}
+
+ExportProperties::ExportProperties(const ExportProperties &other)
+	 : mIntProperties(other.mIntProperties),
+   mFloatProperties(other.mFloatProperties),
+   mStringProperties(other.mStringProperties),
+   mMatrixProperties(other.mMatrixProperties)
+{
+	
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void ExportProperties :: SetPropertyInteger(const char* szName, int iValue, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<int>(mIntProperties, szName,iValue,bWasExisting);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void ExportProperties :: SetPropertyFloat(const char* szName, float iValue, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<float>(mFloatProperties, szName,iValue,bWasExisting);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void ExportProperties :: SetPropertyString(const char* szName, const std::string& value, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<std::string>(mStringProperties, szName,value,bWasExisting);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void ExportProperties :: SetPropertyMatrix(const char* szName, const aiMatrix4x4& value, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value,bWasExisting);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+int ExportProperties :: GetPropertyInteger(const char* szName, 
+	int iErrorReturn /*= 0xffffffff*/) const
+{
+	return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+float ExportProperties :: GetPropertyFloat(const char* szName, 
+	float iErrorReturn /*= 10e10*/) const
+{
+	return GetGenericProperty<float>(mFloatProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+const std::string ExportProperties :: GetPropertyString(const char* szName, 
+	const std::string& iErrorReturn /*= ""*/) const
+{
+	return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+const aiMatrix4x4 ExportProperties :: GetPropertyMatrix(const char* szName, 
+	const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const
+{
+	return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+bool ExportProperties :: HasPropertyInteger(const char* szName) const
+{
+	return HasGenericProperty<int>(mIntProperties, szName);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+bool ExportProperties :: HasPropertyBool(const char* szName) const
+{
+	return HasGenericProperty<int>(mIntProperties, szName);
+};
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+bool ExportProperties :: HasPropertyFloat(const char* szName) const
+{
+	return HasGenericProperty<float>(mFloatProperties, szName);
+};
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+bool ExportProperties :: HasPropertyString(const char* szName) const
+{
+	return HasGenericProperty<std::string>(mStringProperties, szName);
+};
+
+// ------------------------------------------------------------------------------------------------
+// Has a configuration property
+bool ExportProperties :: HasPropertyMatrix(const char* szName) const
+{
+	return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName);
+};
+
+
 #endif // !ASSIMP_BUILD_NO_EXPORT

+ 2 - 1
code/FBXBinaryTokenizer.cpp

@@ -78,7 +78,8 @@ namespace {
 
 // ------------------------------------------------------------------------------------------------
 // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
-void TokenizeError(const std::string& message, unsigned int offset)
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset)
 {
 	throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
 }

+ 4 - 1
code/FBXConverter.cpp

@@ -1256,8 +1256,11 @@ private:
 				// taking notes so we don't need to do it twice.
 				BOOST_FOREACH(WeightIndexArray::value_type index, indices) {
 
-					unsigned int count;
+					unsigned int count = 0;
 					const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
+					// ToOutputVertexIndex only returns NULL if index is out of bounds
+					// which should never happen
+					ai_assert(out_idx != NULL);
 
 					index_out_indices.push_back(no_index_sentinel);
 					count_out_indices.push_back(0);

+ 137 - 137
code/FBXDocument.h

@@ -242,20 +242,20 @@ public:
 
 public:
 
-	fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0));
-	fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0));
-	fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0));
+	fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
+	fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
+	fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0))
 
-	fbx_simple_property(AspectWidth, float, 1.0f);
-	fbx_simple_property(AspectHeight, float, 1.0f);
-	fbx_simple_property(FilmWidth, float, 1.0f);
-	fbx_simple_property(FilmHeight, float, 1.0f);
+	fbx_simple_property(AspectWidth, float, 1.0f)
+	fbx_simple_property(AspectHeight, float, 1.0f)
+	fbx_simple_property(FilmWidth, float, 1.0f)
+	fbx_simple_property(FilmHeight, float, 1.0f)
 
-	fbx_simple_property(FilmAspectRatio, float, 1.0f);
-	fbx_simple_property(ApertureMode, int, 0);
+	fbx_simple_property(FilmAspectRatio, float, 1.0f)
+	fbx_simple_property(ApertureMode, int, 0)
 
-	fbx_simple_property(FieldOfView, float, 1.0f);
-	fbx_simple_property(FocalLength, float, 1.0f);
+	fbx_simple_property(FieldOfView, float, 1.0f)
+	fbx_simple_property(FocalLength, float, 1.0f)
 
 private:
 };
@@ -314,37 +314,37 @@ public:
 
 public:
 
-	fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1));
-	fbx_simple_enum_property(LightType, Type, 0);
-	fbx_simple_property(CastLightOnObject, bool, false);
-	fbx_simple_property(DrawVolumetricLight, bool, true);
-	fbx_simple_property(DrawGroundProjection, bool, true);
-	fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false);
-	fbx_simple_property(Intensity, float, 1.0f);
-	fbx_simple_property(InnerAngle, float, 0.0f);
-	fbx_simple_property(OuterAngle, float, 45.0f);
-	fbx_simple_property(Fog, int, 50);
-	fbx_simple_enum_property(DecayType, Decay, 0);
-	fbx_simple_property(DecayStart, int, 0);
-	fbx_simple_property(FileName, std::string, "");
-
-	fbx_simple_property(EnableNearAttenuation, bool, false);
-	fbx_simple_property(NearAttenuationStart, float, 0.0f);
-	fbx_simple_property(NearAttenuationEnd, float, 0.0f);
-	fbx_simple_property(EnableFarAttenuation, bool, false);
-	fbx_simple_property(FarAttenuationStart, float, 0.0f);
-	fbx_simple_property(FarAttenuationEnd, float, 0.0f);
-
-	fbx_simple_property(CastShadows, bool, true);
-	fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0));
-
-	fbx_simple_property(AreaLightShape, int, 0);
-
-	fbx_simple_property(LeftBarnDoor, float, 20.0f);
-	fbx_simple_property(RightBarnDoor, float, 20.0f);
-	fbx_simple_property(TopBarnDoor, float, 20.0f);
-	fbx_simple_property(BottomBarnDoor, float, 20.0f);
-	fbx_simple_property(EnableBarnDoor, bool, true);
+	fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1))
+	fbx_simple_enum_property(LightType, Type, 0)
+	fbx_simple_property(CastLightOnObject, bool, false)
+	fbx_simple_property(DrawVolumetricLight, bool, true)
+	fbx_simple_property(DrawGroundProjection, bool, true)
+	fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false)
+	fbx_simple_property(Intensity, float, 1.0f)
+	fbx_simple_property(InnerAngle, float, 0.0f)
+	fbx_simple_property(OuterAngle, float, 45.0f)
+	fbx_simple_property(Fog, int, 50)
+	fbx_simple_enum_property(DecayType, Decay, 0)
+	fbx_simple_property(DecayStart, int, 0)
+	fbx_simple_property(FileName, std::string, "")
+
+	fbx_simple_property(EnableNearAttenuation, bool, false)
+	fbx_simple_property(NearAttenuationStart, float, 0.0f)
+	fbx_simple_property(NearAttenuationEnd, float, 0.0f)
+	fbx_simple_property(EnableFarAttenuation, bool, false)
+	fbx_simple_property(FarAttenuationStart, float, 0.0f)
+	fbx_simple_property(FarAttenuationEnd, float, 0.0f)
+
+	fbx_simple_property(CastShadows, bool, true)
+	fbx_simple_property(ShadowColor, aiVector3D, aiVector3D(0,0,0))
+
+	fbx_simple_property(AreaLightShape, int, 0)
+
+	fbx_simple_property(LeftBarnDoor, float, 20.0f)
+	fbx_simple_property(RightBarnDoor, float, 20.0f)
+	fbx_simple_property(TopBarnDoor, float, 20.0f)
+	fbx_simple_property(BottomBarnDoor, float, 20.0f)
+	fbx_simple_property(EnableBarnDoor, bool, true)
 
 
 private:
@@ -387,81 +387,81 @@ public:
 
 public:
 
-	fbx_simple_property(QuaternionInterpolate, int, 0);
-
-	fbx_simple_property(RotationOffset, aiVector3D, aiVector3D());
-	fbx_simple_property(RotationPivot, aiVector3D, aiVector3D());
-	fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D());
-	fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D());
-	fbx_simple_property(TranslationActive, bool, false);
-
-	fbx_simple_property(TranslationMin, aiVector3D, aiVector3D());
-	fbx_simple_property(TranslationMax, aiVector3D, aiVector3D());
-
-	fbx_simple_property(TranslationMinX, bool, false);
-	fbx_simple_property(TranslationMaxX, bool, false);
-	fbx_simple_property(TranslationMinY, bool, false);
-	fbx_simple_property(TranslationMaxY, bool, false);
-	fbx_simple_property(TranslationMinZ, bool, false);
-	fbx_simple_property(TranslationMaxZ, bool, false);
-
-	fbx_simple_enum_property(RotationOrder, RotOrder, 0);
-	fbx_simple_property(RotationSpaceForLimitOnly, bool, false);
-	fbx_simple_property(RotationStiffnessX, float, 0.0f);
-	fbx_simple_property(RotationStiffnessY, float, 0.0f);
-	fbx_simple_property(RotationStiffnessZ, float, 0.0f);
-	fbx_simple_property(AxisLen, float, 0.0f);
-
-	fbx_simple_property(PreRotation, aiVector3D, aiVector3D());
-	fbx_simple_property(PostRotation, aiVector3D, aiVector3D());
-	fbx_simple_property(RotationActive, bool, false);
-
-	fbx_simple_property(RotationMin, aiVector3D, aiVector3D());
-	fbx_simple_property(RotationMax, aiVector3D, aiVector3D());
-
-	fbx_simple_property(RotationMinX, bool, false);
-	fbx_simple_property(RotationMaxX, bool, false);
-	fbx_simple_property(RotationMinY, bool, false);
-	fbx_simple_property(RotationMaxY, bool, false);
-	fbx_simple_property(RotationMinZ, bool, false);
-	fbx_simple_property(RotationMaxZ, bool, false);
-	fbx_simple_enum_property(InheritType, TransformInheritance, 0);
-
-	fbx_simple_property(ScalingActive, bool, false);
-	fbx_simple_property(ScalingMin, aiVector3D, aiVector3D());
-	fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f));
-	fbx_simple_property(ScalingMinX, bool, false);
-	fbx_simple_property(ScalingMaxX, bool, false);
-	fbx_simple_property(ScalingMinY, bool, false);
-	fbx_simple_property(ScalingMaxY, bool, false);
-	fbx_simple_property(ScalingMinZ, bool, false);
-	fbx_simple_property(ScalingMaxZ, bool, false);
-
-	fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D());
-	fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D());
-	fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f));
-
-	fbx_simple_property(MinDampRangeX, float, 0.0f);
-	fbx_simple_property(MinDampRangeY, float, 0.0f);
-	fbx_simple_property(MinDampRangeZ, float, 0.0f);
-	fbx_simple_property(MaxDampRangeX, float, 0.0f);
-	fbx_simple_property(MaxDampRangeY, float, 0.0f);
-	fbx_simple_property(MaxDampRangeZ, float, 0.0f);
-
-	fbx_simple_property(MinDampStrengthX, float, 0.0f);
-	fbx_simple_property(MinDampStrengthY, float, 0.0f);
-	fbx_simple_property(MinDampStrengthZ, float, 0.0f);
-	fbx_simple_property(MaxDampStrengthX, float, 0.0f);
-	fbx_simple_property(MaxDampStrengthY, float, 0.0f);
-	fbx_simple_property(MaxDampStrengthZ, float, 0.0f);
-
-	fbx_simple_property(PreferredAngleX, float, 0.0f);
-	fbx_simple_property(PreferredAngleY, float, 0.0f);
-	fbx_simple_property(PreferredAngleZ, float, 0.0f);
-
-	fbx_simple_property(Show, bool, true);
-	fbx_simple_property(LODBox, bool, false);
-	fbx_simple_property(Freeze, bool, false);
+	fbx_simple_property(QuaternionInterpolate, int, 0)
+
+	fbx_simple_property(RotationOffset, aiVector3D, aiVector3D())
+	fbx_simple_property(RotationPivot, aiVector3D, aiVector3D())
+	fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D())
+	fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D())
+	fbx_simple_property(TranslationActive, bool, false)
+
+	fbx_simple_property(TranslationMin, aiVector3D, aiVector3D())
+	fbx_simple_property(TranslationMax, aiVector3D, aiVector3D())
+
+	fbx_simple_property(TranslationMinX, bool, false)
+	fbx_simple_property(TranslationMaxX, bool, false)
+	fbx_simple_property(TranslationMinY, bool, false)
+	fbx_simple_property(TranslationMaxY, bool, false)
+	fbx_simple_property(TranslationMinZ, bool, false)
+	fbx_simple_property(TranslationMaxZ, bool, false)
+
+	fbx_simple_enum_property(RotationOrder, RotOrder, 0)
+	fbx_simple_property(RotationSpaceForLimitOnly, bool, false)
+	fbx_simple_property(RotationStiffnessX, float, 0.0f)
+	fbx_simple_property(RotationStiffnessY, float, 0.0f)
+	fbx_simple_property(RotationStiffnessZ, float, 0.0f)
+	fbx_simple_property(AxisLen, float, 0.0f)
+
+	fbx_simple_property(PreRotation, aiVector3D, aiVector3D())
+	fbx_simple_property(PostRotation, aiVector3D, aiVector3D())
+	fbx_simple_property(RotationActive, bool, false)
+
+	fbx_simple_property(RotationMin, aiVector3D, aiVector3D())
+	fbx_simple_property(RotationMax, aiVector3D, aiVector3D())
+
+	fbx_simple_property(RotationMinX, bool, false)
+	fbx_simple_property(RotationMaxX, bool, false)
+	fbx_simple_property(RotationMinY, bool, false)
+	fbx_simple_property(RotationMaxY, bool, false)
+	fbx_simple_property(RotationMinZ, bool, false)
+	fbx_simple_property(RotationMaxZ, bool, false)
+	fbx_simple_enum_property(InheritType, TransformInheritance, 0)
+
+	fbx_simple_property(ScalingActive, bool, false)
+	fbx_simple_property(ScalingMin, aiVector3D, aiVector3D())
+	fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f))
+	fbx_simple_property(ScalingMinX, bool, false)
+	fbx_simple_property(ScalingMaxX, bool, false)
+	fbx_simple_property(ScalingMinY, bool, false)
+	fbx_simple_property(ScalingMaxY, bool, false)
+	fbx_simple_property(ScalingMinZ, bool, false)
+	fbx_simple_property(ScalingMaxZ, bool, false)
+
+	fbx_simple_property(GeometricTranslation, aiVector3D, aiVector3D())
+	fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D())
+	fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f))
+
+	fbx_simple_property(MinDampRangeX, float, 0.0f)
+	fbx_simple_property(MinDampRangeY, float, 0.0f)
+	fbx_simple_property(MinDampRangeZ, float, 0.0f)
+	fbx_simple_property(MaxDampRangeX, float, 0.0f)
+	fbx_simple_property(MaxDampRangeY, float, 0.0f)
+	fbx_simple_property(MaxDampRangeZ, float, 0.0f)
+
+	fbx_simple_property(MinDampStrengthX, float, 0.0f)
+	fbx_simple_property(MinDampStrengthY, float, 0.0f)
+	fbx_simple_property(MinDampStrengthZ, float, 0.0f)
+	fbx_simple_property(MaxDampStrengthX, float, 0.0f)
+	fbx_simple_property(MaxDampStrengthY, float, 0.0f)
+	fbx_simple_property(MaxDampStrengthZ, float, 0.0f)
+
+	fbx_simple_property(PreferredAngleX, float, 0.0f)
+	fbx_simple_property(PreferredAngleY, float, 0.0f)
+	fbx_simple_property(PreferredAngleZ, float, 0.0f)
+
+	fbx_simple_property(Show, bool, true)
+	fbx_simple_property(LODBox, bool, false)
+	fbx_simple_property(Freeze, bool, false)
 
 public:
 
@@ -1015,10 +1015,10 @@ public:
 
 public:
 
-	fbx_simple_property(LocalStart, uint64_t, 0L);
-	fbx_simple_property(LocalStop, uint64_t, 0L);
-	fbx_simple_property(ReferenceStart, uint64_t, 0L);
-	fbx_simple_property(ReferenceStop, uint64_t, 0L);
+	fbx_simple_property(LocalStart, uint64_t, 0L)
+	fbx_simple_property(LocalStop, uint64_t, 0L)
+	fbx_simple_property(ReferenceStart, uint64_t, 0L)
+	fbx_simple_property(ReferenceStop, uint64_t, 0L)
 
 
 
@@ -1227,18 +1227,18 @@ public:
 	}
 
 
-	fbx_simple_property(UpAxis, int, 1);
-	fbx_simple_property(UpAxisSign, int, 1);
-	fbx_simple_property(FrontAxis, int, 2);
-	fbx_simple_property(FrontAxisSign, int, 1);
-	fbx_simple_property(CoordAxis, int, 0);
-	fbx_simple_property(CoordAxisSign, int, 1);
-	fbx_simple_property(OriginalUpAxis, int, 0);
-	fbx_simple_property(OriginalUpAxisSign, int, 1);
-	fbx_simple_property(UnitScaleFactor, double, 1);
-	fbx_simple_property(OriginalUnitScaleFactor, double, 1);
-	fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0));
-	fbx_simple_property(DefaultCamera, std::string, "");
+	fbx_simple_property(UpAxis, int, 1)
+	fbx_simple_property(UpAxisSign, int, 1)
+	fbx_simple_property(FrontAxis, int, 2)
+	fbx_simple_property(FrontAxisSign, int, 1)
+	fbx_simple_property(CoordAxis, int, 0)
+	fbx_simple_property(CoordAxisSign, int, 1)
+	fbx_simple_property(OriginalUpAxis, int, 0)
+	fbx_simple_property(OriginalUpAxisSign, int, 1)
+	fbx_simple_property(UnitScaleFactor, double, 1)
+	fbx_simple_property(OriginalUnitScaleFactor, double, 1)
+	fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0))
+	fbx_simple_property(DefaultCamera, std::string, "")
 
 
 	enum FrameRate {
@@ -1261,10 +1261,10 @@ public:
 		FrameRate_MAX// end-of-enum sentinel
 	};
 
-	fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT);
-	fbx_simple_property(TimeSpanStart, uint64_t, 0L);
-	fbx_simple_property(TimeSpanStop, uint64_t, 0L);
-	fbx_simple_property(CustomFrameRate, float, -1.0f);
+	fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT)
+	fbx_simple_property(TimeSpanStart, uint64_t, 0L)
+	fbx_simple_property(TimeSpanStop, uint64_t, 0L)
+	fbx_simple_property(CustomFrameRate, float, -1.0f)
 
 
 private:

+ 4 - 2
code/FBXParser.cpp

@@ -68,13 +68,15 @@ namespace {
 
 	// ------------------------------------------------------------------------------------------------
 	// signal parse error, this is always unrecoverable. Throws DeadlyImportError.
-	void ParseError(const std::string& message, const Token& token)
+	AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
+	AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
 	{
 		throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token));
 	}
 
 	// ------------------------------------------------------------------------------------------------
-	void ParseError(const std::string& message, const Element* element = NULL)
+	AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
+	AI_WONT_RETURN void ParseError(const std::string& message, const Element* element)
 	{
 		if(element) {
 			ParseError(message,element->KeyToken());

+ 2 - 1
code/FBXTokenizer.cpp

@@ -86,7 +86,8 @@ namespace {
 
 // ------------------------------------------------------------------------------------------------
 // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
-void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
 {
 	throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column));
 }

+ 2 - 2
code/FindDegenerates.cpp

@@ -107,7 +107,7 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
 		bool first = true;
 
 		// check whether the face contains degenerated entries
-		for (register unsigned int i = 0; i < face.mNumIndices; ++i)
+		for (unsigned int i = 0; i < face.mNumIndices; ++i)
 		{
 			// Polygons with more than 4 points are allowed to have double points, that is
 			// simulating polygons with holes just with concave polygons. However,
@@ -116,7 +116,7 @@ void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
 			if (face.mNumIndices > 4)
 				limit = std::min(limit,i+2);
 
-			for (register unsigned int t = i+1; t < limit; ++t)
+			for (unsigned int t = i+1; t < limit; ++t)
 			{
 				if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]])
 				{

+ 1 - 1
code/FindInvalidDataProcess.cpp

@@ -89,7 +89,7 @@ void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMap
 		unsigned int out = 0;
 		for (unsigned int a = 0; a < node->mNumMeshes;++a)	{
 
-			register unsigned int ref = node->mMeshes[a];
+			unsigned int ref = node->mMeshes[a];
 			if (UINT_MAX != (ref = meshMapping[ref]))	{
 				node->mMeshes[out++] = ref;
 			}

+ 1 - 1
code/GenVertexNormalsProcess.cpp

@@ -195,7 +195,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int
 			// Write the smoothed normal back to all affected normals
 			for (unsigned int a = 0; a < verticesFound.size(); ++a)
 			{
-				register unsigned int vidx = verticesFound[a];
+				unsigned int vidx = verticesFound[a];
 				pcNew[vidx] = pcNor;
 				abHad[vidx] = true;
 			}

+ 13 - 0
code/GenericProperty.h

@@ -108,5 +108,18 @@ inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
 		*bWasExisting = true;
 }
 
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline const bool HasGenericProperty(const std::map< unsigned int, T >& list, 
+	const char* szName)
+{
+	ai_assert(NULL != szName);
+	const uint32_t hash = SuperFastHash(szName);
+
+	typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+	if (it == list.end()) return false;
+	
+	return true;
+}
 
 #endif // !! AI_GENERIC_PROPERTY_H_INCLUDED

+ 437 - 334
code/IFCBoolean.cpp

@@ -50,42 +50,97 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ProcessHelper.h"
 
 #include <iterator>
+#include <boost/tuple/tuple.hpp>
+
 
 namespace Assimp {
 	namespace IFC {
 		
 // ------------------------------------------------------------------------------------------------
-enum Intersect {
-	Intersect_No,
-	Intersect_LiesOnPlane,
-	Intersect_Yes
-};
-
-// ------------------------------------------------------------------------------------------------
-Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0, 
-								const IfcVector3& e1, 
-								IfcVector3& out) 
+// Calculates intersection between line segment and plane. To catch corner cases, specify which side you prefer.
+// The function then generates a hit only if the end is beyond a certain margin in that direction, filtering out
+// "very close to plane" ghost hits as long as start and end stay directly on or within the given plane side.
+bool IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0, 
+	const IfcVector3& e1, bool assumeStartOnWhiteSide, IfcVector3& out)
 {
-	const IfcVector3 pdelta = e0 - p, seg = e1-e0;
+	const IfcVector3 pdelta = e0 - p, seg = e1 - e0;
 	const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
 
-	if (std::fabs(dotOne) < 1e-6) {
-		return std::fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
+	// if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this
+	// point leaves the plane through the other side
+	if( std::abs(dotOne + dotTwo) < 1e-6 )
+		return false;
+
+	// if segment starts on the plane, report a hit only if the end lies on the *other* side
+	if( std::abs(dotTwo) < 1e-6 )
+	{
+		if( (assumeStartOnWhiteSide && dotOne + dotTwo < 1e-6) || (!assumeStartOnWhiteSide && dotOne + dotTwo > -1e-6) )
+		{
+			out = e0;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
 	}
 
-	const IfcFloat t = dotTwo/dotOne;
+	// ignore if segment is parallel to plane and far away from it on either side
+	// Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered
+	if( std::abs(dotOne) < 1e-6 )
+		return false;
+
 	// t must be in [0..1] if the intersection point is within the given segment
-	if (t > 1.f || t < 0.f) {
-		return Intersect_No;
+	const IfcFloat t = dotTwo / dotOne;
+	if( t > 1.0 || t < 0.0 )
+		return false;
+
+	out = e0 + t*seg;
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void FilterPolygon(std::vector<IfcVector3>& resultpoly)
+{
+	if( resultpoly.size() < 3 )
+	{
+		resultpoly.clear();
+		return;
 	}
-	out = e0+t*seg;
-	return Intersect_Yes;
+
+	IfcVector3 vmin, vmax;
+	ArrayBounds(resultpoly.data(), resultpoly.size(), vmin, vmax);
+
+	// filter our IfcFloat points - those may happen if a point lies
+	// directly on the intersection line or directly on the clipping plane
+	const IfcFloat epsilon = (vmax - vmin).SquareLength() / 1e6f;
+	FuzzyVectorCompare fz(epsilon);
+	std::vector<IfcVector3>::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz);
+
+	if( e != resultpoly.end() )
+		resultpoly.erase(e, resultpoly.end());
+
+	if( !resultpoly.empty() && fz(resultpoly.front(), resultpoly.back()) )
+		resultpoly.pop_back();
 }
 
 // ------------------------------------------------------------------------------------------------
-void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result, 
-									   const TempMesh& first_operand, 
-									   ConversionData& /*conv*/)
+void WritePolygon(std::vector<IfcVector3>& resultpoly, TempMesh& result)
+{
+	FilterPolygon(resultpoly);
+
+	if( resultpoly.size() > 2 )
+	{
+		result.verts.insert(result.verts.end(), resultpoly.begin(), resultpoly.end());
+		result.vertcnt.push_back(resultpoly.size());
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
+	const TempMesh& first_operand,
+	ConversionData& /*conv*/)
 {
 	ai_assert(hs != NULL);
 
@@ -120,20 +175,14 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
 	for(iit = begin; iit != end; vidx += *iit++) {
 
 		unsigned int newcount = 0;
-		for(unsigned int i = 0; i < *iit; ++i) {
-			const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
+		bool isAtWhiteSide = (in[vidx] - p) * n > -1e-6;
+		for( unsigned int i = 0; i < *iit; ++i ) {
+			const IfcVector3& e0 = in[vidx + i], e1 = in[vidx + (i + 1) % *iit];
 
 			// does the next segment intersect the plane?
 			IfcVector3 isectpos;
-			const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
-			if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
-				if ( (e0-p).Normalize()*n > 0 ) {
-					outvert.push_back(e0);
-					++newcount;
-				}
-			}
-			else if (isect == Intersect_Yes) {
-				if ( (e0-p).Normalize()*n > 0 ) {
+			if( IntersectSegmentPlane(p, n, e0, e1, isAtWhiteSide, isectpos) ) {
+				if( isAtWhiteSide ) {
 					// e0 is on the right side, so keep it 
 					outvert.push_back(e0);
 					outvert.push_back(isectpos);
@@ -144,8 +193,16 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
 					outvert.push_back(isectpos);
 					++newcount;
 				}
+				isAtWhiteSide = !isAtWhiteSide;
+			}
+			else
+			{
+				if( isAtWhiteSide ) {
+					outvert.push_back(e0);
+					++newcount;
+				}
 			}
-		}	
+		}
 
 		if (!newcount) {
 			continue;
@@ -185,76 +242,114 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
 // ------------------------------------------------------------------------------------------------
 // Check if e0-e1 intersects a sub-segment of the given boundary line.
 // note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
-bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
-	std::vector<size_t>& intersected_boundary_segments,
-	std::vector<IfcVector3>& intersected_boundary_points,
-	bool half_open = false,
-	bool* e0_hits_border = NULL)
+// New version takes the supposed inside/outside state as a parameter and treats corner cases as if
+// the line stays on that side. This should make corner cases more stable.
+// Two million assumptions! Boundary should have all z at 0.0, will be treated as closed, should not have
+// segments with length <1e-6, self-intersecting might break the corner case handling... just don't go there, ok?
+bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
+	const bool isStartAssumedInside, std::vector<std::pair<size_t, IfcVector3> >& intersect_results,
+	const bool halfOpen = false)
 {
-	ai_assert(intersected_boundary_segments.empty());
-	ai_assert(intersected_boundary_points.empty());
-
-	if(e0_hits_border) {
-		*e0_hits_border = false;
+	ai_assert(intersect_results.empty());
+
+	// determine winding order - necessary to detect segments going "inwards" or "outwards" from a point directly on the border
+	// positive sum of angles means clockwise order when looking down the -Z axis
+	IfcFloat windingOrder = 0.0;
+	for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
+		IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i];
+		IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount];
+		IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane
+		// Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation
+		// could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side))
+		windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y);
 	}
+	windingOrder = windingOrder > 0.0 ? 1.0 : -1.0;
 
-	const IfcVector3& e = e1 - e0;
+	const IfcVector3 e = e1 - e0;
 
-	for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) {
+	for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
 		// boundary segment i: b0-b1
 		const IfcVector3& b0 = boundary[i];
-		const IfcVector3& b1 = boundary[(i+1) % bcount];
-
-		const IfcVector3& b = b1 - b0;
+		const IfcVector3& b1 = boundary[(i + 1) % bcount];
+		IfcVector3 b = b1 - b0;
+		IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
 
 		// segment-segment intersection
 		// solve b0 + b*s = e0 + e*t for (s,t)
 		const IfcFloat det = (-b.x * e.y + e.x * b.y);
-		if(std::fabs(det) < 1e-6) {
+		if( std::abs(det) < 1e-6 ) {
 			// no solutions (parallel lines)
 			continue;
 		}
 
 		const IfcFloat x = b0.x - e0.x;
 		const IfcFloat y = b0.y - e0.y;
-
-		const IfcFloat s = (x*e.y - e.x*y)/det;
-		const IfcFloat t = (x*b.y - b.x*y)/det;
-
+		const IfcFloat s = (x*e.y - e.x*y) / det; // scale along boundary edge
+		const IfcFloat t = (x*b.y - b.x*y) / det; // scale along given segment
+		const IfcVector3 p = e0 + e*t;
 #ifdef ASSIMP_BUILD_DEBUG
-		const IfcVector3 check = b0 + b*s  - (e0 + e*t);
-		ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5);
+		const IfcVector3 check = b0 + b*s - p;
+		ai_assert((IfcVector2(check.x, check.y)).SquareLength() < 1e-5);
 #endif
 
-		// for a valid intersection, s-t should be in range [0,1].
-		// note that for t (i.e. the segment point) we only use a
-		// half-sided epsilon because the next segment should catch
-		// this case.
-		const IfcFloat epsilon = 1e-6;
-		if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
+		// also calculate the distance of e0 and e1 to the segment. We need to detect the "starts directly on segment" 
+		// and "ends directly at segment" cases
+		bool startsAtSegment, endsAtSegment;
+		{
+			// calculate closest point to each end on the segment, clamp that point to the segment's length, then check
+			// distance to that point. This approach is like testing if e0 is inside a capped cylinder.
+			IfcFloat et0 = (b.x*(e0.x - b0.x) + b.y*(e0.y - b0.y)) * b_sqlen_inv;
+			IfcVector3 closestPosToE0OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et0)) * b;
+			startsAtSegment = (closestPosToE0OnBoundary - IfcVector3(e0.x, e0.y, 0.0)).SquareLength() < 1e-12;
+			IfcFloat et1 = (b.x*(e1.x - b0.x) + b.y*(e1.y - b0.y)) * b_sqlen_inv;
+			IfcVector3 closestPosToE1OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et1)) * b;
+			endsAtSegment = (closestPosToE1OnBoundary - IfcVector3(e1.x, e1.y, 0.0)).SquareLength() < 1e-12;
+		}
+
+		// Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments
+		if( endsAtSegment && !halfOpen )
+			continue;
 
-			if (e0_hits_border && !*e0_hits_border) {
-				*e0_hits_border = std::fabs(t) < 1e-5f;
+		// Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE
+		// state. This should catch the case where a connected set of segments has a point directly on the boundary,
+		// one segment not hitting it because it ends there and the next segment not hitting it because it starts there
+		// Should NOT generate a hit if the segment only touches the boundary but turns around and stays inside.
+		if( startsAtSegment )
+		{
+			IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder;
+			bool isGoingInside = (inside_dir * e) > 0.0;
+			if( isGoingInside == isStartAssumedInside )
+				continue;
+
+			// only insert the point into the list if it is sufficiently far away from the previous intersection point. 
+			// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
+			if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
+			{
+				const IfcVector3 diff = intersect_results.back().second - e0;
+				if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
+					continue;
 			}
-	
-			const IfcVector3& p = e0 + e*t;
-
-			// only insert the point into the list if it is sufficiently
-			// far away from the previous intersection point. This way,
-			// we avoid duplicate detection if the intersection is
-			// directly on the vertex between two segments.
-			if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) {
-				const IfcVector3 diff = intersected_boundary_points.back() - p;
-				if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) {
+			intersect_results.push_back(std::make_pair(i, e0));
+			continue;
+		}
+
+		// for a valid intersection, s and t should be in range [0,1]. Including a bit of epsilon on s, potential double
+		// hits on two consecutive boundary segments are filtered
+		if( s >= -1e-6 * b_sqlen_inv && s <= 1.0 + 1e-6*b_sqlen_inv && t >= 0.0 && (t <= 1.0 || halfOpen) )
+		{
+			// only insert the point into the list if it is sufficiently far away from the previous intersection point. 
+			// This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
+			if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
+			{
+				const IfcVector3 diff = intersect_results.back().second - p;
+				if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
 					continue;
-				}
 			}
-			intersected_boundary_segments.push_back(i);
-			intersected_boundary_points.push_back(p);
+			intersect_results.push_back(std::make_pair(i, p));
 		}
 	}
 
-	return !intersected_boundary_segments.empty();
+	return !intersect_results.empty();
 }
 
 
@@ -272,47 +367,21 @@ bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
 	// the border of the polygon. If any of our attempts produces this result,
 	// we return false immediately.
 
-	std::vector<size_t> intersected_boundary_segments;
-	std::vector<IfcVector3> intersected_boundary_points;
+	std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
 	size_t votes = 0;
 
-	bool is_border;
-	IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary, 
-		intersected_boundary_segments, 
-		intersected_boundary_points, true, &is_border);
-
-	if(is_border) {
-		return false;
-	}
-
-	votes += intersected_boundary_segments.size() % 2;
-
-	intersected_boundary_segments.clear();
-	intersected_boundary_points.clear();
-
-	IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary, 
-		intersected_boundary_segments, 
-		intersected_boundary_points, true, &is_border);
-
-	if(is_border) {
-		return false;
-	}
-
-	votes += intersected_boundary_segments.size() % 2;
+	IntersectsBoundaryProfile(p, p + IfcVector3(1.0, 0, 0), boundary, true, intersected_boundary, true);
+	votes += intersected_boundary.size() % 2;
 
-	intersected_boundary_segments.clear();
-	intersected_boundary_points.clear();
+	intersected_boundary.clear();
+	IntersectsBoundaryProfile(p, p + IfcVector3(0, 1.0, 0), boundary, true, intersected_boundary, true);
+	votes += intersected_boundary.size() % 2;
 
-	IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary, 
-		intersected_boundary_segments, 
-		intersected_boundary_points, true, &is_border);
+	intersected_boundary.clear();
+	IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true);
+	votes += intersected_boundary.size() % 2;
 
-	if(is_border) {
-		return false;
-	}
-
-	votes += intersected_boundary_segments.size() % 2;
-	//ai_assert(votes == 3 || votes == 0);
+//	ai_assert(votes == 3 || votes == 0);
 	return votes > 1;
 }
 
@@ -350,6 +419,9 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
 		return;
 	}
 
+	// determine winding order by calculating the normal.
+	IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(profile->verts.data(), profile->verts.size());
+
 	IfcMatrix4 proj_inv;
 	ConvertAxisPlacement(proj_inv,hs->Position);
 
@@ -361,256 +433,287 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
 	// clip the current contents of `meshout` against the plane we obtained from the second operand
 	const std::vector<IfcVector3>& in = first_operand.verts;
 	std::vector<IfcVector3>& outvert = result.verts;
-
-	std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(), 
-		end = first_operand.vertcnt.end(), iit;
+	std::vector<unsigned int>& outvertcnt = result.vertcnt;
 
 	outvert.reserve(in.size());
-	result.vertcnt.reserve(first_operand.vertcnt.size());
-
-	std::vector<size_t> intersected_boundary_segments;
-	std::vector<IfcVector3> intersected_boundary_points;
+	outvertcnt.reserve(first_operand.vertcnt.size());
 
-	// TODO: the following algorithm doesn't handle all cases. 
 	unsigned int vidx = 0;
-	for(iit = begin; iit != end; vidx += *iit++) {
-		if (!*iit) {
-			continue;
+	std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin();
+	std::vector<unsigned int>::const_iterator end = first_operand.vertcnt.end();
+	std::vector<unsigned int>::const_iterator iit;
+	for( iit = begin; iit != end; vidx += *iit++ )
+	{
+		// Our new approach: we cut the poly along the plane, then we intersect the part on the black side of the plane 
+		// against the bounding polygon. All the white parts, and the black part outside the boundary polygon, are kept.
+		std::vector<IfcVector3> whiteside, blackside;
+
+		{
+			const IfcVector3* srcVertices = &in[vidx];
+			const size_t srcVtxCount = *iit;
+			if( srcVtxCount == 0 )
+				continue;
+
+			IfcVector3 polyNormal = TempMesh::ComputePolygonNormal(srcVertices, srcVtxCount, true);
+
+			// if the poly is parallel to the plane, put it completely on the black or white side
+			if( std::abs(polyNormal * n) > 0.9999 )
+			{
+				bool isOnWhiteSide = (srcVertices[0] - p) * n > -1e-6;
+				std::vector<IfcVector3>& targetSide = isOnWhiteSide ? whiteside : blackside;
+				targetSide.insert(targetSide.end(), srcVertices, srcVertices + srcVtxCount);
+			}
+			else
+			{
+				// otherwise start building one polygon for each side. Whenever the current line segment intersects the plane
+				// we put a point there as an end of the current segment. Then we switch to the other side, put a point there, too,
+				// as a beginning of the current segment, and simply continue accumulating vertices. 
+				bool isCurrentlyOnWhiteSide = ((srcVertices[0]) - p) * n > -1e-6;
+				for( size_t a = 0; a < srcVtxCount; ++a )
+				{
+					IfcVector3 e0 = srcVertices[a];
+					IfcVector3 e1 = srcVertices[(a + 1) % srcVtxCount];
+					IfcVector3 ei;
+
+					// put starting point to the current mesh
+					std::vector<IfcVector3>& trgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
+					trgt.push_back(srcVertices[a]);
+
+					// if there's an intersection, put an end vertex there, switch to the other side's mesh,
+					// and add a starting vertex there, too
+					bool isPlaneHit = IntersectSegmentPlane(p, n, e0, e1, isCurrentlyOnWhiteSide, ei);
+					if( isPlaneHit )
+					{
+						if( trgt.empty() || (trgt.back() - ei).SquareLength() > 1e-12 )
+							trgt.push_back(ei);
+						isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide;
+						std::vector<IfcVector3>& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
+						newtrgt.push_back(ei);
+					}
+				}
+			}
 		}
 
-		unsigned int newcount = 0;
-		bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts);
-
-		// used any more?
-		//size_t last_intersected_boundary_segment;
-		IfcVector3 last_intersected_boundary_point;
-
-		bool extra_point_flag = false;
-		IfcVector3 extra_point;
-
-		IfcVector3 enter_volume;
-		bool entered_volume_flag = false;
-
-		for(unsigned int i = 0; i < *iit; ++i) {
-			// current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set
-			const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i];
-			const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit];
-
-			// does the current segment intersect the polygonal boundary?
-			const IfcVector3& e0_plane = proj * e0;
-			const IfcVector3& e1_plane = proj * e1;
-
-			intersected_boundary_segments.clear();
-			intersected_boundary_points.clear();
-
-			const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts);
-			const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary;
-			
-			IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts, 
-				intersected_boundary_segments, 
-				intersected_boundary_points);
-		
-			ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty());
-
-			// does the current segment intersect the plane?
-			// (no extra check if this is an extra point)
-			IfcVector3 isectpos;
-			const Intersect isect =  extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos);
+		// the part on the white side can be written into the target mesh right away
+		WritePolygon(whiteside, result);
+
+		// The black part is the piece we need to get rid of, but only the part of it within the boundary polygon.
+		// So we now need to construct all the polygons that result from BlackSidePoly minus BoundaryPoly.
+		FilterPolygon(blackside);
+
+		// Complicated, II. We run along the polygon. a) When we're inside the boundary, we run on until we hit an 
+		// intersection, which means we're leaving it. We then start a new out poly there. b) When we're outside the 
+		// boundary, we start collecting vertices until we hit an intersection, then we run along the boundary until we hit 
+		// an intersection, then we switch back to the poly and run on on this one again, and so on until we got a closed 
+		// loop. Then we continue with the path we left to catch potential additional polys on the other side of the 
+		// boundary as described in a)
+		if( !blackside.empty() )
+		{
+			// poly edge index, intersection point, edge index in boundary poly
+			std::vector<boost::tuple<size_t, IfcVector3, size_t> > intersections;
+			bool startedInside = PointInPoly(proj * blackside.front(), profile->verts);
+			bool isCurrentlyInside = startedInside;
+
+			std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
+
+			for( size_t a = 0; a < blackside.size(); ++a )
+			{
+				const IfcVector3 e0 = proj * blackside[a];
+				const IfcVector3 e1 = proj * blackside[(a + 1) % blackside.size()];
+
+				intersected_boundary.clear();
+				IntersectsBoundaryProfile(e0, e1, profile->verts, isCurrentlyInside, intersected_boundary);
+				// sort the hits by distance from e0 to get the correct in/out/in sequence. Manually :-( I miss you, C++11.
+				if( intersected_boundary.size() > 1 )
+				{
+					bool keepSorting = true;
+					while( keepSorting )
+					{
+						keepSorting = false;
+						for( size_t b = 0; b < intersected_boundary.size() - 1; ++b )
+						{
+							if( (intersected_boundary[b + 1].second - e0).SquareLength() < (intersected_boundary[b].second - e0).SquareLength() )
+							{
+								keepSorting = true;
+								std::swap(intersected_boundary[b + 1], intersected_boundary[b]);
+							}
+						}
+					}
+				}
+				// now add them to the list of intersections
+				for( size_t b = 0; b < intersected_boundary.size(); ++b )
+					intersections.push_back(boost::make_tuple(a, proj_inv * intersected_boundary[b].second, intersected_boundary[b].first));
 
-#ifdef ASSIMP_BUILD_DEBUG
-			if (isect == Intersect_Yes) {
-				const IfcFloat f = std::fabs((isectpos - p)*n);
-				ai_assert(f < 1e-5);
+				// and calculate our new inside/outside state
+				if( intersected_boundary.size() & 1 )
+					isCurrentlyInside = !isCurrentlyInside;
 			}
-#endif
-
-			const bool is_white_side = (e0-p)*n >= -1e-6;
 
-			// e0 on good side of plane? (i.e. we should keep all geometry on this side)
-			if (is_white_side) {
-				// but is there an intersection in e0-e1 and is e1 in the clipping
-				// boundary? In this case, generate a line that only goes to the
-				// intersection point.
-				if (isect == Intersect_Yes  && !is_outside_boundary) {
-					outvert.push_back(e0);
-					++newcount;
-
-					outvert.push_back(isectpos);
-					++newcount;
-					
-					/*
-					// this is, however, only a line that goes to the plane, but not
-					// necessarily to the point where the bounding volume on the
-					// black side of the plane is hit. So basically, we need another 
-					// check for [isectpos-e1], which should yield an intersection
-					// point.
-					extra_point_flag = true;
-					extra_point = isectpos;
-
-					was_outside_boundary = true; 
-					continue; */
-
-					// [isectpos, enter_volume] potentially needs extra points.
-					// For this, we determine the intersection point with the
-					// bounding volume and project it onto the plane. 
-					/*
-					const IfcVector3& enter_volume_proj = proj * enter_volume;
-					const IfcVector3& enter_isectpos = proj * isectpos;
-
-					intersected_boundary_segments.clear();
-					intersected_boundary_points.clear();
-
-					IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts, 
-						intersected_boundary_segments, 
-						intersected_boundary_points);
-
-					if(!intersected_boundary_segments.empty()) {
-
-						vec = vec + ((p - vec) * n) * n;
-					}
-					*/				
+			// we got a list of in-out-combinations of intersections. That should be an even number of intersections, or
+			// we're fucked.
+			if( (intersections.size() & 1) != 0 )
+			{
+				IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check.");
+				continue;
+			}
 
-					//entered_volume_flag = true;
+			if( intersections.size() > 1 )
+			{
+				// If we started outside, the first intersection is a out->in intersection. Cycle them so that it
+				// starts with an intersection leaving the boundary
+				if( !startedInside )
+				for( size_t b = 0; b < intersections.size() - 1; ++b )
+					std::swap(intersections[b], intersections[(b + intersections.size() - 1) % intersections.size()]);
+
+				// Filter pairs of out->in->out that lie too close to each other. 
+				for( size_t a = 0; intersections.size() > 0 && a < intersections.size() - 1; /**/ )
+				{
+					if( (intersections[a].get<1>() - intersections[(a + 1) % intersections.size()].get<1>()).SquareLength() < 1e-10 )
+						intersections.erase(intersections.begin() + a, intersections.begin() + a + 2);
+					else
+						a++;
 				}
-				else {
-					outvert.push_back(e0);
-					++newcount;
+				if( intersections.size() > 1 && (intersections.back().get<1>() - intersections.front().get<1>()).SquareLength() < 1e-10 )
+				{
+					intersections.pop_back(); intersections.erase(intersections.begin());
 				}
 			}
-			// e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side,
-			// but only if it is within the bounding volume).
-			else if (isect == Intersect_Yes) {
-				// is e0 within the clipping volume? Insert the intersection point
-				// of [e0,e1] and the plane instead of e0.
-				if(was_outside_boundary) {
-					outvert.push_back(e0);
-				}
-				else {
-					if(entered_volume_flag) {
-						const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n;
-						outvert.push_back(fix_point);
-						++newcount;
-					}
 
-					outvert.push_back(isectpos);	
+
+			// no intersections at all: either completely inside the boundary, so everything gets discarded, or completely outside.
+			// in the latter case we're implementional lost. I'm simply going to ignore this, so a large poly will not get any
+			// holes if the boundary is smaller and does not touch it anywhere.
+			if( intersections.empty() )
+			{
+				// starting point was outside -> everything is outside the boundary -> nothing is clipped -> add black side 
+				// to result mesh unchanged
+				if( !startedInside )
+				{
+					outvertcnt.push_back(blackside.size());
+					outvert.insert(outvert.end(), blackside.begin(), blackside.end());
+					continue;
+				}
+				else
+				{
+					// starting point was inside the boundary -> everything is inside the boundary -> nothing is spared from the 
+					// clipping -> nothing left to add to the result mesh
+					continue;
 				}
-				entered_volume_flag = false;
-				++newcount;
 			}
-			else { // no intersection with plane or parallel; e0,e1 are on the bad side
-			
-				// did we just pass the boundary line to the poly bounding?
-				if (is_boundary_intersection) {
-
-					// and are now outside the clipping boundary?
-					if (is_outside_boundary) {
-						// in this case, get the point where the clipping boundary
-						// was entered first. Then, get the point where the clipping
-						// boundary volume was left! These two points with the plane
-						// normal form another plane that intersects the clipping
-						// volume. There are two ways to get from the first to the
-						// second point along the intersection curve, try to pick the
-						// one that lies within the current polygon.
-
-						// TODO this approach doesn't handle all cases
-
-						// ...
-
-						IfcFloat d = 1e20;
-						IfcVector3 vclosest;
-						BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
-							const IfcFloat dn = (v-e1_plane).SquareLength();
-							if (dn < d) {
-								d = dn;
-								vclosest = v;
-							}
-						}
 
-						vclosest = proj_inv * vclosest;
-						if(entered_volume_flag) {
-							const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n;
-							outvert.push_back(fix_point);
-							++newcount;
+			// determine the direction in which we're marching along the boundary polygon. If the src poly is faced upwards
+			// and the boundary is also winded this way, we need to march *backwards* on the boundary.
+			const IfcVector3 polyNormal = IfcMatrix3(proj) * TempMesh::ComputePolygonNormal(blackside.data(), blackside.size());
+			bool marchBackwardsOnBoundary = (profileNormal * polyNormal) >= 0.0;
+
+			// Build closed loops from these intersections. Starting from an intersection leaving the boundary we
+			// walk along the polygon to the next intersection (which should be an IS entering the boundary poly).
+			// From there we walk along the boundary until we hit another intersection leaving the boundary,
+			// walk along the poly to the next IS and so on until we're back at the starting point.
+			// We remove every intersection we "used up", so any remaining intersection is the start of a new loop.
+			while( !intersections.empty() )
+			{
+				std::vector<IfcVector3> resultpoly;
+				size_t currentIntersecIdx = 0;
+
+				while( true )
+				{
+					ai_assert(intersections.size() > currentIntersecIdx + 1);
+					boost::tuple<size_t, IfcVector3, size_t> currintsec = intersections[currentIntersecIdx + 0];
+					boost::tuple<size_t, IfcVector3, size_t> nextintsec = intersections[currentIntersecIdx + 1];
+					intersections.erase(intersections.begin() + currentIntersecIdx, intersections.begin() + currentIntersecIdx + 2);
+
+					// we start with an in->out intersection
+					resultpoly.push_back(currintsec.get<1>());
+					// climb along the polygon to the next intersection, which should be an out->in
+					size_t numPolyPoints = (currintsec.get<0>() > nextintsec.get<0>() ? blackside.size() : 0)
+						+ nextintsec.get<0>() - currintsec.get<0>();
+					for( size_t a = 1; a <= numPolyPoints; ++a )
+						resultpoly.push_back(blackside[(currintsec.get<0>() + a) % blackside.size()]);
+					// put the out->in intersection 
+					resultpoly.push_back(nextintsec.get<1>());
+
+					// generate segments along the boundary polygon that lie in the poly's plane until we hit another intersection
+					IfcVector3 startingPoint = proj * nextintsec.get<1>();
+					size_t currentBoundaryEdgeIdx = (nextintsec.get<2>() + (marchBackwardsOnBoundary ? 1 : 0)) % profile->verts.size();
+					size_t nextIntsecIdx = SIZE_MAX;
+					while( nextIntsecIdx == SIZE_MAX )
+					{
+						IfcFloat t = 1e10;
+
+						size_t nextBoundaryEdgeIdx = marchBackwardsOnBoundary ? (currentBoundaryEdgeIdx + profile->verts.size() - 1) : currentBoundaryEdgeIdx + 1;
+						nextBoundaryEdgeIdx %= profile->verts.size();
+						// vertices of the current boundary segments
+						IfcVector3 currBoundaryPoint = profile->verts[currentBoundaryEdgeIdx];
+						IfcVector3 nextBoundaryPoint = profile->verts[nextBoundaryEdgeIdx];
+						// project the two onto the polygon
+						if( std::abs(polyNormal.z) > 1e-5 )
+						{
+							currBoundaryPoint.z = startingPoint.z + (currBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (currBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z;
+							nextBoundaryPoint.z = startingPoint.z + (nextBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (nextBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z;
+						}
 
-							entered_volume_flag = false;
+						// build a direction that goes along the boundary border but lies in the poly plane
+						IfcVector3 boundaryPlaneNormal = ((nextBoundaryPoint - currBoundaryPoint) ^ profileNormal).Normalize();
+						IfcVector3 dirAtPolyPlane = (boundaryPlaneNormal ^ polyNormal).Normalize() * (marchBackwardsOnBoundary ? -1.0 : 1.0);
+						// if we can project the direction to the plane, we can calculate a maximum marching distance along that dir
+						// until we finish that boundary segment and continue on the next 
+						if( std::abs(polyNormal.z) > 1e-5 )
+						{
+							t = std::min(t, (nextBoundaryPoint - startingPoint).Length());
 						}
 
-						outvert.push_back(vclosest);
-						++newcount;
+						// check if the direction hits the loop start - if yes, we got a poly to output
+						IfcVector3 dirToThatPoint = proj * resultpoly.front() - startingPoint;
+						IfcFloat tpt = dirToThatPoint * dirAtPolyPlane;
+						if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
+						{
+							nextIntsecIdx = intersections.size(); // dirty hack to end marching along the boundary and signal the end of the loop
+							t = tpt;
+						}
 
-						//outvert.push_back(e1);
-						//++newcount;
-					}
-					else {
-						entered_volume_flag = true;
-
-						// we just entered the clipping boundary. Record the point
-						// and the segment where we entered and also generate this point.
-						//last_intersected_boundary_segment = intersected_boundary_segments.front();
-						//last_intersected_boundary_point = intersected_boundary_points.front();
-
-						outvert.push_back(e0);
-						++newcount;
-
-						IfcFloat d = 1e20;
-						IfcVector3 vclosest;
-						BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
-							const IfcFloat dn = (v-e0_plane).SquareLength();
-							if (dn < d) {
-								d = dn;
-								vclosest = v;
+						// also check if the direction hits any in->out intersections earlier. If we hit one, we can switch back
+						// to marching along the poly border from that intersection point
+						for( size_t a = 0; a < intersections.size(); a += 2 )
+						{
+							dirToThatPoint = proj * intersections[a].get<1>() - startingPoint;
+							tpt = dirToThatPoint * dirAtPolyPlane;
+							if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
+							{
+								nextIntsecIdx = a; // switch back to poly and march on from this in->out intersection
+								t = tpt;
 							}
 						}
 
-						enter_volume = proj_inv * vclosest;
-						outvert.push_back(enter_volume);
-						++newcount;
-					}
-				}				
-				// if not, we just keep the vertex
-				else if (is_outside_boundary) {
-					outvert.push_back(e0);
-					++newcount;
-
-					entered_volume_flag = false;
-				}
-			}
+						// if we keep marching on the boundary, put the segment end point to the result poly and well... keep marching
+						if( nextIntsecIdx == SIZE_MAX )
+						{
+							resultpoly.push_back(proj_inv * nextBoundaryPoint);
+							currentBoundaryEdgeIdx = nextBoundaryEdgeIdx;
+							startingPoint = nextBoundaryPoint;
+						}
 
-			was_outside_boundary = is_outside_boundary;
-			extra_point_flag = false;
-		}
-	
-		if (!newcount) {
-			continue;
-		}
-		
-		IfcVector3 vmin,vmax;
-		ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
+						// quick endless loop check
+						if( resultpoly.size() > blackside.size() + profile->verts.size() )
+						{
+							IFCImporter::LogError("Encountered endless loop while clipping polygon against poly-bounded half space.");
+							break;
+						}
+					}
 
-		// filter our IfcFloat points - those may happen if a point lies
-		// directly on the intersection line. However, due to IfcFloat
-		// precision a bitwise comparison is not feasible to detect
-		// this case.
-		const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
-		FuzzyVectorCompare fz(epsilon);
+					// we're back on the poly - if this is the intersection we started from, we got a closed loop.
+					if( nextIntsecIdx >= intersections.size() )
+					{
+						break;
+					}
 
-		std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
+					// otherwise it's another intersection. Continue marching from there.
+					currentIntersecIdx = nextIntsecIdx;
+				}
 
-		if (e != outvert.end()) {
-			newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
-			outvert.erase(e,outvert.end());
-		}
-		if (fz(*( outvert.end()-newcount),outvert.back())) {
-			outvert.pop_back();
-			--newcount;
-		} 
-		if(newcount > 2) {
-			result.vertcnt.push_back(newcount);
-		}
-		else while(newcount-->0) {
-			result.verts.pop_back();
+				WritePolygon(resultpoly, result);
+			}
 		}
-
 	}
 	IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");	
 }

+ 2 - 2
code/IFCCurve.cpp

@@ -648,10 +648,10 @@ void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
 	ai_assert(InRange(a) && InRange(b));
 
 	const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b));
-	out.verts.reserve( out.verts.size() + cnt );
+	out.verts.reserve( out.verts.size() + cnt + 1);
 
 	IfcFloat p = a, delta = (b-a)/cnt;
-	for(size_t i = 0; i < cnt; ++i, p += delta) {
+	for(size_t i = 0; i <= cnt; ++i, p += delta) {
 		out.verts.push_back(Eval(p));
 	}
 }

+ 33 - 16
code/IFCGeometry.cpp

@@ -579,6 +579,11 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
 	IfcVector3 min = in[0];
 	dir *= IfcMatrix3(trafo);
 
+	// reverse profile polygon if it's winded in the wrong direction in relation to the extrusion direction
+	IfcVector3 profileNormal = TempMesh::ComputePolygonNormal( in.data(), in.size());
+	if( profileNormal * dir < 0.0 )
+		std::reverse( in.begin(), in.end());
+
 	std::vector<IfcVector3> nors;
 	const bool openings = !!conv.apply_openings && conv.apply_openings->size();
 	
@@ -619,9 +624,9 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
 		curmesh.vertcnt.push_back(4);
 		
 		out.push_back(in[i]);
-		out.push_back(in[i]+dir);
-		out.push_back(in[next]+dir);
 		out.push_back(in[next]);
+		out.push_back(in[next]+dir);
+		out.push_back(in[i]+dir);
 
 		if(openings) {
 			if((in[i]-in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings,nors,temp,true, true, dir)) {
@@ -646,8 +651,12 @@ void ProcessExtrudedAreaSolid(const IfcExtrudedAreaSolid& solid, TempMesh& resul
 	if(has_area) {
 
 		for(size_t n = 0; n < 2; ++n) {
-			for(size_t i = size; i--; ) {
-				out.push_back(in[i]+(n?dir:IfcVector3()));
+			if( n > 0 ) {
+				for(size_t i = 0; i < size; ++i ) 
+					out.push_back(in[i]+dir);
+			} else {
+				for(size_t i = size; i--; )
+					out.push_back(in[i]);
 			}
 
 			curmesh.vertcnt.push_back(size);
@@ -699,10 +708,10 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout,
 }
 
 // ------------------------------------------------------------------------------------------------
-bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned int>& mesh_indices, 
+bool ProcessGeometricItem(const IfcRepresentationItem& geo, unsigned int matid, std::vector<unsigned int>& mesh_indices, 
 	ConversionData& conv)
 {
-	bool fix_orientation = true;
+	bool fix_orientation = false;
 	boost::shared_ptr< TempMesh > meshtmp = boost::make_shared<TempMesh>(); 
 	if(const IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<IfcShellBasedSurfaceModel>()) {
 		BOOST_FOREACH(boost::shared_ptr<const IfcShell> shell,shellmod->SbsmBoundary) {
@@ -716,24 +725,27 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned
 				IFCImporter::LogWarn("unexpected type error, IfcShell ought to inherit from IfcConnectedFaceSet");
 			}
 		}
+		fix_orientation = true;
 	}
 	else  if(const IfcConnectedFaceSet* fset = geo.ToPtr<IfcConnectedFaceSet>()) {
 		ProcessConnectedFaceSet(*fset,*meshtmp.get(),conv);
+		fix_orientation = true;
 	}	
 	else  if(const IfcSweptAreaSolid* swept = geo.ToPtr<IfcSweptAreaSolid>()) {
 		ProcessSweptAreaSolid(*swept,*meshtmp.get(),conv);
 	}   
 	else  if(const IfcSweptDiskSolid* disk = geo.ToPtr<IfcSweptDiskSolid>()) {
 		ProcessSweptDiskSolid(*disk,*meshtmp.get(),conv);
-		fix_orientation = false;
 	}   
 	else if(const IfcManifoldSolidBrep* brep = geo.ToPtr<IfcManifoldSolidBrep>()) {
 		ProcessConnectedFaceSet(brep->Outer,*meshtmp.get(),conv);
+		fix_orientation = true;
 	} 
 	else if(const IfcFaceBasedSurfaceModel* surf = geo.ToPtr<IfcFaceBasedSurfaceModel>()) {
 		BOOST_FOREACH(const IfcConnectedFaceSet& fc, surf->FbsmFaces) {
 			ProcessConnectedFaceSet(fc,*meshtmp.get(),conv);
 		}
+		fix_orientation = true;
 	}  
 	else  if(const IfcBooleanResult* boolean = geo.ToPtr<IfcBooleanResult>()) {
 		ProcessBoolean(*boolean,*meshtmp.get(),conv);
@@ -777,7 +789,7 @@ bool ProcessGeometricItem(const IfcRepresentationItem& geo, std::vector<unsigned
 
 	aiMesh* const mesh = meshtmp->ToMesh();
 	if(mesh) {
-		mesh->mMaterialIndex = ProcessMaterials(geo,conv);
+		mesh->mMaterialIndex = matid; 
 		mesh_indices.push_back(conv.meshes.size());
 		conv.meshes.push_back(mesh);
 		return true;
@@ -807,10 +819,11 @@ void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,
 
 // ------------------------------------------------------------------------------------------------
 bool TryQueryMeshCache(const IfcRepresentationItem& item, 
-	std::vector<unsigned int>& mesh_indices, 
+	std::vector<unsigned int>& mesh_indices, unsigned int mat_index,
 	ConversionData& conv) 
 {
-	ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(&item);
+	ConversionData::MeshCacheIndex idx(&item, mat_index);
+	ConversionData::MeshCache::const_iterator it = conv.cached_meshes.find(idx);
 	if (it != conv.cached_meshes.end()) {
 		std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(mesh_indices));
 		return true;
@@ -820,21 +833,25 @@ bool TryQueryMeshCache(const IfcRepresentationItem& item,
 
 // ------------------------------------------------------------------------------------------------
 void PopulateMeshCache(const IfcRepresentationItem& item, 
-	const std::vector<unsigned int>& mesh_indices, 
+	const std::vector<unsigned int>& mesh_indices, unsigned int mat_index,
 	ConversionData& conv)
 {
-	conv.cached_meshes[&item] = mesh_indices;
+	ConversionData::MeshCacheIndex idx(&item, mat_index);
+	conv.cached_meshes[idx] = mesh_indices;
 }
 
 // ------------------------------------------------------------------------------------------------
-bool ProcessRepresentationItem(const IfcRepresentationItem& item, 
+bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid,
 	std::vector<unsigned int>& mesh_indices, 
 	ConversionData& conv)
 {
-	if (!TryQueryMeshCache(item,mesh_indices,conv)) {
-		if(ProcessGeometricItem(item,mesh_indices,conv)) {
+	// determine material
+	unsigned int localmatid = ProcessMaterials(item.GetID(), matid, conv, true);
+
+	if (!TryQueryMeshCache(item,mesh_indices,localmatid,conv)) {
+		if(ProcessGeometricItem(item,localmatid,mesh_indices,conv)) {
 			if(mesh_indices.size()) {
-				PopulateMeshCache(item,mesh_indices,conv);
+				PopulateMeshCache(item,mesh_indices,localmatid,conv);
 			}
 		}
 		else return false;

+ 16 - 7
code/IFCLoader.cpp

@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
 
 #include <iterator>
+#include <limits>
 #include <boost/tuple/tuple.hpp>
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
@@ -428,7 +429,7 @@ void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
 }
 
 // ------------------------------------------------------------------------------------------------
-bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
+bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, unsigned int matid, ConversionData& conv)
 {
 	// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
 	std::auto_ptr<aiNode> nd(new aiNode());
@@ -453,11 +454,12 @@ bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector<
 		}
 	}
 
+	unsigned int localmatid = ProcessMaterials(mapped.GetID(),matid,conv,false);
 	const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
 
 	bool got = false;
 	BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
-		if(!ProcessRepresentationItem(item,meshes,conv)) {
+		if(!ProcessRepresentationItem(item,localmatid,meshes,conv)) {
 			IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
 		}
 		else got = true;
@@ -557,7 +559,11 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
 	if(!el.Representation) {
 		return;
 	}
+
+	// extract Color from metadata, if present
+	unsigned int matid = ProcessMaterials( el.GetID(), std::numeric_limits<uint32_t>::max(), conv, false);
 	std::vector<unsigned int> meshes;
+
 	// we want only one representation type, so bring them in a suitable order (i.e try those
 	// that look as if we could read them quickly at first). This way of reading
 	// representation is relatively generic and allows the concrete implementations
@@ -571,10 +577,10 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
 		bool res = false;
 		BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
 			if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
-				res = ProcessMappedItem(*geo,nd,subnodes,conv) || res;
+				res = ProcessMappedItem(*geo,nd,subnodes,matid,conv) || res;
 			}
 			else {
-				res = ProcessRepresentationItem(item,meshes,conv) || res;
+				res = ProcessRepresentationItem(item,matid,meshes,conv) || res;
 			}
 		}
 		// if we got something meaningful at this point, skip any further representations
@@ -671,10 +677,11 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
 	const STEP::DB::RefMap& refs = conv.db.GetRefs();
 
 	// skip over space and annotation nodes - usually, these have no meaning in Assimp's context
+	bool skipGeometry = false;
 	if(conv.settings.skipSpaceRepresentations) {
 		if(const IfcSpace* const space = el.ToPtr<IfcSpace>()) {
 			IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings");
-			return NULL;
+			skipGeometry = true;
 		}
 	}
 
@@ -844,8 +851,10 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
 			conv.apply_openings = &openings;
 		}
 
-		ProcessProductRepresentation(el,nd.get(),subnodes,conv);
-		conv.apply_openings = conv.collect_openings = NULL;
+		if (!skipGeometry) {
+		  ProcessProductRepresentation(el,nd.get(),subnodes,conv);
+		  conv.apply_openings = conv.collect_openings = NULL;
+		}
 
 		if (subnodes.size()) {
 			nd->mChildren = new aiNode*[subnodes.size()]();

+ 48 - 22
code/IFCMaterial.cpp

@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
 #include "IFCUtil.h"
+#include <limits>
 
 namespace Assimp {
 	namespace IFC {
@@ -132,45 +133,70 @@ void FillMaterial(aiMaterial* mat,const IFC::IfcSurfaceStyle* surf,ConversionDat
 }
 
 // ------------------------------------------------------------------------------------------------
-unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv)
+unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat)
 {
-	if (conv.materials.empty()) {
-		aiString name;
-		std::auto_ptr<aiMaterial> mat(new aiMaterial());
-
-		name.Set("<IFCDefault>");
-		mat->AddProperty(&name,AI_MATKEY_NAME);
-
-		const aiColor4D col = aiColor4D(0.6f,0.6f,0.6f,1.0f);
-		mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE);
-
-		conv.materials.push_back(mat.release());
-	}
-
-	STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(item.GetID());
+	STEP::DB::RefMapRange range = conv.db.GetRefs().equal_range(id);
 	for(;range.first != range.second; ++range.first) {
 		if(const IFC::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr<IFC::IfcStyledItem>()) {
 			BOOST_FOREACH(const IFC::IfcPresentationStyleAssignment& as, styled->Styles) {
 				BOOST_FOREACH(boost::shared_ptr<const IFC::IfcPresentationStyleSelect> sel, as.Styles) {
 
-					if (const IFC::IfcSurfaceStyle* const surf =  sel->ResolveSelectPtr<IFC::IfcSurfaceStyle>(conv.db)) {
+					if( const IFC::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr<IFC::IfcSurfaceStyle>(conv.db) ) {
+						// try to satisfy from cache
+						ConversionData::MaterialCache::iterator mit = conv.cached_materials.find(surf);
+						if( mit != conv.cached_materials.end() )
+							return mit->second;
+
+						// not found, create new material
 						const std::string side = static_cast<std::string>(surf->Side);
-						if (side != "BOTH") {
+						if( side != "BOTH" ) {
 							IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: " + side);
 						}
 
 						std::auto_ptr<aiMaterial> mat(new aiMaterial());
 
-						FillMaterial(mat.get(),surf,conv);
+						FillMaterial(mat.get(), surf, conv);
 
 						conv.materials.push_back(mat.release());
-						return conv.materials.size()-1;
-					}
-				}
+						unsigned int matindex = conv.materials.size() - 1;
+						conv.cached_materials[surf] = matindex;
+						return matindex;
 			}
 		}
 	}
-	return 0;
+		}
+	}
+
+	// no local material defined. If there's global one, use that instead
+	if( prevMatId != std::numeric_limits<uint32_t>::max() )
+		return prevMatId;
+
+	// we're still here - create an default material if required, or simply fail otherwise
+	if( !forceDefaultMat )
+		return std::numeric_limits<uint32_t>::max();
+
+	aiString name;
+	name.Set("<IFCDefault>");
+	//  ConvertColorToString( color, name);
+
+	// look if there's already a default material with this base color
+	for( size_t a = 0; a < conv.materials.size(); ++a )
+	{
+		aiString mname;
+		conv.materials[a]->Get(AI_MATKEY_NAME, mname);
+		if( name == mname )
+			return (unsigned int)a;
+	}
+
+	// we're here, yet - no default material with suitable color available. Generate one
+	std::auto_ptr<aiMaterial> mat(new aiMaterial());
+	mat->AddProperty(&name,AI_MATKEY_NAME);
+
+	const aiColor4D col = aiColor4D( 0.6f, 0.6f, 0.6f, 1.0f); // aiColor4D( color.r, color.g, color.b, 1.0f);
+	mat->AddProperty(&col,1, AI_MATKEY_COLOR_DIFFUSE);
+
+	conv.materials.push_back(mat.release());
+	return (unsigned int) conv.materials.size() - 1;
 }
 
 } // ! IFC

+ 17 - 30
code/IFCOpenings.cpp

@@ -902,6 +902,14 @@ size_t CloseWindows(ContourVector& contours,
 			curmesh.verts.reserve(curmesh.verts.size() + (*it).contour.size() * 4);
 			curmesh.vertcnt.reserve(curmesh.vertcnt.size() + (*it).contour.size());
 
+			// compare base poly normal and contour normal to detect if we need to reverse the face winding
+			IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal( curmesh.verts.data(), curmesh.vertcnt.front());
+			std::vector<IfcVector3> worldSpaceContourVtx( it->contour.size());
+			for( size_t a = 0; a < it->contour.size(); ++a )
+				worldSpaceContourVtx[a] = minv * IfcVector3( it->contour[a].x, it->contour[a].y, 0.0);
+			IfcVector3 contourNormal = TempMesh::ComputePolygonNormal( worldSpaceContourVtx.data(), worldSpaceContourVtx.size());
+			bool reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0;
+
 			// XXX this algorithm is really a bit inefficient - both in terms
 			// of constant factor and of asymptotic runtime.
 			std::vector<bool>::const_iterator skipit = skipbegin;
@@ -909,9 +917,6 @@ size_t CloseWindows(ContourVector& contours,
 			IfcVector3 start0;
 			IfcVector3 start1;
 
-			IfcVector2 last_proj; 
-			//const IfcVector2& first_proj; 
-
 			const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end();
 
 			bool drop_this_edge = false;
@@ -923,18 +928,8 @@ size_t CloseWindows(ContourVector& contours,
 				IfcFloat best = static_cast<IfcFloat>(1e10);
 				IfcVector3 bestv;
 
-				/* debug code to check for unwanted diagonal lines in window contours
-				if (cit != cbegin) {
-					const IfcVector2& vdelta = proj_point - last_proj;
-					if (std::fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) {
-						//continue;
-					}
-				} */
-
 				const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f);
 				
-				last_proj = proj_point;
-
 				BOOST_FOREACH(const TempOpening* opening, refs) {
 					BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) {
 						const IfcFloat sqdist = (world_point - other).SquareLength();
@@ -956,8 +951,8 @@ size_t CloseWindows(ContourVector& contours,
 					curmesh.verts.pop_back();
 				}
 				else {
-					curmesh.verts.push_back(cit == cbegin ? world_point : bestv);
-					curmesh.verts.push_back(cit == cbegin ? bestv : world_point);
+					curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? world_point : bestv);
+					curmesh.verts.push_back(((cit == cbegin) != reverseCountourFaces) ? bestv : world_point);
 
 					curmesh.vertcnt.push_back(4);
 					++closed;
@@ -969,8 +964,8 @@ size_t CloseWindows(ContourVector& contours,
 					continue;
 				}
 
-				curmesh.verts.push_back(world_point);
-				curmesh.verts.push_back(bestv);
+				curmesh.verts.push_back(reverseCountourFaces ? bestv : world_point);
+				curmesh.verts.push_back(reverseCountourFaces ? world_point : bestv);
 
 				if (cit == cend - 1) {
 					drop_this_edge = *skipit;
@@ -984,16 +979,11 @@ size_t CloseWindows(ContourVector& contours,
 						curmesh.verts.pop_back();
 					}
 					else {
-						curmesh.verts.push_back(start1);
-						curmesh.verts.push_back(start0);
+						curmesh.verts.push_back(reverseCountourFaces ? start0 : start1);
+						curmesh.verts.push_back(reverseCountourFaces ? start1 : start0);
 					}
 				}
 			}
-			/*
-			BOOST_FOREACH(TempOpening* opening, refs) {
-				//opening->wallPoints.clear();
-			}*/
-
 		}
 		else {
 			
@@ -1194,16 +1184,13 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 					profile_data = opening.profileMesh2D.get();
 					is_2d_source = true;
 				}
-				else {
-					//continue;
-				}
 			}
 			else {
 				// vertical extrusion
 				if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
-					continue;
-				}
-				continue;
+					profile_data = opening.profileMesh2D.get();
+					is_2d_source = true;
+				} 
 			}
 		}
 		std::vector<IfcVector3> profile_verts = profile_data->verts;

+ 152 - 24
code/IFCUtil.cpp

@@ -122,7 +122,7 @@ void TempMesh::Transform(const IfcMatrix4& mat)
 // ------------------------------------------------------------------------------
 IfcVector3 TempMesh::Center() const
 {
-	return std::accumulate(verts.begin(),verts.end(),IfcVector3()) / static_cast<IfcFloat>(verts.size());
+	return (verts.size() == 0) ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(verts.begin(),verts.end(),IfcVector3()) / static_cast<IfcFloat>(verts.size()));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -149,7 +149,7 @@ void TempMesh::RemoveDegenerates()
 	for (std::vector<unsigned int>::iterator it = vertcnt.begin(); it != vertcnt.end(); ++inor) {
 		const unsigned int pcount = *it;
 		
-		if (normals[inor].SquareLength() < 1e-5f) {
+		if (normals[inor].SquareLength() < 1e-10f) {
 			it = vertcnt.erase(it);
 			vit = verts.erase(vit, vit + pcount);
 
@@ -166,6 +166,23 @@ void TempMesh::RemoveDegenerates()
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize)
+{
+	std::vector<IfcFloat> temp((cnt+2)*3);
+	for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs )
+	{
+		const IfcVector3& v = vtcs[vofs];
+		temp[i++] = v.x;
+		temp[i++] = v.y;
+		temp[i++] = v.z;
+	}
+
+	IfcVector3 nor;
+	NewellNormal<3, 3, 3>(nor, cnt, &temp[0], &temp[1], &temp[2]);
+	return normalize ? nor.Normalize() : nor;
+}
+
 // ------------------------------------------------------------------------------------------------
 void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals, 
 	bool normalize, 
@@ -214,37 +231,148 @@ void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
 // Compute the normal of the last polygon in the given mesh
 IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const
 {
-	size_t total = vertcnt.back(), vidx = verts.size() - total;
-	std::vector<IfcFloat> temp((total+2)*3);
-	for(size_t vofs = 0, cnt = 0; vofs < total; ++vofs) {
-		const IfcVector3& v = verts[vidx+vofs];
-		temp[cnt++] = v.x;
-		temp[cnt++] = v.y;
-		temp[cnt++] = v.z;
-	}
-	IfcVector3 nor;
-	NewellNormal<3,3,3>(nor,total,&temp[0],&temp[1],&temp[2]);
-	return normalize ? nor.Normalize() : nor;
+	return ComputePolygonNormal(&verts[verts.size() - vertcnt.back()], vertcnt.back(), normalize);
 }
 
+struct CompareVector
+{
+	bool operator () (const IfcVector3& a, const IfcVector3& b) const
+	{
+		IfcVector3 d = a - b;
+		IfcFloat eps = 1e-6;
+		return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
+	}
+};
+struct FindVector
+{
+	IfcVector3 v;
+	FindVector(const IfcVector3& p) : v(p) { }
+	bool operator () (const IfcVector3& p) { return FuzzyVectorCompare(1e-6)(p, v); }
+};
+
 // ------------------------------------------------------------------------------------------------
 void TempMesh::FixupFaceOrientation()
 {
 	const IfcVector3 vavg = Center();
 
-	std::vector<IfcVector3> normals;
-	ComputePolygonNormals(normals);
-
-	size_t c = 0, ofs = 0;
-	BOOST_FOREACH(unsigned int cnt, vertcnt) {
-		if (cnt>2){
-			const IfcVector3& thisvert = verts[c];
-			if (normals[ofs]*(thisvert-vavg) < 0) {
-				std::reverse(verts.begin()+c,verts.begin()+cnt+c);
+	// create a list of start indices for all faces to allow random access to faces
+	std::vector<size_t> faceStartIndices(vertcnt.size());
+	for( size_t i = 0, a = 0; a < vertcnt.size(); i += vertcnt[a], ++a )
+		faceStartIndices[a] = i;
+
+	// list all faces on a vertex
+	std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
+	for( size_t a = 0; a < vertcnt.size(); ++a )
+	{
+		for( size_t b = 0; b < vertcnt[a]; ++b )
+			facesByVertex[verts[faceStartIndices[a] + b]].push_back(a);
+	}
+	// determine neighbourhood for all polys
+	std::vector<size_t> neighbour(verts.size(), SIZE_MAX);
+	std::vector<size_t> tempIntersect(10);
+	for( size_t a = 0; a < vertcnt.size(); ++a )
+	{
+		for( size_t b = 0; b < vertcnt[a]; ++b )
+		{
+			size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % vertcnt[a];
+			const std::vector<size_t>& facesOnB = facesByVertex[verts[ib]];
+			const std::vector<size_t>& facesOnNB = facesByVertex[verts[nib]];
+			// there should be exactly one or two faces which appear in both lists. Our face and the other side
+			std::vector<size_t>::iterator sectstart = tempIntersect.begin();
+			std::vector<size_t>::iterator sectend = std::set_intersection(
+				facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
+
+			if( std::distance(sectstart, sectend) != 2 )
+				continue;
+			if( *sectstart == a )
+				++sectstart;
+			neighbour[ib] = *sectstart;
+		}
+	}
+
+	// now we're getting started. We take the face which is the farthest away from the center. This face is most probably
+	// facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring 
+	// faces to have the same winding until all faces have been tested.
+	std::vector<bool> faceDone(vertcnt.size(), false);
+	while( std::count(faceDone.begin(), faceDone.end(), false) != 0 )
+	{
+		// find the farthest of the remaining faces
+		size_t farthestIndex = SIZE_MAX;
+		IfcFloat farthestDistance = -1.0;
+		for( size_t a = 0; a < vertcnt.size(); ++a )
+		{
+			if( faceDone[a] )
+				continue;
+			IfcVector3 faceCenter = std::accumulate(verts.begin() + faceStartIndices[a],
+				verts.begin() + faceStartIndices[a] + vertcnt[a], IfcVector3(0.0)) / IfcFloat(vertcnt[a]);
+			IfcFloat dst = (faceCenter - vavg).SquareLength();
+			if( dst > farthestDistance ) { farthestDistance = dst; farthestIndex = a; }
+		}
+
+		// calculate its normal and reverse the poly if its facing towards the mesh center
+		IfcVector3 farthestNormal = ComputePolygonNormal(verts.data() + faceStartIndices[farthestIndex], vertcnt[farthestIndex]);
+		IfcVector3 farthestCenter = std::accumulate(verts.begin() + faceStartIndices[farthestIndex],
+			verts.begin() + faceStartIndices[farthestIndex] + vertcnt[farthestIndex], IfcVector3(0.0))
+			/ IfcFloat(vertcnt[farthestIndex]);
+		// We accapt a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in 
+		// the file.
+		if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 )
+		{
+			size_t fsi = faceStartIndices[farthestIndex], fvc = vertcnt[farthestIndex];
+			std::reverse(verts.begin() + fsi, verts.begin() + fsi + fvc);
+			std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
+			// because of the neighbour index belonging to the edge starting with the point at the same index, we need to 
+			// cycle the neighbours through to match the edges again.
+			// Before: points A - B - C - D with edge neighbour p - q - r - s
+			// After: points D - C - B - A, reversed neighbours are s - r - q - p, but the should be
+			//                r   q   p   s
+			for( size_t a = 0; a < fvc - 1; ++a )
+				std::swap(neighbour[fsi + a], neighbour[fsi + a + 1]);
+		}
+		faceDone[farthestIndex] = true;
+		std::vector<size_t> todo;
+		todo.push_back(farthestIndex);
+
+		// go over its neighbour faces recursively and adapt their winding order to match the farthest face 
+		while( !todo.empty() )
+		{
+			size_t tdf = todo.back();
+			size_t vsi = faceStartIndices[tdf], vc = vertcnt[tdf];
+			todo.pop_back();
+
+			// check its neighbours
+			for( size_t a = 0; a < vc; ++a )
+			{
+				// ignore neighbours if we already checked them
+				size_t nbi = neighbour[vsi + a];
+				if( nbi == SIZE_MAX || faceDone[nbi] )
+					continue;
+
+				const IfcVector3& vp = verts[vsi + a];
+				size_t nbvsi = faceStartIndices[nbi], nbvc = vertcnt[nbi];
+				std::vector<IfcVector3>::iterator it = std::find_if(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc, FindVector(vp));
+				ai_assert(it != verts.begin() + nbvsi + nbvc);
+				size_t nb_vidx = std::distance(verts.begin() + nbvsi, it);
+				// two faces winded in the same direction should have a crossed edge, where one face has p0->p1 and the other
+				// has p1'->p0'. If the next point on the neighbouring face is also the next on the current face, we need 
+				// to reverse the neighbour
+				nb_vidx = (nb_vidx + 1) % nbvc;
+				size_t oursideidx = (a + 1) % vc;
+				if( FuzzyVectorCompare(1e-6)(verts[vsi + oursideidx], verts[nbvsi + nb_vidx]) )
+				{
+					std::reverse(verts.begin() + nbvsi, verts.begin() + nbvsi + nbvc);
+					std::reverse(neighbour.begin() + nbvsi, neighbour.begin() + nbvsi + nbvc);
+					for( size_t a = 0; a < nbvc - 1; ++a )
+						std::swap(neighbour[nbvsi + a], neighbour[nbvsi + a + 1]);
+				}
+
+				// either way we're done with the neighbour. Mark it as done and continue checking from there recursively
+				faceDone[nbi] = true;
+				todo.push_back(nbi);
 			}
 		}
-		c += cnt;
-		++ofs;
+
+		// no more faces reachable from this part of the surface, start over with a disjunct part and its farthest face
 	}
 }
 

+ 17 - 7
code/IFCUtil.h

@@ -97,10 +97,10 @@ struct TempMesh
 	void RemoveDegenerates();
 
 	void FixupFaceOrientation();
+
+	static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
 	IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
-	void ComputePolygonNormals(std::vector<IfcVector3>& normals, 
-		bool normalize = true, 
-		size_t ofs = 0) const;
+	void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
 
 	void Swap(TempMesh& other);
 };
@@ -195,9 +195,19 @@ struct ConversionData
 	std::vector<aiMesh*> meshes;
 	std::vector<aiMaterial*> materials;
 
-	typedef std::map<const IFC::IfcRepresentationItem*, std::vector<unsigned int> > MeshCache;
+	struct MeshCacheIndex {
+		const IFC::IfcRepresentationItem* item; unsigned int matindex;
+		MeshCacheIndex() : item(NULL), matindex(0) { }
+		MeshCacheIndex(const IFC::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { }
+		bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; }
+		bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
+	};
+	typedef std::map<MeshCacheIndex, std::vector<unsigned int> > MeshCache;
 	MeshCache cached_meshes;
 
+	typedef std::map<const IFC::IfcSurfaceStyle*, unsigned int> MaterialCache;
+	MaterialCache cached_materials;
+
 	const IFCImporter::Settings& settings;
 
 	// Intermediate arrays used to resolve openings in walls: only one of them
@@ -220,7 +230,7 @@ struct FuzzyVectorCompare {
 
 	FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
 	bool operator()(const IfcVector3& a, const IfcVector3& b) {
-		return std::fabs((a-b).SquareLength()) < epsilon;
+		return std::abs((a-b).SquareLength()) < epsilon;
 	}
 
 	const IfcFloat epsilon;
@@ -263,11 +273,11 @@ IfcFloat ConvertSIPrefix(const std::string& prefix);
 bool ProcessProfile(const IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
 
 // IFCMaterial.cpp
-unsigned int ProcessMaterials(const IFC::IfcRepresentationItem& item, ConversionData& conv);
+unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
 
 // IFCGeometry.cpp
 IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
-bool ProcessRepresentationItem(const IfcRepresentationItem& item, std::vector<unsigned int>& mesh_indices, ConversionData& conv);
+bool ProcessRepresentationItem(const IfcRepresentationItem& item, unsigned int matid, std::vector<unsigned int>& mesh_indices, ConversionData& conv);
 void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
 
 void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, 

+ 8 - 0
code/ImporterRegistry.cpp

@@ -173,6 +173,10 @@ corresponding preprocessor flag to selectively disable formats.
 #   include "AssbinLoader.h"
 #endif 
 
+#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
+#   include "C4DImporter.h"
+#endif
+
 namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
@@ -303,6 +307,10 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
     out.push_back( new AssbinImporter() );
 #endif
+
+#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
+	out.push_back( new C4DImporter() );
+#endif
 }
 
 }

+ 1 - 1
code/ImproveCacheLocality.cpp

@@ -303,7 +303,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
 		ivdx = -1; 
 		int max_priority = -1;
 		for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur)	{
-			register const unsigned int dp = *piCur;
+			const unsigned int dp = *piCur;
 
 			// must have live triangles
 			if (piNumTriPtr[dp] > 0)	{

+ 8 - 0
code/LWOBLoader.cpp

@@ -139,7 +139,15 @@ void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& face
 	while (cursor < end && max--)
 	{
 		uint16_t numIndices;
+		// must have 2 shorts left for numIndices and surface
+		if (end - cursor < 2) {
+			throw DeadlyImportError("LWOB: Unexpected end of file");
+		}
 		::memcpy(&numIndices, cursor++, 2);
+		// must have enough left for indices and surface
+		if (end - cursor < (1 + numIndices)) {
+			throw DeadlyImportError("LWOB: Unexpected end of file");
+		}
 		verts += numIndices;
 		faces++;
 		cursor += numIndices;

+ 1 - 1
code/LWOFileData.h

@@ -346,7 +346,7 @@ struct VColorChannel : public VMapEntry
 		if (!rawData.empty())
 			return; // return if already allocated
 
-		register unsigned int m = num*dims;
+		unsigned int m = num*dims;
 		rawData.reserve(m + (m>>2u)); // 25% as  extra storage for VMADs
 		rawData.resize(m);
 

+ 10 - 5
code/LWOLoader.cpp

@@ -344,7 +344,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 
 					// copy all vertices
 					for (unsigned int q = 0; q  < face.mNumIndices;++q,++vert)	{
-						register unsigned int idx = face.mIndices[q];
+						unsigned int idx = face.mIndices[q];
 						*pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
 
 						// process UV coordinates
@@ -491,7 +491,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>&
 		aiFace& face = *begin;
 		for (unsigned int i = 0; i < face.mNumIndices;++i)
 		{
-			register unsigned int tt = face.mIndices[i];
+			unsigned int tt = face.mIndices[i];
 			sSort.Add(mesh->mVertices[tt],tt,*it);
 		}
 	}
@@ -510,7 +510,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>&
 			unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
 			for (; beginIdx != endIdx; ++beginIdx)
 			{
-				register unsigned int idx = *beginIdx;
+				unsigned int idx = *beginIdx;
 				sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
 				std::vector<unsigned int>::const_iterator a, end = poResult.end();
 
@@ -533,7 +533,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>&
 			unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
 			for (; beginIdx != endIdx; ++beginIdx)
 			{
-				register unsigned int idx = *beginIdx;
+				unsigned int idx = *beginIdx;
 				if (vertexDone[idx])
 					continue;
 				sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
@@ -730,7 +730,12 @@ void LWOImporter::LoadLWOPoints(unsigned int length)
 	// --- this function is used for both LWO2 and LWOB but for
 	// LWO2 we need to allocate 25% more storage - it could be we'll 
 	// need to duplicate some points later.
-	register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
+	const size_t vertexLen = 12;
+	if ((length % vertexLen) != 0)
+	{
+		throw DeadlyImportError( "LWO2: Points chunk length is not multiple of vertexLen (12)");
+	}
+	unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
 	if (mIsLWO2)
 	{
 		mCurLayer->mTempPoints.reserve	( regularSize + (regularSize>>2u) );

+ 1 - 1
code/MD2Loader.cpp

@@ -377,7 +377,7 @@ void MD2Importer::InternReadFile( const std::string& pFile,
 		for (unsigned int c = 0; c < 3;++c,++iCurrent)	{
 
 			// validate vertex indices
-			register unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
+			unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 			if (iIndex >= m_pcHeader->numVertices)	{
 				DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range");
 				iIndex = m_pcHeader->numVertices-1;

+ 1 - 1
code/MD5Parser.cpp

@@ -88,7 +88,7 @@ MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize )
 
 // ------------------------------------------------------------------------------------------------
 // Report error to the log stream
-/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line)
+/*static*/ AI_WONT_RETURN void MD5Parser::ReportError (const char* error, unsigned int line)
 {
 	char szBuffer[1024];
 	::sprintf(szBuffer,"[MD5] Line %i: %s",line,error);

+ 1 - 1
code/MD5Parser.h

@@ -367,7 +367,7 @@ public:
 	 *  @param error Error message to be reported
 	 *  @param line Index of the line where the error occured
 	 */
-	static void ReportError (const char* error, unsigned int line);
+	AI_WONT_RETURN static void ReportError (const char* error, unsigned int line) AI_WONT_RETURN_SUFFIX;
 
 	// -------------------------------------------------------------------
 	/** Report a specific warning

+ 1 - 1
code/NFFLoader.cpp

@@ -400,7 +400,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
 						if (TokenMatch(sz,"0x",2))
 						{
 							hasColor = true;
-							register unsigned int numIdx = ::strtoul16(sz,&sz);
+							unsigned int numIdx = ::strtoul16(sz,&sz);
 							aiColor4D clr;
 							clr.a = 1.f;
 

+ 7 - 0
code/OFFLoader.cpp

@@ -127,6 +127,13 @@ void OFFImporter::InternReadFile( const std::string& pFile,
 	const unsigned int numVertices = strtoul10(sz,&sz);SkipSpaces(&sz);
 	const unsigned int numFaces = strtoul10(sz,&sz);
 
+	if (!numVertices) {
+		throw DeadlyImportError("OFF: There are no valid vertices");
+	}
+	if (!numFaces) {
+		throw DeadlyImportError("OFF: There are no valid faces");
+	}
+
 	pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = 1 ];
 	aiMesh* mesh = pScene->mMeshes[0] = new aiMesh();
 	aiFace* faces = mesh->mFaces = new aiFace [mesh->mNumFaces = numFaces];

+ 1 - 1
code/ObjExporter.cpp

@@ -51,7 +51,7 @@ namespace Assimp	{
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp
-void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	// invoke the exporter 
 	ObjExporter exporter(pFile, pScene);

+ 2 - 1
code/OgreXmlSerializer.cpp

@@ -54,7 +54,8 @@ namespace Assimp
 namespace Ogre
 {
 
-void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "")
+AI_WONT_RETURN void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error)
 {
 	if (!error.empty())
 	{

+ 1 - 1
code/OptimizeMeshes.cpp

@@ -170,7 +170,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
 
 			// Find meshes to merge with us
 			for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) {
-				register unsigned int am = pNode->mMeshes[a];
+				unsigned int am = pNode->mMeshes[a];
 				if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) {
 
 					merge_list.push_back(mScene->mMeshes[am]);

+ 40 - 35
code/ParsingUtils.h

@@ -64,7 +64,7 @@ static const unsigned int BufferSize = 4096;
 template <class char_t>
 AI_FORCE_INLINE char_t ToLower( char_t in)
 {
-    return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in;
+	return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in;
 }
 
 // ---------------------------------------------------------------------------------
@@ -77,35 +77,35 @@ AI_FORCE_INLINE char_t ToUpper( char_t in) {
 template <class char_t>
 AI_FORCE_INLINE bool IsUpper( char_t in)
 {
-    return (in >= (char_t)'A' && in <= (char_t)'Z');
+	return (in >= (char_t)'A' && in <= (char_t)'Z');
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsLower( char_t in)
 {
-    return (in >= (char_t)'a' && in <= (char_t)'z');
+	return (in >= (char_t)'a' && in <= (char_t)'z');
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsSpace( char_t in)
 {
-    return (in == (char_t)' ' || in == (char_t)'\t');
+	return (in == (char_t)' ' || in == (char_t)'\t');
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsLineEnd( char_t in)
 {
-    return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f');
+	return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f');
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsSpaceOrNewLine( char_t in)
 {
-    return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
+	return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
 }
 
 // ---------------------------------------------------------------------------------
@@ -115,15 +115,15 @@ AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out)
     while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) {
         ++in;
     }
-    *out = in;
-    return !IsLineEnd<char_t>(*in);
+	*out = in;
+	return !IsLineEnd<char_t>(*in);
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpaces( const char_t** inout)
 {
-    return SkipSpaces<char_t>(*inout,inout);
+	return SkipSpaces<char_t>(*inout,inout);
 }
 
 // ---------------------------------------------------------------------------------
@@ -134,19 +134,19 @@ AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out)
         ++in;
     }
 
-    // files are opened in binary mode. Ergo there are both NL and CR
+	// files are opened in binary mode. Ergo there are both NL and CR
     while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
         ++in;
     }
-    *out = in;
-    return *in != (char_t)'\0';
+	*out = in;
+	return *in != (char_t)'\0';
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipLine( const char_t** inout)
 {
-    return SkipLine<char_t>(*inout,inout);
+	return SkipLine<char_t>(*inout,inout);
 }
 
 // ---------------------------------------------------------------------------------
@@ -156,15 +156,15 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out)
     while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
         ++in;
     }
-    *out = in;
-    return *in != '\0';
+	*out = in;
+	return *in != '\0';
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t** inout)
 {
-    return SkipSpacesAndLineEnd<char_t>(*inout,inout);
+	return SkipSpacesAndLineEnd<char_t>(*inout,inout);
 }
 
 // ---------------------------------------------------------------------------------
@@ -175,12 +175,12 @@ AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize
         return false;
     }
 
-    char* _out = out;
+	char* _out = out;
     char* const end = _out + BufferSize;
     while( !IsLineEnd( *buffer ) && _out < end ) {
         *_out++ = *buffer++;
     }
-    *_out = (char_t)'\0';
+	*_out = (char_t)'\0';
 
     while( IsLineEnd( *buffer ) && '\0' != *buffer ) {
         ++buffer;
@@ -193,19 +193,24 @@ AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize
 template <class char_t>
 AI_FORCE_INLINE bool IsNumeric( char_t in)
 {
-    return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in;
+	return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in;
 }
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len)
 {
-    if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
-        in += len+1;
-        return true;
-    }
+	if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
+		if (in[len] != '\0') {
+			in += len+1;
+		} else {
+			// If EOF after the token make sure we don't go past end of buffer
+			in += len;
+		}
+		return true;
+	}
 
-    return false;
+	return false;
 }
 // ---------------------------------------------------------------------------------
 /** @brief Case-ignoring version of TokenMatch
@@ -215,25 +220,25 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len
  */
 AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len)
 {
-    if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
-        in += len+1;
-        return true;
-    }
-    return false;
+	if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
+		in += len+1;
+		return true;
+	}
+	return false;
 }
 // ---------------------------------------------------------------------------------
 AI_FORCE_INLINE void SkipToken(const char*& in)
 {
-    SkipSpaces(&in);
-    while (!IsSpaceOrNewLine(*in))++in;
+	SkipSpaces(&in);
+	while (!IsSpaceOrNewLine(*in))++in;
 }
 // ---------------------------------------------------------------------------------
 AI_FORCE_INLINE std::string GetNextToken(const char*& in)
 {
-    SkipSpacesAndLineEnd(&in);
-    const char* cur = in;
-    while (!IsSpaceOrNewLine(*in))++in;
-    return std::string(cur,(size_t)(in-cur));
+	SkipSpacesAndLineEnd(&in);
+	const char* cur = in;
+	while (!IsSpaceOrNewLine(*in))++in;
+	return std::string(cur,(size_t)(in-cur));
 }
 
 // ---------------------------------------------------------------------------------

+ 2 - 2
code/PlyExporter.cpp

@@ -50,7 +50,7 @@ namespace Assimp	{
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to PLY. Prototyped and registered in Exporter.cpp
-void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	// invoke the exporter 
 	PlyExporter exporter(pFile, pScene);
@@ -64,7 +64,7 @@ void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
 	outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
 }
 
-void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	// invoke the exporter 
 	PlyExporter exporter(pFile, pScene, true);

+ 45 - 28
code/PlyLoader.cpp

@@ -64,6 +64,24 @@ static const aiImporterDesc desc = {
 	"ply" 
 };
 
+
+// ------------------------------------------------------------------------------------------------
+// Internal stuff
+namespace
+{
+	// ------------------------------------------------------------------------------------------------
+	// Checks that property index is within range
+	template <class T>
+	const T &GetProperty(const std::vector<T> &props, int idx)
+	{
+		if (idx >= props.size())
+			throw DeadlyImportError("Invalid .ply file: Property index is out of range.");
+
+		return props[idx];
+	}
+}
+
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 PLYImporter::PLYImporter()
@@ -156,7 +174,6 @@ void PLYImporter::InternReadFile( const std::string& pFile,
 	}
 	else
 	{
-		delete[] this->mBuffer;
 		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		throw DeadlyImportError( "Invalid .ply file: Missing format specification");
 	}
@@ -432,13 +449,13 @@ void PLYImporter::LoadTextureCoordinates(std::vector<aiVector2D>* pvOut)
 			if (0xFFFFFFFF != aiPositions[0])
 			{
 				vOut.x = PLY::PropertyInstance::ConvertTo<float>(
-					(*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+					GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]);
 			}
 
 			if (0xFFFFFFFF != aiPositions[1])
 			{
 				vOut.y = PLY::PropertyInstance::ConvertTo<float>(
-					(*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+					GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]);
 			}
 			// and add them to our nice list
 			pvOut->push_back(vOut);
@@ -542,19 +559,19 @@ void PLYImporter::LoadVertices(std::vector<aiVector3D>* pvOut, bool p_bNormals)
 			if (0xFFFFFFFF != aiPositions[0])
 			{
 				vOut.x = PLY::PropertyInstance::ConvertTo<float>(
-					(*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+					GetProperty((*i).alProperties, aiPositions[0]).avList.front(),aiTypes[0]);
 			}
 
 			if (0xFFFFFFFF != aiPositions[1])
 			{
 				vOut.y = PLY::PropertyInstance::ConvertTo<float>(
-					(*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+					GetProperty((*i).alProperties, aiPositions[1]).avList.front(),aiTypes[1]);
 			}
 
 			if (0xFFFFFFFF != aiPositions[2])
 			{
 				vOut.z = PLY::PropertyInstance::ConvertTo<float>(
-					(*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]);
+					GetProperty((*i).alProperties, aiPositions[2]).avList.front(),aiTypes[2]);
 			}
 
 			// and add them to our nice list
@@ -660,28 +677,28 @@ void PLYImporter::LoadVertexColor(std::vector<aiColor4D>* pvOut)
 			
 			if (0xFFFFFFFF != aiPositions[0])
 			{
-				vOut.r = NormalizeColorValue((*i).alProperties[
-					aiPositions[0]].avList.front(),aiTypes[0]);
+				vOut.r = NormalizeColorValue(GetProperty((*i).alProperties,
+					aiPositions[0]).avList.front(),aiTypes[0]);
 			}
 
 			if (0xFFFFFFFF != aiPositions[1])
 			{
-				vOut.g = NormalizeColorValue((*i).alProperties[
-					aiPositions[1]].avList.front(),aiTypes[1]);
+				vOut.g = NormalizeColorValue(GetProperty((*i).alProperties,
+					aiPositions[1]).avList.front(),aiTypes[1]);
 			}
 
 			if (0xFFFFFFFF != aiPositions[2])
 			{
-				vOut.b = NormalizeColorValue((*i).alProperties[
-					aiPositions[2]].avList.front(),aiTypes[2]);
+				vOut.b = NormalizeColorValue(GetProperty((*i).alProperties,
+					aiPositions[2]).avList.front(),aiTypes[2]);
 			}
 
 			// assume 1.0 for the alpha channel ifit is not set
 			if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f;
 			else
 			{
-				vOut.a = NormalizeColorValue((*i).alProperties[
-					aiPositions[3]].avList.front(),aiTypes[3]);
+				vOut.a = NormalizeColorValue(GetProperty((*i).alProperties,
+					aiPositions[3]).avList.front(),aiTypes[3]);
 			}
 
 			// and add them to our nice list
@@ -774,11 +791,11 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
 				// parse the list of vertex indices
 				if (0xFFFFFFFF != iProperty)
 				{
-					const unsigned int iNum = (unsigned int)(*i).alProperties[iProperty].avList.size();
+					const unsigned int iNum = (unsigned int)GetProperty((*i).alProperties, iProperty).avList.size();
 					sFace.mIndices.resize(iNum);
 
 					std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p = 
-						(*i).alProperties[iProperty].avList.begin();
+						GetProperty((*i).alProperties, iProperty).avList.begin();
 
 					for (unsigned int a = 0; a < iNum;++a,++p)
 					{
@@ -790,7 +807,7 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
 				if (0xFFFFFFFF != iMaterialIndex)
 				{
 					sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo<unsigned int>(
-						(*i).alProperties[iMaterialIndex].avList.front(),eType2);
+						GetProperty((*i).alProperties, iMaterialIndex).avList.front(),eType2);
 				}
 				pvOut->push_back(sFace);
 			}
@@ -801,7 +818,7 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
 			// a value of -1 indicates a restart of the strip
 			bool flip = false;
 			for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) {
-				const std::vector<PLY::PropertyInstance::ValueUnion>& quak = (*i).alProperties[iProperty].avList;
+				const std::vector<PLY::PropertyInstance::ValueUnion>& quak = GetProperty((*i).alProperties, iProperty).avList;
 				pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u));
 
 				int aiTable[2] = {-1,-1};
@@ -852,30 +869,30 @@ void PLYImporter::GetMaterialColor(const std::vector<PLY::PropertyInstance>& avL
 	if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f;
 	else
 	{
-		clrOut->r = NormalizeColorValue(avList[
-			aiPositions[0]].avList.front(),aiTypes[0]);
+		clrOut->r = NormalizeColorValue(GetProperty(avList,
+			aiPositions[0]).avList.front(),aiTypes[0]);
 	}
 
 	if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f;
 	else
 	{
-		clrOut->g = NormalizeColorValue(avList[
-			aiPositions[1]].avList.front(),aiTypes[1]);
+		clrOut->g = NormalizeColorValue(GetProperty(avList,
+			aiPositions[1]).avList.front(),aiTypes[1]);
 	}
 
 	if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f;
 	else
 	{
-		clrOut->b = NormalizeColorValue(avList[
-			aiPositions[2]].avList.front(),aiTypes[2]);
+		clrOut->b = NormalizeColorValue(GetProperty(avList,
+			aiPositions[2]).avList.front(),aiTypes[2]);
 	}
 
 	// assume 1.0 for the alpha channel ifit is not set
 	if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f;
 	else
 	{
-		clrOut->a = NormalizeColorValue(avList[
-			aiPositions[3]].avList.front(),aiTypes[3]);
+		clrOut->a = NormalizeColorValue(GetProperty(avList, 
+			aiPositions[3]).avList.front(),aiTypes[3]);
 	}
 }
 
@@ -1026,7 +1043,7 @@ void PLYImporter::LoadMaterial(std::vector<aiMaterial*>* pvOut)
 			// handle phong power and shading mode
 			int iMode;
 			if (0xFFFFFFFF != iPhong)	{
-				float fSpec = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),ePhong);
+				float fSpec = PLY::PropertyInstance::ConvertTo<float>(GetProperty((*i).alProperties, iPhong).avList.front(),ePhong);
 
 				// if shininess is 0 (and the pow() calculation would therefore always
 				// become 1, not depending on the angle), use gouraud lighting
@@ -1044,7 +1061,7 @@ void PLYImporter::LoadMaterial(std::vector<aiMaterial*>* pvOut)
 
 			// handle opacity
 			if (0xFFFFFFFF != iOpacity)	{
-				float fOpacity = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),eOpacity);
+				float fOpacity = PLY::PropertyInstance::ConvertTo<float>(GetProperty((*i).alProperties, iPhong).avList.front(),eOpacity);
 				pcHelper->AddProperty<float>(&fOpacity, 1, AI_MATKEY_OPACITY);
 			}
 

+ 3 - 3
code/PlyParser.cpp

@@ -436,7 +436,7 @@ bool PLY::DOM::ParseHeader (const char* pCur,const char** pCurOut,bool isBinary)
 	*pCurOut = pCur;
 
 	// parse all elements
-	while (true)
+	while ((*pCur) != '\0')
 	{
 		// skip all comments
 		PLY::DOM::SkipComments(pCur,&pCur);
@@ -794,7 +794,7 @@ bool PLY::PropertyInstance::ParseValue(
 {
 	ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
 
-	register bool ret = true;
+	bool ret = true;
 	*pCurOut = pCur;
 	switch (eType)
 	{
@@ -841,7 +841,7 @@ bool PLY::PropertyInstance::ParseValueBinary(
 {
 	ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
 
-	register bool ret = true;
+	bool ret = true;
 	switch (eType)
 	{
 	case EDT_UInt:

+ 1 - 1
code/Q3DLoader.cpp

@@ -314,7 +314,7 @@ void Q3DImporter::InternReadFile( const std::string& pFile,
 				if (!tex->mWidth || !tex->mHeight)
 					throw DeadlyImportError("Quick3D: Invalid texture. Width or height is zero");
 
-				register unsigned int mul = tex->mWidth * tex->mHeight;
+				unsigned int mul = tex->mWidth * tex->mHeight;
 				aiTexel* begin = tex->pcData = new aiTexel[mul];
 				aiTexel* const end = & begin [mul];
 

+ 2 - 2
code/RemoveVCProcess.cpp

@@ -84,7 +84,7 @@ inline void ArrayDelete(T**& in, unsigned int& num)
 // "don't remove" flag not set. Nodes with meshes are never deleted.
 bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
 {
-	register bool b = false;
+	bool b = false;
 
 	std::list<aiNode*> mine;
 	for (unsigned int i = 0; i < node->mNumChildren;++i)
@@ -271,7 +271,7 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
 	}
 
 	// handle texture coordinates
-	register bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS));
+	bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS));
 	for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real)
 	{
 		if (!pMesh->mTextureCoords[i])break;

+ 2 - 2
code/STLExporter.cpp

@@ -50,7 +50,7 @@ namespace Assimp	{
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Stereolithograpy. Prototyped and registered in Exporter.cpp
-void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	// invoke the exporter 
 	STLExporter exporter(pFile, pScene);
@@ -63,7 +63,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
 
 	outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
 }
-void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	// invoke the exporter 
 	STLExporter exporter(pFile, pScene, true);

+ 8 - 2
code/STLLoader.cpp

@@ -229,6 +229,9 @@ void STLImporter::LoadASCIIFile()
 	size_t temp;
 	// setup the name of the node
 	if ((temp = (size_t)(sz-szMe)))	{
+		if (temp >= MAXLEN) {
+			throw DeadlyImportError( "STL: Node name too long" );
+		}
 
 		pScene->mRootNode->mName.length = temp;
 		memcpy(pScene->mRootNode->mName.data,szMe,temp);
@@ -305,6 +308,7 @@ void STLImporter::LoadASCIIFile()
 		{
 			if (3 == curVertex)	{
 				DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
+				++sz;
 			}
 			else
 			{
@@ -323,8 +327,10 @@ void STLImporter::LoadASCIIFile()
 			break;
 		}
 		// else skip the whole identifier
-		else while (!::IsSpaceOrNewLine(*sz)) {
-			++sz;
+		else {
+			do {
+				++sz;
+			} while (!::IsSpaceOrNewLine(*sz));
 		}
 	}
 

+ 1 - 1
code/SmoothingGroups.inl

@@ -106,7 +106,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
 		std::vector<unsigned int> poResult;
 		for (unsigned int c = 0; c < 3;++c)
 		{
-			register unsigned int idx = (*i).mIndices[c];
+			unsigned int idx = (*i).mIndices[c];
 			if (vertexDone[idx])continue;
 
 			sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup,

+ 1 - 1
code/SortByPTypeProcess.cpp

@@ -289,7 +289,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene)
 
 				for (unsigned int q = 0; q < in.mNumIndices; ++q)
 				{
-					register unsigned int idx = in.mIndices[q];
+					unsigned int idx = in.mIndices[q];
 
 					// process all bones of this index
 					if (avw)

+ 3 - 3
code/StringComparison.h

@@ -137,7 +137,7 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2)
 	return ::strcasecmp(s1,s2);
 #else
 	
-	register char c1, c2;
+	char c1, c2;
 	do	{
 		c1 = tolower(*s1++);
 		c2 = tolower(*s2++);
@@ -156,7 +156,7 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2)
  */
 inline int ASSIMP_stricmp(const std::string& a, const std::string& b)
 {
-	register int i = (int)b.length()-(int)a.length();
+	int i = (int)b.length()-(int)a.length();
 	return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str()));
 }
 
@@ -186,7 +186,7 @@ inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n)
 	return ::strncasecmp(s1,s2, n);
 
 #else
-	register char c1, c2;
+	char c1, c2;
 	unsigned int p = 0;
 	do 
 	{

+ 3 - 0
code/Subdivision.cpp

@@ -290,6 +290,8 @@ void CatmullClarkSubdivider::InternSubdivide (
 		}
 	}
 	
+	{
+	// we want edges to go away before the recursive calls so begin a new scope
 	EdgeMap edges;
 
 	// ---------------------------------------------------------------------
@@ -572,6 +574,7 @@ void CatmullClarkSubdivider::InternSubdivide (
 			}
 		}
 	}
+	}  // end of scope for edges, freeing its memory
 
 	// ---------------------------------------------------------------------
 	// 7. Apply the next subdivision step. 

+ 1 - 1
code/TerragenLoader.cpp

@@ -225,7 +225,7 @@ void TerragenImporter::InternReadFile( const std::string& pFile,
 
 					// make verts
 					const float fy = (float)yy, fx = (float)xx;
-					register unsigned tmp,tmp2;
+					unsigned tmp,tmp2;
 					*pv++ = aiVector3D(fx,fy,    (float)data[(tmp2=x*yy)    + xx] * hscale + bheight);
 					*pv++ = aiVector3D(fx,fy+1,  (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight);
 					*pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp  + xx+1]         * hscale + bheight);

+ 2 - 2
code/TextureTransform.cpp

@@ -120,7 +120,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
 	 * offset 2 and 3)
 	 */
 	if ((rounded  = (int)info.mTranslation.x))	{
-		float out;
+		float out = 0.0f;
 		szTemp[0] = 0;
 		if (aiTextureMapMode_Wrap == info.mapU)	{
 			// Wrap - simple take the fraction of the field
@@ -153,7 +153,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
 	 * offset 2 and 3)
 	 */
 	if ((rounded  = (int)info.mTranslation.y))	{
-		float out;
+		float out = 0.0f;
 		szTemp[0] = 0;
 		if (aiTextureMapMode_Wrap == info.mapV)	{
 			// Wrap - simple take the fraction of the field

+ 28 - 9
code/XFileExporter.cpp

@@ -59,7 +59,7 @@ namespace Assimp
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
 {
 	std::string path = "";
 	std::string file = pFile;
@@ -78,13 +78,19 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce
 		}
 	}
 
+	// create/copy Properties
+	ExportProperties props(*pProperties);
+
+	// set standard properties if not set
+	if (!props.HasPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT)) props.SetPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT, false);
+
 	// invoke the exporter 
-	XFileExporter iDoTheExportThing( pScene, pIOSystem, path, file);
+	XFileExporter iDoTheExportThing( pScene, pIOSystem, path, file, &props);
 
 	// we're still here - export successfully completed. Write result to the given IOSYstem
 	boost::scoped_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
 	if(outfile == NULL) {
-		throw DeadlyExportError("could not open output .dae file: " + std::string(pFile));
+		throw DeadlyExportError("could not open output .x file: " + std::string(pFile));
 	}
 
 	// XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
@@ -96,7 +102,7 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce
 
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
-XFileExporter::XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
+XFileExporter::XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file, const ExportProperties* pProperties) : mIOSystem(pIOSystem), mPath(path), mFile(file), mProperties(pProperties)
 {
 	// make sure that all formatting happens using the standard, C locale and not the user's current locale
 	mOutput.imbue( std::locale("C") );
@@ -126,7 +132,7 @@ void XFileExporter::WriteFile()
 {
 	// note, that all realnumber values must be comma separated in x files
 	mOutput.setf(std::ios::fixed);
-	mOutput.precision(6); // precission for float
+	mOutput.precision(16); // precission for double
 
 	// entry of writing the file
 	WriteHeader();
@@ -148,7 +154,10 @@ void XFileExporter::WriteFile()
 // Writes the asset header
 void XFileExporter::WriteHeader()
 {
-	mOutput << startstr << "xof 0303txt 0032" << endstr;
+	if (mProperties->GetPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT) == true)
+		mOutput << startstr << "xof 0303txt 0064" << endstr;
+	else
+		mOutput << startstr << "xof 0303txt 0032" << endstr;
 	mOutput << endstr;
 	mOutput << startstr << "template Frame {" << endstr;
 	PushTag();
@@ -298,7 +307,7 @@ void XFileExporter::WriteNode( aiNode* pNode)
 		ss << "Node_" << pNode;
 		pNode->mName.Set(ss.str());
 	}
-	mOutput << startstr << "Frame " << pNode->mName.C_Str() << " {" << endstr;
+	mOutput << startstr << "Frame " << toXFileString(pNode->mName) << " {" << endstr;
 
 	PushTag();
 
@@ -318,9 +327,9 @@ void XFileExporter::WriteNode( aiNode* pNode)
 	mOutput << startstr << "}" << endstr << endstr;
 }
 
-void XFileExporter::WriteMesh(const aiMesh* mesh)
+void XFileExporter::WriteMesh(aiMesh* mesh)
 {
-	mOutput << startstr << "Mesh " << mesh->mName.C_Str() << "_mShape" << " {" << endstr;
+	mOutput << startstr << "Mesh " << toXFileString(mesh->mName) << "_mShape" << " {" << endstr;
 
 	PushTag();
 
@@ -496,6 +505,16 @@ void XFileExporter::WriteMesh(const aiMesh* mesh)
 
 }
 
+std::string XFileExporter::toXFileString(aiString &name)
+{
+	std::string str = std::string(name.C_Str());
+	std::replace(str.begin(), str.end(), '<', '_');
+	std::replace(str.begin(), str.end(), '>', '_');
+	std::replace(str.begin(), str.end(), '{', '_');
+	std::replace(str.begin(), str.end(), '}', '_');
+	std::replace(str.begin(), str.end(), '$', '_');
+	return str;
+}
 
 void XFileExporter::writePath(aiString path)
 {

+ 8 - 2
code/XFileExporter.h

@@ -61,7 +61,7 @@ class XFileExporter
 {
 public:
 	/// Constructor for a specific scene to export
-	XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file);
+	XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file, const ExportProperties* pProperties);
 
 	/// Destructor
 	virtual ~XFileExporter();
@@ -80,7 +80,7 @@ protected:
 	void WriteNode( aiNode* pNode );
 
 	/// write a mesh entry of the scene
-	void WriteMesh(const aiMesh* mesh);
+	void WriteMesh( aiMesh* mesh);
 
 	/// Enters a new xml element, which increases the indentation
 	void PushTag() { startstr.append( "  "); }
@@ -94,6 +94,12 @@ public:
 
 protected:
 
+	/// normalize the name to be accepted by xfile readers
+	std::string toXFileString(aiString &name);
+
+	/// hold the properties pointer
+	const ExportProperties* mProperties;
+
 	/// write a path
 	void writePath(aiString path);	
 

+ 5 - 1
code/XFileParser.cpp

@@ -214,6 +214,10 @@ XFileParser::XFileParser( const std::vector<char>& pBuffer)
 			AI_SWAP2(ofs); 
 			P += 4;
 
+			if (P + ofs > End + 2) {
+				throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
+			}
+
 			// push data to the stream
 			stream.next_in   = (Bytef*)P;
 			stream.avail_in  = ofs;
@@ -1428,7 +1432,7 @@ aiColor3D XFileParser::ReadRGB()
 
 // ------------------------------------------------------------------------------------------------
 // Throws an exception with a line number and the given text.
-void XFileParser::ThrowException( const std::string& pText)
+AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText)
 {
 	if( mIsBinaryFormat)
 		throw DeadlyImportError( pText);

+ 1 - 1
code/XFileParser.h

@@ -134,7 +134,7 @@ protected:
 	aiColor4D ReadRGBA();
 
 	/** Throws an exception with a line number and the given text. */
-	void ThrowException( const std::string& pText);
+	AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX;
 
 	/** Filters the imported hierarchy for some degenerated cases that some exporters produce.
 	 * @param pData The sub-hierarchy to filter

+ 1 - 1
code/irrXMLWrapper.h

@@ -102,7 +102,7 @@ public:
 
 	// ----------------------------------------------------------------------------------
 	//! Virtual destructor
-	virtual ~CIrrXML_IOStreamReader() {};
+	virtual ~CIrrXML_IOStreamReader() {}
 
 	// ----------------------------------------------------------------------------------
 	//!   Reads an amount of bytes from the file.

+ 5 - 2
contrib/zlib/CMakeLists.txt

@@ -1,9 +1,12 @@
 cmake_minimum_required(VERSION 2.4.4)
 set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
 
-# use the old project command http://www.cmake.org/cmake/help/v3.0/policy/CMP0048.html
+# CMake 3.0 changed the project command, setting policy CMP0048 reverts to the old behaviour.
+# See http://www.cmake.org/cmake/help/v3.0/policy/CMP0048.html
 cmake_policy(PUSH)
-cmake_policy(SET CMP0048 OLD)
+if(CMAKE_MAJOR_VERSION GREATER 2)
+	cmake_policy(SET CMP0048 OLD)
+endif()
 project(zlib C)
 cmake_policy(POP)
 

+ 1 - 1
include/assimp/DefaultLogger.hpp

@@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------------------------------------------------
 */
-/** @file DefaultLogger.h
+/** @file DefaultLogger.hpp
 */
 
 #ifndef INCLUDED_AI_DEFAULTLOGGER

+ 187 - 11
include/assimp/Exporter.hpp

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file  export.hpp
+/** @file  Exporter.hpp
 *  @brief Defines the CPP-API for the Assimp export interface
 */
 #ifndef AI_EXPORT_HPP_INC
@@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 
 #include "cexport.h"
+#include <map>
 
 namespace Assimp	{
 	class ExporterPimpl;
@@ -72,6 +73,9 @@ namespace Assimp	{
  * #ExportToBlob is especially useful if you intend to work 
  * with the data in-memory. 
 */
+
+class ASSIMP_API ExportProperties;
+
 class ASSIMP_API Exporter
 	// TODO: causes good ol' base class has no dll interface warning
 //#ifdef __cplusplus
@@ -81,7 +85,7 @@ class ASSIMP_API Exporter
 public:
 
 	/** Function pointer type of a Export worker function */
-	typedef void (*fpExportFunc)(const char*,IOSystem*, const aiScene*);
+	typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 
 	/** Internal description of an Assimp export format option */
 	struct ExportFormatEntry
@@ -171,8 +175,8 @@ public:
 	*   Any IO handlers set via #SetIOHandler are ignored here.
 	* @note Use aiCopyScene() to get a modifiable copy of a previously
 	*   imported scene. */
-	const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u );
-	inline const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u );
+	const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL);
+	inline const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const std::string& pFormatId, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL);
 
 
 	// -------------------------------------------------------------------
@@ -181,7 +185,7 @@ public:
 	 *  about the output data flow of the export process.
 	 * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL.
 	 * @param pPath Full target file name. Target must be accessible.
-	 * @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated
+	 * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated
 	 *   flags, but in reality only a subset of them makes sense here. Specifying
 	 *   'preprocessing' flags is useful if the input scene does not conform to 
 	 *   Assimp's default conventions as specified in the @link data Data Structures Page @endlink. 
@@ -208,8 +212,8 @@ public:
 	 * @return AI_SUCCESS if everything was fine. 
 	 * @note Use aiCopyScene() to get a modifiable copy of a previously
 	 *   imported scene.*/
-	aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing = 0u);
-	inline aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath,  unsigned int pPreprocessing = 0u);
+	aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL);
+	inline aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath,  unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = NULL);
 
 
 	// -------------------------------------------------------------------
@@ -309,16 +313,188 @@ protected:
 };
 
 
+class ASSIMP_API ExportProperties
+{
+public:
+	// Data type to store the key hash
+	typedef unsigned int KeyType;
+	
+	// typedefs for our four configuration maps.
+	// We don't need more, so there is no need for a generic solution
+	typedef std::map<KeyType, int> IntPropertyMap;
+	typedef std::map<KeyType, float> FloatPropertyMap;
+	typedef std::map<KeyType, std::string> StringPropertyMap;
+	typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
+
+public:
+
+	/** Standard constructor
+	* @see ExportProperties()
+	*/
+
+	ExportProperties();
+
+	// -------------------------------------------------------------------
+	/** Copy constructor.
+	 * 
+	 * This copies the configuration properties of another ExportProperties.
+	 * @see ExportProperties(const ExportProperties& other)
+	 */
+	ExportProperties(const ExportProperties& other);
+
+	// -------------------------------------------------------------------
+	/** Set an integer configuration property.
+	 * @param szName Name of the property. All supported properties
+	 *   are defined in the aiConfig.g header (all constants share the
+	 *   prefix AI_CONFIG_XXX and are simple strings).
+	 * @param iValue New value of the property
+	 * @param bWasExisting Optional pointer to receive true if the
+	 *   property was set before. The new value replaces the previous value
+	 *   in this case.
+	 * @note Property of different types (float, int, string ..) are kept
+	 *   on different stacks, so calling SetPropertyInteger() for a 
+	 *   floating-point property has no effect - the loader will call
+	 *   GetPropertyFloat() to read the property, but it won't be there.
+	 */
+	void SetPropertyInteger(const char* szName, int iValue, 
+		bool* bWasExisting = NULL);
+
+	// -------------------------------------------------------------------
+	/** Set a boolean configuration property. Boolean properties
+	 *  are stored on the integer stack internally so it's possible
+	 *  to set them via #SetPropertyBool and query them with
+	 *  #GetPropertyBool and vice versa.
+	 * @see SetPropertyInteger()
+	 */
+	void SetPropertyBool(const char* szName, bool value, bool* bWasExisting = NULL)	{
+		SetPropertyInteger(szName,value,bWasExisting);
+	}
+
+	// -------------------------------------------------------------------
+	/** Set a floating-point configuration property.
+	 * @see SetPropertyInteger()
+	 */
+	void SetPropertyFloat(const char* szName, float fValue, 
+		bool* bWasExisting = NULL);
+
+	// -------------------------------------------------------------------
+	/** Set a string configuration property.
+	 * @see SetPropertyInteger()
+	 */
+	void SetPropertyString(const char* szName, const std::string& sValue, 
+		bool* bWasExisting = NULL);
+
+	// -------------------------------------------------------------------
+	/** Set a matrix configuration property.
+	 * @see SetPropertyInteger()
+	 */
+	void SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue, 
+		bool* bWasExisting = NULL);
+
+	// -------------------------------------------------------------------
+	/** Get a configuration property.
+	 * @param szName Name of the property. All supported properties
+	 *   are defined in the aiConfig.g header (all constants share the
+	 *   prefix AI_CONFIG_XXX).
+	 * @param iErrorReturn Value that is returned if the property 
+	 *   is not found. 
+	 * @return Current value of the property
+	 * @note Property of different types (float, int, string ..) are kept
+	 *   on different lists, so calling SetPropertyInteger() for a 
+	 *   floating-point property has no effect - the loader will call
+	 *   GetPropertyFloat() to read the property, but it won't be there.
+	 */
+	int GetPropertyInteger(const char* szName, 
+		int iErrorReturn = 0xffffffff) const;
+
+	// -------------------------------------------------------------------
+	/** Get a boolean configuration property. Boolean properties
+	 *  are stored on the integer stack internally so it's possible
+	 *  to set them via #SetPropertyBool and query them with
+	 *  #GetPropertyBool and vice versa.
+	 * @see GetPropertyInteger()
+	 */
+	bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const {
+		return GetPropertyInteger(szName,bErrorReturn)!=0;
+	}
+
+	// -------------------------------------------------------------------
+	/** Get a floating-point configuration property
+	 * @see GetPropertyInteger()
+	 */
+	float GetPropertyFloat(const char* szName, 
+		float fErrorReturn = 10e10f) const;
+
+	// -------------------------------------------------------------------
+	/** Get a string configuration property
+	 *
+	 *  The return value remains valid until the property is modified.
+	 * @see GetPropertyInteger()
+	 */
+	const std::string GetPropertyString(const char* szName,
+		const std::string& sErrorReturn = "") const;
+
+	// -------------------------------------------------------------------
+	/** Get a matrix configuration property
+	 *
+	 *  The return value remains valid until the property is modified.
+	 * @see GetPropertyInteger()
+	 */
+	const aiMatrix4x4 GetPropertyMatrix(const char* szName,
+		const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const;
+
+	// -------------------------------------------------------------------
+	/** Determine a integer configuration property has been set.
+	* @see HasPropertyInteger()
+	 */
+	bool HasPropertyInteger(const char* szName) const;
+
+	/** Determine a boolean configuration property has been set.
+	* @see HasPropertyBool()
+	 */
+	bool HasPropertyBool(const char* szName) const;
+
+	/** Determine a boolean configuration property has been set.
+	* @see HasPropertyFloat()
+	 */
+	bool HasPropertyFloat(const char* szName) const;
+
+	/** Determine a String configuration property has been set.
+	* @see HasPropertyString()
+	 */
+	bool HasPropertyString(const char* szName) const;
+
+	/** Determine a Matrix configuration property has been set.
+	* @see HasPropertyMatrix()
+	 */
+	bool HasPropertyMatrix(const char* szName) const;
+
+protected:
+
+	/** List of integer properties */
+	IntPropertyMap mIntProperties;
+
+	/** List of floating-point properties */
+	FloatPropertyMap mFloatProperties;
+
+	/** List of string properties */
+	StringPropertyMap mStringProperties;
+
+	/** List of Matrix properties */
+	MatrixPropertyMap mMatrixProperties;
+};
+
+
 // ----------------------------------------------------------------------------------
-inline const aiExportDataBlob* Exporter :: ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,unsigned int pPreprocessing ) 
+inline const aiExportDataBlob* Exporter :: ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,unsigned int pPreprocessing, const ExportProperties* pProperties) 
 {
-	return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing);
+	return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties);
 }
 
 // ----------------------------------------------------------------------------------
-inline aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing )
+inline aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, unsigned int pPreprocessing, const ExportProperties* pProperties)
 {
-	return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing);
+	return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties);
 }
 
 } // namespace Assimp

+ 1 - 1
include/assimp/IOStream.hpp

@@ -38,7 +38,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
-/** @file IOStream.h
+/** @file IOStream.hpp
  *  @brief File I/O wrappers for C++. 
  */
 

+ 1 - 1
include/assimp/IOSystem.hpp

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file IOSystem.h
+/** @file IOSystem.hpp
  *  @brief File system wrapper for C++. Inherit this class to supply
  *  custom file handling logic to the Import library.
 */

+ 3 - 3
include/assimp/Importer.hpp

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file  assimp.hpp
+/** @file  Importer.hpp
  *  @brief Defines the C++-API to the Open Asset Import Library.
  */
 #ifndef INCLUDED_AI_ASSIMP_HPP
@@ -624,8 +624,8 @@ public:
 
 	// -------------------------------------------------------------------
 	/** Private, do not use. */
-	ImporterPimpl* Pimpl() { return pimpl; };
-	const ImporterPimpl* Pimpl() const { return pimpl; };
+	ImporterPimpl* Pimpl() { return pimpl; }
+	const ImporterPimpl* Pimpl() const { return pimpl; }
 
 protected:
 

+ 1 - 1
include/assimp/LogStream.hpp

@@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file LogStream.h
+/** @file LogStream.hpp
  *  @brief Abstract base class 'LogStream', representing an output log stream.
  */
 #ifndef INCLUDED_AI_LOGSTREAM_H

+ 1 - 1
include/assimp/NullLogger.hpp

@@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file  NullLogger.h
+/** @file  NullLogger.hpp
  *  @brief Dummy logger
 */
 

+ 3 - 3
include/assimp/ProgressHandler.hpp

@@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file ProgressHandler.h
+/** @file ProgressHandler.hpp
  *  @brief Abstract base class 'ProgressHandler'.
  */
 #ifndef INCLUDED_AI_PROGRESSHANDLER_H
@@ -99,7 +99,7 @@ public:
 	virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) {
 		float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f;
 		Update( f * 0.5f );
-	};
+	}
 
 	// -------------------------------------------------------------------
 	/** @brief Progress callback for post-processing steps
@@ -113,7 +113,7 @@ public:
 	virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) {
 		float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f;
 		Update( f * 0.5f + 0.5f );
-	};
+	}
 
 }; // !class ProgressHandler 
 // ------------------------------------------------------------------------------------

+ 1 - 1
include/assimp/ai_assert.h

@@ -1,4 +1,4 @@
-/** @file assert.h
+/** @file ai_assert.h
  */
 #ifndef AI_DEBUG_H_INC
 #define AI_DEBUG_H_INC

+ 2 - 5
include/assimp/cexport.h

@@ -122,10 +122,7 @@ ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn);
 * @param pFormatId ID string to specify to which format you want to export to. Use 
 * aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available.
 * @param pFileName Output file to write
-* @param pIO custom IO implementation to be used. Use this if you use your own storage methods.
-*   If none is supplied, a default implementation using standard file IO is used. Note that
-*   #aiExportSceneToBlob is provided as convenience function to export to memory buffers.
-* @param pPreprocessing Accepts any choice of the #aiPostProcessing enumerated
+* @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated
 *   flags, but in reality only a subset of them makes sense here. Specifying
 *   'preprocessing' flags is useful if the input scene does not conform to 
 *   Assimp's default conventions as specified in the @link data Data Structures Page @endlink. 
@@ -183,7 +180,7 @@ ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene,
 
 // --------------------------------------------------------------------------------
 /** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an
-* exported scene. The memory referred by this structure is owned by Assimp. Use #aiReleaseExportedFile()
+* exported scene. The memory referred by this structure is owned by Assimp. 
 * to free its resources. Don't try to free the memory on your side - it will crash for most build configurations
 * due to conflicting heaps.
 *

+ 1 - 1
include/assimp/cfileio.h

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file aiFileIO.h
+/** @file cfileio.h
  *  @brief Defines generic C routines to access memory-mapped files
  */
 #ifndef AI_FILEIO_H_INC

+ 45 - 11
include/assimp/cimport.h

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file  assimp.h
+/** @file  cimport.h
  *  @brief Defines the C-API to the Open Asset Import Library. 
  */
 #ifndef AI_ASSIMP_H_INC
@@ -139,7 +139,17 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileEx(
 // --------------------------------------------------------------------------------
 /** Same as #aiImportFileEx, but adds an extra parameter containing importer settings.
  *
+ * @param pFile Path and filename of the file to be imported, 
+ *   expected to be a null-terminated c-string. NULL is not a valid value.
+ * @param pFlags Optional post processing steps to be executed after 
+ *   a successful import. Provide a bitwise combination of the
+ *   #aiPostProcessSteps flags.
+ * @param pFS aiFileIO structure. Will be used to open the model file itself
+ *   and any other files the loader needs to open.  Pass NULL to use the default
+ *   implementation.
  * @param pProps #aiPropertyStore instance containing import settings. 
+ * @return Pointer to the imported data or NULL if the import failed.  
+ * @note Include <aiFileIO.h> for the definition of #aiFileIO.
  * @see aiImportFileEx
  */
 ASSIMP_API const C_STRUCT aiScene* aiImportFileExWithProperties( 
@@ -188,7 +198,29 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory(
 // --------------------------------------------------------------------------------
 /** Same as #aiImportFileFromMemory, but adds an extra parameter containing importer settings.
  *
+ * @param pBuffer Pointer to the file data
+ * @param pLength Length of pBuffer, in bytes
+ * @param pFlags Optional post processing steps to be executed after 
+ *   a successful import. Provide a bitwise combination of the 
+ *   #aiPostProcessSteps flags. If you wish to inspect the imported
+ *   scene first in order to fine-tune your post-processing setup,
+ *   consider to use #aiApplyPostProcessing().
+ * @param pHint An additional hint to the library. If this is a non empty string,
+ *   the library looks for a loader to support the file extension specified by pHint
+ *   and passes the file to the first matching loader. If this loader is unable to 
+ *   completely the request, the library continues and tries to determine the file
+ *   format on its own, a task that may or may not be successful. 
+ *   Check the return value, and you'll know ...
  * @param pProps #aiPropertyStore instance containing import settings. 
+ * @return A pointer to the imported data, NULL if the import failed.
+ *
+ * @note This is a straightforward way to decode models from memory
+ * buffers, but it doesn't handle model formats that spread their 
+ * data across multiple files or even directories. Examples include
+ * OBJ or MD3, which outsource parts of their material info into
+ * external scripts. If you need full functionality, provide
+ * a custom IOSystem to make Assimp find these files and use
+ * the regular aiImportFileEx()/aiImportFileExWithProperties() API.
  * @see aiImportFileFromMemory
  */
 ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties( 
@@ -210,7 +242,7 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties(
  *   meaning this is still the same #aiScene which you passed for pScene. However,
  *   _if_ post-processing failed, the scene could now be NULL. That's quite a rare
  *   case, post processing steps are not really designed to 'fail'. To be exact, 
- *   the #aiProcess_ValidateDS flag is currently the only post processing step 
+ *   the #aiProcess_ValidateDataStructure flag is currently the only post processing step 
  *   which can actually cause the scene to be reset to NULL.
  */
 ASSIMP_API const C_STRUCT aiScene* aiApplyPostProcessing(
@@ -266,7 +298,7 @@ ASSIMP_API void aiEnableVerboseLogging(aiBool d);
 // --------------------------------------------------------------------------------
 /** Detach a custom log stream from the libraries' logging system.
  *
- *  This is the counterpart of #aiAttachPredefinedLogStream. If you attached a stream,
+ *  This is the counterpart of #aiAttachLogStream. If you attached a stream,
  *  don't forget to detach it again.
  *  @param stream The log stream to be detached.
  *  @return AI_SUCCESS if the log stream has been detached successfully.
@@ -356,8 +388,9 @@ ASSIMP_API void aiReleasePropertyStore(C_STRUCT aiPropertyStore* p);
  *  interface, properties are always shared by all imports. It is not possible to 
  *  specify them per import.
  *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
  * @param szName Name of the configuration property to be set. All supported 
- *   public properties are defined in the config.h header file (#AI_CONFIG_XXX).
+ *   public properties are defined in the config.h header file (AI_CONFIG_XXX).
  * @param value New value for the property
  */
 ASSIMP_API void aiSetImportPropertyInteger(
@@ -372,8 +405,9 @@ ASSIMP_API void aiSetImportPropertyInteger(
  *  interface, properties are always shared by all imports. It is not possible to 
  *  specify them per import.
  *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
  * @param szName Name of the configuration property to be set. All supported 
- *   public properties are defined in the config.h header file (#AI_CONFIG_XXX).
+ *   public properties are defined in the config.h header file (AI_CONFIG_XXX).
  * @param value New value for the property
  */
 ASSIMP_API void aiSetImportPropertyFloat(
@@ -388,10 +422,10 @@ ASSIMP_API void aiSetImportPropertyFloat(
  *  interface, properties are always shared by all imports. It is not possible to 
  *  specify them per import.
  *
- * @param property store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
  * @param szName Name of the configuration property to be set. All supported 
- *   public properties are defined in the config.h header file (#AI_CONFIG_XXX).
- * @param value New value for the property
+ *   public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param st New value for the property
  */
 ASSIMP_API void aiSetImportPropertyString(
 	C_STRUCT aiPropertyStore* store,
@@ -405,10 +439,10 @@ ASSIMP_API void aiSetImportPropertyString(
  *  interface, properties are always shared by all imports. It is not possible to 
  *  specify them per import.
  *
- * @param property store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
  * @param szName Name of the configuration property to be set. All supported 
- *   public properties are defined in the config.h header file (#AI_CONFIG_XXX).
- * @param value New value for the property
+ *   public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param mat New value for the property
  */
 ASSIMP_API void aiSetImportPropertyMatrix(
 	C_STRUCT aiPropertyStore* store,

+ 1 - 1
include/assimp/color4.h

@@ -38,7 +38,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
-/** @file aiColor4D.h
+/** @file color4.h
  *  @brief RGBA color structure, including operators when compiling in C++
  */
 #ifndef AI_COLOR4D_H_INC

+ 1 - 1
include/assimp/color4.inl

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file  aiColor4D.inl
+/** @file  color4.inl
  *  @brief Inline implementation of aiColor4t<TReal> operators
  */
 #ifndef AI_COLOR4D_INL_INC

+ 24 - 12
include/assimp/config.h

@@ -209,7 +209,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	"PP_RRM_EXCLUDE_LIST"
 
 // ---------------------------------------------------------------------------
-/** @brief Configures the #aiProcess_PretransformVertices step to
+/** @brief Configures the #aiProcess_PreTransformVertices step to
  *  keep the scene hierarchy. Meshes are moved to worldspace, but
  *  no optimization is performed (read: meshes with equal materials are not 
  *  joined. The total number of meshes won't change).
@@ -224,7 +224,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	"PP_PTV_KEEP_HIERARCHY"
 
 // ---------------------------------------------------------------------------
-/** @brief Configures the #aiProcess_PretransformVertices step to normalize
+/** @brief Configures the #aiProcess_PreTransformVertices step to normalize
  *  all vertex components into the [-1,1] range. That is, a bounding box
  *  for the whole scene is computed, the maximum component is taken and all
  *  meshes are scaled appropriately (uniformly of course!).
@@ -234,7 +234,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	"PP_PTV_NORMALIZE"
 
 // ---------------------------------------------------------------------------
-/** @brief Configures the #aiProcess_PretransformVertices step to use
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
  *  a users defined matrix as the scene root node transformation before
  *  transforming vertices. 
  *  Property type: bool. Default value: false.
@@ -243,7 +243,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	"PP_PTV_ADD_ROOT_TRANSFORMATION"
 
 // ---------------------------------------------------------------------------
-/** @brief Configures the #aiProcess_PretransformVertices step to use
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
  *  a users defined matrix as the scene root node transformation before
  *  transforming vertices. This property correspond to the 'a1' component
  *  of the transformation matrix.
@@ -376,7 +376,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // ---------------------------------------------------------------------------
 /** @brief Enumerates components of the aiScene and aiMesh data structures
- *  that can be excluded from the import using the #aiPrpcess_RemoveComponent step.
+ *  that can be excluded from the import using the #aiProcess_RemoveComponent step.
  *
  *  See the documentation to #aiProcess_RemoveComponent for more details.
  */
@@ -715,7 +715,7 @@ enum aiComponent
 /** @brief  Tells the MD3 loader which skin files to load.
  *
  * When loading MD3 files, Assimp checks whether a file 
- * <md3_file_name>_<skin_name>.skin is existing. These files are used by
+ * [md3_file_name]_[skin_name].skin is existing. These files are used by
  * Quake III to be able to assign different skins (e.g. red and blue team) 
  * to models. 'default', 'red', 'blue' are typical skin names.
  * Property type: String. Default value: "default".
@@ -728,14 +728,14 @@ enum aiComponent
  *  MD3 file. This can also be a search path.
  *
  * By default Assimp's behaviour is as follows: If a MD3 file 
- * <tt><any_path>/models/<any_q3_subdir>/<model_name>/<file_name>.md3</tt> is 
+ * <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is 
  * loaded, the library tries to locate the corresponding shader file in
- * <tt><any_path>/scripts/<model_name>.shader</tt>. This property overrides this
+ * <tt>any_path/scripts/model_name.shader</tt>. This property overrides this
  * behaviour. It can either specify a full path to the shader to be loaded
  * or alternatively the path (relative or absolute) to the directory where
  * the shaders for all MD3s to be loaded reside. Assimp attempts to open 
- * <tt><dir>/<model_name>.shader</tt> first, <tt><dir>/<file_name>.shader</tt> 
- * is the fallback file. Note that <dir> should have a terminal (back)slash.
+ * <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt> 
+ * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash.
  * Property type: String. Default value: n/a.
  */
 #define AI_CONFIG_IMPORT_MD3_SHADER_SRC \
@@ -818,12 +818,13 @@ enum aiComponent
 /** @brief Ogre Importer detect the texture usage from its filename.
  *
  * Ogre material texture units do not define texture type, the textures usage
- * depends on the used shader or Ogres fixed pipeline. If this config property
+ * depends on the used shader or Ogre's fixed pipeline. If this config property
  * is true Assimp will try to detect the type from the textures filename postfix:
  * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec,
  * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ 
  * and _occlusion for light map, _disp and _displacement for displacement map.
- * The matching is case insensitive. Post fix is taken between last "_" and last ".".
+ * The matching is case insensitive. Post fix is taken between the last 
+ * underscore and the last period.
  * Default behavior is to detect type from lower cased texture unit name by 
  * matching against: normalmap, specularmap, lightmap and displacementmap.
  * For both cases if no match is found aiTextureType_DIFFUSE is used.
@@ -878,4 +879,15 @@ enum aiComponent
 
 #define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION"
 
+
+// ---------- All the Export defines ------------
+
+/** @brief Specifies the xfile use double for real values of float
+ *
+ * Property type: Bool. Default value: false.
+ */
+
+#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
+
+
 #endif // !! AI_CONFIG_H_INC

+ 3 - 3
include/assimp/defs.h

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file aiDefines.h
+/** @file defs.h
  *  @brief Assimp build configuration setup. See the notes in the comment
  *  blocks to find out how to customize _your_ Assimp build.
  */
@@ -162,8 +162,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	define AI_FORCE_INLINE inline
 #endif // (defined _MSC_VER)
 
-#ifdef __clang__
-#	define AI_WONT_RETURN_SUFFIX  __attribute__((analyzer_noreturn))
+#ifdef __GNUC__
+#	define AI_WONT_RETURN_SUFFIX  __attribute__((noreturn))
 #else
 #	define AI_WONT_RETURN_SUFFIX
 #endif // (defined __clang__)

+ 1 - 2
include/assimp/importerdesc.h

@@ -98,8 +98,7 @@ struct aiImporterDesc
 	/** Implementation comments, i.e. unimplemented features*/
 	const char* mComments;
 
-	/** Any combination of the #aiLoaderFlags enumerated values.
-	    These flags indicate some characteristics common to many
+	/** These flags indicate some characteristics common to many
 		importers. */
 	unsigned int mFlags;
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio