浏览代码

MD5 bugfix.
WIP version of the OptimizeGraph-Step.
Added hashes to some string routines (speedup).
Improved property system for float and strings.
Refactoring code for computing normals from smoothinggroups.
Implemented C-ASSIMP.

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

aramis_acg 17 年之前
父节点
当前提交
38d04b6796
共有 62 个文件被更改,包括 3358 次插入1185 次删除
  1. 38 66
      code/3DSConverter.cpp
  2. 13 45
      code/3DSHelper.h
  3. 93 221
      code/3DSLoader.cpp
  4. 2 7
      code/3DSLoader.h
  5. 2 73
      code/ASELoader.cpp
  6. 6 57
      code/ASEParser.cpp
  7. 4 13
      code/ASEParser.h
  8. 167 6
      code/Assimp.cpp
  9. 1 1
      code/CalcTangentsProcess.cpp
  10. 100 0
      code/DXFLoader.cpp
  11. 93 0
      code/DXFLoader.h
  12. 2 3
      code/GenVertexNormalsProcess.cpp
  13. 88 0
      code/GenericProperty.h
  14. 42 2
      code/Hash.h
  15. 63 38
      code/Importer.cpp
  16. 2 0
      code/JoinVerticesProcess.h
  17. 11 11
      code/LWOLoader.cpp
  18. 1 1
      code/LimitBoneWeightsProcess.cpp
  19. 201 208
      code/MD2Loader.cpp
  20. 2 2
      code/MD3Loader.cpp
  21. 36 27
      code/MD5Loader.cpp
  22. 3 3
      code/MDCLoader.cpp
  23. 5 5
      code/MDLLoader.cpp
  24. 7 8
      code/MDLLoader.h
  25. 1 1
      code/MDLMaterialLoader.cpp
  26. 3 3
      code/MaterialSystem.cpp
  27. 5 5
      code/ObjFileImporter.cpp
  28. 3 3
      code/ObjFileParser.cpp
  29. 909 0
      code/OptimizeGraphProcess.cpp
  30. 340 0
      code/OptimizeGraphProcess.h
  31. 14 46
      code/PlyLoader.cpp
  32. 6 6
      code/PretransformVertices.cpp
  33. 39 69
      code/SGSpatialSort.cpp
  34. 11 17
      code/SGSpatialSort.h
  35. 2 2
      code/SMDLoader.cpp
  36. 103 0
      code/SmoothingGroups.h
  37. 48 52
      code/SmoothingGroups.inl
  38. 2 2
      code/SplitLargeMeshes.cpp
  39. 2 2
      contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user
  40. 1 0
      include/IOStream.h
  41. 60 9
      include/aiConfig.h
  42. 32 17
      include/aiFileIO.h
  43. 0 11
      include/aiMesh.h
  44. 55 3
      include/aiPostProcess.h
  45. 3 0
      include/aiVector3D.h
  46. 37 11
      include/assimp.h
  47. 83 35
      include/assimp.hpp
  48. 60 11
      port/jAssimp/src/assimp/ConfigProperty.java
  49. 110 36
      port/jAssimp/src/assimp/Importer.java
  50. 40 14
      port/jAssimp/src/assimp/PostProcessStep.java
  51. 107 0
      test/unit/utImporter.cpp
  52. 57 0
      test/unit/utImporter.h
  53. 85 0
      test/unit/utJoinVertices.cpp
  54. 36 0
      test/unit/utJoinVertices.h
  55. 0 2
      test/unit/utLimitBoneWeights.h
  56. 0 0
      test/unit/utOptimizeGraph.cpp
  57. 35 0
      test/unit/utOptimizeGraph.h
  58. 1 1
      tools/assimp_view/assimp_view.cpp
  59. 1 1
      workspaces/jidea5.1/jAssimp.ipr
  60. 20 0
      workspaces/vc8/UnitTest.vcproj
  61. 45 13
      workspaces/vc8/assimp.vcproj
  62. 20 16
      workspaces/vc8/jAssimp.vcproj

+ 38 - 66
code/3DSConverter.cpp

@@ -59,10 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-#ifdef _MSC_VER
-#	define sprintf sprintf_s
-#endif
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ReplaceDefaultMaterial()
 void Dot3DSImporter::ReplaceDefaultMaterial()
 {
 {
@@ -130,70 +126,66 @@ void Dot3DSImporter::ReplaceDefaultMaterial()
 	}
 	}
 	return;
 	return;
 }
 }
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh)
+void Dot3DSImporter::CheckIndices(Dot3DS::Mesh& sMesh)
 {
 {
 	for (std::vector< Dot3DS::Face >::iterator
 	for (std::vector< Dot3DS::Face >::iterator
-		 i =  sMesh->mFaces.begin();
-		 i != sMesh->mFaces.end();++i)
+		 i =  sMesh.mFaces.begin();
+		 i != sMesh.mFaces.end();++i)
 	{
 	{
 		// check whether all indices are in range
 		// check whether all indices are in range
-		if ((*i).mIndices[0] >= sMesh->mPositions.size())
-		{
-			DefaultLogger::get()->warn("Face index overflow in 3DS file (#1)");
-			(*i).mIndices[0] = (uint32_t)sMesh->mPositions.size()-1;
-		}
-		if ((*i).mIndices[1] >= sMesh->mPositions.size())
-		{
-			DefaultLogger::get()->warn("Face index overflow in 3DS file (#2)");
-			(*i).mIndices[1] = (uint32_t)sMesh->mPositions.size()-1;
-		}
-		if ((*i).mIndices[2] >= sMesh->mPositions.size())
+		for (unsigned int a = 0; a < 3;++a)
 		{
 		{
-			DefaultLogger::get()->warn("Face index overflow in 3DS file (#3)");
-			(*i).mIndices[2] = (uint32_t)sMesh->mPositions.size()-1;
+			if ((*i).mIndices[a] >= sMesh.mPositions.size())
+			{
+				DefaultLogger::get()->warn("3DS: Face index overflow)");
+				(*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
+			}
 		}
 		}
 	}
 	}
 	return;
 	return;
 }
 }
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh)
+void Dot3DSImporter::MakeUnique(Dot3DS::Mesh& sMesh)
 {
 {
 	unsigned int iBase = 0;
 	unsigned int iBase = 0;
 
 
 	std::vector<aiVector3D> vNew;
 	std::vector<aiVector3D> vNew;
 	std::vector<aiVector2D> vNew2;
 	std::vector<aiVector2D> vNew2;
 
 
-	vNew.resize(sMesh->mFaces.size() * 3);
-	if (sMesh->mTexCoords.size())vNew2.resize(sMesh->mFaces.size() * 3);
+	vNew.resize(sMesh.mFaces.size() * 3);
+	if (sMesh.mTexCoords.size())vNew2.resize(sMesh.mFaces.size() * 3);
 
 
-	for (unsigned int i = 0; i < sMesh->mFaces.size();++i)
+	for (unsigned int i = 0; i < sMesh.mFaces.size();++i)
 	{
 	{
 		uint32_t iTemp1,iTemp2;
 		uint32_t iTemp1,iTemp2;
 
 
 		// positions
 		// positions
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[2]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[2]];
 		iTemp1 = iBase++;
 		iTemp1 = iBase++;
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[1]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[1]];
 		iTemp2 = iBase++;
 		iTemp2 = iBase++;
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[0]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[0]];
 
 
 		// texture coordinates
 		// texture coordinates
-		if (sMesh->mTexCoords.size())
+		if (sMesh.mTexCoords.size())
 		{
 		{
-			vNew2[iTemp1]   = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[2]];
-			vNew2[iTemp2]   = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[1]];
-			vNew2[iBase]    = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[0]];
+			vNew2[iTemp1]   = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[2]];
+			vNew2[iTemp2]   = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[1]];
+			vNew2[iBase]    = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[0]];
 		}
 		}
 
 
-		sMesh->mFaces[i].mIndices[2] = iBase++;
-		sMesh->mFaces[i].mIndices[0] = iTemp1;
-		sMesh->mFaces[i].mIndices[1] = iTemp2;
+		sMesh.mFaces[i].mIndices[2] = iBase++;
+		sMesh.mFaces[i].mIndices[0] = iTemp1;
+		sMesh.mFaces[i].mIndices[1] = iTemp2;
 	}
 	}
-	sMesh->mPositions = vNew;
-	sMesh->mTexCoords = vNew2;
+	sMesh.mPositions = vNew;
+	sMesh.mTexCoords = vNew2;
 	return;
 	return;
 }
 }
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	MaterialHelper& mat)
 	MaterialHelper& mat)
@@ -201,14 +193,14 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	// NOTE: Pass the background image to the viewer by bypassing the
 	// NOTE: Pass the background image to the viewer by bypassing the
 	// material system. This is an evil hack, never do it  again!
 	// material system. This is an evil hack, never do it  again!
 	if (0 != this->mBackgroundImage.length() && this->bHasBG)
 	if (0 != this->mBackgroundImage.length() && this->bHasBG)
-		{
+	{
 		aiString tex;
 		aiString tex;
 		tex.Set( this->mBackgroundImage);
 		tex.Set( this->mBackgroundImage);
 		mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
 		mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
 
 
 		// be sure this is only done for the first material
 		// be sure this is only done for the first material
 		this->mBackgroundImage = std::string("");
 		this->mBackgroundImage = std::string("");
-		}
+	}
 
 
 	// At first add the base ambient color of the
 	// At first add the base ambient color of the
 	// scene to	the material
 	// scene to	the material
@@ -250,7 +242,7 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	// two sided rendering?
 	// two sided rendering?
 	if (oldMat.mTwoSided)
 	if (oldMat.mTwoSided)
 	{
 	{
-		int i = 0;
+		int i = 1;
 		mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
 		mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
 	}
 	}
 
 
@@ -268,8 +260,6 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 			eShading = aiShadingMode_Gouraud; break;
 			eShading = aiShadingMode_Gouraud; break;
 
 
 		// assume cook-torrance shading for metals.
 		// assume cook-torrance shading for metals.
-		// NOTE: I assume the real shader inside 3ds max is an anisotropic
-		// Phong-Blinn shader, but this is a good approximation too
 		case Dot3DS::Dot3DSFile::Phong :
 		case Dot3DS::Dot3DSFile::Phong :
 			eShading = aiShadingMode_Phong; break;
 			eShading = aiShadingMode_Phong; break;
 
 
@@ -409,20 +399,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 			a =  (*i).mFaceMaterials.begin();
 			a =  (*i).mFaceMaterials.begin();
 			a != (*i).mFaceMaterials.end();++a,++iNum)
 			a != (*i).mFaceMaterials.end();++a,++iNum)
 		{
 		{
-			// check range
-			// FIX: shouldn't be necessary anymore, has been moved to ReplaceDefaultMaterial()
-#if 0
-			if ((*a) >= this->mScene->mMaterials.size())
-			{
-				DefaultLogger::get()->error("3DS face material index is out of range");
-
-				// use the last material instead
-				// TODO: assign the default material index
-				aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
-			}
-			else
-#endif	
-		aiSplit[*a].push_back(iNum);
+			aiSplit[*a].push_back(iNum);
 		}
 		}
 		// now generate submeshes
 		// now generate submeshes
 		bool bFirst = true;
 		bool bFirst = true;
@@ -506,12 +483,9 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 	pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
 	pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
 	pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
 	pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
 	for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
 	for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
-	{
 		pcOut->mMeshes[a] = avOutMeshes[a];
 		pcOut->mMeshes[a] = avOutMeshes[a];
-	}
 
 
-	if (!iFaceCnt)
-		throw new ImportErrorException("No faces loaded. The mesh is empty");
+	if (!iFaceCnt)throw new ImportErrorException("No faces loaded. The mesh is empty");
 
 
 	// for each material in the scene we need to setup the UV source
 	// for each material in the scene we need to setup the UV source
 	// set for each texture
 	// set for each texture
@@ -519,6 +493,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 		TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
 		TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
 	return;
 	return;
 }
 }
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn)
 void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn)
 {
 {
@@ -533,11 +508,8 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 			ai_assert(NULL != pcMesh);
 			ai_assert(NULL != pcMesh);
 
 
 			// do case independent comparisons here, just for safety
 			// do case independent comparisons here, just for safety
-			if (pcIn->mName.length() == pcMesh->mName.length() &&
-				!ASSIMP_stricmp(pcIn->mName.c_str(),pcMesh->mName.c_str()))
-			{
+			if (!ASSIMP_stricmp(pcIn->mName,pcMesh->mName))
 				iArray.push_back(a);
 				iArray.push_back(a);
-			}
 		}
 		}
 		if (!iArray.empty())
 		if (!iArray.empty())
 		{
 		{
@@ -568,10 +540,11 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 						pvCurrent->y -= pivot.y;
 						pvCurrent->y -= pivot.y;
 						pvCurrent->z -= pivot.z;
 						pvCurrent->z -= pivot.z;
 						*pvCurrent = mTrafo * (*pvCurrent);
 						*pvCurrent = mTrafo * (*pvCurrent);
-						std::swap( pvCurrent->y, pvCurrent->z );
+						//std::swap( pvCurrent->y, pvCurrent->z );
 						++pvCurrent;
 						++pvCurrent;
 					}
 					}
 				}
 				}
+#if 0
 				else
 				else
 				{
 				{
 					while (pvCurrent != pvEnd)
 					while (pvCurrent != pvEnd)
@@ -580,6 +553,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 						++pvCurrent;
 						++pvCurrent;
 					}
 					}
 				}
 				}
+#endif
 				pcOut->mMeshes[i] = iIndex;
 				pcOut->mMeshes[i] = iIndex;
 			}
 			}
 		}
 		}
@@ -658,9 +632,7 @@ void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
 
 
 	// if the root node is a default node setup a name for it
 	// if the root node is a default node setup a name for it
 	if (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')
 	if (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')
-	{
 		pcOut->mRootNode->mName.Set("<root>");
 		pcOut->mRootNode->mName.Set("<root>");
-	}
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ConvertScene(aiScene* pcOut)
 void Dot3DSImporter::ConvertScene(aiScene* pcOut)

+ 13 - 45
code/3DSHelper.h

@@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
 
 
-/** @file Defines the helper data structures for importing 3DS files.
+/** @file Defines helper data structures for the import of 3DS files.
 http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
 http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
 
 
 #ifndef AI_3DSFILEHELPER_H_INC
 #ifndef AI_3DSFILEHELPER_H_INC
@@ -55,20 +55,18 @@ http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
 #include "../include/aiMaterial.h"
 #include "../include/aiMaterial.h"
 
 
 #include "SpatialSort.h"
 #include "SpatialSort.h"
+#include "SmoothingGroups.h"
 
 
 namespace Assimp	{
 namespace Assimp	{
 namespace Dot3DS	{
 namespace Dot3DS	{
 
 
 #include "./Compiler/pushpack1.h"
 #include "./Compiler/pushpack1.h"
 
 
-#ifdef _MSC_VER
-#	define sprintf sprintf_s
-#endif
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
 /** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
 *  and data structures.
 *  and data structures.
 */
 */
+// ---------------------------------------------------------------------------
 class Dot3DSFile
 class Dot3DSFile
 {
 {
 public:
 public:
@@ -91,32 +89,27 @@ public:
 	typedef enum
 	typedef enum
 	{
 	{
 		// translated to gouraud shading with wireframe active
 		// translated to gouraud shading with wireframe active
-		Wire = 0,
+		Wire = 0x0,
 
 
 		// if this material is set, no vertex normals will
 		// if this material is set, no vertex normals will
 		// be calculated for the model. Face normals + gouraud
 		// be calculated for the model. Face normals + gouraud
-		Flat = 1,
+		Flat = 0x1,
 
 
 		// standard gouraud shading
 		// standard gouraud shading
-		Gouraud = 2,
+		Gouraud = 0x2,
 
 
 		// phong shading
 		// phong shading
-		Phong = 3,
+		Phong = 0x3,
 
 
 		// cooktorrance or anistropic phong shading ...
 		// cooktorrance or anistropic phong shading ...
 		// the exact meaning is unknown, if you know it
 		// the exact meaning is unknown, if you know it
 		// feel free to tell me ;-)
 		// feel free to tell me ;-)
-		Metal = 4,
+		Metal = 0x4,
 
 
 		// required by the ASE loader
 		// required by the ASE loader
-		Blinn = 5
+		Blinn = 0x5
 	} shadetype3ds;
 	} shadetype3ds;
 
 
-	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-	// enum for all chunks in 3ds files. Unused
-	// ones are commented, list is not complete since
-	// there are many undocumented chunks
-	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	enum 
 	enum 
 	{
 	{
 
 
@@ -316,26 +309,10 @@ public:
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds mesh face */
 /** Helper structure representing a 3ds mesh face */
-struct Face
+struct Face : public FaceWithSmoothingGroup
 {
 {
-	Face() : iSmoothGroup(0), bFlipped(false)
-	{
-		// let the rest uninitialized for performance
-		this->mIndices[0] = 0;
-		this->mIndices[1] = 0;
-		this->mIndices[2] = 0;
-	}
-
-	
-	//! Indices. .3ds is using uint16. However, after
-	//! an unique vrtex set has been geneerated it might
-	//! be an index becomes > 2^16
-	uint32_t mIndices[3];
-
-	//! specifies to which smoothing group the face belongs to
-	uint32_t iSmoothGroup;
-
-	//! Specifies that the face normal must be flipped
+	//! Specifies that the face normal must be flipped.
+	//! todo: do we really need this?
 	bool bFlipped;
 	bool bFlipped;
 };
 };
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -442,7 +419,7 @@ struct Material
 };
 };
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure to represent a 3ds file mesh */
 /** Helper structure to represent a 3ds file mesh */
-struct Mesh
+struct Mesh : public MeshWithSmoothingGroups<Dot3DS::Face>
 {
 {
 	//! Default constructor
 	//! Default constructor
 	Mesh()
 	Mesh()
@@ -457,21 +434,12 @@ struct Mesh
 	//! Name of the mesh
 	//! Name of the mesh
 	std::string mName;
 	std::string mName;
 
 
-	//! Vertex positions
-	std::vector<aiVector3D> mPositions;
-
-	//! Face lists
-	std::vector<Face> mFaces;
-
 	//! Texture coordinates
 	//! Texture coordinates
 	std::vector<aiVector2D> mTexCoords;
 	std::vector<aiVector2D> mTexCoords;
 
 
 	//! Face materials
 	//! Face materials
 	std::vector<unsigned int> mFaceMaterials;
 	std::vector<unsigned int> mFaceMaterials;
 
 
-	//! Normal vectors
-	std::vector<aiVector3D> mNormals;
-
 	//! Local transformation matrix
 	//! Local transformation matrix
 	aiMatrix4x4 mMat;
 	aiMatrix4x4 mMat;
 };
 };

+ 93 - 221
code/3DSLoader.cpp

@@ -50,23 +50,48 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 // public ASSIMP headers
 // public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/assimp.hpp"
 #include "../include/assimp.hpp"
 
 
 // boost headers
 // boost headers
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
+		
+
+// begin a chunk: parse it, validate its length, get a pointer to its end
+#define ASSIMP_3DS_BEGIN_CHUNK() \
+	const Dot3DSFile::Chunk* psChunk; \
+	this->ReadChunk(&psChunk); \
+	const unsigned char* pcCur = this->mCurrent; \
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size \
+		- sizeof(Dot3DSFile::Chunk));
 
 
-#if (!defined ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG)
-#	define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG			\
-	"WARNING: Size of chunk data plus size of "			\
-	"subordinate chunks is larger than the size "		\
-	"specified in the top-level chunk header."			
+// process the end of a chunk and return if the end of the file is reached
+#define ASSIMP_3DS_END_CHUNK() \
+	this->mCurrent = pcCurNext; \
+	piRemaining -= psChunk->Size; \
+	if (0 >= piRemaining)return;
+
+
+// check whether the size of all subordinate chunks of a chunks is
+// not larger than the size of the chunk itself
+#ifdef _DEBUG
+#	define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \
+		"Size of chunk data plus size of subordinate chunks is " \
+		"larger than the size specified in the top-level chunk header."	
+
+#	define ASSIMP_3DS_VALIDATE_CHUNK_SIZE() \
+	if (pcCurNext < this->mCurrent) \
+	{ \
+		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); \
+		pcCurNext = this->mCurrent; \
+	}
+#else
+#	define ASSIMP_3DS_VALIDATE_CHUNK_SIZE()
 #endif
 #endif
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -92,7 +117,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 		return false;
 		return false;
 	std::string extension = pFile.substr( pos);
 	std::string extension = pFile.substr( pos);
 
 
-	// not brilliant but working ;-)
+	// not brillant but working ;-)
 	if( extension == ".3ds" || extension == ".3DS" || 
 	if( extension == ".3ds" || extension == ".3DS" || 
 		extension == ".3Ds" || extension == ".3dS")
 		extension == ".3Ds" || extension == ".3dS")
 		return true;
 		return true;
@@ -103,7 +128,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 // Setup configuration properties
 // Setup configuration properties
 void Dot3DSImporter::SetupProperties(const Importer* pImp)
 void Dot3DSImporter::SetupProperties(const Importer* pImp)
 {
 {
-	this->configSkipPivot = pImp->GetProperty(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false;
+	this->configSkipPivot = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 
@@ -151,11 +176,11 @@ void Dot3DSImporter::InternReadFile(
 		i != this->mScene->mMeshes.end();++i)
 		i != this->mScene->mMeshes.end();++i)
 	{
 	{
 		// TODO: see function body
 		// TODO: see function body
-		this->CheckIndices(&(*i));
-		this->MakeUnique(&(*i));
+		this->CheckIndices(*i);
+		this->MakeUnique(*i);
 
 
 		// first generate normals for the mesh
 		// first generate normals for the mesh
-		this->GenNormals(&(*i));
+		ComputeNormalsWithSmoothingsGroups<Dot3DS::Face>(*i);
 	}
 	}
 
 
 	// Apply scaling and offsets to all texture coordinates
 	// Apply scaling and offsets to all texture coordinates
@@ -179,11 +204,7 @@ void Dot3DSImporter::InternReadFile(
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ApplyMasterScale(aiScene* pScene)
 void Dot3DSImporter::ApplyMasterScale(aiScene* pScene)
 {
 {
-	// NOTE: Some invalid files have masterscale set to 0.0
-	if (0.0f == this->mMasterScale)
-		{
-		this->mMasterScale = 1.0f;
-		}
+	if (!this->mMasterScale)this->mMasterScale = 1.0f;
 	else this->mMasterScale = 1.0f / this->mMasterScale;
 	else this->mMasterScale = 1.0f / this->mMasterScale;
 
 
 	// construct an uniform scaling matrix and multiply with it
 	// construct an uniform scaling matrix and multiply with it
@@ -200,9 +221,8 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
 
 
 	// read chunk
 	// read chunk
 	if (this->mCurrent >= this->mLast)
 	if (this->mCurrent >= this->mLast)
-	{
 		throw new ImportErrorException("Unexpected end of file, can't read chunk header");
 		throw new ImportErrorException("Unexpected end of file, can't read chunk header");
-	}
+
 	const uintptr_t iDiff = this->mLast - this->mCurrent;
 	const uintptr_t iDiff = this->mLast - this->mCurrent;
 	if (iDiff < sizeof(Dot3DSFile::Chunk)) 
 	if (iDiff < sizeof(Dot3DSFile::Chunk)) 
 	{
 	{
@@ -211,23 +231,15 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
 	}
 	}
 	*p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent;
 	*p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent;
 	if ((**p_ppcOut).Size + this->mCurrent > this->mLast)
 	if ((**p_ppcOut).Size + this->mCurrent > this->mLast)
-	{
 		throw new ImportErrorException("Unexpected end of file, can't read chunk footer");
 		throw new ImportErrorException("Unexpected end of file, can't read chunk footer");
-	}
+
 	this->mCurrent += sizeof(Dot3DSFile::Chunk);
 	this->mCurrent += sizeof(Dot3DSFile::Chunk);
 	return;
 	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseMainChunk(int& piRemaining)
 void Dot3DSImporter::ParseMainChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	
-
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
-		- sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -237,27 +249,14 @@ void Dot3DSImporter::ParseMainChunk(int& piRemaining)
 		this->ParseEditorChunk(iRemaining);
 		this->ParseEditorChunk(iRemaining);
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseMainChunk(piRemaining);
 	return this->ParseMainChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
 void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -291,27 +290,14 @@ void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
 		}
 		}
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseEditorChunk(piRemaining);
 	return this->ParseEditorChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
 void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	const unsigned char* sz = this->mCurrent;
 	const unsigned char* sz = this->mCurrent;
 	unsigned int iCnt = 0;
 	unsigned int iCnt = 0;
@@ -384,17 +370,8 @@ void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
 		break;
 		break;
 
 
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseObjectChunk(piRemaining);
 	return this->ParseObjectChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -409,11 +386,7 @@ void Dot3DSImporter::SkipChunk()
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseChunk(int& piRemaining)
 void Dot3DSImporter::ParseChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -424,28 +397,14 @@ void Dot3DSImporter::ParseChunk(int& piRemaining)
 		this->ParseMeshChunk(iRemaining);
 		this->ParseMeshChunk(iRemaining);
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseChunk(piRemaining);
 	return this->ParseChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
 void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -456,18 +415,8 @@ void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
 		this->ParseHierarchyChunk(iRemaining);
 		this->ParseHierarchyChunk(iRemaining);
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseKeyframeChunk(piRemaining);
 	return this->ParseKeyframeChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -489,14 +438,7 @@ void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurr
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	
-
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
-		- sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	const unsigned char* sz = (unsigned char*)this->mCurrent;
 	const unsigned char* sz = (unsigned char*)this->mCurrent;
@@ -548,6 +490,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 
 
 		// pivot = origin of rotation and scaling
 		// pivot = origin of rotation and scaling
 		this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent);
 		this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent);
+		std::swap(this->mCurrentNode->vPivot.y,this->mCurrentNode->vPivot.z);
 		this->mCurrent += sizeof(aiVector3D);
 		this->mCurrent += sizeof(aiVector3D);
 		break;
 		break;
 
 
@@ -576,8 +519,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			this->mCurrent += sizeof(uint16_t);
 			this->mCurrent += sizeof(uint16_t);
 
 
-			aiVectorKey v;
-			v.mTime = (double)sNum;
+			aiVectorKey v;v.mTime = (double)sNum;
 
 
 			this->mCurrent += sizeof(uint32_t);
 			this->mCurrent += sizeof(uint32_t);
 			v.mValue =  *((const aiVector3D*)this->mCurrent);
 			v.mValue =  *((const aiVector3D*)this->mCurrent);
@@ -591,7 +533,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 			}
 			}
 			// add the new keyframe
 			// add the new keyframe
-			if (v.mTime != -10e10f)this->mCurrentNode->aPositionKeys.push_back(v);
+			if (v.mTime != -10e10f)
+				this->mCurrentNode->aPositionKeys.push_back(v);
 		}
 		}
 		break;
 		break;
 
 
@@ -618,9 +561,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			this->mCurrent += sizeof(uint16_t);
 			this->mCurrent += sizeof(uint16_t);
 
 
-			aiQuatKey v;
-			v.mTime = (double)sNum;
-
+			aiQuatKey v;v.mTime = (double)sNum;
 			this->mCurrent += sizeof(uint32_t);
 			this->mCurrent += sizeof(uint32_t);
 
 
 			float fRadians = *((const float*)this->mCurrent);
 			float fRadians = *((const float*)this->mCurrent);
@@ -640,7 +581,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 			}
 			}
 			// add the new keyframe
 			// add the new keyframe
-			if (v.mTime != -10e10f)this->mCurrentNode->aRotationKeys.push_back(v);
+			if (v.mTime != -10e10f)
+				this->mCurrentNode->aRotationKeys.push_back(v);
 		}
 		}
 		break;
 		break;
 
 
@@ -698,31 +640,16 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 		break;
 		break;
 #endif
 #endif
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next top-level chunk
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return this->ParseHierarchyChunk(piRemaining);
 	return this->ParseHierarchyChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
 void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
+	ASSIMP_3DS_BEGIN_CHUNK();
 	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
 	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
 
 
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
-
 	// get chunk type
 	// get chunk type
 	const unsigned char* sz = this->mCurrent;
 	const unsigned char* sz = this->mCurrent;
 	uint32_t iCnt = 0,iTemp;
 	uint32_t iCnt = 0,iTemp;
@@ -796,31 +723,16 @@ void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
 
 
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next chunk on this level
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return ParseFaceChunk(piRemaining);
 	return ParseFaceChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
+	ASSIMP_3DS_BEGIN_CHUNK();
 	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
 	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
 
 
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
-
 	// get chunk type
 	// get chunk type
 	const unsigned char* sz = this->mCurrent;
 	const unsigned char* sz = this->mCurrent;
 	unsigned int iCnt = 0;
 	unsigned int iCnt = 0;
@@ -837,7 +749,7 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 		{
 		{
 			mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent));
 			mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent));
 			aiVector3D& v = mMesh.mPositions.back();
 			aiVector3D& v = mMesh.mPositions.back();
-			//std::swap( v.y, v.z);
+			std::swap( v.y, v.z);
 			//v.y *= -1.0f;
 			//v.y *= -1.0f;
 			this->mCurrent += sizeof(aiVector3D);
 			this->mCurrent += sizeof(aiVector3D);
 		}
 		}
@@ -924,28 +836,14 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 		break;
 		break;
 
 
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next chunk on this level
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return ParseMeshChunk(piRemaining);
 	return ParseMeshChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	const unsigned char* sz = this->mCurrent;
 	const unsigned char* sz = this->mCurrent;
@@ -958,9 +856,8 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 	case Dot3DSFile::CHUNK_MAT_MATNAME:
 	case Dot3DSFile::CHUNK_MAT_MATNAME:
 
 
 		// string in file is zero-terminated, 
 		// string in file is zero-terminated, 
-		// this should be no problem. However, validate whether
-		// it overlaps the end of the chunk, if yes we should
-		// truncate it.
+		// this should be no problem. However, validate whether it overlaps 
+		// the end of the chunk, if yes we should truncate it.
 		while (*sz++ != '\0')
 		while (*sz++ != '\0')
 		{
 		{
 			if (sz > pcCurNext-1)
 			if (sz > pcCurNext-1)
@@ -1017,8 +914,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		pcf = &this->mScene->mMaterials.back().mTransparency;
 		pcf = &this->mScene->mMaterials.back().mTransparency;
 		*pcf = this->ParsePercentageChunk();
 		*pcf = this->ParsePercentageChunk();
 		// NOTE: transparency, not opacity
 		// NOTE: transparency, not opacity
-		if (is_qnan(*pcf))
-			*pcf = 1.0f;
+		if (is_qnan(*pcf))*pcf = 1.0f;
 		else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
 		else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
 		break;
 		break;
 
 
@@ -1037,16 +933,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 	case Dot3DSFile::CHUNK_MAT_SHININESS:
 	case Dot3DSFile::CHUNK_MAT_SHININESS:
 		pcf = &this->mScene->mMaterials.back().mSpecularExponent;
 		pcf = &this->mScene->mMaterials.back().mSpecularExponent;
 		*pcf = this->ParsePercentageChunk();
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf *= (float)0xFFFF;
 		else *pcf *= (float)0xFFFF;
 		break;
 		break;
 
 
 	case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT:
 	case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT:
 		pcf = &this->mScene->mMaterials.back().mShininessStrength;
 		pcf = &this->mScene->mMaterials.back().mShininessStrength;
 		*pcf = this->ParsePercentageChunk();
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf *= (float)0xffff / 100.0f;
 		else *pcf *= (float)0xffff / 100.0f;
 		break;
 		break;
 
 
@@ -1054,8 +948,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		// TODO: need to multiply with emissive base color?
 		// TODO: need to multiply with emissive base color?
 		pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend;
 		pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend;
 		*pcf = this->ParsePercentageChunk();
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf = *pcf * (float)0xFFFF / 100.0f;
 		else *pcf = *pcf * (float)0xFFFF / 100.0f;
 		break;
 		break;
 
 
@@ -1085,31 +978,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive);
 		this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive);
 		break;
 		break;
 	};
 	};
-	if (pcCurNext < this->mCurrent)
-	{
-		// place an error message. If we crash the programmer
-		// will be able to find it
-		DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG);
-		pcCurNext = this->mCurrent;
-	}
-	// Go to the starting position of the next chunk on this level
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return ParseMaterialChunk(piRemaining);
 	return ParseMaterialChunk(piRemaining);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
 void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
 {
 {
-	const Dot3DSFile::Chunk* psChunk;
-
-	this->ReadChunk(&psChunk);
-	
-
-	const unsigned char* pcCur = this->mCurrent;
-	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
-		- sizeof(Dot3DSFile::Chunk));
+	ASSIMP_3DS_BEGIN_CHUNK();
 
 
 	// get chunk type
 	// get chunk type
 	const unsigned char* sz = this->mCurrent;
 	const unsigned char* sz = this->mCurrent;
@@ -1184,11 +1060,8 @@ void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
 		break;
 		break;
 	};
 	};
 
 
-	// Go to the starting position of the next chunk on this level
-	this->mCurrent = pcCurNext;
-
-	piRemaining -= psChunk->Size;
-	if (0 >= piRemaining)return;
+	ASSIMP_3DS_VALIDATE_CHUNK_SIZE();
+	ASSIMP_3DS_END_CHUNK();
 	return ParseTextureChunk(piRemaining,pcOut);
 	return ParseTextureChunk(piRemaining,pcOut);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -1226,20 +1099,22 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 
 
 	const Dot3DSFile::Chunk* psChunk;
 	const Dot3DSFile::Chunk* psChunk;
 	this->ReadChunk(&psChunk);
 	this->ReadChunk(&psChunk);
-	if (NULL == psChunk)
+	if (!psChunk)
 	{
 	{
 		*p_pcOut = clrError;
 		*p_pcOut = clrError;
 		return;
 		return;
 	}
 	}
+	const unsigned int diff = psChunk->Size - sizeof(Dot3DSFile::Chunk);
+
 	const unsigned char* pcCur = this->mCurrent;
 	const unsigned char* pcCur = this->mCurrent;
-	this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk);
+	this->mCurrent += diff;
 	bool bGamma = false;
 	bool bGamma = false;
 	switch(psChunk->Flag)
 	switch(psChunk->Flag)
 	{
 	{
 	case Dot3DSFile::CHUNK_LINRGBF:
 	case Dot3DSFile::CHUNK_LINRGBF:
 		bGamma = true;
 		bGamma = true;
 	case Dot3DSFile::CHUNK_RGBF:
 	case Dot3DSFile::CHUNK_RGBF:
-		if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (sizeof(float) * 3 > diff)
 		{
 		{
 			*p_pcOut = clrError;
 			*p_pcOut = clrError;
 			return;
 			return;
@@ -1252,7 +1127,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 	case Dot3DSFile::CHUNK_LINRGBB:
 	case Dot3DSFile::CHUNK_LINRGBB:
 		bGamma = true;
 		bGamma = true;
 	case Dot3DSFile::CHUNK_RGBB:
 	case Dot3DSFile::CHUNK_RGBB:
-		if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (sizeof(char) * 3 > diff)
 		{
 		{
 			*p_pcOut = clrError;
 			*p_pcOut = clrError;
 			return;
 			return;
@@ -1265,7 +1140,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 	// percentage chunks: accepted to be compatible with various
 	// percentage chunks: accepted to be compatible with various
 	// .3ds files with very curious content
 	// .3ds files with very curious content
 	case Dot3DSFile::CHUNK_PERCENTF:
 	case Dot3DSFile::CHUNK_PERCENTF:
-		if (p_bAcceptPercent && 4 <= psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (p_bAcceptPercent && 4 <= diff)
 		{
 		{
 			p_pcOut->r = *((float*)pcCur);
 			p_pcOut->r = *((float*)pcCur);
 			p_pcOut->g = *((float*)pcCur);
 			p_pcOut->g = *((float*)pcCur);
@@ -1275,7 +1150,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 		*p_pcOut = clrError;
 		*p_pcOut = clrError;
 		return;
 		return;
 	case Dot3DSFile::CHUNK_PERCENTW:
 	case Dot3DSFile::CHUNK_PERCENTW:
-		if (p_bAcceptPercent && 1 <= psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (p_bAcceptPercent && 1 <= diff)
 		{
 		{
 			p_pcOut->r = (float)pcCur[0] / 255.0f;
 			p_pcOut->r = (float)pcCur[0] / 255.0f;
 			p_pcOut->g = (float)pcCur[0] / 255.0f;
 			p_pcOut->g = (float)pcCur[0] / 255.0f;
@@ -1289,9 +1164,6 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 		// skip unknown chunks, hope this won't cause any problems.
 		// skip unknown chunks, hope this won't cause any problems.
 		return this->ParseColorChunk(p_pcOut,p_bAcceptPercent);
 		return this->ParseColorChunk(p_pcOut,p_bAcceptPercent);
 	};
 	};
-	// assume input gamma = 1.0, output gamma = 2.2 
-	// Not sure whether this is correct, too tired to 
-	// think about it ;-)
 	if (bGamma)
 	if (bGamma)
 	{
 	{
 		p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f);
 		p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f);

+ 2 - 7
code/3DSLoader.h

@@ -206,15 +206,10 @@ protected:
 	*/
 	*/
 	void ConvertScene(aiScene* pcOut);
 	void ConvertScene(aiScene* pcOut);
 
 
-	// -------------------------------------------------------------------
-	/** generate normal vectors for a given mesh
-	*/
-	void GenNormals(Dot3DS::Mesh* sMesh);
-
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** generate unique vertices for a mesh
 	/** generate unique vertices for a mesh
 	*/
 	*/
-	void MakeUnique(Dot3DS::Mesh* sMesh);
+	void MakeUnique(Dot3DS::Mesh& sMesh);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Add a node to the node graph
 	/** Add a node to the node graph
@@ -235,7 +230,7 @@ protected:
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Clamp all indices in the file to a valid range
 	/** Clamp all indices in the file to a valid range
 	*/
 	*/
-	void CheckIndices(Dot3DS::Mesh* sMesh);
+	void CheckIndices(Dot3DS::Mesh& sMesh);
 
 
 
 
 protected:
 protected:

+ 2 - 73
code/ASELoader.cpp

@@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 // internal headers
 // internal headers
 #include "ASELoader.h"
 #include "ASELoader.h"
-#include "3DSSpatialSort.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 #include "StringComparison.h"
 #include "TextureTransform.h"
 #include "TextureTransform.h"
@@ -142,6 +141,7 @@ void ASEImporter::InternReadFile(
 		if ((*i).bSkip)continue;
 		if ((*i).bSkip)continue;
 
 
 		this->TransformVertices(*i);
 		this->TransformVertices(*i);
+
 		// now we need to create proper meshes from the import we need to 
 		// now we need to create proper meshes from the import we need to 
 		// split them by materials, build valid vertex/face lists ...
 		// split them by materials, build valid vertex/face lists ...
 		this->BuildUniqueRepresentation(*i);
 		this->BuildUniqueRepresentation(*i);
@@ -1135,9 +1135,7 @@ void ASEImporter::BuildMaterialIndices()
 	}
 	}
 	// prepare for the next step
 	// prepare for the next step
 	for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans)
 	for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans)
-	{
 		TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]);
 		TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]);
-	}
 
 
 	// now we need to iterate through all meshes,
 	// now we need to iterate through all meshes,
 	// generating correct texture coordinates and material uv indices
 	// generating correct texture coordinates and material uv indices
@@ -1182,75 +1180,6 @@ void ASEImporter::GenerateNormals(ASE::Mesh& mesh)
 		}
 		}
 	}
 	}
 	if (mesh.mNormals.empty())
 	if (mesh.mNormals.empty())
-	{
-		// need to calculate normals ... 
-		// TODO: Find a way to merge this with the code in 3DSGenNormals.cpp
-		mesh.mNormals.resize(mesh.mPositions.size(),aiVector3D());
-		for( unsigned int a = 0; a < mesh.mFaces.size(); a++)
-		{
-			const ASE::Face& face = mesh.mFaces[a];
-
-			// assume it is a triangle
-			aiVector3D* pV1 = &mesh.mPositions[face.mIndices[2]];
-			aiVector3D* pV2 = &mesh.mPositions[face.mIndices[1]];
-			aiVector3D* pV3 = &mesh.mPositions[face.mIndices[0]];
-
-			aiVector3D pDelta1 = *pV2 - *pV1;
-			aiVector3D pDelta2 = *pV3 - *pV1;
-			aiVector3D vNor = pDelta1 ^ pDelta2;
-
-			for (unsigned int i = 0; i < 3;++i)
-				mesh.mNormals[face.mIndices[i]] = vNor;
-		}
-
-		// calculate the position bounds so we have a reliable epsilon to 
-		// check position differences against 
-		// @Schrompf: This is the 7th time this snippet is repeated!
-		aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
-		for( unsigned int a = 0; a < mesh.mPositions.size(); a++)
-		{
-			minVec.x = std::min( minVec.x, mesh.mPositions[a].x);
-			minVec.y = std::min( minVec.y, mesh.mPositions[a].y);
-			minVec.z = std::min( minVec.z, mesh.mPositions[a].z);
-			maxVec.x = std::max( maxVec.x, mesh.mPositions[a].x);
-			maxVec.y = std::max( maxVec.y, mesh.mPositions[a].y);
-			maxVec.z = std::max( maxVec.z, mesh.mPositions[a].z);
-		}
-		const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
-
-		std::vector<aiVector3D> avNormals;
-		avNormals.resize(mesh.mNormals.size());
-
-		// now generate the spatial sort tree
-		D3DSSpatialSorter sSort;
-		for( std::vector<ASE::Face>::iterator
-			i =  mesh.mFaces.begin();
-			i != mesh.mFaces.end();++i){sSort.AddFace(&(*i),mesh.mPositions);}
-		sSort.Prepare();
-
-		for( std::vector<ASE::Face>::iterator
-			i =  mesh.mFaces.begin();
-			i != mesh.mFaces.end();++i)
-		{
-			std::vector<unsigned int> poResult;
-			for (unsigned int c = 0; c < 3;++c)
-			{
-				sSort.FindPositions(mesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
-					posEpsilon,poResult);
-
-				aiVector3D vNormals;
-				for (std::vector<unsigned int>::const_iterator
-					a =  poResult.begin();
-					a != poResult.end();++a)
-				{
-					vNormals += mesh.mNormals[(*a)];
-				}
-				vNormals.Normalize();
-				avNormals[(*i).mIndices[c]] = vNormals;
-				poResult.clear();
-			}
-		}
-		mesh.mNormals = avNormals;
-	}
+		ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh);
 	return;
 	return;
 }
 }

+ 6 - 57
code/ASEParser.cpp

@@ -64,7 +64,7 @@ using namespace Assimp::ASE;
 #define BLUBB(_message_) \
 #define BLUBB(_message_) \
 	{this->LogError(_message_);return;}
 	{this->LogError(_message_);return;}
 
 
-
+// ------------------------------------------------------------------------------------------------
 #define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \
 #define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('}' == *this->m_szFile) \
 	else if ('}' == *this->m_szFile) \
@@ -87,6 +87,7 @@ using namespace Assimp::ASE;
 	} else bLastWasEndLine = false; \
 	} else bLastWasEndLine = false; \
 	++this->m_szFile; 
 	++this->m_szFile; 
 
 
+// ------------------------------------------------------------------------------------------------
 #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
 #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('}' == *this->m_szFile) \
 	else if ('}' == *this->m_szFile) \
@@ -1723,62 +1724,29 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
 	ai_assert(NULL != apOut);
 	ai_assert(NULL != apOut);
 
 
 	for (unsigned int i = 0; i < 3;++i)
 	for (unsigned int i = 0; i < 3;++i)
-	{
-		// skip spaces and tabs
-		if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-		{
-			// LOG 
-			this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]");
-			++this->iLineNumber;
-			apOut[0] = apOut[1] = apOut[2] = 0;
-			return;
-		}
-		apOut[i] = strtol10(this->m_szFile,&this->m_szFile);
-	}
+		ParseLV4MeshLong(apOut[i]);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
 void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
 {
 {
 	ai_assert(NULL != apOut);
 	ai_assert(NULL != apOut);
 
 
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse indexed long triple: unexpected EOL [#4]");
-		rIndexOut = 0;
-		apOut[0] = apOut[1] = apOut[2] = 0;
-		++this->iLineNumber;
-		return;
-	}
 	// parse the index
 	// parse the index
-	rIndexOut = strtol10(this->m_szFile,&this->m_szFile);
+	ParseLV4MeshLong(rIndexOut);
 
 
 	// parse the three others
 	// parse the three others
 	this->ParseLV4MeshLongTriple(apOut);
 	this->ParseLV4MeshLongTriple(apOut);
-	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
 void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
 {
 {
 	ai_assert(NULL != apOut);
 	ai_assert(NULL != apOut);
 
 
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse indexed float triple: unexpected EOL [#1]");
-		rIndexOut = 0;
-		apOut[0] = apOut[1] = apOut[2] = 0.0f;
-		++this->iLineNumber;
-		return;
-	}
 	// parse the index
 	// parse the index
-	rIndexOut = strtol10(this->m_szFile,&this->m_szFile);
+	ParseLV4MeshLong(rIndexOut);
 	
 	
 	// parse the three others
 	// parse the three others
 	this->ParseLV4MeshFloatTriple(apOut);
 	this->ParseLV4MeshFloatTriple(apOut);
-	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloatTriple(float* apOut)
 void Parser::ParseLV4MeshFloatTriple(float* apOut)
@@ -1786,20 +1754,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut)
 	ai_assert(NULL != apOut);
 	ai_assert(NULL != apOut);
 
 
 	for (unsigned int i = 0; i < 3;++i)
 	for (unsigned int i = 0; i < 3;++i)
-	{
-		// skip spaces and tabs
-		if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-		{
-			// LOG 
-			this->LogWarning("Unable to parse float triple: unexpected EOL [#5]");
-			apOut[0] = apOut[1] = apOut[2] = 0.0f;
-			++this->iLineNumber;
-			return;
-		}
-		// parse the float
-		this->m_szFile = fast_atof_move(this->m_szFile,apOut[i]);
-	}
-	return;
+		ParseLV4MeshFloat(apOut[i]);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloat(float& fOut)
 void Parser::ParseLV4MeshFloat(float& fOut)
@@ -1815,9 +1770,6 @@ void Parser::ParseLV4MeshFloat(float& fOut)
 	}
 	}
 	// parse the first float
 	// parse the first float
 	this->m_szFile = fast_atof_move(this->m_szFile,fOut);
 	this->m_szFile = fast_atof_move(this->m_szFile,fOut);
-	// go to the next valid sequence
-	//this->SkipToNextToken();
-	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshLong(unsigned int& iOut)
 void Parser::ParseLV4MeshLong(unsigned int& iOut)
@@ -1833,7 +1785,4 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut)
 	}
 	}
 	// parse the value
 	// parse the value
 	iOut = strtol10(this->m_szFile,&this->m_szFile);
 	iOut = strtol10(this->m_szFile,&this->m_szFile);
-	// go to the next valid sequence
-	//this->SkipToNextToken();
-	return;
 }
 }

+ 4 - 13
code/ASEParser.h

@@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiAnim.h"
 #include "../include/aiAnim.h"
 
 
 // for some helper routines like IsSpace()
 // for some helper routines like IsSpace()
-#include "PlyParser.h"
+#include "ParsingUtils.h"
 #include "qnan.h"
 #include "qnan.h"
 
 
 // ASE is quite similar to 3ds. We can reuse some structures
 // ASE is quite similar to 3ds. We can reuse some structures
@@ -87,7 +87,7 @@ struct Material : public Dot3DS::Material
 };
 };
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file face */
 /** Helper structure to represent an ASE file face */
-struct Face : public Dot3DS::Face
+struct Face : public FaceWithSmoothingGroup
 {
 {
 	//! Default constructor. Initializes everything with 0
 	//! Default constructor. Initializes everything with 0
 	Face()
 	Face()
@@ -224,13 +224,13 @@ struct DecompTransform
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file mesh */
 /** Helper structure to represent an ASE file mesh */
-struct Mesh
+struct Mesh : public MeshWithSmoothingGroups<ASE::Face>
 {
 {
 	//! Constructor. Creates a default name for the mesh
 	//! Constructor. Creates a default name for the mesh
 	Mesh() : bSkip(false)
 	Mesh() : bSkip(false)
 	{
 	{
 		static int iCnt = 0;
 		static int iCnt = 0;
-		char szTemp[128];
+		char szTemp[128]; // should be sufficiently large
 		::sprintf(szTemp,"UNNAMED_%i",iCnt++);
 		::sprintf(szTemp,"UNNAMED_%i",iCnt++);
 		mName = szTemp;
 		mName = szTemp;
 
 
@@ -249,21 +249,12 @@ struct Mesh
 	//! "" if there is no parent ...
 	//! "" if there is no parent ...
 	std::string mParent;
 	std::string mParent;
 
 
-	//! vertex positions
-	std::vector<aiVector3D> mPositions;
-
-	//! List of all faces loaded
-	std::vector<ASE::Face> mFaces;
-
 	//! List of all texture coordinate sets
 	//! List of all texture coordinate sets
 	std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 	std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 
 
 	//! List of all vertex color sets.
 	//! List of all vertex color sets.
 	std::vector<aiColor4D> mVertexColors;
 	std::vector<aiColor4D> mVertexColors;
 
 
-	//! List of normal vectors
-	std::vector<aiVector3D> mNormals;
-
 	//! List of all bone vertices
 	//! List of all bone vertices
 	std::vector<BoneVertex> mBoneVertices;
 	std::vector<BoneVertex> mBoneVertices;
 
 

+ 167 - 6
code/Assimp.cpp

@@ -40,14 +40,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 /** @file Implementation of the Plain-C API */
 /** @file Implementation of the Plain-C API */
 
 
-// CRT headers
-#include <map>
 
 
 // public ASSIMP headers
 // public ASSIMP headers
 #include "../include/assimp.h"
 #include "../include/assimp.h"
+#include "../include/aiFileIO.h"
 #include "../include/assimp.hpp"
 #include "../include/assimp.hpp"
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+
+#include "GenericProperty.h"
 
 
 // boost headers
 // boost headers
 #define AI_C_THREADSAFE
 #define AI_C_THREADSAFE
@@ -67,19 +70,158 @@ static ImporterMap gActiveImports;
 /** Error message of the last failed import process */
 /** Error message of the last failed import process */
 static std::string gLastErrorString;
 static std::string gLastErrorString;
 
 
+/** Configuration properties */
+static Importer::IntPropertyMap			gIntProperties;
+static Importer::FloatPropertyMap		gFloatProperties;
+static Importer::StringPropertyMap		gStringProperties;
+
 #if (defined AI_C_THREADSAFE)
 #if (defined AI_C_THREADSAFE)
 /** Global mutex to manage the access to the importer map */
 /** Global mutex to manage the access to the importer map */
 static boost::mutex gMutex;
 static boost::mutex gMutex;
 #endif
 #endif
 
 
+class CIOSystemWrapper;
+class CIOStreamWrapper;
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOStreamWrapper : public IOStream
+{
+	friend class CIOSystemWrapper;
+public:
+
+	CIOStreamWrapper(aiFile* pFile)
+		: mFile(pFile)
+	{}
+
+	// -------------------------------------------------------------------
+    size_t Read(void* pvBuffer, 
+		size_t pSize, 
+		size_t pCount)
+	{
+		// need to typecast here as C has no void*
+		return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount);
+	}
+
+
+	// -------------------------------------------------------------------
+   size_t Write(const void* pvBuffer, 
+		size_t pSize,
+		size_t pCount)
+   {
+	   // need to typecast here as C has no void*
+	   return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount);
+   }
+
+
+	// -------------------------------------------------------------------
+	aiReturn Seek(size_t pOffset,
+		aiOrigin pOrigin)
+	{
+		return mFile->SeekProc(mFile,pOffset,pOrigin);
+	}
+
+
+	// -------------------------------------------------------------------
+    size_t Tell(void) const
+	{
+		return mFile->TellProc(mFile);
+	}
+
+
+	// -------------------------------------------------------------------
+	size_t	FileSize() const
+	{
+		return mFile->FileSizeProc(mFile);
+	}
+
+private:
+	aiFile* mFile;
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOSystemWrapper : public IOSystem
+{
+public:
+
+	CIOSystemWrapper(aiFileIO* pFile)
+		: mFileSystem(pFile)
+	{}
+
+	// -------------------------------------------------------------------
+	bool Exists( const std::string& pFile) const
+	{
+		CIOSystemWrapper* pip = const_cast<CIOSystemWrapper*>(this);
+		IOStream* p = pip->Open(pFile);
+		if (p){pip->Close(p);return true;}
+		return false;
+	}
+
+	// -------------------------------------------------------------------
+	std::string getOsSeparator() const
+	{
+		return "/";
+	}
+
+	// -------------------------------------------------------------------
+	IOStream* Open(const std::string& pFile,
+		const std::string& pMode = std::string("rb"))
+	{
+		aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile.c_str(),pMode.c_str());
+		if (!p)return NULL;
+		return new CIOStreamWrapper(p);
+	}
+
+	// -------------------------------------------------------------------
+	void Close( IOStream* pFile)
+	{
+		if (!pFile)return;
+		mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile);
+		delete pFile;
+	}
+
+private:
+
+	aiFileIO* mFileSystem;
+};
+
+// ------------------------------------------------------------------------------------------------
+void ReportSceneNotFoundError()
+{
+	DefaultLogger::get()->error("Unable to find the Importer instance for this scene. "
+		"Are you sure it has been created by aiImportFile(ex)(...)?");
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads the given file and returns its content. 
 // Reads the given file and returns its content. 
 const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
 const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
+{
+	return aiImportFileEx(pFile,pFlags,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, 
+	aiFileIO* pFS)
 {
 {
 	ai_assert(NULL != pFile);
 	ai_assert(NULL != pFile);
 
 
 	// create an Importer for this file
 	// create an Importer for this file
 	Assimp::Importer* imp = new Assimp::Importer;
 	Assimp::Importer* imp = new Assimp::Importer;
+
+	// copy the global property lists to the Importer instance
+	// (we are a friend of Importer)
+	imp->mIntProperties		= gIntProperties;
+	imp->mFloatProperties	= gFloatProperties;
+	imp->mStringProperties	= gStringProperties;
+
+	// setup a custom IO system if necessary
+	if (pFS)
+	{
+		imp->SetIOHandler( new CIOSystemWrapper (pFS) );
+	}
+
 	// and have it read the file
 	// and have it read the file
 	const aiScene* scene = imp->ReadFile( pFile, pFlags);
 	const aiScene* scene = imp->ReadFile( pFile, pFlags);
 
 
@@ -118,8 +260,7 @@ void aiReleaseImport( const aiScene* pScene)
 	// it should be there... else the user is playing fools with us
 	// it should be there... else the user is playing fools with us
 	if( it == gActiveImports.end())
 	if( it == gActiveImports.end())
 	{
 	{
-		DefaultLogger::get()->error("Unable to find the Importer instance for this scene. "
-			"Are you sure it has been created by aiImportFile(ex)(...)?");
+		ReportSceneNotFoundError();
 		return;
 		return;
 	}
 	}
 
 
@@ -196,11 +337,31 @@ void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
 	// it should be there... else the user is playing fools with us
 	// it should be there... else the user is playing fools with us
 	if( it == gActiveImports.end())
 	if( it == gActiveImports.end())
 	{
 	{
-		DefaultLogger::get()->error("Unable to find the Importer instance for this scene. "
-			"Are you sure it has been created by aiImportFile(ex)(...)?");
+		ReportSceneNotFoundError();
 		return;
 		return;
 	}
 	}
 	// get memory statistics
 	// get memory statistics
 	it->second->GetMemoryRequirements(*in);
 	it->second->GetMemoryRequirements(*in);
 }
 }
 
 
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value)
+{
+	SetGenericProperty<int>(gIntProperties,szName,value,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value)
+{
+	SetGenericProperty<float>(gFloatProperties,szName,value,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiSetImportPropertyString(const char* szName,
+	const C_STRUCT aiString* st)
+{
+	if (!st)return;
+
+	SetGenericProperty<std::string>(gStringProperties,szName,
+		std::string( st->data ),NULL);
+}

+ 1 - 1
code/CalcTangentsProcess.cpp

@@ -86,7 +86,7 @@ bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
 void CalcTangentsProcess::SetupProperties(const Importer* pImp)
 void CalcTangentsProcess::SetupProperties(const Importer* pImp)
 {
 {
 	// get the current value of the property
 	// get the current value of the property
-	this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45000) / 1000.0f;
+	this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
 	this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
 	this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
 	this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
 	this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
 }
 }

+ 100 - 0
code/DXFLoader.cpp

@@ -0,0 +1,100 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the DXF importer class */
+#include "DXFLoader.h"
+
+// public ASSIMP headers
+#include "../include/aiScene.h"
+#include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+
+#include "../include/DefaultLogger.h"
+
+// boost headers
+#include <boost/scoped_ptr.hpp>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DXFImporter::DXFImporter()
+{
+		// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+DXFImporter::~DXFImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool DXFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
+{
+	// simple check of file extension is enough for the moment
+	std::string::size_type pos = pFile.find_last_of('.');
+	// no file extension - can't read
+	if( pos == std::string::npos)
+		return false;
+	std::string extension = pFile.substr( pos);
+
+	return !(extension.length() != 4 || extension[0] != '.' ||
+			 extension[1] != 'd' && extension[1] != 'D' ||
+			 extension[2] != 'x' && extension[2] != 'X' ||
+			 extension[3] != 'f' && extension[3] != 'F');
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void DXFImporter::InternReadFile( const std::string& pFile, 
+	aiScene* pScene, IOSystem* pIOHandler)
+{
+	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+	// Check whether we can read from the file
+	if( file.get() == NULL)
+		throw new ImportErrorException( "Failed to open DXF file " + pFile + "");
+
+	throw new ImportErrorException("DXF: not yet implemented");
+}

+ 93 - 0
code/DXFLoader.h

@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Declaration of the .dxf importer class. */
+#ifndef AI_DXFLOADER_H_INCLUDED
+#define AI_DXFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+namespace Assimp	{
+
+// ---------------------------------------------------------------------------
+/** DXF importer class
+*/
+class DXFImporter : public BaseImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	DXFImporter();
+
+	/** Destructor, private as well */
+	~DXFImporter();
+
+public:
+
+	// -------------------------------------------------------------------
+	/** Returns whether the class can handle the format of the given file. 
+	* See BaseImporter::CanRead() for details.	*/
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
+
+protected:
+
+	// -------------------------------------------------------------------
+	/** Called by Importer::GetExtensionList() for each loaded importer.
+	 * See BaseImporter::GetExtensionList() for details
+	 */
+	void GetExtensionList(std::string& append)
+	{
+		append.append("*.dxf");
+	}
+
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. 
+	* See BaseImporter::InternReadFile() for details
+	*/
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 2 - 3
code/GenVertexNormalsProcess.cpp

@@ -76,7 +76,7 @@ bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
 void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
 void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
 {
 {
 	// get the current value of the property
 	// get the current value of the property
-	this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180000) / 1000.0f;
+	this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180.f);
 	this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
 	this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f);
 	this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
 	this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
 }
 }
@@ -145,8 +145,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
 	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
 	std::vector<unsigned int> verticesFound;
 	std::vector<unsigned int> verticesFound;
 
 
-	const float fLimit = (AI_MESH_SMOOTHING_ANGLE_NOT_SET == pMesh->mMaxSmoothingAngle
-		? this->configMaxAngle : pMesh->mMaxSmoothingAngle);
+	const float fLimit = this->configMaxAngle; 
 
 
 	aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
 	aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
 	for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
 	for (unsigned int i = 0; i < pMesh->mNumVertices;++i)

+ 88 - 0
code/GenericProperty.h

@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_GENERIC_PROPERTY_H_INCLUDED
+#define AI_GENERIC_PROPERTY_H_INCLUDED
+
+#include "./../include/assimp.hpp"
+#include "Hash.h"
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void SetGenericProperty(std::map< uint32_t, T >& list, 
+	const char* szName, const T& value, bool* bWasExisting)
+{
+	ai_assert(NULL != szName);
+
+	typedef std::map< uint32_t, T >  GenericPropertyMap;
+	typedef std::pair< uint32_t, T > GenericPair;
+
+	uint32_t hash = SuperFastHash(szName);
+
+	GenericPropertyMap::iterator it = list.find(hash);
+	if (it == list.end())
+	{
+		if (bWasExisting)*bWasExisting = false;
+		list.insert(GenericPair( hash, value ));
+		return;
+	}
+	(*it).second = value;
+	if (bWasExisting)*bWasExisting = true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline T GetGenericProperty(const std::map< uint32_t, T >& list, 
+	const char* szName, const T& errorReturn)
+{
+	ai_assert(NULL != szName);
+
+	typedef std::map< uint32_t, T >  GenericPropertyMap;
+	typedef std::pair< uint32_t, T > GenericPair;
+
+	uint32_t hash = SuperFastHash(szName);
+
+	GenericPropertyMap::const_iterator it = list.find(hash);
+	if (it == list.end())return errorReturn;
+	return (*it).second;
+}
+
+#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED

+ 42 - 2
code/Hash.h

@@ -1,3 +1,42 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
 
 
 #ifndef AI_HASH_H_INCLUDED
 #ifndef AI_HASH_H_INCLUDED
 #define AI_HASH_H_INCLUDED
 #define AI_HASH_H_INCLUDED
@@ -19,11 +58,12 @@
 #endif
 #endif
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-inline uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) {
+inline uint32_t SuperFastHash (const char * data, unsigned int len = 0, uint32_t hash = 0) {
 uint32_t tmp;
 uint32_t tmp;
 int rem;
 int rem;
 
 
-    if (len <= 0 || data == NULL) return 0;
+    if (!data) return 0;
+	if (!len)len = (unsigned int)::strlen(data);
 
 
     rem = len & 3;
     rem = len & 3;
     len >>= 2;
     len >>= 2;

+ 63 - 38
code/Importer.cpp

@@ -57,6 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BaseProcess.h"
 #include "BaseProcess.h"
 #include "DefaultIOStream.h"
 #include "DefaultIOStream.h"
 #include "DefaultIOSystem.h"
 #include "DefaultIOSystem.h"
+#include "GenericProperty.h"
 
 
 // Importers
 // Importers
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #if (!defined AI_BUILD_NO_X_IMPORTER)
@@ -151,6 +152,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
 #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
 #	include "RemoveRedundantMaterials.h"
 #	include "RemoveRedundantMaterials.h"
 #endif
 #endif
+#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+#	include "OptimizeGraphProcess.h"
+#endif
 
 
 
 
 using namespace Assimp;
 using namespace Assimp;
@@ -168,6 +172,9 @@ Importer::Importer() :
 	bExtraVerbose = false; // disable extra verbose mode by default
 	bExtraVerbose = false; // disable extra verbose mode by default
 
 
 	// add an instance of each worker class here
 	// add an instance of each worker class here
+	// the order doesn't really care, however file formats that are
+	// used more frequently than others should be at the beginning.
+
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 	mImporter.push_back( new XFileImporter());
 	mImporter.push_back( new XFileImporter());
 #endif
 #endif
@@ -217,13 +224,18 @@ Importer::Importer() :
 #endif
 #endif
 
 
 	// add an instance of each post processing step here in the order 
 	// add an instance of each post processing step here in the order 
-	// of sequence it is executed
+	// of sequence it is executed. steps that are added here are not validated -
+	// as RegisterPPStep() does - all dependencies must be there.
+
 #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
 #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
 	mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first
 	mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first
 #endif
 #endif
 #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
 #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
 	mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
 	mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
 #endif
 #endif
+#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+	mPostProcessingSteps.push_back( new OptimizeGraphProcess());
+#endif
 #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
 #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
 	mPostProcessingSteps.push_back( new TriangulateProcess());
 	mPostProcessingSteps.push_back( new TriangulateProcess());
 #endif
 #endif
@@ -379,8 +391,16 @@ bool ValidateFlags(unsigned int pFlags)
 	if (pFlags & aiProcess_GenSmoothNormals &&
 	if (pFlags & aiProcess_GenSmoothNormals &&
 		pFlags & aiProcess_GenNormals)
 		pFlags & aiProcess_GenNormals)
 	{
 	{
-		DefaultLogger::get()->error("aiProcess_GenSmoothNormals and aiProcess_GenNormals "
-			"may not be specified together");
+		DefaultLogger::get()->error("aiProcess_GenSmoothNormals and "
+			"aiProcess_GenNormals may not be specified together");
+		return false;
+	}
+
+	if (pFlags & aiProcess_PreTransformVertices &&
+		pFlags & aiProcess_OptimizeGraph)
+	{
+		DefaultLogger::get()->error("aiProcess_PreTransformVertives and "
+			"aiProcess_OptimizeGraph may not be specified together");
 		return false;
 		return false;
 	}
 	}
 
 
@@ -488,15 +508,21 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Helper function to check whether an extension is supported by ASSIMP
 // Helper function to check whether an extension is supported by ASSIMP
 bool Importer::IsExtensionSupported(const std::string& szExtension)
 bool Importer::IsExtensionSupported(const std::string& szExtension)
+{
+	return NULL != FindLoader(szExtension);
+}
+
+// ------------------------------------------------------------------------------------------------
+BaseImporter* Importer::FindLoader (const std::string& szExtension)
 {
 {
 	for (std::vector<BaseImporter*>::const_iterator
 	for (std::vector<BaseImporter*>::const_iterator
 		i =  this->mImporter.begin();
 		i =  this->mImporter.begin();
 		i != this->mImporter.end();++i)
 		i != this->mImporter.end();++i)
 	{
 	{
 		// pass the file extension to the CanRead(..,NULL)-method
 		// pass the file extension to the CanRead(..,NULL)-method
-		if ((*i)->CanRead(szExtension,NULL))return true;
+		if ((*i)->CanRead(szExtension,NULL))return *i;
 	}
 	}
-	return false;
+	return NULL;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -519,49 +545,48 @@ void Importer::GetExtensionList(std::string& szOut)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Set a configuration property
 // Set a configuration property
-int Importer::SetProperty(const char* szName, int iValue)
+void Importer::SetPropertyInteger(const char* szName, int iValue, 
+	bool* bWasExisting /*= NULL*/)
 {
 {
-	ai_assert(NULL != szName);
+	SetGenericProperty<int>(mIntProperties, szName,iValue,bWasExisting);	
+}
 
 
-	// search in the list ...
-	for (std::vector<IntPropertyInfo>::iterator
-		i =  this->mIntProperties.begin();
-		i != this->mIntProperties.end();++i)
-	{
-		if (0 == ::strcmp( (*i).name.c_str(), szName ))
-		{
-			int iOld = (*i).value;
-			(*i).value = iValue;
-			return iOld;
-		}
-	}
-	// the property is not yet in the list ...
-	this->mIntProperties.push_back( IntPropertyInfo() );
-	IntPropertyInfo& me = this->mIntProperties.back();
-	me.name = std::string(szName);
-	me.value = iValue;
-	return AI_PROPERTY_WAS_NOT_EXISTING;
+// ------------------------------------------------------------------------------------------------
+void Importer::SetPropertyFloat(const char* szName, float iValue, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<float>(mFloatProperties, szName,iValue,bWasExisting);	
+}
+
+// ------------------------------------------------------------------------------------------------
+void Importer::SetPropertyString(const char* szName, const std::string& value, 
+	bool* bWasExisting /*= NULL*/)
+{
+	SetGenericProperty<std::string>(mStringProperties, szName,value,bWasExisting);	
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Get a configuration property
 // Get a configuration property
-int Importer::GetProperty(const char* szName, 
+int Importer::GetPropertyInteger(const char* szName, 
 	int iErrorReturn /*= 0xffffffff*/) const
 	int iErrorReturn /*= 0xffffffff*/) const
 {
 {
-	ai_assert(NULL != szName);
+	return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
+}
 
 
-	// search in the list ...
-	for (std::vector<IntPropertyInfo>::const_iterator
-		i =  this->mIntProperties.begin();
-		i != this->mIntProperties.end();++i)
-	{
-		if (0 == ::strcmp( (*i).name.c_str(), szName ))
-		{
-			return (*i).value;
-		}
-	}
-	return iErrorReturn;
+// ------------------------------------------------------------------------------------------------
+float Importer::GetPropertyFloat(const char* szName, 
+	float iErrorReturn /*= 10e10*/) const
+{
+	return GetGenericProperty<float>(mFloatProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string Importer::GetPropertyString(const char* szName, 
+	const std::string& iErrorReturn /*= ""*/) const
+{
+	return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
 }
 }
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
 void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
 {
 {

+ 2 - 0
code/JoinVerticesProcess.h

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiTypes.h"
 #include "../include/aiTypes.h"
 
 
 struct aiMesh;
 struct aiMesh;
+class JoinVerticesTest;
 
 
 namespace Assimp
 namespace Assimp
 {
 {
@@ -62,6 +63,7 @@ namespace Assimp
 class ASSIMP_API JoinVerticesProcess : public BaseProcess
 class ASSIMP_API JoinVerticesProcess : public BaseProcess
 {
 {
 	friend class Importer;
 	friend class Importer;
+	friend class ::JoinVerticesTest;
 
 
 protected:
 protected:
 	/** Constructor to be privately used by Importer */
 	/** Constructor to be privately used by Importer */

+ 11 - 11
code/LWOLoader.cpp

@@ -95,8 +95,8 @@ bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 // Setup configuration properties
 // Setup configuration properties
 void LWOImporter::SetupProperties(const Importer* pImp)
 void LWOImporter::SetupProperties(const Importer* pImp)
 {
 {
-	this->configGradientResX = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512);
-	this->configGradientResY = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512);
+	this->configGradientResX = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512);
+	this->configGradientResY = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 
@@ -174,10 +174,10 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 	std::vector<aiMesh*> apcMeshes;
 	std::vector<aiMesh*> apcMeshes;
 	std::vector<aiNode*> apcNodes;
 	std::vector<aiNode*> apcNodes;
 	apcNodes.reserve(mLayers->size());
 	apcNodes.reserve(mLayers->size());
-	apcMeshes.reserve(mLayers->size()*std::min((mSurfaces->size()/2u), 1u));
+	apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
 
 
 	// the RemoveRedundantMaterials step will clean this up later
 	// the RemoveRedundantMaterials step will clean this up later
-	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = mSurfaces->size()];
+	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
 	for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat)
 	for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat)
 	{
 	{
 		MaterialHelper* pcMat = new MaterialHelper();
 		MaterialHelper* pcMat = new MaterialHelper();
@@ -192,7 +192,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 		const LWO::Layer& layer = *lit;
 		const LWO::Layer& layer = *lit;
 
 
 		// I don't know whether there could be dummy layers, but it would be possible
 		// I don't know whether there could be dummy layers, but it would be possible
-		const unsigned int meshStart = apcMeshes.size();
+		const unsigned int meshStart = (unsigned int)apcMeshes.size();
 		if (!layer.mFaces.empty() && !layer.mTempPoints.empty())
 		if (!layer.mFaces.empty() && !layer.mTempPoints.empty())
 		{
 		{
 			// now sort all faces by the surfaces assigned to them
 			// now sort all faces by the surfaces assigned to them
@@ -207,13 +207,13 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 				if (idx >= mTags->size())
 				if (idx >= mTags->size())
 				{
 				{
 					DefaultLogger::get()->warn("LWO: Invalid face surface index");
 					DefaultLogger::get()->warn("LWO: Invalid face surface index");
-					idx = mTags->size()-1;
+					idx = (unsigned int)mTags->size()-1;
 				}
 				}
 				if(0xffffffff == (idx = _mMapping[idx]))
 				if(0xffffffff == (idx = _mMapping[idx]))
 				{
 				{
 					if (0xffffffff == iDefaultSurface)
 					if (0xffffffff == iDefaultSurface)
 					{
 					{
-						iDefaultSurface = mSurfaces->size();
+						iDefaultSurface = (unsigned int)mSurfaces->size();
 						mSurfaces->push_back(LWO::Surface());
 						mSurfaces->push_back(LWO::Surface());
 						LWO::Surface& surf = mSurfaces->back();
 						LWO::Surface& surf = mSurfaces->back();
 						surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; 
 						surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; 
@@ -240,7 +240,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 				// generate the mesh 
 				// generate the mesh 
 				aiMesh* mesh = new aiMesh();
 				aiMesh* mesh = new aiMesh();
 				apcMeshes.push_back(mesh);
 				apcMeshes.push_back(mesh);
-				mesh->mNumFaces = sorted.size();
+				mesh->mNumFaces = (unsigned int)sorted.size();
 
 
 				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
 				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
 					it != end;++it)
 					it != end;++it)
@@ -281,7 +281,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 		apcNodes.push_back(pcNode);
 		apcNodes.push_back(pcNode);
 		pcNode->mName.Set(layer.mName);
 		pcNode->mName.Set(layer.mName);
 		pcNode->mParent = reinterpret_cast<aiNode*>(layer.mParent);
 		pcNode->mParent = reinterpret_cast<aiNode*>(layer.mParent);
-		pcNode->mNumMeshes = apcMeshes.size() - meshStart;
+		pcNode->mNumMeshes = (unsigned int)apcMeshes.size() - meshStart;
 		pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
 		pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
 		for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
 		for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
 			pcNode->mMeshes[p] = p + meshStart;
 			pcNode->mMeshes[p] = p + meshStart;
@@ -292,7 +292,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 	// copy the meshes to the output structure
 	// copy the meshes to the output structure
 	if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash
 	if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash
 	{
 	{
-		pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = apcMeshes.size() ];
+		pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
 		::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
 		::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
 	}
 	}
 }
 }
@@ -414,7 +414,7 @@ void LWOImporter::CopyFaceIndices(FaceList::iterator& it,
 				if (mi > mCurLayer->mTempPoints.size())
 				if (mi > mCurLayer->mTempPoints.size())
 				{
 				{
 					DefaultLogger::get()->warn("LWO: face index is out of range");
 					DefaultLogger::get()->warn("LWO: face index is out of range");
-					mi = mCurLayer->mTempPoints.size()-1;
+					mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
 				}
 				}
 			}
 			}
 		}
 		}

+ 1 - 1
code/LimitBoneWeightsProcess.cpp

@@ -89,7 +89,7 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene)
 void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
 void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
 {
 {
 	// get the current value of the property
 	// get the current value of the property
-	this->mMaxWeights = pImp->GetProperty(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
+	this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 201 - 208
code/MD2Loader.cpp

@@ -45,14 +45,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ByteSwap.h"
 #include "ByteSwap.h"
 #include "MD2NormalTable.h" // shouldn't be included by other units
 #include "MD2NormalTable.h" // shouldn't be included by other units
 
 
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
+// public ASSIMP headers
+#include "../include/assimp.hpp"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
-#include "../include/assimp.hpp"
 
 
+// boost headers
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
@@ -117,10 +118,10 @@ void MD2Importer::SetupProperties(const Importer* pImp)
 {
 {
 	// The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
 	// The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff)))
 		AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff)))
 	{
 	{
-		this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+		this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
 	}
 	}
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -190,255 +191,247 @@ void MD2Importer::InternReadFile( const std::string& pFile,
 	if( fileSize < sizeof(MD2::Header))
 	if( fileSize < sizeof(MD2::Header))
 		throw new ImportErrorException( "MD2 File is too small");
 		throw new ImportErrorException( "MD2 File is too small");
 
 
-	try
-	{
-		// allocate storage and copy the contents of the file to a memory buffer
-		this->mBuffer = new unsigned char[fileSize];
-		file->Read( (void*)mBuffer, 1, fileSize);
+	std::vector<unsigned char> mBuffer2(fileSize);
+	file->Read(&mBuffer2[0], 1, fileSize);
+	this->mBuffer = &mBuffer2[0];
+
 
 
-		this->m_pcHeader = (const MD2::Header*)this->mBuffer;
+	this->m_pcHeader = (const MD2::Header*)this->mBuffer;
 
 
 #ifdef AI_BUILD_BIG_ENDIAN
 #ifdef AI_BUILD_BIG_ENDIAN
 
 
-		ByteSwap::Swap4(&m_pcHeader->frameSize);
-		ByteSwap::Swap4(&m_pcHeader->magic);
-		ByteSwap::Swap4(&m_pcHeader->numFrames);
-		ByteSwap::Swap4(&m_pcHeader->numGlCommands);
-		ByteSwap::Swap4(&m_pcHeader->numSkins);
-		ByteSwap::Swap4(&m_pcHeader->numTexCoords);
-		ByteSwap::Swap4(&m_pcHeader->numTriangles);
-		ByteSwap::Swap4(&m_pcHeader->numVertices);
-		ByteSwap::Swap4(&m_pcHeader->offsetEnd);
-		ByteSwap::Swap4(&m_pcHeader->offsetFrames);
-		ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
-		ByteSwap::Swap4(&m_pcHeader->offsetSkins);
-		ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
-		ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
-		ByteSwap::Swap4(&m_pcHeader->skinHeight);
-		ByteSwap::Swap4(&m_pcHeader->skinWidth);
-		ByteSwap::Swap4(&m_pcHeader->version);
+	ByteSwap::Swap4(&m_pcHeader->frameSize);
+	ByteSwap::Swap4(&m_pcHeader->magic);
+	ByteSwap::Swap4(&m_pcHeader->numFrames);
+	ByteSwap::Swap4(&m_pcHeader->numGlCommands);
+	ByteSwap::Swap4(&m_pcHeader->numSkins);
+	ByteSwap::Swap4(&m_pcHeader->numTexCoords);
+	ByteSwap::Swap4(&m_pcHeader->numTriangles);
+	ByteSwap::Swap4(&m_pcHeader->numVertices);
+	ByteSwap::Swap4(&m_pcHeader->offsetEnd);
+	ByteSwap::Swap4(&m_pcHeader->offsetFrames);
+	ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
+	ByteSwap::Swap4(&m_pcHeader->offsetSkins);
+	ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
+	ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
+	ByteSwap::Swap4(&m_pcHeader->skinHeight);
+	ByteSwap::Swap4(&m_pcHeader->skinWidth);
+	ByteSwap::Swap4(&m_pcHeader->version);
 
 
 #endif
 #endif
 
 
-		this->ValidateHeader();
+	this->ValidateHeader();
 
 
-		// there won't be more than one mesh inside the file
-		pScene->mNumMaterials = 1;
-		pScene->mRootNode = new aiNode();
-		pScene->mRootNode->mNumMeshes = 1;
-		pScene->mRootNode->mMeshes = new unsigned int[1];
-		pScene->mRootNode->mMeshes[0] = 0;
-		pScene->mMaterials = new aiMaterial*[1];
-		pScene->mMaterials[0] = new MaterialHelper();
-		pScene->mNumMeshes = 1;
-		pScene->mMeshes = new aiMesh*[1];
-		aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+	// there won't be more than one mesh inside the file
+	pScene->mNumMaterials = 1;
+	pScene->mRootNode = new aiNode();
+	pScene->mRootNode->mNumMeshes = 1;
+	pScene->mRootNode->mMeshes = new unsigned int[1];
+	pScene->mRootNode->mMeshes[0] = 0;
+	pScene->mMaterials = new aiMaterial*[1];
+	pScene->mMaterials[0] = new MaterialHelper();
+	pScene->mNumMeshes = 1;
+	pScene->mMeshes = new aiMesh*[1];
+	aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
 
 
-		// navigate to the begin of the frame data
-		const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*)
-			this->m_pcHeader + this->m_pcHeader->offsetFrames);
-		pcFrame += this->configFrameID;
+	// navigate to the begin of the frame data
+	const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*)
+		this->m_pcHeader + this->m_pcHeader->offsetFrames);
+	pcFrame += this->configFrameID;
 
 
-		// navigate to the begin of the triangle data
-		MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
-			this->m_pcHeader + this->m_pcHeader->offsetTriangles);
+	// navigate to the begin of the triangle data
+	MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
+		this->m_pcHeader + this->m_pcHeader->offsetTriangles);
 
 
-		// navigate to the begin of the tex coords data
-		const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*)
-			this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
+	// navigate to the begin of the tex coords data
+	const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*)
+		this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
 
 
-		// navigate to the begin of the vertex data
-		const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
+	// navigate to the begin of the vertex data
+	const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
 
 
 #ifdef AI_BUILD_BIG_ENDIAN
 #ifdef AI_BUILD_BIG_ENDIAN
-		for (uint32_t i = 0; i< m_pcHeader->numTriangles)
-		{
-			for (unsigned int p = 0; p < 3;++p)
-			{
-				ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
-				ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
-			}
-		}
-		for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
+	for (uint32_t i = 0; i< m_pcHeader->numTriangles)
+	{
+		for (unsigned int p = 0; p < 3;++p)
 		{
 		{
-			ByteSwap::Swap2(& pcTexCoords[i].s);
-			ByteSwap::Swap2(& pcTexCoords[i].t);
+			ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
+			ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
 		}
 		}
-		ByteSwap::Swap4( & pcFrame->scale[0] );
-		ByteSwap::Swap4( & pcFrame->scale[1] );
-		ByteSwap::Swap4( & pcFrame->scale[2] );
-		ByteSwap::Swap4( & pcFrame->translate[0] );
-		ByteSwap::Swap4( & pcFrame->translate[1] );
-		ByteSwap::Swap4( & pcFrame->translate[2] );
+	}
+	for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
+	{
+		ByteSwap::Swap2(& pcTexCoords[i].s);
+		ByteSwap::Swap2(& pcTexCoords[i].t);
+	}
+	ByteSwap::Swap4( & pcFrame->scale[0] );
+	ByteSwap::Swap4( & pcFrame->scale[1] );
+	ByteSwap::Swap4( & pcFrame->scale[2] );
+	ByteSwap::Swap4( & pcFrame->translate[0] );
+	ByteSwap::Swap4( & pcFrame->translate[1] );
+	ByteSwap::Swap4( & pcFrame->translate[2] );
 #endif
 #endif
 
 
-		pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
-		pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
+	pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
+	pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
 
 
-		// allocate output storage
-		pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
-		pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
-		pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+	// allocate output storage
+	pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
+	pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+	pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
 
 
-		// not sure whether there are MD2 files without texture coordinates
-		// NOTE: texture coordinates can be there without a texture,
-		// but a texture can't be there without a valid UV channel
-		if (this->m_pcHeader->numTexCoords && this->m_pcHeader->numSkins)
-		{
-			// navigate to the first texture associated with the mesh
-			const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + 
-				this->m_pcHeader->offsetSkins);
+	// not sure whether there are MD2 files without texture coordinates
+	// NOTE: texture coordinates can be there without a texture,
+	// but a texture can't be there without a valid UV channel
+	if (this->m_pcHeader->numTexCoords && this->m_pcHeader->numSkins)
+	{
+		// navigate to the first texture associated with the mesh
+		const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + 
+			this->m_pcHeader->offsetSkins);
 
 
-			const int iMode = (int)aiShadingMode_Gouraud;
-			MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
-			pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+		const int iMode = (int)aiShadingMode_Gouraud;
+		MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
 
 
-			aiColor3D clr;
-			clr.b = clr.g = clr.r = 1.0f;
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 1.0f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
 
 
-			clr.b = clr.g = clr.r = 0.05f;
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+		clr.b = clr.g = clr.r = 0.05f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 
 
-			if (pcSkins->name[0])
-			{
-				aiString szString;
-				const size_t iLen = ::strlen(pcSkins->name);
-				::memcpy(szString.data,pcSkins->name,iLen);
-				szString.data[iLen] = '\0';
-				szString.length = iLen;
+		if (pcSkins->name[0])
+		{
+			aiString szString;
+			const size_t iLen = ::strlen(pcSkins->name);
+			::memcpy(szString.data,pcSkins->name,iLen);
+			szString.data[iLen] = '\0';
+			szString.length = iLen;
 
 
-				pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
-			}
-			else
-			{
-				DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
-			}
+			pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
 		}
 		}
 		else
 		else
 		{
 		{
-			// apply a default material
-			const int iMode = (int)aiShadingMode_Gouraud;
-			MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
-			pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
-
-			aiColor3D clr;
-			clr.b = clr.g = clr.r = 0.6f;
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
-
-			clr.b = clr.g = clr.r = 0.05f;
-			pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
-
-			aiString szName;
-			szName.Set(AI_DEFAULT_MATERIAL_NAME);
-			pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+			DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
 		}
 		}
+	}
+	else
+	{
+		// apply a default material
+		const int iMode = (int)aiShadingMode_Gouraud;
+		MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.6f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+		clr.b = clr.g = clr.r = 0.05f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+		aiString szName;
+		szName.Set(AI_DEFAULT_MATERIAL_NAME);
+		pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+	}
 
 
 
 
-		// now read all triangles of the first frame, apply scaling and translation
-		unsigned int iCurrent = 0;
+	// now read all triangles of the first frame, apply scaling and translation
+	unsigned int iCurrent = 0;
 
 
-		float fDivisorU,fDivisorV;
-		if (this->m_pcHeader->numTexCoords)
-		{
-			// allocate storage for texture coordinates, too
-			pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
-			pcMesh->mNumUVComponents[0] = 2;
+	float fDivisorU,fDivisorV;
+	if (this->m_pcHeader->numTexCoords)
+	{
+		// allocate storage for texture coordinates, too
+		pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+		pcMesh->mNumUVComponents[0] = 2;
 
 
-			// check whether the skin width or height are zero (this would
-			// cause a division through zero)
-			if (!this->m_pcHeader->skinWidth)
-			{
-				DefaultLogger::get()->error("Skin width is zero but there are "
-					"valid absolute texture coordinates. Unable to compute "
-					"relative texture coordinates ranging from 0 to 1");
-				fDivisorU = 1.0f;
-			}
-			else fDivisorU = (float)this->m_pcHeader->skinWidth;
-			if (!this->m_pcHeader->skinHeight)
-			{
-				DefaultLogger::get()->error("Skin height is zero but there are "
-					"valid absolute texture coordinates. Unable to compute "
-					"relative texture coordinates ranging from 0 to 1");
-				fDivisorV = 1.0f;
-			}
-			else fDivisorV = (float)this->m_pcHeader->skinHeight;
+		// check whether the skin width or height are zero (this would
+		// cause a division through zero)
+		if (!this->m_pcHeader->skinWidth)
+		{
+			DefaultLogger::get()->error("Skin width is zero but there are "
+				"valid absolute texture coordinates. Unable to compute "
+				"relative texture coordinates ranging from 0 to 1");
+			fDivisorU = 1.0f;
+		}
+		else fDivisorU = (float)this->m_pcHeader->skinWidth;
+		if (!this->m_pcHeader->skinHeight)
+		{
+			DefaultLogger::get()->error("Skin height is zero but there are "
+				"valid absolute texture coordinates. Unable to compute "
+				"relative texture coordinates ranging from 0 to 1");
+			fDivisorV = 1.0f;
 		}
 		}
+		else fDivisorV = (float)this->m_pcHeader->skinHeight;
+	}
 
 
-		for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
+	for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
+	{
+		// allocate the face
+		pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
+		pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
+
+		// copy texture coordinates
+		// check whether they are different from the previous value at this index.
+		// In this case, create a full separate set of vertices/normals/texcoords
+		unsigned int iTemp = iCurrent;
+		for (unsigned int c = 0; c < 3;++c,++iCurrent)
 		{
 		{
-			// allocate the face
-			pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
-			pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
-
-			// copy texture coordinates
-			// check whether they are different from the previous value at this index.
-			// In this case, create a full separate set of vertices/normals/texcoords
-			unsigned int iTemp = iCurrent;
-			for (unsigned int c = 0; c < 3;++c,++iCurrent)
+			// validate vertex indices
+			if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
 			{
 			{
-				// validate vertex indices
-				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
-				{
-					DefaultLogger::get()->error("Vertex index is outside the allowed range");
-					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
-				}
+				DefaultLogger::get()->error("Vertex index is outside the allowed range");
+				pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+			}
 
 
-				// copy face indices
-				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
+			// copy face indices
+			unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 
 
-				// read x,y, and z component of the vertex
-				aiVector3D& vec = pcMesh->mVertices[iCurrent];
+			// read x,y, and z component of the vertex
+			aiVector3D& vec = pcMesh->mVertices[iCurrent];
 
 
-				vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
-				vec.x += pcFrame->translate[0];
+			vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
+			vec.x += pcFrame->translate[0];
 
 
-				// (flip z and y component)
-				// FIX: no .... invert y instead
-				vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
-				vec.y += pcFrame->translate[1];
-				vec.y *= -1.0f;
+			// (flip z and y component)
+			// FIX: no .... invert y instead
+			vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
+			vec.y += pcFrame->translate[1];
+			vec.y *= -1.0f;
 
 
-				vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
-				vec.z += pcFrame->translate[2];
+			vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
+			vec.z += pcFrame->translate[2];
 
 
-				// read the normal vector from the precalculated normal table
-				aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
-				LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
-				vNormal.y *= -1.0f;
+			// read the normal vector from the precalculated normal table
+			aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
+			LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
+			vNormal.y *= -1.0f;
 
 
-				if (this->m_pcHeader->numTexCoords)
+			if (this->m_pcHeader->numTexCoords)
+			{
+				// validate texture coordinates
+				if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
 				{
 				{
-					// validate texture coordinates
-					if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
-					{
-						DefaultLogger::get()->error("UV index is outside the allowed range");
-						pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
-					}
-
-					aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
-					float u,v;
-
-					// the texture coordinates are absolute values but we
-					// need relative values between 0 and 1
-					u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
-					v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
-					pcOut.x = u;
-					pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
+					DefaultLogger::get()->error("UV index is outside the allowed range");
+					pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
 				}
 				}
+
+				aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
+				float u,v;
+
+				// the texture coordinates are absolute values but we
+				// need relative values between 0 and 1
+				u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
+				v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
+				pcOut.x = u;
+				pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
 			}
 			}
-			// FIX: flip the face order for use with OpenGL
-			pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
-			pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
-			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
 		}
 		}
+		// FIX: flip the face order for use with OpenGL
+		pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
+		pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
+		pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
 	}
 	}
-	catch (ImportErrorException* ex)
-	{
-		delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
-		throw ex;
-	}
-	delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 }
 }

+ 2 - 2
code/MD3Loader.cpp

@@ -144,10 +144,10 @@ void MD3Importer::SetupProperties(const Importer* pImp)
 {
 {
 	// The AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
 	// The AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff)))
 		AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff)))
 	{
 	{
-		this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+		this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
 	}
 	}
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 36 - 27
code/MD5Loader.cpp

@@ -61,14 +61,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-// we're just doing this with static buffers whose size is known at
-// compile time, so the compiler should automatically expand to
-// sprintf<array_length>(...)
-
-#if _MSC_VER >= 1400
-#	define sprintf sprintf_s
-#endif
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 MD5Importer::MD5Importer()
 MD5Importer::MD5Importer()
@@ -122,9 +114,8 @@ void MD5Importer::InternReadFile(
 
 
 	// make sure we return no incomplete data
 	// make sure we return no incomplete data
 	if (!bHadMD5Mesh && !bHadMD5Anim)
 	if (!bHadMD5Mesh && !bHadMD5Anim)
-	{
 		throw new ImportErrorException("Failed to read valid data from this MD5");
 		throw new ImportErrorException("Failed to read valid data from this MD5");
-	}
+	
 	if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY;
 	if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -285,30 +276,48 @@ void MD5Importer::LoadMD5MeshFile ()
 	pScene->mRootNode->mNumChildren = 2;
 	pScene->mRootNode->mNumChildren = 2;
 	pScene->mRootNode->mChildren = new aiNode*[2];
 	pScene->mRootNode->mChildren = new aiNode*[2];
 
 
-	aiNode* pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
-	pcNode->mNumMeshes = (unsigned int)meshParser.mMeshes.size();
-	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
-	pcNode->mName.Set("MD5Mesh");
-	pcNode->mParent = pScene->mRootNode;
-	for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
-	{
-		pcNode->mMeshes[i] = i;
-	}
-
 	// now create the hierarchy of animated bones
 	// now create the hierarchy of animated bones
-	pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
+	aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
 	pcNode->mName.Set("MD5Anim");
 	pcNode->mName.Set("MD5Anim");
 	pcNode->mParent = pScene->mRootNode;
 	pcNode->mParent = pScene->mRootNode;
 	AttachChilds(-1,pcNode,meshParser.mJoints);
 	AttachChilds(-1,pcNode,meshParser.mJoints);
 
 
+	pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
+	pcNode->mName.Set("MD5Mesh");
+	pcNode->mParent = pScene->mRootNode;
+
+	std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
+
+	// FIX: MD5 files exported from Blender can have empty meshes
+	for (std::vector<MD5::MeshDesc>::const_iterator 
+		 it  = meshParser.mMeshes.begin(),
+		 end = meshParser.mMeshes.end(); it != end;++it)
+	{
+		if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
+			++pScene->mNumMaterials;
+	}
+
 	// generate all meshes
 	// generate all meshes
-	pScene->mNumMeshes = pScene->mNumMaterials = (unsigned int)meshParser.mMeshes.size();
+	pScene->mNumMeshes = pScene->mNumMaterials;
 	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
 	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
-	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+
+	//  storage for node mesh indices
+	pcNode->mNumMeshes = pScene->mNumMeshes;
+	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+	for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
+		pcNode->mMeshes[m] = m;
+
+	unsigned int n = 0;
+	for (std::vector<MD5::MeshDesc>::iterator 
+		 it  = meshParser.mMeshes.begin(),
+		 end = meshParser.mMeshes.end(); it != end;++it)
 	{
 	{
-		aiMesh* mesh = pScene->mMeshes[i] = new aiMesh();
-		MD5::MeshDesc& meshSrc = meshParser.mMeshes[i];
+		MD5::MeshDesc& meshSrc = *it;
+		if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
+			continue;
+
+		aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
 
 
 		// generate unique vertices in our internal verbose format
 		// generate unique vertices in our internal verbose format
 		MakeDataUnique(meshSrc);
 		MakeDataUnique(meshSrc);
@@ -424,9 +433,9 @@ void MD5Importer::LoadMD5MeshFile ()
 
 
 		// generate a material for the mesh
 		// generate a material for the mesh
 		MaterialHelper* mat = new MaterialHelper();
 		MaterialHelper* mat = new MaterialHelper();
-		pScene->mMaterials[i] = mat;
+		pScene->mMaterials[n] = mat;
 		mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
 		mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
-		mesh->mMaterialIndex = i;
+		mesh->mMaterialIndex = n++;
 	}
 	}
 
 
 	// delete the file again
 	// delete the file again

+ 3 - 3
code/MDCLoader.cpp

@@ -194,10 +194,10 @@ void MDCImporter::SetupProperties(const Importer* pImp)
 {
 {
 	// The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
 	// The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff)))
 		AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff)))
 	{
 	{
-		this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+		this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
 	}
 	}
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -443,7 +443,7 @@ void MDCImporter::InternReadFile(
 		pScene->mMeshes[i]->mTextureCoords[3] = NULL;
 		pScene->mMeshes[i]->mTextureCoords[3] = NULL;
 
 
 	// create materials
 	// create materials
-	pScene->mNumMaterials = aszShaders.size();
+	pScene->mNumMaterials = (unsigned int)aszShaders.size();
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
 	for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
 	for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
 	{
 	{

+ 5 - 5
code/MDLLoader.cpp

@@ -51,11 +51,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 // public ASSIMP headers
 // public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/assimp.hpp"
 #include "../include/assimp.hpp"
 
 
 // boost headers
 // boost headers
@@ -119,11 +118,12 @@ void MDLImporter::SetupProperties(const Importer* pImp)
 {
 {
 	// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
 	// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
 		AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
 	{
 	{
-		this->configFrameID =  pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+		this->configFrameID =  pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
 	}
 	}
+	this->configPalette =  pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 

+ 7 - 8
code/MDLLoader.h

@@ -55,8 +55,7 @@ struct aiNode;
 #include "MDLFileData.h"
 #include "MDLFileData.h"
 #include "HalfLifeFileData.h"
 #include "HalfLifeFileData.h"
 
 
-namespace Assimp
-{
+namespace Assimp	{
 class MaterialHelper;
 class MaterialHelper;
 
 
 using namespace MDL;
 using namespace MDL;
@@ -442,6 +441,9 @@ protected:
 	/** Configuration option: frame to be loaded */
 	/** Configuration option: frame to be loaded */
 	unsigned int configFrameID;
 	unsigned int configFrameID;
 
 
+	/** Configuration option: palette to be used to decode palletized images*/
+	std::string configPalette;
+
 	/** Buffer to hold the loaded file */
 	/** Buffer to hold the loaded file */
 	unsigned char* mBuffer;
 	unsigned char* mBuffer;
 
 
@@ -449,16 +451,13 @@ protected:
 	 * (MDL7 doesn't need this, the format has a separate loader) */
 	 * (MDL7 doesn't need this, the format has a separate loader) */
 	unsigned int iGSFileVersion;
 	unsigned int iGSFileVersion;
 
 
-	/** Output I/O handler. used to load external lmp files
-	*/
+	/** Output I/O handler. used to load external lmp files */
 	IOSystem* pIOHandler;
 	IOSystem* pIOHandler;
 
 
-	/** Output scene to be filled
-	*/
+	/** Output scene to be filled */
 	aiScene* pScene;
 	aiScene* pScene;
 
 
-	/** Size of the input file in bytes
-	 */
+	/** Size of the input file in bytes */
 	unsigned int iFileSize;
 	unsigned int iFileSize;
 };
 };
 
 

+ 1 - 1
code/MDLMaterialLoader.cpp

@@ -61,7 +61,7 @@ using namespace Assimp;
 void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
 void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
 {
 {
 	// now try to find the color map in the current directory
 	// now try to find the color map in the current directory
-	IOStream* pcStream = this->pIOHandler->Open("colormap.lmp","rb");
+	IOStream* pcStream = this->pIOHandler->Open(configPalette,"rb");
 
 
 	const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
 	const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
 	if(pcStream)
 	if(pcStream)

+ 3 - 3
code/MaterialSystem.cpp

@@ -268,7 +268,7 @@ uint32_t MaterialHelper::ComputeHash()
 		// NOTE: We need to exclude the material name from the hash
 		// NOTE: We need to exclude the material name from the hash
 		if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey.data,AI_MATKEY_NAME)) 
 		if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey.data,AI_MATKEY_NAME)) 
 		{
 		{
-			hash = SuperFastHash(prop->mKey.data,prop->mKey.length,hash);
+			hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash);
 			hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
 			hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
 		}
 		}
 	}
 	}
@@ -371,8 +371,8 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput,
 	const char* pKey)
 	const char* pKey)
 {
 {
 	// fix ... don't keep the whole string buffer
 	// fix ... don't keep the whole string buffer
-	return this->AddBinaryProperty(pInput,
-		pInput->length+1+ (size_t)((uint8_t*)&pInput->data - (uint8_t*)&pInput->length),
+	return this->AddBinaryProperty(pInput,(unsigned int)pInput->length+1+
+		(unsigned int)(((uint8_t*)&pInput->data - (uint8_t*)&pInput->length)),
 		pKey,aiPTI_String);
 		pKey,aiPTI_String);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 5 - 5
code/ObjFileImporter.cpp

@@ -41,13 +41,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ObjFileImporter.h"
 #include "ObjFileImporter.h"
 #include "ObjFileParser.h"
 #include "ObjFileParser.h"
 #include "ObjFileData.h"
 #include "ObjFileData.h"
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
+#include "MaterialSystem.h"
+
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
-#include "MaterialSystem.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/format.hpp>
 #include <boost/format.hpp>
@@ -237,7 +237,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
 	if ( meshSizeDiff > 0 )
 	if ( meshSizeDiff > 0 )
 	{
 	{
 		pNode->mMeshes = new unsigned int[ meshSizeDiff ];
 		pNode->mMeshes = new unsigned int[ meshSizeDiff ];
-		pNode->mNumMeshes = meshSizeDiff;
+		pNode->mNumMeshes = (unsigned int)meshSizeDiff;
 		size_t index = 0;
 		size_t index = 0;
 		for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
 		for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
 		{
 		{

+ 3 - 3
code/ObjFileParser.cpp

@@ -306,8 +306,8 @@ void ObjFileParser::getFace()
 	
 	
 	// Store the face
 	// Store the face
 	m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
 	m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
-	m_pModel->m_pCurrentMesh->m_uiNumIndices += face->m_pVertices->size();
-	m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += face->m_pTexturCoords[0].size(); 
+	m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
+	m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size(); 
 	
 	
 	// Skip the rest of the line
 	// Skip the rest of the line
 	m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 	m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
@@ -462,7 +462,7 @@ int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
 		{
 		{
 			if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
 			if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
 			{
 			{
-				mat_index = index;
+				mat_index = (int)index;
 				break;
 				break;
 			}
 			}
 		}
 		}

+ 909 - 0
code/OptimizeGraphProcess.cpp

@@ -0,0 +1,909 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** Implementation of the OptimizeGraphProcess post-processing step*/
+
+#include <vector>
+#include <list>
+
+#include "OptimizeGraphProcess.h"
+#include "Hash.h"
+
+#include "../include/aiPostProcess.h"
+#include "../include/aiScene.h"
+#include "../include/aiAssert.h"
+#include "../include/assimp.hpp"
+#include "../include/DefaultLogger.h"
+
+using namespace Assimp;
+
+// MSB for type unsigned int
+#define AI_OG_UINT_MSB (1u<<((sizeof(unsigned int)*8u)-1u))
+#define AI_OG_UINT_MSB_2 (AI_OG_UINT_MSB>>1)
+
+// check whether a node/a mesh is locked
+#define AI_OG_IS_NODE_LOCKED(nd) (nd->mNumChildren & AI_OG_UINT_MSB)
+#define AI_OG_IS_MESH_LOCKED(ms) (ms->mNumBones & AI_OG_UINT_MSB)
+
+// check whether a node has locked meshes in its list
+#define AI_OG_HAS_NODE_LOCKED_MESHES(nd) (nd->mNumChildren & AI_OG_UINT_MSB_2)
+
+// unmask the two upper bits of an unsigned int
+#define AI_OG_UNMASK(p) (p & (~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2)))
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeGraphProcess::OptimizeGraphProcess()
+{
+	configRemoveAnimations = AI_OG_REMOVE_ANIMATIONS;
+	configMinNumFaces = AI_OG_MIN_NUM_FACES;
+	configJoinInequalTransforms = AI_OG_JOIN_INEQUAL_TRANSFORMS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeGraphProcess::~OptimizeGraphProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_OptimizeGraph) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties of the step
+void OptimizeGraphProcess::SetupProperties(const Importer* pImp)
+{
+	// remove animation nodes?
+	configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_REMOVE_ANIMATIONS,
+		AI_OG_REMOVE_ANIMATIONS) != 0 ? true : false;
+
+	// join nods with inequal transformations?
+	configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS,
+		AI_OG_JOIN_INEQUAL_TRANSFORMS) != 0 ? true : false;
+
+	// minimum face number per node
+	configMinNumFaces = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_MIN_NUM_FACES,
+		AI_OG_MIN_NUM_FACES);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node)
+{
+	ai_assert(NULL != node);
+
+	std::vector<aiNode*> out;
+	RemoveAnimationNodes(node,out);
+	if (out.empty())
+		throw new ImportErrorException("OptimizeGraphProcess: no nodes are remaining.");
+	if (1 == out.size())
+		return out[0];
+	aiNode* p = new aiNode();
+	p->mName.Set("<dummy_root>");
+	p->mNumChildren = (unsigned int)out.size();
+	p->mChildren = new aiNode*[p->mNumChildren];
+	::memcpy(p->mChildren,&out[0],p->mNumChildren*sizeof(void*));
+	return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out)
+{
+	ai_assert(NULL != node);
+
+	// if this is an animation node: shift all children on this layer
+	if (!node->mNumMeshes)
+	{
+		unsigned int old = (unsigned int)out.size();
+		for (unsigned int i = 0; i < node->mNumChildren;++i)
+		{
+			RemoveAnimationNodes(node->mChildren[i],out);
+		}
+		// update the transformations of all shifted childs
+		std::vector<aiNode*>::iterator it2 = out.end(),it = out.begin()+old;
+		for (; it != it2; ++it)
+			(*it)->mTransformation = node->mTransformation * (*it)->mTransformation;
+		
+		delete[] node->mChildren;node->mChildren = NULL;
+		delete node;
+	}
+	else 
+	{
+		// *this* node remains on this layer, and the children, too
+		out.push_back(node);
+
+		std::vector<aiNode*> outNew;
+		for (unsigned int i = 0; i < node->mNumChildren;++i)
+		{
+			RemoveAnimationNodes(node->mChildren[i],outNew);
+		}
+		if (outNew.size() > node->mNumChildren)
+		{
+			delete[] node->mChildren;
+			node->mChildren = new aiNode*[outNew.size()];
+		}
+		node->mNumChildren = (unsigned int)outNew.size();
+		::memcpy(node->mChildren,&outNew[0],node->mNumChildren*sizeof(void*));
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
+{
+	ai_assert(NULL != node);
+
+	for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
+	{
+		aiAnimation* pani = pScene->mAnimations[i];
+		for (unsigned int a = 0; a < pani->mNumBones;++a)
+		{
+			aiBoneAnim* pba = pani->mBones[a];
+			if (pba->mBoneName == node->mName)
+			{
+				// this node is locked
+				node->mNumChildren |= AI_OG_UINT_MSB;
+			}
+		}
+	}
+	// call all children
+	for (unsigned int i = 0; i < node->mNumChildren;++i)
+		FindLockedNodes(node->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount)
+{
+	ai_assert(NULL != node && NULL != pRefCount);
+	for (unsigned int i = 0;i < node->mNumMeshes;++i)
+	{
+		unsigned int m = node->mMeshes[i];
+		if (pRefCount[m].first)
+		{
+			// we have already one reference - lock the first node
+			// that had a referenced to this mesh too if it has only
+			// one mesh assigned. If there are multiple meshes,
+			// the others could still be used for optimizations.
+			if (pRefCount[m].second)
+			{
+				pRefCount[m].second->mNumChildren |= (pRefCount[m].second->mNumMeshes <= 1 
+					? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
+
+				pRefCount[m].second = NULL;
+			}
+			pScene->mMeshes[m]->mNumBones |= AI_OG_UINT_MSB;
+
+			// lock this node
+			node->mNumChildren |= (node->mNumMeshes <= 1 
+				? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
+		}
+		else pRefCount[m].second = node;
+		++pRefCount[m].first;
+	}
+	// call all children
+	for (unsigned int i = 0; i < node->mNumChildren;++i)
+		FindLockedMeshes(node->mChildren[i],pRefCount);
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::FindLockedMeshes(aiNode* node)
+{
+	ai_assert(NULL != node);
+	MeshRefCount* pRefCount = new MeshRefCount[pScene->mNumMeshes];
+	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+		pRefCount[i] = MeshRefCount();
+
+	// execute the algorithm
+	FindLockedMeshes(node,pRefCount);
+
+	delete[] pRefCount;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::UnlockNodes(aiNode* node)
+{
+	ai_assert(NULL != node);
+	node->mNumChildren &= ~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2);
+
+	// call all children
+	for (unsigned int i = 0; i < node->mNumChildren;++i)
+		UnlockNodes(node->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::UnlockMeshes()
+{
+	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+		pScene->mMeshes[i]->mNumBones &= ~AI_OG_UINT_MSB;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::ComputeMeshHashes()
+{
+	mMeshHashes.resize(pScene->mNumMeshes);
+	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+	{
+		unsigned int iRet = 0;
+		aiMesh* pcMesh = pScene->mMeshes[i];
+
+		// normals
+		if (pcMesh->HasNormals())iRet |= 0x1;
+		// tangents and bitangents
+		if (pcMesh->HasTangentsAndBitangents())iRet |= 0x2;
+
+		// texture coordinates
+		unsigned int p = 0;
+		ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
+		while (pcMesh->HasTextureCoords(p))
+		{
+			iRet |= (0x100 << p++);
+
+			// NOTE: meshes with numUVComp != 3 && != 2 aren't handled correctly here
+			ai_assert(pcMesh->mNumUVComponents[p] == 3 || pcMesh->mNumUVComponents[p] == 2);
+			if (3 == pcMesh->mNumUVComponents[p])
+				iRet |= (0x1000 << p++);
+		}
+		// vertex colors
+		p = 0;
+		ai_assert(4 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+		while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++);
+		mMeshHashes[i] = iRet;
+
+		// material index -store it in the upper 1 1/2 bytes, so
+		// are able to encode 2^12 material indices.
+
+		iRet |= (pcMesh->mMaterialIndex << 20u);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArray, 
+	unsigned int min, unsigned int& index, unsigned int iStart)
+{
+	unsigned int first = iStart,last = (unsigned int)sortedArray.size()-1;
+	while (first <= last) 
+	{
+		unsigned int mid = (first + last) / 2;  
+		unsigned int id = sortedArray[mid].second;
+
+		if (min > id) 
+			first = mid + 1;  
+		else if (min <= id) 
+		{
+			last = mid - 1;
+			if (!mid || min > sortedArray[last].second)
+			{
+				index = sortedArray[last].first;
+				return mid;
+			}
+		}
+	}
+	return (unsigned int)sortedArray.size();
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::BuildUniqueBoneList(
+	std::vector<aiMesh*>::const_iterator it,
+	std::vector<aiMesh*>::const_iterator end,
+	std::list<BoneWithHash>& asBones)
+{
+
+	unsigned int iOffset = 0;
+	for (; it != end;++it)
+	{
+		for (unsigned int l = 0; l < (*it)->mNumBones;++l)
+		{
+			aiBone* p = (*it)->mBones[l];
+			uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
+
+			std::list<BoneWithHash>::iterator it2  = asBones.begin();
+			std::list<BoneWithHash>::iterator end2 = asBones.end();
+
+			for (;it2 != end2;++it2)
+			{
+				if ((*it2).first == itml)
+				{
+					(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
+					break;
+				}
+			}
+			if (end2 == it2)
+			{
+				// need to begin a new bone entry
+				asBones.push_back(BoneWithHash());
+				BoneWithHash& btz = asBones.back();
+
+				// setup members
+				btz.first = itml;
+				btz.second = &p->mName;
+				btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
+			}
+		}
+		iOffset += (*it)->mNumVertices;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::JoinBones(
+	std::vector<aiMesh*>::const_iterator it,
+	std::vector<aiMesh*>::const_iterator end,
+	aiMesh* out)
+{
+	ai_assert(NULL != out);
+
+	// find we need to build an unique list of all bones.
+	// we work with hashes to make the comparisons MUCH faster,
+	// at least if we have many bones.
+	std::list<BoneWithHash> asBones;
+	BuildUniqueBoneList(it,end,asBones);
+	
+	// now create the output bones
+	out->mBones = new aiBone*[asBones.size()];
+
+	for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),
+		end = asBones.end(); it != end;++it)
+	{
+		aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
+		pc->mName = aiString( *((*it).second ));
+
+		// get an itrator to the end of the list
+		std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
+
+		// loop through all bones to be joined for this bone
+		for (std::vector< BoneSrcIndex >::const_iterator 
+			wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
+		{
+			pc->mNumWeights += (*wmit).first->mNumWeights;
+
+			// NOTE: different offset matrices for bones with equal names
+			// are - at the moment - not handled correctly. 
+			if (wmit != (*it).pSrcBones.begin() &&
+				pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
+			{
+				DefaultLogger::get()->warn("Bones with equal names but different "
+					"offset matrices can't be joined at the moment. If this causes "
+					"problems, deactivate the OptimizeGraph-Step");
+
+				continue;
+			}
+			pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
+		}
+		// allocate the vertex weight array
+		aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+
+		// and copy the final weights - adjust the vertex IDs by the 
+		// face index offset of the coresponding mesh.
+		for (std::vector< BoneSrcIndex >::const_iterator 
+			wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
+		{
+			aiBone* pip = (*wmit).first;
+			for (unsigned int mp = 0; mp < pip->mNumWeights;++mp)
+			{
+				aiVertexWeight& vf = aiVertexWeight(pip->mWeights[mp]);
+				vf.mVertexId += (*wmit).second;
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::JoinMeshes(std::vector<aiMesh*>& meshList,
+	aiMesh*& out, unsigned int max)
+{
+	ai_assert(NULL != out && 0 != max);
+
+	out->mMaterialIndex = meshList[0]->mMaterialIndex;
+
+	// allocate the output mesh
+	out = new aiMesh();
+	std::vector<aiMesh*>::const_iterator end = meshList.begin()+max;
+	for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+	{
+		out->mNumVertices	+= (*it)->mNumVertices;
+		out->mNumFaces		+= (*it)->mNumFaces;
+		out->mNumBones		+= AI_OG_UNMASK((*it)->mNumBones);
+	}
+
+	if (out->mNumVertices) // just for safety
+	{
+		aiVector3D* pv2;
+
+		// copy vertex positions
+		if (meshList[0]->HasPositions())
+		{
+			pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
+			for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+			{
+				::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
+				pv2 += (*it)->mNumVertices;
+			}
+		}
+		// copy normals
+		if (meshList[0]->HasNormals())
+		{
+			pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
+			for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+			{
+				::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
+				pv2 += (*it)->mNumVertices;
+			}
+		}
+		// copy tangents and bitangents
+		if (meshList[0]->HasTangentsAndBitangents())
+		{
+			pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
+			aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
+
+			for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+			{
+				::memcpy(pv2, (*it)->mTangents,	 (*it)->mNumVertices*sizeof(aiVector3D));
+				::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
+				pv2  += (*it)->mNumVertices;
+				pv2b += (*it)->mNumVertices;
+			}
+		}
+		// copy texture coordinates
+		unsigned int n = 0;
+		while (meshList[0]->HasTextureCoords(n))
+		{
+			out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n];
+
+			pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
+			for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+			{
+				::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
+				pv2 += (*it)->mNumVertices;
+			}
+			++n;
+		}
+		// copy vertex colors
+		n = 0;
+		while (meshList[0]->HasVertexColors(n))
+		{
+			aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
+			for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+			{
+				::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
+				pv2 += (*it)->mNumVertices;
+			}
+			++n;
+		}
+	}
+
+	if (out->mNumFaces) // just for safety
+	{
+		// copy faces
+		out->mFaces = new aiFace[out->mNumFaces];
+		aiFace* pf2 = out->mFaces;
+
+		unsigned int ofs = 0;
+		for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+		{
+			for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)
+			{
+				aiFace& face = (*it)->mFaces[m];
+				pf2->mNumIndices = face.mNumIndices;
+				pf2->mIndices = face.mIndices;
+
+				if (ofs)
+				{
+					// add the offset to the vertex
+					for (unsigned int q = 0; q < face.mNumIndices; ++q)
+						face.mIndices[q] += ofs;	
+				}
+				ofs += (*it)->mNumVertices;
+				face.mIndices = NULL;
+			}
+		}
+	}
+
+	// bones - as this is quite lengthy, I moved the code to a separate function
+	if (out->mNumBones)JoinBones(meshList.begin(),end,out);
+
+	// delete all source meshes
+	for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
+		delete *it;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
+{
+	ai_assert(NULL != pNode);
+
+	// find all meshes which are compatible and could therefore be joined.
+	// we can't join meshes that are locked 
+	std::vector<aiMesh*> apcMeshes(pNode->mNumMeshes);
+	unsigned int iNumMeshes;
+
+	for (unsigned int m = 0, ttt = 0; m < pNode->mNumMeshes;++m)
+	{
+		iNumMeshes = 0;
+
+		unsigned int nm = pNode->mMeshes[m];
+		if (0xffffffff == nm || AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nm]))continue;
+
+		for (unsigned int q = m+1; q < pNode->mNumMeshes;++q)
+		{
+			register unsigned int nq = pNode->mMeshes[q];
+
+			// skip locked meshes
+			if (AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nq]))continue;
+
+			// compare the mesh hashes
+			if (mMeshHashes[nm] == mMeshHashes[nq])
+			{
+				apcMeshes[iNumMeshes++] = pScene->mMeshes[nq];
+				pNode->mMeshes[q] = 0xffffffff;
+			}
+		}
+		aiMesh* out;
+		if (iNumMeshes > 0)
+		{
+			apcMeshes[iNumMeshes++] = pScene->mMeshes[nm];
+			JoinMeshes(apcMeshes,out,iNumMeshes);
+		}
+		else out = pScene->mMeshes[nm];
+
+		pNode->mMeshes[ttt++] = (unsigned int)mOutputMeshes.size();
+		mOutputMeshes.push_back(out);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::TransformMeshes(aiNode* quak,aiNode* pNode)
+{
+	for (unsigned int ä = 0; ä < quak->mNumMeshes;++ä)
+	{
+		aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[ä]];
+		aiMatrix4x4 mMatTransform = pNode->mTransformation;
+
+		// transformation: first back to the parent's local space,
+		// later into the local space of the destination child node
+		mMatTransform.Inverse();
+		mMatTransform = quak->mTransformation * mMatTransform;
+
+		// transform all vertices
+		for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
+			mariusIsHot->mVertices[oo] = mMatTransform * mariusIsHot->mVertices[oo];
+
+		// transform all normal vectors 
+		if (mariusIsHot->HasNormals())
+		{
+			mMatTransform.Inverse().Transpose();
+			for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
+				mariusIsHot->mNormals[oo] = mMatTransform * mariusIsHot->mNormals[oo];
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::ApplyOptimizations(aiNode* node)
+{
+	ai_assert(NULL != node);
+
+	unsigned int iJoinedIndex = 0;
+
+	// first: node index; second: number of faces in node
+	NodeIndexList aiBelowTreshold;
+	aiBelowTreshold.reserve(node->mNumChildren);
+	
+	for (unsigned int i = 0; i < node->mNumChildren;++i)
+	{
+		aiNode* pChild = node->mChildren[i];
+		if (AI_OG_IS_NODE_LOCKED(pChild) || !pChild->mNumMeshes)continue;
+
+		// find out how many faces this node is referencing
+		unsigned int iFaceCnt = 0;
+		for (unsigned int a = 0; a < pChild->mNumMeshes;++a)
+			iFaceCnt += pScene->mMeshes[pChild->mMeshes[a]]->mNumFaces;
+		
+		// are we below the treshold?
+		if (iFaceCnt < configMinNumFaces)
+		{
+			aiBelowTreshold.push_back(NodeIndexEntry());
+			NodeIndexEntry& p = aiBelowTreshold.back();
+			p.first = i;
+			p.second = iFaceCnt;
+			p.pNode = pChild;
+		}
+	}
+
+	if (!aiBelowTreshold.empty())
+	{
+		// some typedefs for the data structures we'll need
+		typedef std::pair<unsigned int, unsigned int> JoinListEntry;
+		std::vector<JoinListEntry> aiJoinList(aiBelowTreshold.size());
+		std::vector<unsigned int>  aiTempList(aiBelowTreshold.size());
+
+		unsigned int iNumJoins, iNumTemp;
+
+		// sort the list by size
+		std::sort(aiBelowTreshold.begin(),aiBelowTreshold.end());
+
+		unsigned int iStart = 0;
+		for (NodeIndexList::const_iterator it = aiBelowTreshold.begin(),end = aiBelowTreshold.end();
+			it != end; /*++it */++iStart)
+		{
+			aiNode* pNode = node->mChildren[(*it).first];
+
+			// get the hash of the mesh
+			const unsigned int iMeshVFormat = mMeshHashes[pNode->mMeshes[0]];
+
+			// we search for a node with more faces than this ... find
+			// the one that fits best and continue until we've reached 
+			// treshold size. 
+			int iDiff = configMinNumFaces-(*it).second;
+			for (;;)
+			{
+				// do a binary search and start the iteration there
+				unsigned int index;
+				unsigned int start = BinarySearch(aiBelowTreshold,iDiff,index,iStart);
+
+				if (index == (*it).first)start++;
+
+				if (start >= aiBelowTreshold.size())
+				{
+					// there is no node with enough faces. take the first
+					start = 0;
+				}
+
+				// todo: implement algorithm to find the best possible combination ...
+				iNumTemp = 0;
+
+				while( start < aiBelowTreshold.size())
+				{
+					// check whether the node has akready been processed before
+					const NodeIndexEntry& entry = aiBelowTreshold[start];
+					if (!entry.pNode)continue;
+
+					const aiNode* pip = node->mChildren[entry.first];
+					if (configJoinInequalTransforms )
+					{
+						// we need to check whether this node has locked meshes
+						// in this case we can't add it here - the meshes will
+						// be transformed from one to another coordinate space
+
+						if (!AI_OG_HAS_NODE_LOCKED_MESHES(pip) || pip->mTransformation == pNode->mTransformation)
+							aiTempList[iNumTemp++] = start;
+					}
+					else if (node->mChildren[entry.first]->mTransformation == pNode->mTransformation)
+					{
+						aiTempList[iNumTemp++] = start;
+						break;
+					}
+					++start;
+				}
+
+				if (iNumTemp)
+				{
+					// search for a node which has a mesh with
+					//  - the same material index
+					//  - the same vertex layout
+					unsigned int d = iNumJoins = 0; 
+					for (unsigned int m = 0; m < iNumTemp;++m)
+					{
+						register unsigned int mn = aiTempList[m];
+						aiNode* pip = aiBelowTreshold[mn].pNode;
+
+						for (unsigned int tt = 0; tt < pip->mNumMeshes;++tt)
+						{
+							register unsigned int mm = pip->mMeshes[tt];
+
+							if (mMeshHashes  [ mm ] == iMeshVFormat)
+							{
+								d = mn;
+								goto break_out;
+							}
+						}
+					}
+break_out:
+					aiJoinList[iNumJoins++] = JoinListEntry( aiBelowTreshold[d].first, d );
+					iDiff -= aiBelowTreshold[d].second;
+				}
+				// did we reach the target treshold?
+				if (iDiff <= 0)break;
+			}
+
+			// did we found any nodes to be joined with *this* one?
+			if (iNumJoins)
+			{
+				unsigned int iNumTotalChilds = pNode->mNumChildren;
+				unsigned int iNumTotalMeshes = pNode->mNumMeshes;
+				std::vector<JoinListEntry>::const_iterator wend = aiJoinList.begin()+iNumJoins;
+
+				// get output array bounds
+				for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
+					wit != wend;++wit )
+				{
+					aiNode*& quak = node->mChildren[(*wit).first];
+					iNumTotalChilds += AI_OG_UNMASK( quak->mNumChildren );
+					iNumTotalMeshes += quak->mNumMeshes;
+				}
+
+				// build the output child list
+				if (iNumTotalChilds != pNode->mNumChildren)
+				{
+					aiNode** ppc = pNode->mChildren;
+					delete[] pNode->mChildren;
+					pNode->mChildren = new aiNode*[iNumTotalChilds];
+					::memcpy(pNode->mChildren,ppc, sizeof(void*)* AI_OG_UNMASK( pNode->mNumChildren ));
+
+					for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
+						wit != wend;++wit )
+					{
+						aiNode*& quak = node->mChildren[(*wit).first];
+						::memcpy(pNode->mChildren+pNode->mNumChildren,
+							quak->mChildren, sizeof(void*)*quak->mNumChildren);
+
+						 pNode->mNumChildren += AI_OG_UNMASK( quak->mNumChildren );
+					}
+				}
+
+				// build the output mesh list
+				unsigned int* ppc = pNode->mMeshes;
+				delete[] pNode->mMeshes;
+				pNode->mMeshes = new unsigned int[iNumTotalMeshes];
+				::memcpy(pNode->mMeshes,ppc, sizeof(void*)*pNode->mNumMeshes);
+
+				for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
+					wit != wend;++wit )
+				{
+					aiNode*& quak = node->mChildren[(*wit).first];
+					::memcpy(pNode->mMeshes+pNode->mNumMeshes,
+						quak->mMeshes, sizeof(unsigned int)*quak->mNumMeshes);
+
+					// if the node has a transformation matrix that is not equal to ours,
+					// we'll need to transform all vertices of the mesh into our
+					// local coordinate space.
+					if (configJoinInequalTransforms && quak->mTransformation != pNode->mTransformation)
+						TransformMeshes(quak,pNode);
+
+					pNode->mNumMeshes += quak->mNumMeshes;
+
+					// remove the joined nodes from all lists.
+					aiBelowTreshold[(*wit).second].pNode = NULL;
+					if ((*wit).second == iStart+1)++iStart;
+				}
+
+				// now generate an output name for the joined nodes
+				if (1 == iNumTotalChilds)
+				{
+					pNode->mName.length = ::sprintf( pNode->mName.data, "<Joined_%i_%i>",
+						iJoinedIndex++,iNumJoins+1);
+				}
+			}
+
+			// now optimize the meshes in this node
+			ApplyNodeMeshesOptimization(pNode);
+
+			// note - this has been optimized away. The search in the binary
+			// list starts with iStart, which is incremented each iteration
+			++it; // = aiBelowTreshold.erase(it);
+		}
+	}
+
+	// call all children recursively
+	for (unsigned int i = 0; i < node->mNumChildren;++i)
+		ApplyOptimizations(node->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void OptimizeGraphProcess::BuildOutputMeshList()
+{
+	// all meshes should have been deleted before if they are
+	// not contained in the new mesh list
+
+	if (pScene->mNumMeshes < mOutputMeshes.size())
+	{
+		delete[] pScene->mMeshes; 
+		pScene->mMeshes = new aiMesh*[mOutputMeshes.size()];
+	}
+	pScene->mNumMeshes = (unsigned int)mOutputMeshes.size();
+	::memcpy(pScene->mMeshes,&mOutputMeshes[0],pScene->mNumMeshes*sizeof(void*));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void OptimizeGraphProcess::Execute( aiScene* pScene)
+{
+	this->pScene = pScene;
+	/*
+
+	a) the term "mesh node" stands for a node with numMeshes > 0
+	b) the term "animation node" stands for a node with numMeshes == 0,
+	   regardless whether the node is referenced by animation channels.
+
+	Algorithm:
+
+	1. Compute hashes for all meshes that we're able to check whether
+	   two meshes are compatible.
+	2. Remove animation nodes if we have been configured to do so
+	3. Find out which nodes may not be moved, so to speak are "locked" - a
+	   locked node will never be joined with neighbors.
+	   - A node lock is indicated by a set MSB in the aiNode::mNumChildren member
+    4. Find out which meshes are locked - they are referenced by
+	   more than one node. They will never be joined. Mark all 
+	   nodes referencing such a mesh as "locked", too.
+       - A mesh lock is indicated by a set MSB in the aiMesh::mNumBones member
+    5. For each unlocked node count the face numbers of all assigned meshes 
+	   - if it is below the pre-defined treshold add the node to a list.
+	   For each node in the list - try to find enough joinable nodes to
+	   have enough faces all together. 
+		  Two nodes are joined if:
+		  - none of them is locked
+		  - (optional) their world matrices are identical
+		  - nodes whose meshes share the same material indices are prefered
+		  Two meshes in one node are joined if:
+		  - their material indices are identical
+		  - none of them is locked
+		  - they share the same vertex format
+    6. Build the final mesh list
+	7. For all meshes and all nodes - remove locks.
+
+	*/
+
+	throw new ImportErrorException("OG step is still undeer development and not yet finished");
+
+	// STEP 1
+	ComputeMeshHashes();
+
+	// STEP 2
+	if (configRemoveAnimations)
+		pScene->mRootNode = RemoveAnimationNodes(pScene->mRootNode);
+
+	// STEP 3
+	else FindLockedNodes(pScene->mRootNode);
+
+	// STEP 4
+	FindLockedMeshes(pScene->mRootNode);
+
+	// STEP 5
+	ApplyOptimizations(pScene->mRootNode);
+
+	// STEP 6
+	BuildOutputMeshList();
+
+	// STEP 7
+	UnlockNodes(pScene->mRootNode);
+	UnlockMeshes();
+}

+ 340 - 0
code/OptimizeGraphProcess.h

@@ -0,0 +1,340 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to refactor the output node graph to 
+    be more compact */
+#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
+#define AI_OPTIMIZEGRAPHPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+struct aiNode;
+struct aiBone;
+class OptimizeGraphProcessTest;
+
+namespace Assimp	{
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java, 
+//  ConfigProperty.OG_MAX_HIERARCHY_DEPTH
+//  ConfigProperty.OG_MIN_NUM_FACES
+//  ConfigProperty.JOIN_INEQUAL_TRANSFORMS
+// **********************************************************
+
+#if (!defined AI_OG_MAX_DEPTH)
+#	define AI_OG_MAX_DEPTH	0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+#if (!defined AI_OG_MIN_NUM_FACES)
+#	define AI_OG_MIN_NUM_FACES 0xffffffff
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+#if (!defined AI_OG_REMOVE_ANIMATIONS)
+#	define AI_OG_REMOVE_ANIMATIONS false
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+#if (!defined AI_OG_JOIN_INEQUAL_TRANSFORMS)
+#	define AI_OG_JOIN_INEQUAL_TRANSFORMS false
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+
+// ---------------------------------------------------------------------------
+struct NodeIndexEntry : public std::pair<unsigned int, unsigned int>
+{
+	// binary operator < for use with std::sort
+	bool operator< (const NodeIndexEntry& other)
+	{
+		return second < other.second;
+	}
+
+	// pointer to the original node
+	aiNode* pNode;
+};
+typedef std::vector<NodeIndexEntry> NodeIndexList;
+typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
+
+// ---------------------------------------------------------------------------
+struct BoneWithHash : public std::pair<uint32_t,aiString*>
+{
+	std::vector<BoneSrcIndex> pSrcBones;
+};
+
+
+// ---------------------------------------------------------------------------
+/** This post processing step reformats the output node graph to be more
+ *  compact. There are several options, e.g. maximum hierachy depth or
+ *  minimum mesh size. Some files store every face as a new mesh, so
+ *  some Assimp steps (such as FixInfacingNormals or SmoothNormals) don't
+ *  work properly on these meshes. This step joins such small meshes and
+ *  nodes. Animations are kept during the step.
+ *  @note Use the PretransformVertices step to remove the node graph
+ *    completely (and all animations, too).
+*/
+class ASSIMP_API OptimizeGraphProcess : public BaseProcess
+{
+	friend class Importer;
+	friend class ::OptimizeGraphProcessTest;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	OptimizeGraphProcess();
+
+	/** Destructor, private as well */
+	~OptimizeGraphProcess();
+
+
+	typedef std::pair< unsigned int, aiNode* > MeshRefCount;
+	typedef unsigned int MeshHash;
+
+public:
+
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag.
+	* @param pFlags The processing flags the importer was called with. 
+	*   A bitwise combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, 
+	*   false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Called prior to ExecuteOnScene().
+	* The function is a request to the process to update its configuration
+	* basing on the Importer's configuration property list.
+	*/
+	void SetupProperties(const Importer* pImp);
+
+
+	// set the configMinNumfaces property
+	inline void SetMinNumFaces(unsigned int n)
+	{
+		configMinNumFaces = n;
+	}
+
+	// set the configRemoveAnimations property
+	inline void SetRemoveAnimations(bool b)
+	{
+		configRemoveAnimations = b;
+	}
+
+
+protected:
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+
+	// -------------------------------------------------------------------
+	/** Removes animation nodes from the tree. 
+	 * @param node Current node
+	 * @param out Receives a list of replacement nodes for *this* node -
+	 *   if *this* node should be kept, it must be added to the list.
+	*/
+	void RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out);
+
+
+	// -------------------------------------------------------------------
+	/** Entry point to the RemoveAnimationNodes algorithm.
+	 * @param node Root node to start with
+	 * @return New root node
+	 */
+	aiNode* RemoveAnimationNodes (aiNode* node);
+
+
+	// -------------------------------------------------------------------
+	/** Finds and marks all locked nodes in the tree.
+	 * A node is locked if it is referenced by animations.
+	 * @param node ROot node to start with
+	 * @note A locked node has the MSB set in its mNumChildren member
+	 */
+	void FindLockedNodes(aiNode* node);
+
+
+	// -------------------------------------------------------------------
+	/** Searches for locked meshes. A mesh is locked if it is referenced
+	 *  by more than one node in the hierarchy.
+	 * @param node Root node to start with
+	 */
+	void FindLockedMeshes(aiNode* node);
+	void FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount);
+
+
+	// -------------------------------------------------------------------
+	/** Unlocks all nodes in the output tree.
+	 * @param node Root node to start with
+	 */
+	void UnlockNodes(aiNode* node);
+
+
+	// -------------------------------------------------------------------
+	/** Unlocks all meshes in the output tree.
+	 */
+	void UnlockMeshes();
+
+
+	// -------------------------------------------------------------------
+	/** Apply the final optimization algorithm to the tree. 
+	 * See the Execute-method for a detailled description of the algorithm.
+	 * @param node Root node to start with
+	 */
+	void ApplyOptimizations(aiNode* node);
+
+
+	// -------------------------------------------------------------------
+	/** Binary search for the first element that is >= min.
+	 * @param sortedArray Input array
+	 * @param min Treshold
+	 */
+	unsigned int BinarySearch(NodeIndexList& sortedArray,
+		unsigned int min, unsigned int& index, unsigned int iStart);
+
+
+	// -------------------------------------------------------------------
+	/** Compute stable hashes for all meshes and fill mMeshHashes 
+	 *  with the results.
+	 */
+	void ComputeMeshHashes();
+
+
+	// -------------------------------------------------------------------
+	/** Optimizes the meshes in a single node aftr the joining process.
+	 * @param pNode Node to be optimized. Childs noded won't be processed
+	 *   automatically.
+	 */
+	void ApplyNodeMeshesOptimization(aiNode* pNode);
+
+
+	// -------------------------------------------------------------------
+	/** Join meshes.
+	 * The output meshes are deleted afterwards.
+	 * @param meshList List of meshes to be joined
+	 * @param out Receives a pointer to the output mesh.
+	 */
+	void JoinMeshes(std::vector<aiMesh*>& meshList,aiMesh*& out,
+		unsigned int max);
+
+
+	// -------------------------------------------------------------------
+	/** Join bones from a collection of meshes.
+	 * 
+	 * @param it First mesh to be processed
+	 * @param end Last mesh to be processed
+	 * @param out Valid output mesh to receive the output bone list.
+	 */
+	void JoinBones(std::vector<aiMesh*>::const_iterator it,
+		std::vector<aiMesh*>::const_iterator end,
+		aiMesh* out);
+
+
+	// -------------------------------------------------------------------
+	/** Build a list of unique bones from a collection of meshes.
+	 * 
+	 * @param it First mesh to be processed
+	 * @param end Last mesh to be processed
+	 * @param asBones Receives a list of unique bones
+	 */
+	void BuildUniqueBoneList(std::vector<aiMesh*>::const_iterator it,
+		std::vector<aiMesh*>::const_iterator end,
+		std::list<BoneWithHash>& asBones);
+
+	// -------------------------------------------------------------------
+	/** Build the output mesh list.
+	 */
+	void BuildOutputMeshList();
+
+
+	// -------------------------------------------------------------------
+	/** Transform meshes from one coordinate space into another.
+	 * @param quak Input space. All meshes referenced by this node -
+	 *   assuming none of them is locked - are transformed in the
+	 *   local coordinate space of pNode
+	 * @param pNode Destination coordinate space
+	 */
+	void TransformMeshes(aiNode* quak,aiNode* pNode);
+
+private:
+
+	/** Configuration option: specifies the minimum number of faces
+	    a node should have. The steps tries to join meshes with less
+		vertices that are on the same hierarchy level. 
+		If this value is set to a very large value (e.g. 0xffffffff)
+		all meshes on the same hierarchy level are joined - if they
+		aren't animation nodes and if they have the same world matrices
+	 */
+	unsigned int configMinNumFaces;
+
+
+	/** Configuration option: specifies whether animations are removed
+	    from the node graph. If animations aren't needed by the caller,
+	    this allows for further optimization.
+	 */
+	bool configRemoveAnimations;
+
+	/** Configuration option: specifies whether nodes with inequal
+	    world matrices are joined if they are on the same hierarchy
+	    level and if it seems to make sense.
+	 */
+	bool configJoinInequalTransforms;
+
+	/** Working data */
+	aiScene* pScene;
+
+	/** List of hash identifiers for all meshes. 
+	    The hashes are build from both the meshes vertex format
+		and the material indices. Bones are not taken into account.
+	*/
+	std::vector<MeshHash> mMeshHashes;
+
+	/** List of output meshes.
+	 */
+	std::vector<aiMesh*> mOutputMeshes;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC

+ 14 - 46
code/PlyLoader.cpp

@@ -40,16 +40,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 /** @file Implementation of the PLY importer class */
 /** @file Implementation of the PLY importer class */
+
+// internal headers
 #include "PlyLoader.h"
 #include "PlyLoader.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 #include "StringComparison.h"
 
 
+// public ASSIMP headers
 #include "../include/IOStream.h"
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 
+// boost headeers
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
@@ -95,24 +100,19 @@ void PLYImporter::InternReadFile(
 
 
 	// Check whether we can read from the file
 	// Check whether we can read from the file
 	if( file.get() == NULL)
 	if( file.get() == NULL)
-	{
 		throw new ImportErrorException( "Failed to open PLY file " + pFile + ".");
 		throw new ImportErrorException( "Failed to open PLY file " + pFile + ".");
-	}
 
 
 	// check whether the ply file is large enough to contain
 	// check whether the ply file is large enough to contain
 	// at least the file header
 	// at least the file header
 	size_t fileSize = file->FileSize();
 	size_t fileSize = file->FileSize();
 	if( fileSize < 10)
 	if( fileSize < 10)
-	{
 		throw new ImportErrorException( "PLY File is too small.");
 		throw new ImportErrorException( "PLY File is too small.");
-	}
 
 
 	// allocate storage and copy the contents of the file to a memory buffer
 	// allocate storage and copy the contents of the file to a memory buffer
 	// (terminate it with zero)
 	// (terminate it with zero)
-	// FIX: Allocate an extra buffer of 12.5% to be sure we won't crash
-	// if an overrun occurs.
-	this->mBuffer = new unsigned char[fileSize+1 + (fileSize>>3)];
-	file->Read( (void*)mBuffer, 1, fileSize);
+	std::vector<unsigned char> mBuffer2(fileSize+1);
+	file->Read( &mBuffer2[0], 1, fileSize);
+	this->mBuffer = &mBuffer2[0];
 	this->mBuffer[fileSize] = '\0';
 	this->mBuffer[fileSize] = '\0';
 
 
 	// the beginning of the file must be PLY
 	// the beginning of the file must be PLY
@@ -120,8 +120,6 @@ void PLYImporter::InternReadFile(
 		this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
 		this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
 		this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
 		this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
 	{
 	{
-		delete[] this->mBuffer;
-		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
 		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
 	}
 	}
 	char* szMe = (char*)&this->mBuffer[3];
 	char* szMe = (char*)&this->mBuffer[3];
@@ -137,11 +135,7 @@ void PLYImporter::InternReadFile(
 			szMe += 6;
 			szMe += 6;
 			SkipLine(szMe,(const char**)&szMe);
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom))
 			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom))
-			{
-				delete[] this->mBuffer;
-				AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
-			}
 		}
 		}
 		else if (0 == ASSIMP_strincmp(szMe,"binary_",7))
 		else if (0 == ASSIMP_strincmp(szMe,"binary_",7))
 		{
 		{
@@ -159,18 +153,9 @@ void PLYImporter::InternReadFile(
 			// skip the line, parse the rest of the header and build the DOM
 			// skip the line, parse the rest of the header and build the DOM
 			SkipLine(szMe,(const char**)&szMe);
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
 			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
-			{
-				delete[] this->mBuffer;
-				AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
-			}
-		}
-		else
-		{
-			delete[] this->mBuffer;
-			AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
-			throw new ImportErrorException( "Invalid .ply file: Unknown file format");
 		}
 		}
+		else throw new ImportErrorException( "Invalid .ply file: Unknown file format");
 	}
 	}
 	else
 	else
 	{
 	{
@@ -185,12 +170,8 @@ void PLYImporter::InternReadFile(
 	this->LoadVertices(&avPositions,false);
 	this->LoadVertices(&avPositions,false);
 
 
 	if (avPositions.empty())
 	if (avPositions.empty())
-	{
-		delete[] this->mBuffer;
-		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		throw new ImportErrorException( "Invalid .ply file: No vertices found. "
 		throw new ImportErrorException( "Invalid .ply file: No vertices found. "
-			"Unable to interpret the data format of the PLY file");
-	}
+			"Unable to parse the data format of the PLY file.");
 
 
 	// now load a list of normals. 
 	// now load a list of normals. 
 	std::vector<aiVector3D> avNormals;
 	std::vector<aiVector3D> avNormals;
@@ -206,10 +187,8 @@ void PLYImporter::InternReadFile(
 	{
 	{
 		if (avPositions.size() < 3)
 		if (avPositions.size() < 3)
 		{
 		{
-			delete[] this->mBuffer;
-			AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
-			throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
-				"a face list. ");
+			throw new ImportErrorException( "Invalid .ply file: Not enough "
+				"vertices to build a face list. ");
 		}
 		}
 
 
 		unsigned int iNum = (unsigned int)avPositions.size() / 3;
 		unsigned int iNum = (unsigned int)avPositions.size() / 3;
@@ -244,11 +223,7 @@ void PLYImporter::InternReadFile(
 		&avColors,&avTexCoords,&avMaterials,&avMeshes);
 		&avColors,&avTexCoords,&avMaterials,&avMeshes);
 
 
 	if (avMeshes.empty())
 	if (avMeshes.empty())
-	{
-		delete[] this->mBuffer;
-		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
 		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
-	}
 
 
 	// now generate the output scene object. Fill the material list
 	// now generate the output scene object. Fill the material list
 	pScene->mNumMaterials = (unsigned int)avMaterials.size();
 	pScene->mNumMaterials = (unsigned int)avMaterials.size();
@@ -269,12 +244,6 @@ void PLYImporter::InternReadFile(
 
 
 	for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
 	for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
 		pScene->mRootNode->mMeshes[i] = i;
 		pScene->mRootNode->mMeshes[i] = i;
-
-	// delete the file buffer
-	delete[] this->mBuffer;AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
-
-	// DOM is lying on the stack, will be deconstructed automatically
-	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
 void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
@@ -819,9 +788,8 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
 
 
 					if (3 > iNum)
 					if (3 > iNum)
 					{
 					{
-						// We must filter out all degenerates. Leave a message
-						// in the log ...
-						// LOG
+						// We must filter out all degenerates.
+						DefaultLogger::get()->warn("PLY: Found degenerated triangle");
 						continue;
 						continue;
 					}
 					}
 
 

+ 6 - 6
code/PretransformVertices.cpp

@@ -52,17 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
+// ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 PretransformVertices::PretransformVertices()
 PretransformVertices::PretransformVertices()
-	{
-	}
-
+{
+}
+// ------------------------------------------------------------------------------------------------
 // Destructor, private as well
 // Destructor, private as well
 PretransformVertices::~PretransformVertices()
 PretransformVertices::~PretransformVertices()
-	{
+{
 	// nothing to do here
 	// nothing to do here
-	}
-
+}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
 // Returns whether the processing step is present in the given flag field.
 bool PretransformVertices::IsActive( unsigned int pFlags) const
 bool PretransformVertices::IsActive( unsigned int pFlags) const

+ 39 - 69
code/3DSSpatialSort.cpp → code/SGSpatialSort.cpp

@@ -12,18 +12,18 @@ with or without modification, are permitted provided that the following
 conditions are met:
 conditions are met:
 
 
 * Redistributions of source code must retain the above
 * Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
+copyright notice, this list of conditions and the
+following disclaimer.
 
 
 * Redistributions in binary form must reproduce the above
 * 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.
+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
 * Neither the name of the ASSIMP team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the ASSIMP Development Team.
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the ASSIMP Development Team.
 
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
@@ -40,66 +40,53 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 /** @file Implementation of the helper class to quickly find 
 /** @file Implementation of the helper class to quickly find 
- vertices close to a given position. Special implementation for 
- the 3ds loader handling smooth groups correctly  */
+vertices close to a given position. Special implementation for 
+the 3ds loader handling smooth groups correctly  */
 
 
 #include <algorithm>
 #include <algorithm>
-#include "3DSSpatialSort.h"
+#include "SGSpatialSort.h"
 
 
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
 
 
 using namespace Assimp;
 using namespace Assimp;
-using namespace Assimp::Dot3DS;
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-D3DSSpatialSorter::D3DSSpatialSorter()
-	{
+SGSpatialSort::SGSpatialSort()
+{
 	// define the reference plane. We choose some arbitrary vector away from all basic axises 
 	// define the reference plane. We choose some arbitrary vector away from all basic axises 
 	// in the hope that no model spreads all its vertices along this plane.
 	// in the hope that no model spreads all its vertices along this plane.
 	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
 	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
 	mPlaneNormal.Normalize();
 	mPlaneNormal.Normalize();
-	}
+}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Destructor
 // Destructor
-D3DSSpatialSorter::~D3DSSpatialSorter()
-	{
+SGSpatialSort::~SGSpatialSort()
+{
 	// nothing to do here, everything destructs automatically
 	// nothing to do here, everything destructs automatically
-	}
+}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void D3DSSpatialSorter::AddFace(const Dot3DS::Face* pcFace,
-	const std::vector<aiVector3D>& vPositions)
+void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index,
+	unsigned int smoothingGroup)
 {
 {
-	ai_assert(NULL != pcFace);
-
 	// store position by index and distance
 	// store position by index and distance
-	float distance = vPositions[pcFace->mIndices[0]] * mPlaneNormal;
-	mPositions.push_back( Entry( pcFace->mIndices[0], vPositions[pcFace->mIndices[0]], 
-		distance, pcFace->iSmoothGroup));
-
-	// triangle vertex 2
-	distance = vPositions[pcFace->mIndices[1]] * mPlaneNormal;
-	mPositions.push_back( Entry( pcFace->mIndices[1], vPositions[pcFace->mIndices[1]], 
-		distance, pcFace->iSmoothGroup));
-
-	// triangle vertex 3
-	distance = vPositions[pcFace->mIndices[2]] * mPlaneNormal;
-	mPositions.push_back( Entry( pcFace->mIndices[2], vPositions[pcFace->mIndices[2]], 
-		distance, pcFace->iSmoothGroup));
+	float distance = vPosition * mPlaneNormal;
+	mPositions.push_back( Entry( index, vPosition, 
+		distance, smoothingGroup));
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void D3DSSpatialSorter::Prepare()
+void SGSpatialSort::Prepare()
 {
 {
 	// now sort the array ascending by distance.
 	// now sort the array ascending by distance.
 	std::sort( this->mPositions.begin(), this->mPositions.end());
 	std::sort( this->mPositions.begin(), this->mPositions.end());
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns an iterator for all positions close to the given position.
 // Returns an iterator for all positions close to the given position.
-void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, 
-	uint32_t pSG,
-	float pRadius,
-	std::vector<unsigned int>& poResults) const
-	{
+void SGSpatialSort::FindPositions( const aiVector3D& pPosition, 
+									  uint32_t pSG,
+									  float pRadius,
+									  std::vector<unsigned int>& poResults) const
+{
 	float dist = pPosition * mPlaneNormal;
 	float dist = pPosition * mPlaneNormal;
 	float minDist = dist - pRadius, maxDist = dist + pRadius;
 	float minDist = dist - pRadius, maxDist = dist + pRadius;
 
 
@@ -119,14 +106,14 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
 	unsigned int index = (unsigned int)mPositions.size() / 2;
 	unsigned int index = (unsigned int)mPositions.size() / 2;
 	unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
 	unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
 	while( binaryStepSize > 1)
 	while( binaryStepSize > 1)
-		{
+	{
 		if( mPositions[index].mDistance < minDist)
 		if( mPositions[index].mDistance < minDist)
 			index += binaryStepSize;
 			index += binaryStepSize;
 		else
 		else
 			index -= binaryStepSize;
 			index -= binaryStepSize;
 
 
 		binaryStepSize /= 2;
 		binaryStepSize /= 2;
-		}
+	}
 
 
 	// depending on the direction of the last step we need to single step a bit back or forth
 	// depending on the direction of the last step we need to single step a bit back or forth
 	// to find the actual beginning element of the range
 	// to find the actual beginning element of the range
@@ -140,33 +127,16 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
 
 
 	float squareEpsilon = pRadius * pRadius;
 	float squareEpsilon = pRadius * pRadius;
 	std::vector<Entry>::const_iterator it = mPositions.begin() + index;
 	std::vector<Entry>::const_iterator it = mPositions.begin() + index;
-	if (0 == pSG)
-		{
-		while( it->mDistance < maxDist)
-			{
-			if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
-				{
-				poResults.push_back( it->mIndex);
-				}
-			++it;
-			if( it == mPositions.end())
-				break;
-			}
-		}
-	else
+	while( it->mDistance < maxDist)
+	{
+		if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
+			(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups || 0 == pSG))
 		{
 		{
-		while( it->mDistance < maxDist)
-			{
-			if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
-				(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups))
-				{
-				poResults.push_back( it->mIndex);
-				}
-			++it;
-			if( it == mPositions.end())
-				break;
-			}
+			poResults.push_back( it->mIndex);
 		}
 		}
-	return;
+		++it;
+		if( it == mPositions.end())
+			break;
 	}
 	}
+}
 
 

+ 11 - 17
code/3DSSpatialSort.h → code/SGSpatialSort.h

@@ -44,40 +44,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_D3DSSPATIALSORT_H_INC
 #define AI_D3DSSPATIALSORT_H_INC
 
 
 #include <vector>
 #include <vector>
-#include "../include/aiVector3D.h"
-
-
-#if (!defined AI_BUILD_NO_ASE_IMPORTER)
-#	include "3DSHelper.h"
-#endif
+#include "../include/aiTypes.h"
 
 
 namespace Assimp
 namespace Assimp
 {
 {
 
 
-using namespace Dot3DS;
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 /** Specialized version of SpatialSort to support smoothing groups
 /** Specialized version of SpatialSort to support smoothing groups
  *  This is used in the .3ds loader
  *  This is used in the .3ds loader
  */
  */
-class D3DSSpatialSorter
+class SGSpatialSort
 {
 {
 public:
 public:
 
 
-	D3DSSpatialSorter();
+	SGSpatialSort();
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Construction from a given face array, handling smoothing groups properly
 	/** Construction from a given face array, handling smoothing groups properly
 	 */
 	 */
-	D3DSSpatialSorter(const std::vector<aiVector3D>& vPositions);
+	SGSpatialSort(const std::vector<aiVector3D>& vPositions);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Add a face to the spatial sorter
-	 * @param pcFace Face to be added
-	 * @param vPositions Input position list
+	/** Add a vertex to the spatial sort
+	 * @param vPosition Vertex position to be added
+	 * @param index Index of the vrtex
+	 * @param smoothingGroup SmoothingGroup for this vertex
 	 */
 	 */
-	void AddFace(const Dot3DS::Face* pcFace,
-		const std::vector<aiVector3D>& vPositions);
+	void Add(const aiVector3D& vPosition, unsigned int index,
+		unsigned int smoothingGroup);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Prepare the spatial sorter for use
 	/** Prepare the spatial sorter for use
@@ -85,7 +79,7 @@ public:
 	void Prepare();
 	void Prepare();
 
 
 	/** Destructor */
 	/** Destructor */
-	~D3DSSpatialSorter();
+	~SGSpatialSort();
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Returns an iterator for all positions close to the given position.
 	/** Returns an iterator for all positions close to the given position.

+ 2 - 2
code/SMDLoader.cpp

@@ -110,10 +110,10 @@ void SMDImporter::SetupProperties(const Importer* pImp)
 {
 {
 	// The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
 	// The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff)))
 		AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff)))
 	{
 	{
-		this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+		this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
 	}
 	}
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 103 - 0
code/SmoothingGroups.h

@@ -0,0 +1,103 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Defines the helper data structures for importing 3DS files.
+http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
+
+#ifndef AI_SMOOTHINGGROUPS_H_INC
+#define AI_SMOOTHINGGROUPS_H_INC
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a face with smoothing groups assigned */
+struct FaceWithSmoothingGroup
+{
+	FaceWithSmoothingGroup() : iSmoothGroup(0)
+	{
+		// let the rest uninitialized for performance - in release builds.
+		// in debug builds set all indices to a common magic value
+#ifdef _DEBUG
+		this->mIndices[0] = 0xffffffff;
+		this->mIndices[1] = 0xffffffff;
+		this->mIndices[2] = 0xffffffff;
+#endif
+	}
+
+	
+	//! Indices. .3ds is using uint16. However, after
+	//! an unique vrtex set has been geneerated it might
+	//! be an index becomes > 2^16
+	uint32_t mIndices[3];
+
+	//! specifies to which smoothing group the face belongs to
+	uint32_t iSmoothGroup;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a mesh whose faces have smoothing
+    groups assigned. This allows us to reuse the code for normal computations 
+	from smoothings groups for several loaders (3DS, ASE). All of them 
+	use face structures which inherit from #FaceWithSmoothingGroup,
+	but as they add extra members and need to be copied by value we
+	need to use a template here.
+	*/
+template <class T>
+struct MeshWithSmoothingGroups
+{
+	//! Vertex positions
+	std::vector<aiVector3D> mPositions;
+
+	//! Face lists
+	std::vector<T> mFaces;
+
+	//! List of normal vectors
+	std::vector<aiVector3D> mNormals;
+};
+
+// ---------------------------------------------------------------------------
+/** Computes normal vectors for the mesh
+ */
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh);
+
+
+// include implementations
+#include "SmoothingGroups.inl"
+
+#endif // !! AI_SMOOTHINGGROUPS_H_INC

+ 48 - 52
code/3DSGenNormals.cpp → code/SmoothingGroups.inl

@@ -39,80 +39,81 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 */
 */
 
 
-/** @file Implementation of the 3ds importer class */
-#include "3DSLoader.h"
-#include "MaterialSystem.h"
+/** @file Generation of normal vectors basing on smoothing groups */
+
+#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
+#define AI_SMOOTHINGGROUPS_INL_INCLUDED
+
+// internal headers
+#include "SGSpatialSort.h"
+
+// CRT header
 #include <algorithm>
 #include <algorithm>
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
-#include "../include/aiScene.h"
-#include "../include/aiAssert.h"
-#include "3DSSpatialSort.h"
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
 {
 {
 	// First generate face normals
 	// First generate face normals
-	sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D());
-	for( unsigned int a = 0; a < sMesh->mFaces.size(); a++)
+	sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D());
+	for( unsigned int a = 0; a < sMesh.mFaces.size(); a++)
 	{
 	{
-		Dot3DS::Face& face = sMesh->mFaces[a];
+		T& face = sMesh.mFaces[a];
 
 
 		// assume it is a triangle
 		// assume it is a triangle
-		aiVector3D* pV1 = &sMesh->mPositions[face.mIndices[0]];
-		aiVector3D* pV2 = &sMesh->mPositions[face.mIndices[1]];
-		aiVector3D* pV3 = &sMesh->mPositions[face.mIndices[2]];
+		aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]];
+		aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]];
+		aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]];
 
 
-		// FIX invert all vertex normals
-		aiVector3D pDelta1 = *pV3 - *pV1;
-		aiVector3D pDelta2 = *pV2 - *pV1;
+		aiVector3D pDelta1 = *pV2 - *pV1;
+		aiVector3D pDelta2 = *pV3 - *pV1;
 		aiVector3D vNor = pDelta1 ^ pDelta2;
 		aiVector3D vNor = pDelta1 ^ pDelta2;
 
 
-		sMesh->mNormals[face.mIndices[0]] = vNor;
-		sMesh->mNormals[face.mIndices[1]] = vNor;
-		sMesh->mNormals[face.mIndices[2]] = vNor;
+		sMesh.mNormals[face.mIndices[0]] = vNor;
+		sMesh.mNormals[face.mIndices[1]] = vNor;
+		sMesh.mNormals[face.mIndices[2]] = vNor;
 	}
 	}
 
 
 	// calculate the position bounds so we have a reliable epsilon to 
 	// calculate the position bounds so we have a reliable epsilon to 
 	// check position differences against 
 	// check position differences against 
 	// @Schrompf: This is the 6th time this snippet is repeated!
 	// @Schrompf: This is the 6th time this snippet is repeated!
 	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
 	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
-	for( unsigned int a = 0; a < sMesh->mPositions.size(); a++)
+	for( unsigned int a = 0; a < sMesh.mPositions.size(); a++)
 	{
 	{
-		minVec.x = std::min( minVec.x, sMesh->mPositions[a].x);
-		minVec.y = std::min( minVec.y, sMesh->mPositions[a].y);
-		minVec.z = std::min( minVec.z, sMesh->mPositions[a].z);
-		maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x);
-		maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y);
-		maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z);
+		minVec.x = std::min( minVec.x, sMesh.mPositions[a].x);
+		minVec.y = std::min( minVec.y, sMesh.mPositions[a].y);
+		minVec.z = std::min( minVec.z, sMesh.mPositions[a].z);
+		maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x);
+		maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y);
+		maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z);
 	}
 	}
 	const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
 	const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
 	std::vector<aiVector3D> avNormals;
 	std::vector<aiVector3D> avNormals;
-	avNormals.resize(sMesh->mNormals.size());
+	avNormals.resize(sMesh.mNormals.size());
 	
 	
 	// now generate the spatial sort tree
 	// now generate the spatial sort tree
-	D3DSSpatialSorter sSort;
-	for( std::vector<Dot3DS::Face>::iterator
-		i =  sMesh->mFaces.begin();
-		i != sMesh->mFaces.end();++i)
+	SGSpatialSort sSort;
+	for( std::vector<T>::iterator
+		i =  sMesh.mFaces.begin();
+		i != sMesh.mFaces.end();++i)
 	{
 	{
-		sSort.AddFace(&(*i),sMesh->mPositions);
+		sSort.Add(sMesh.mPositions[(*i).mIndices[0]],(*i).mIndices[0],(*i).iSmoothGroup);
+		sSort.Add(sMesh.mPositions[(*i).mIndices[1]],(*i).mIndices[1],(*i).iSmoothGroup);
+		sSort.Add(sMesh.mPositions[(*i).mIndices[2]],(*i).mIndices[2],(*i).iSmoothGroup);
 	}
 	}
 	sSort.Prepare();
 	sSort.Prepare();
 
 
-	for( std::vector<Dot3DS::Face>::iterator
-		i =  sMesh->mFaces.begin();
-		i != sMesh->mFaces.end();++i)
+	for( std::vector<T>::iterator
+		i =  sMesh.mFaces.begin();
+		i != sMesh.mFaces.end();++i)
 	{
 	{
 		std::vector<unsigned int> poResult;
 		std::vector<unsigned int> poResult;
 
 
 		for (unsigned int c = 0; c < 3;++c)
 		for (unsigned int c = 0; c < 3;++c)
 		{
 		{
-
-			sSort.FindPositions(sMesh->mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
+			sSort.FindPositions(sMesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup,
 				posEpsilon,poResult);
 				posEpsilon,poResult);
 
 
 			aiVector3D vNormals;
 			aiVector3D vNormals;
@@ -121,21 +122,16 @@ void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
 				a =  poResult.begin();
 				a =  poResult.begin();
 				a != poResult.end();++a)
 				a != poResult.end();++a)
 			{
 			{
-				vNormals += sMesh->mNormals[(*a)];
+				vNormals += sMesh.mNormals[(*a)];
 				fDiv += 1.0f;
 				fDiv += 1.0f;
 			}
 			}
-			vNormals.x /= fDiv;
-			vNormals.y /= fDiv;
-			vNormals.z /= fDiv;
-			vNormals.Normalize();
-
-			// do the common coordinate system adjustment
-			std::swap(vNormals.y,vNormals.z);
-
+			vNormals.x /= fDiv;vNormals.y /= fDiv;vNormals.z /= fDiv;
+			//vNormals.Normalize();
 			avNormals[(*i).mIndices[c]] = vNormals;
 			avNormals[(*i).mIndices[c]] = vNormals;
-			poResult.clear();
+			//poResult.clear();
 		}
 		}
 	}
 	}
-	sMesh->mNormals = avNormals;
-	return;
+	sMesh.mNormals = avNormals;
 }
 }
+
+#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED

+ 2 - 2
code/SplitLargeMeshes.cpp

@@ -101,7 +101,7 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene)
 void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp)
 void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp)
 {
 {
     // get the current value of the split property
     // get the current value of the split property
-	this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
+	this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Update a node after some meshes have been split
 // Update a node after some meshes have been split
@@ -373,7 +373,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene)
 // Setup properties
 // Setup properties
 void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp)
 void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp)
 {
 {
-	this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
+	this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 // Executes the post processing step on the given imported data.

+ 2 - 2
contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user

@@ -34,7 +34,7 @@
 			/>
 			/>
 		</Configuration>
 		</Configuration>
 		<Configuration
 		<Configuration
-			Name="Release|Win32"
+			Name="Debug|x64"
 			>
 			>
 			<DebugSettings
 			<DebugSettings
 				Command=""
 				Command=""
@@ -62,7 +62,7 @@
 			/>
 			/>
 		</Configuration>
 		</Configuration>
 		<Configuration
 		<Configuration
-			Name="Debug|x64"
+			Name="Release|Win32"
 			>
 			>
 			<DebugSettings
 			<DebugSettings
 				Command=""
 				Command=""

+ 1 - 0
include/IOStream.h

@@ -85,6 +85,7 @@ public:
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
     virtual size_t Tell(void) const = 0;
     virtual size_t Tell(void) const = 0;
 
 
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/**	Returns filesize
 	/**	Returns filesize
 	*
 	*

+ 60 - 9
include/aiConfig.h

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * whether a mesh must be splitted or not.
  * whether a mesh must be splitted or not.
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  *       the internal header file SplitLargeMeshes.h
  *       the internal header file SplitLargeMeshes.h
+ * Property type: integer.
  */
  */
 #define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT	"pp.slm.triangle_limit"
 #define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT	"pp.slm.triangle_limit"
 
 
@@ -61,6 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * whether a mesh must be splitted or not.
  * whether a mesh must be splitted or not.
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  *       the internal header file SplitLargeMeshes.h
  *       the internal header file SplitLargeMeshes.h
+ * Property type: integer.
  */
  */
 #define AI_CONFIG_PP_SLM_VERTEX_LIMIT	"pp.slm.vertex_limit"
 #define AI_CONFIG_PP_SLM_VERTEX_LIMIT	"pp.slm.vertex_limit"
 
 
@@ -71,6 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
  * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
  * \note The default value is AI_LBW_MAX_WEIGHTS, defined in
  * \note The default value is AI_LBW_MAX_WEIGHTS, defined in
  *       the internal header file LimitBoneWeightsProcess.h
  *       the internal header file LimitBoneWeightsProcess.h
+ * Property type: integer.
  */
  */
 #define AI_CONFIG_PP_LBW_MAX_WEIGHTS	"pp.lbw.weights_limit"
 #define AI_CONFIG_PP_LBW_MAX_WEIGHTS	"pp.lbw.weights_limit"
 
 
@@ -86,6 +89,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *   for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
  *   for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
  *   options (where XXX is a placeholder for the file format for which you
  *   options (where XXX is a placeholder for the file format for which you
  *   want to override the global setting).
  *   want to override the global setting).
+ * Property type: integer.
  */
  */
 #define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME	"imp.global.kf"
 #define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME	"imp.global.kf"
 
 
@@ -101,7 +105,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** \brief Causes the 3DS loader to ignore pivot points in the file
 /** \brief Causes the 3DS loader to ignore pivot points in the file
  *
  *
  * There are some faulty 3DS files which look only correctly with
  * There are some faulty 3DS files which look only correctly with
- * pivot points disabled 
+ * pivot points disabled.
+ * Property type: integer (1: true; !1: false).
  */
  */
 #define AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT	"imp.3ds.nopivot"
 #define AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT	"imp.3ds.nopivot"
 
 
@@ -112,6 +117,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * LightWave represents the gradients with infinite detail,
  * LightWave represents the gradients with infinite detail,
  * but for use in realtime the loader computes replacement textures.
  * but for use in realtime the loader computes replacement textures.
  * The default size is 512 * 512.
  * The default size is 512 * 512.
+ * Property type: integer. 
  */
  */
 #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESX	"imp.lwo.gradres_x"
 #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESX	"imp.lwo.gradres_x"
 #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESY	"imp.lwo.gradres_y"
 #define AI_CONFIG_IMPORT_LWO_GRADIENT_RESY	"imp.lwo.gradres_y"
@@ -121,8 +127,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *         that their tangents and bitangents are smoothed.
  *         that their tangents and bitangents are smoothed.
  *
  *
  * This applies to the CalcTangentSpace-Step. The angle is specified
  * This applies to the CalcTangentSpace-Step. The angle is specified
- * in degrees * 1000, so 180000 is PI. The default value is
- * 45 degrees. The maximum value is 180000.
+ * in degrees , so 180 is PI. The default value is
+ * 45 degrees. The maximum value is 180.
+ * Property type: float. 
  */
  */
 #define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE "pp.ct.max_smoothing"
 #define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE "pp.ct.max_smoothing"
 
 
@@ -131,15 +138,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *         at the same vertex position that their are smoothed.
  *         at the same vertex position that their are smoothed.
  *
  *
  * This applies to the GenSmoothNormals-Step. The angle is specified
  * This applies to the GenSmoothNormals-Step. The angle is specified
- * in degrees * 1000, so 180000 is PI. The default value is
- * 180 degrees (all vertex normals are smoothed). The maximum value is 180000
- * \note This can be manually overriden by loaders via #aiMesh::mMaxSmoothingAngle;
+ * in degrees, so 180 is PI. The default value is
+ * 180 degrees (all vertex normals are smoothed). The maximum value is 180
+ * Property type: float. 
  */
  */
 #define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE "pp.gsn.max_smoothing"
 #define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE "pp.gsn.max_smoothing"
 
 
 
 
-#define AI_CONFIG_PP_OG_MAX_DEPTH			"pp.og.max_depth"
-#define AI_CONFIG_PP_OG_MIN_TRIS_PER_NODE	"pp.og.min_tris"
-#define AI_CONFIG_PP_OG_MAXIMALLY_SMALL		"pp.og.maximally_small"
+// ---------------------------------------------------------------------------
+/** \brief Specifies the minimum number of faces a node should have.
+ *         This is an input parameter to the OptimizeGraph-Step.
+ *
+ * Nodes whose referenced meshes have less faces than this value
+ * are propably joined with neighbors with identical world matrices.
+ * However, it is just a hint to the step.
+ * Property type: integer 
+ */
+#define AI_CONFIG_PP_OG_MIN_NUM_FACES		"pp.og.min_faces"
+
+
+// ---------------------------------------------------------------------------
+/** \brief Specifies whether animations are removed from the asset.
+ *         This is an input parameter to the OptimizeGraph-Step.
+ *
+ * If an application does not need the animation data, erasing it at the
+ * beginning of the post-process pipeline allows some steps - including
+ * OptimizeGraph itself - to apply further optimizations.
+ * Property type: integer (1: true; !1: false).
+ */
+#define AI_CONFIG_PP_OG_REMOVE_ANIMATIONS	"pp.og.remove_anims"
+
+
+// ---------------------------------------------------------------------------
+/** \brief Specifies whether the OptimizeGraphProcess joins nodes even if
+ *         their local transformations are inequal.
+ *
+ * By default, nodes with different local transformations are never joined.
+ * The intention is that all vertices should remain in their original
+ * local coordinate space where they are correctly centered and aligned,
+ * which does also allow for some significant culling improvements.
+ */
+#define AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS	"pp.og.allow_diffwm"
+
+
+// ---------------------------------------------------------------------------
+/** \brief Sets the colormap (= palette) to be used to decode embedded
+ *         textures in MDL files.
+ *
+ * This must be a valid path to a file. The file is 768 (256*3) bytes
+ * large and contains RGB tripels for each of the 256 palette entries.
+ * The default value is colormap.lmp. If the file is nto found,
+ * a default palette is used.
+ * Property type: string.
+ */
+#define AI_CONFIG_IMPORT_MDL_COLORMAP		"imp.mdl.color_map"
 
 
 #endif // !! AI_CONFIG_H_INC
 #endif // !! AI_CONFIG_H_INC

+ 32 - 17
include/aiFileIO.h

@@ -53,11 +53,13 @@ extern "C" {
 
 
 
 
 struct aiFileIO;
 struct aiFileIO;
+struct aiFile;
 
 
-typedef aiFileIO (*aiFileOpenProc)(C_STRUCT aiFileIO*, const char*, const char*);
-typedef aiReturn (*aiFileCloseProc)(C_STRUCT aiFileIO*);
-typedef unsigned long (*aiFileReadWriteProc)(C_STRUCT aiFileIO*, char*, unsigned int, unsigned int);
-typedef unsigned long (*aiFileTellProc)(C_STRUCT aiFileIO*);
+typedef aiFile* (*aiFileOpenProc)(C_STRUCT aiFileIO*, const char*, const char*);
+typedef void (*aiFileCloseProc)(C_STRUCT aiFileIO*, C_STRUCT aiFile*);
+typedef size_t (*aiFileWriteProc)(C_STRUCT aiFile*, const char*, size_t, size_t);
+typedef size_t (*aiFileReadProc)(C_STRUCT aiFile*, char*, size_t,size_t);
+typedef size_t (*aiFileTellProc)(C_STRUCT aiFile*);
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Define seek origins in fseek()-style.
 /** Define seek origins in fseek()-style.
@@ -70,41 +72,54 @@ enum aiOrigin
 	aiOrigin_END = 0x2		//!< End of file
 	aiOrigin_END = 0x2		//!< End of file
 };
 };
 
 
-typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin);
-
+typedef aiReturn (*aiFileSeek)(aiFile*, size_t, aiOrigin);
 typedef char* aiUserData;
 typedef char* aiUserData;
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions
-*
-* The functions behave the same way as their appropriate fXXXX 
-* counterparts in the CRT.
+/** Defines how C-Assimp accesses files. Provided are functions to open
+ *  and close files.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 struct aiFileIO
 struct aiFileIO
 {
 {
-	//! User data assigned to the structure
-	aiUserData UserData;
-
 	//! Function used to open a new file
 	//! Function used to open a new file
-	aiFileOpenProc OpenFunc;
+	aiFileOpenProc OpenProc;
 
 
 	//! Function used to close an existing file
 	//! Function used to close an existing file
-	aiFileCloseProc CloseFunc;
+	aiFileCloseProc CloseProc;
 
 
+	//! User-defined data
+	aiUserData UserData;
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions
+*
+* The functions behave the same way as their appropriate fXXXX 
+* counterparts in the CRT.
+*/
+// ---------------------------------------------------------------------------
+struct aiFile
+{
 	//! Function used to read from a file
 	//! Function used to read from a file
-	aiFileReadWriteProc ReadFunc;
+	aiFileReadProc ReadProc;
 
 
 	//! Function used to write to a file
 	//! Function used to write to a file
-	aiFileReadWriteProc WriteFunc;
+	aiFileWriteProc WriteProc;
 
 
 	//! Function used to retrieve the current
 	//! Function used to retrieve the current
 	//! position of the file cursor (ftell())
 	//! position of the file cursor (ftell())
 	aiFileTellProc TellProc;
 	aiFileTellProc TellProc;
 
 
+	//! Function used to retrieve the size of the file, in bytes
+	aiFileTellProc FileSizeProc;
+
 	//! Function used to set the current position
 	//! Function used to set the current position
 	//! of the file cursor (fseek())
 	//! of the file cursor (fseek())
 	aiFileSeek SeekProc;
 	aiFileSeek SeekProc;
+
+	//! User-defined data
+	aiUserData UserData;
 };
 };
 
 
 
 

+ 0 - 11
include/aiMesh.h

@@ -356,16 +356,6 @@ struct aiMesh
 	 */
 	 */
 	unsigned int mMaterialIndex;
 	unsigned int mMaterialIndex;
 
 
-	/** The maximum vertex smooth angle for the mesh, in radians
-	 *  If the angle between two vertex normals is larger,
-	 *  the vertex normals should not be smoothed. The GenVertexNormals-Step
-	 *  takes care of this value. The angle is specified in radians.
-	 *  It is set to AI_MESH_SMOOTHING_ANGLE_NOT_SET if the source file didn't
-	 *  contain any additional information related to the calculation of
-	 *  vertex normals.
-	 */
-	float mMaxSmoothingAngle;
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 
 
 	//! Default constructor. Initializes all members to 0
 	//! Default constructor. Initializes all members to 0
@@ -384,7 +374,6 @@ struct aiMesh
 			mColors[a] = NULL;
 			mColors[a] = NULL;
 		mNumBones = 0; mBones = NULL;
 		mNumBones = 0; mBones = NULL;
 		mMaterialIndex = 0;
 		mMaterialIndex = 0;
-		mMaxSmoothingAngle = AI_MESH_SMOOTHING_ANGLE_NOT_SET;
 	}
 	}
 
 
 	//! Deletes all storage allocated for the mesh
 	//! Deletes all storage allocated for the mesh

+ 55 - 3
include/aiPostProcess.h

@@ -169,14 +169,66 @@ enum aiPostProcessSteps
 	 * the bounding box of all vertices + their normals is compared against
 	 * the bounding box of all vertices + their normals is compared against
 	 * the volume of the bounding box of all vertices without their normals.
 	 * the volume of the bounding box of all vertices without their normals.
 	 * This works well for most objects, problems might occur with planar
 	 * This works well for most objects, problems might occur with planar
-	 * surfaces. However the step tries to filter such cases out.
+	 * surfaces. However, the step tries to filter such cases.
 	 * The step inverts all infacing normals. Generally it is recommended
 	 * The step inverts all infacing normals. Generally it is recommended
-	 * to enable this step.
+	 * to enable this step, although the result is not always correct.
 	*/
 	*/
-	aiProcess_FixInfacingNormals = 0x2000
+	aiProcess_FixInfacingNormals = 0x2000,
+
+	/** This step performs some optimizations on the node graph.
+	 * 
+	 * It is incompatible to the PreTransformVertices-Step. Some configuration
+	 * options exist, see aiConfig.h for more details. 
+	 * Generally, two actions are available:<br>
+	 *   1. Remove animation nodes and data from the scene. This allows other
+	 *      steps for further optimizations.<br>
+	 *   2. Combine very small meshes to larger ones. Only if the meshes
+	 *      are used by the same node or by nodes on the same hierarchy (with
+	 *      equal local transformations). Unlike PreTransformVertices, the
+	 *      OptimizeGraph-step doesn't transform vertices from one space 
+	 *      another.<br>
+	 *   3. Remove hierarchy levels<br>
+	 *
+	 *  It is recommended to have this step run with the default configuration.
+	 */
+	aiProcess_OptimizeGraph = 0x4000
 };
 };
 
 
 
 
+/** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST
+ *  @brief Default postprocess configuration targeted at realtime applications
+ *    which need to load models as fast as possible.
+ *  
+ *  If you're using DirectX, don't forget to combine this value with
+ * the #aiProcess_ConvertToLeftHanded step.
+ */
+#define AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST \
+	aiProcess_CalcTangentSpace		|  \
+	aiProcess_GenNormals			|  \
+	aiProcess_JoinIdenticalVertices |  \
+	aiProcess_Triangulate
+
+
+ /** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST
+ *  @brief Default postprocess configuration targeted at realtime applications.
+ *    Unlike AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST, this configuration
+ *    performs some extra optimizations.
+ *  
+ *  If you're using DirectX, don't forget to combine this value with
+ * the #aiProcess_ConvertToLeftHanded step.
+ */
+#define AI_POSTPROCESS_DEFAULT_REALTIME \
+	aiProcess_CalcTangentSpace				|  \
+	aiProcess_GenSmoothNormals				|  \
+	aiProcess_JoinIdenticalVertices			|  \
+	aiProcess_ImproveCacheLocality			|  \
+	aiProcess_LimitBoneWeights				|  \
+	aiProcess_RemoveRedundantMaterials      |  \
+	aiProcess_SplitLargeMeshes				|  \
+	aiProcess_OptimizeGraph					|  \
+	aiProcess_Triangulate
+
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // end of extern "C"
 } // end of extern "C"
 #endif
 #endif

+ 3 - 0
include/aiVector3D.h

@@ -75,6 +75,9 @@ struct aiVector3D
 	inline bool operator!= (const aiVector3D& other) const
 	inline bool operator!= (const aiVector3D& other) const
 		{return x != other.x || y != other.y || z != other.z;}
 		{return x != other.x || y != other.y || z != other.z;}
 
 
+	inline aiVector3D& operator= (float f)
+		{x = y = z = f;return *this;}
+
 #endif // __cplusplus
 #endif // __cplusplus
 
 
 	float x, y, z;	
 	float x, y, z;	

+ 37 - 11
include/assimp.h

@@ -66,8 +66,8 @@ struct aiString;
 * @param pFile Path and filename of the file to be imported, 
 * @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.
 *   expected to be a null-terminated c-string. NULL is not a valid value.
 * @param pFlags Optional post processing steps to be executed after 
 * @param pFlags Optional post processing steps to be executed after 
-*   a successful import. Provide a bitwise combination of the #aiPostProcessSteps
-*   flags.
+*   a successful import. Provide a bitwise combination of the 
+*   #aiPostProcessSteps flags.
 * @return Pointer to the imported data or NULL if the import failed. 
 * @return Pointer to the imported data or NULL if the import failed. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -85,18 +85,19 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFile( const char* pFile,
 * done with it, call aiReleaseImport() to free the resources associated with 
 * done with it, call aiReleaseImport() to free the resources associated with 
 * this file. If the import fails, NULL is returned instead. Call 
 * this file. If the import fails, NULL is returned instead. Call 
 * aiGetErrorString() to retrieve a human-readable error text.
 * aiGetErrorString() to retrieve a human-readable error text.
-* @param pFile aiFileIO structure.  All functions pointers must be
-*   initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc()
-*   will be used to open other files in the fs if the asset to be 
-*   loaded depends on them. NULL is not a valid value.
-* @return Pointer to the imported data or NULL if the import failed. 
-*
-* @note The C-API creates a new Importer instance internally for each call 
-* to this function. Therefore the C-API is thread-safe. 
+* @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.
+* @return Pointer to the imported data or NULL if the import failed.  
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( 
 ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( 
-	const C_STRUCT aiFileIO* pFile);
+	const char* pFile, unsigned int pFlags,
+	C_STRUCT aiFileIO* pFS);
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -151,6 +152,31 @@ ASSIMP_API void aiGetExtensionList(C_STRUCT aiString* szOut);
 ASSIMP_API void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
 ASSIMP_API void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
 	C_STRUCT aiMemoryInfo* in);
 	C_STRUCT aiMemoryInfo* in);
 
 
+
+// ---------------------------------------------------------------------------
+/** Set an integer property. This is the C-version of 
+ *  #Importer::SetPropertyInteger(). In the C-API properties are shared by
+ *  all imports. It is not possible to specify them per asset.
+ *
+ * \param szName Name of the configuration property to be set. All constants
+ *   are defined in the aiConfig.h header file.
+ * \param value New value for the property
+ */
+// ---------------------------------------------------------------------------
+ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value);
+
+// ---------------------------------------------------------------------------
+/**  @see aiSetImportPropertyInteger()
+ */
+ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value);
+
+// ---------------------------------------------------------------------------
+/**  @see aiSetImportPropertyInteger()
+ */
+ASSIMP_API void aiSetImportPropertyString(const char* szName,
+	const C_STRUCT aiString* st);
+
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 83 - 35
include/assimp.hpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 // STL headers
 // STL headers
 #include <string>
 #include <string>
+#include <map>
 #include <vector>
 #include <vector>
 
 
 // public ASSIMP headers
 // public ASSIMP headers
@@ -67,6 +68,8 @@ namespace Assimp
 #define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff
 #define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff
 
 
 struct aiScene;
 struct aiScene;
+struct aiFileIO;
+const aiScene* aiImportFileEx( const char*, unsigned int, aiFileIO*);
 
 
 namespace Assimp
 namespace Assimp
 {
 {
@@ -101,28 +104,14 @@ class ASSIMP_API Importer
 {
 {
 	// used internally
 	// used internally
 	friend class BaseProcess;
 	friend class BaseProcess;
+	friend const aiScene* ::aiImportFileEx( const char*, unsigned int, aiFileIO*);
 
 
-protected:
-
-	template <typename Type>
-	struct PropertyInfo
-	{
-		std::string name;
-		Type value;
-
-		bool operator==(const PropertyInfo<Type>& other) const
-		{
-			return other.name == this->name &&
-				other.value == this->value;
-		}
-
-		bool operator!=(const PropertyInfo<Type>& other) const
-		{
-			return !(other == *this);
-		}
-	};
+public:
 
 
-	typedef PropertyInfo<int> IntPropertyInfo;
+	typedef uint32_t KeyType;
+	typedef std::map<KeyType, int>  		IntPropertyMap;
+	typedef std::map<KeyType, float>		FloatPropertyMap;
+	typedef std::map<KeyType, std::string>	StringPropertyMap;
 
 
 public:
 public:
 
 
@@ -188,28 +177,66 @@ public:
 #endif
 #endif
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Set a configuration property.
+	/** Set an integer configuration property.
 	 * @param szName Name of the property. All supported properties
 	 * @param szName Name of the property. All supported properties
-	 *   are defined in the aiConfig.g header (the constants share the
+	 *   are defined in the aiConfig.g header (all constants share the
 	 *   prefix AI_CONFIG_XXX).
 	 *   prefix AI_CONFIG_XXX).
 	 * @param iValue New value of the property
 	 * @param iValue New value of the property
-	 * @return Old value of the property or AI_PROPERTY_WAS_NOT_EXISTING
-     * if the property has not yet been set.
+	 * @param bWasExisting Optional pointer to receive true if the
+	 *   property was set before. The new value replaced the old 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.
 	 */
 	 */
-	int SetProperty(const char* szName, int iValue);
+	void SetPropertyInteger(const char* szName, int iValue, 
+		bool* bWasExisting = NULL);
+
+	// -------------------------------------------------------------------
+	/** 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);
 
 
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Get a configuration property.
 	/** Get a configuration property.
 	 * @param szName Name of the property. All supported properties
 	 * @param szName Name of the property. All supported properties
-	 *   are defined in the aiConfig.g header (the constants start
-	 *   with AI_CONFIG_XXX).
-	 * @param iErrorReturn Value that is returned if the property
-	 *   is not found. Note that this value, not the default value
-	 *   for the requested property is returned!
+	 *   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
 	 * @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 GetProperty(const char* szName, int iErrorReturn = 0xffffffff) const;
+	int GetPropertyInteger(const char* szName, 
+		int iErrorReturn = 0xffffffff) const;
+
+	// -------------------------------------------------------------------
+	/** Get a floating-point configuration property
+	 * @see GetPropertyInteger()
+	 */
+	float GetPropertyFloat(const char* szName, 
+		float fErrorReturn = 10e10f) const;
+
+	// -------------------------------------------------------------------
+	/** Get a string configuration property
+	 * @see GetPropertyInteger()
+	 */
+	std::string GetPropertyString(const char* szName,
+		const std::string& sErrorReturn = "") const;
 
 
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
@@ -278,10 +305,11 @@ public:
 
 
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Returns whether a given file extension is supported by ASSIMP
+	/** Returns whether a given file extension is supported by ASSIMP.
 	*
 	*
 	* @param szExtension Extension to be checked.
 	* @param szExtension Extension to be checked.
-	*   Must include a leading dot '.'. Example: ".3ds", ".md3"
+	*   Must include a trailing dot '.'. Example: ".3ds", ".md3".
+	*   Cases-insensitive.
 	* @return true if the extension is supported, false otherwise
 	* @return true if the extension is supported, false otherwise
 	*/
 	*/
 	bool IsExtensionSupported(const std::string& szExtension);
 	bool IsExtensionSupported(const std::string& szExtension);
@@ -298,6 +326,18 @@ public:
 	void GetExtensionList(std::string& szOut);
 	void GetExtensionList(std::string& szOut);
 
 
 
 
+	// -------------------------------------------------------------------
+	/** Find the loader corresponding to a specific file extension.
+	*
+	*  This is quite similar to IsExtensionSupported() except a
+	*  BaseImporter instance is returned.
+	*  @param szExtension Extension to be checke, cases insensitive,
+	*    must include a trailing dot.
+	*  @return NULL if there is no loader for the extension.
+	*/
+	BaseImporter* FindLoader (const std::string& szExtension);
+
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Returns the scene loaded by the last successful call to ReadFile()
 	/** Returns the scene loaded by the last successful call to ReadFile()
 	*
 	*
@@ -351,9 +391,17 @@ protected:
 	std::string mErrorString;
 	std::string mErrorString;
 
 
 	/** List of integer properties */
 	/** List of integer properties */
-	std::vector<IntPropertyInfo> mIntProperties;
+	IntPropertyMap mIntProperties;
+
+	/** List of floating-point properties */
+	FloatPropertyMap mFloatProperties;
+
+	/** List of string properties */
+	StringPropertyMap mStringProperties;
 
 
-	/** Used for testing */
+	/** Used for testing - extra verbose mode causes the
+	    validateDataStructure-Step to be executed before
+		and after every single postprocess step */
 	bool bExtraVerbose;
 	bool bExtraVerbose;
 };
 };
 
 

+ 60 - 11
port/jAssimp/src/assimp/ConfigProperty.java

@@ -44,7 +44,7 @@ package assimp;
 
 
 /**
 /**
  * Defines configuration properties.
  * Defines configuration properties.
- *
+ * <p/>
  * Static helper class, can't be instanced. It defines configuration
  * Static helper class, can't be instanced. It defines configuration
  * property keys to be used with <code> Importer.setPropertyInt</code>
  * property keys to be used with <code> Importer.setPropertyInt</code>
  *
  *
@@ -57,7 +57,6 @@ public class ConfigProperty {
     }
     }
 
 
 
 
-
     /**
     /**
      * Default value for the <code>CONFIG_PP_SLM_TRIANGLE_LIMIT</code>
      * Default value for the <code>CONFIG_PP_SLM_TRIANGLE_LIMIT</code>
      * configuration property.
      * configuration property.
@@ -79,15 +78,13 @@ public class ConfigProperty {
     public static final int DEFAULT_LBW_MAX_WEIGHTS = 4;
     public static final int DEFAULT_LBW_MAX_WEIGHTS = 4;
 
 
 
 
-
-
-
     /**
     /**
      * Set the maximum number of vertices in a mesh.
      * Set the maximum number of vertices in a mesh.
      * <p/>
      * <p/>
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * whether a mesh must be splitted or not.
      * whether a mesh must be splitted or not.
-     * \note The default value is <code>DEFAULT_SLM_MAX_TRIANGLES</code>
+     * note: The default value is <code>DEFAULT_SLM_MAX_TRIANGLES</code>.
+     * The type of the property is int.
      */
      */
     public static final String CONFIG_PP_SLM_TRIANGLE_LIMIT
     public static final String CONFIG_PP_SLM_TRIANGLE_LIMIT
             = "pp.slm.triangle_limit";
             = "pp.slm.triangle_limit";
@@ -98,7 +95,8 @@ public class ConfigProperty {
      * <p/>
      * <p/>
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * whether a mesh must be splitted or not.
      * whether a mesh must be splitted or not.
-     * \note The default value is <code>DEFAULT_SLM_MAX_VERTICES</code>
+     * note: The default value is <code>DEFAULT_SLM_MAX_VERTICES</code>.
+     * The type of the property is int.
      */
      */
     public static final String CONFIG_PP_SLM_VERTEX_LIMIT
     public static final String CONFIG_PP_SLM_VERTEX_LIMIT
             = "pp.slm.vertex_limit";
             = "pp.slm.vertex_limit";
@@ -108,7 +106,8 @@ public class ConfigProperty {
      * Set the maximum number of bones affecting a single vertex
      * Set the maximum number of bones affecting a single vertex
      * <p/>
      * <p/>
      * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
      * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
-     * \note The default value is <code>DEFAULT_LBW_MAX_WEIGHTS</code>
+     * note :The default value is <code>DEFAULT_LBW_MAX_WEIGHTS</code>.
+     * The type of the property is int.
      */
      */
     public static final String CONFIG_PP_LBW_MAX_WEIGHTS
     public static final String CONFIG_PP_LBW_MAX_WEIGHTS
             = "pp.lbw.weights_limit";
             = "pp.lbw.weights_limit";
@@ -126,6 +125,7 @@ public class ConfigProperty {
      * <code>CONFIG_IMPORT_XXX_KEYFRAME</code> options (where XXX is a
      * <code>CONFIG_IMPORT_XXX_KEYFRAME</code> options (where XXX is a
      * placeholder for the file format for which you want to override the
      * placeholder for the file format for which you want to override the
      * global setting).
      * global setting).
+     * The type of the property is int.
      */
      */
     public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME
     public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME
             = "imp.global.kf";
             = "imp.global.kf";
@@ -144,11 +144,60 @@ public class ConfigProperty {
      * There are some faulty 3DS files on the internet which look
      * There are some faulty 3DS files on the internet which look
      * only correctly with pivot points disabled. By default,
      * only correctly with pivot points disabled. By default,
      * this option is disabled.
      * this option is disabled.
+     * note: This is a boolean property stored as an integer, 0 is false
      */
      */
     public static final String CONFIG_IMPORT_3DS_IGNORE_PIVOT
     public static final String CONFIG_IMPORT_3DS_IGNORE_PIVOT
             = "imp.3ds.nopivot";
             = "imp.3ds.nopivot";
 
 
-    public static final String CONFIG_PP_OG_MAX_DEPTH = "pp.og.max_depth";
-    public static final String CONFIG_PP_OG_MIN_TRIS_PER_NODE = "pp.og.min_tris";
-    public static final String CONFIG_PP_OG_MAXIMALLY_SMALL = "pp.og.maximally_small";
+
+    /**
+     * Specifies the maximum angle that may be between two vertex tangents
+     * that their tangents and bitangents are smoothed.
+     * <p/>
+     * This applies to the CalcTangentSpace-Step. The angle is specified
+     * in degrees, so 180 is PI. The default value is
+     * 45 degrees. The maximum value is 180.f
+     * The type of the property is float.
+     */
+    public static final String AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE
+            = "pp.ct.max_smoothing";
+
+
+    /**
+     * Specifies the maximum angle that may be between two face normals
+     * at the same vertex position that their are smoothed.
+     * <p/>
+     * This applies to the GenSmoothNormals-Step. The angle is specified
+     * in degrees * 1000, so 180.f is PI. The default value is
+     * 180 degrees (all vertex normals are smoothed). The maximum value is 180.f
+     * The type of the property is float.
+     */
+    public static final String AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE
+            = "pp.gsn.max_smoothing";
+
+
+    /**
+     * Specifies the minimum number of faces a node should have.
+     * This is an input parameter to the OptimizeGraph-Step.
+     * <p/>
+     * Nodes whose referenced meshes have less faces than this value
+     * are propably joined with neighbors with identical world matrices.
+     * However, it is just a hint to the step.
+     * The type of the property is int.
+     */
+    public static final String AI_CONFIG_PP_OG_MIN_NUM_FACES
+            = "pp.og.min_faces";
+
+
+    /** \brief Specifies whether animations are removed from the asset.
+     *         This is an input parameter to the OptimizeGraph-Step.
+     *
+     * If an application does not need the animation data, erasing it at the
+     * beginning of the post-process pipeline allows some steps - including
+     * OptimizeGraph itself - to apply further optimizations.
+     * note: This is a boolean property stored as an integer, 0 is false
+     */
+    public static final String AI_CONFIG_PP_OG_REMOVE_ANIMATIONS
+            = "pp.og.remove_anims";
+
 }
 }

+ 110 - 36
port/jAssimp/src/assimp/Importer.java

@@ -71,6 +71,38 @@ public class Importer {
         Type value;
         Type value;
     }
     }
 
 
+
+    /**
+     * Represents a property list
+     */
+    private class PropertyList<Type> extends Vector<Property<Type>> {
+
+        public void setProperty(final String prop, final Type val) {
+
+            for (Property<Type> i : this) {
+                if (i.key.equals(prop)) {
+                    i.value = val;
+                    return;
+                }
+            }
+
+            Property<Type> propNew = new Property<Type>();
+            propNew.key = prop;
+            propNew.value = val;
+            this.add(propNew);
+        }
+
+        public Type getProperty(final String prop) {
+
+            for (Property<Type> i : this) {
+                if (i.key.equals(prop)) {
+                    return i.value;
+                }
+            }
+            return null;
+        }
+    }
+
     /**
     /**
      * Default implementation of <code>IOStream</code>.
      * Default implementation of <code>IOStream</code>.
      * <br>
      * <br>
@@ -158,12 +190,14 @@ public class Importer {
     /**
     /**
      * I/O system to be used
      * I/O system to be used
      */
      */
-    private IOSystem ioSystem = null;
+    private IOSystem ioSystem = new DefaultIOSystem();
 
 
     /**
     /**
-     * List of config properties
+     * List of config properties for all supported types: int, float and string
      */
      */
-    private Vector<Property<Integer>> properties;
+    private PropertyList<Integer> properties = new PropertyList<Integer>();
+    private PropertyList<Float> propertiesFloat = new PropertyList<Float>();
+    private PropertyList<String> propertiesString = new PropertyList<String>();
 
 
 
 
     /**
     /**
@@ -186,14 +220,11 @@ public class Importer {
      *
      *
      * @param iVersion Version of the JNI interface to be used.
      * @param iVersion Version of the JNI interface to be used.
      * @throws NativeException Thrown if the jassimp library could not be loaded
      * @throws NativeException Thrown if the jassimp library could not be loaded
-     *                     or if the entry point to the module wasn't found. if this exception
-     *                     is not thrown, you can assume that jAssimp is fully available.
+     *                         or if the entry point to the module wasn't found. if this exception
+     *                         is not thrown, you can assume that jAssimp is fully available.
      */
      */
     public Importer(int iVersion) throws NativeException {
     public Importer(int iVersion) throws NativeException {
 
 
-        // allocate a default I/O system
-        ioSystem = new DefaultIOSystem();
-
         if (!bLibInitialized) {
         if (!bLibInitialized) {
 
 
             /** try to load the jassimp library. First try to load the
             /** try to load the jassimp library. First try to load the
@@ -319,7 +350,7 @@ public class Importer {
      * @param path Path to the file to be read
      * @param path Path to the file to be read
      * @return null if the import failed, otherwise a valid Scene instance
      * @return null if the import failed, otherwise a valid Scene instance
      * @throws NativeException This exception is thrown when an unknown error
      * @throws NativeException This exception is thrown when an unknown error
-     *                     occurs in the JNI bridge module.
+     *                         occurs in the JNI bridge module.
      */
      */
     public final Scene readFile(String path) throws NativeException {
     public final Scene readFile(String path) throws NativeException {
         this.scene = new Scene(this);
         this.scene = new Scene(this);
@@ -346,8 +377,10 @@ public class Importer {
             else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100;
             else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100;
             else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200;
             else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200;
             else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400;
             else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400;
-            else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x800;
-            else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x1600;
+            else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x800;
+            else if (step.equals(PostProcessStep.RemoveRedundantMaterials)) flags |= 0x1000;
+            else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x2000;
+            else if (step.equals(PostProcessStep.OptimizeGraph)) flags |= 0x4000;
         }
         }
 
 
         // now load the mesh
         // now load the mesh
@@ -418,30 +451,39 @@ public class Importer {
      *
      *
      * @param prop Name of the config property
      * @param prop Name of the config property
      * @param val  New value for the config property
      * @param val  New value for the config property
-     * @return Old value of the property or <code>PROPERTY_WAS_NOT_EXISTING</code>
-     *         if the property has not yet been set.
      */
      */
-    public final int setPropertyInt(final String prop, int val) {
+    public final void setPropertyInt(final String prop, int val) {
 
 
-        for (Property<Integer> i : this.properties) {
-            if (i.key.equals(prop)) {
-                int old = i.value;
-                i.value = val;
+        this.properties.setProperty(prop, val);
+        this._NativeSetPropertyInt(prop, val, this.getContext());
+    }
 
 
-                // make sure all changes are sent to the native implementation
-                this._NativeSetPropertyInt(prop, val, this.getContext());
-                return old;
-            }
-        }
 
 
-        Property<Integer> propNew = new Property<Integer>();
-        propNew.key = prop;
-        propNew.value = val;
-        this.properties.add(propNew);
+    /**
+     * Set a floating-point property. All supported config properties are
+     * defined as constants in the <code>ConfigProperty</code> class
+     *
+     * @param prop Name of the config property
+     * @param val  New value for the config property
+     */
+    public final void setPropertyFloat(final String prop, float val) {
 
 
-        // make sure all changes are sent to the native implementation
-        this._NativeSetPropertyInt(prop, val, this.getContext());
-        return PROPERTY_WAS_NOT_EXISTING;
+        this.propertiesFloat.setProperty(prop, val);
+        this._NativeSetPropertyFloat(prop, val, this.getContext());
+    }
+
+
+    /**
+     * Set a string property. All supported config properties are
+     * defined as constants in the <code>ConfigProperty</code> class
+     *
+     * @param prop Name of the config property
+     * @param val  New value for the config property
+     */
+    public final void setPropertyString(final String prop, String val) {
+
+        this.propertiesString.setProperty(prop, val);
+        this._NativeSetPropertyString(prop, val, this.getContext());
     }
     }
 
 
 
 
@@ -457,12 +499,36 @@ public class Importer {
      */
      */
     public final int getPropertyInt(final String prop, int error_return) {
     public final int getPropertyInt(final String prop, int error_return) {
 
 
-        for (Property<Integer> i : this.properties) {
-            if (i.key.equals(prop)) {
-                return i.value;
-            }
-        }
-        return error_return;
+        Integer i = this.properties.getProperty(prop);
+        return i != null ? i : error_return;
+    }
+
+
+    /**
+     * Gets a floating-point config property that has been set using
+     * <code>setPropertyFloat</code>. All supported config properties are
+     * defined as constants in the <code>ConfigProperty</code> class
+     *
+     * @see <code>getPropertyInt</code>
+     */
+    public final float getPropertyFloat(final String prop, float error_return) {
+
+        Float i = this.propertiesFloat.getProperty(prop);
+        return i != null ? i : error_return;
+    }
+
+
+    /**
+     * Gets a string config property that has been set using
+     * <code>setPropertyString</code>. All supported config properties are
+     * defined as constants in the <code>ConfigProperty</code> class
+     *
+     * @see <code>getPropertyInt</code>
+     */
+    public final String getPropertyString(final String prop, String error_return) {
+
+        String i = this.propertiesString.getProperty(prop);
+        return i != null ? i : error_return;
     }
     }
 
 
     /**
     /**
@@ -531,4 +597,12 @@ public class Importer {
      * @return 0xffffffff if an error occured
      * @return 0xffffffff if an error occured
      */
      */
     private native int _NativeSetPropertyInt(String name, int prop, long iContext);
     private native int _NativeSetPropertyInt(String name, int prop, long iContext);
+
+    // float-version
+    private native int _NativeSetPropertyFloat(String name,
+       float prop, long iContext);
+
+    // String-version
+    private native int _NativeSetPropertyString(String name,
+       String prop, long iContext);
 }
 }

+ 40 - 14
port/jAssimp/src/assimp/PostProcessStep.java

@@ -174,7 +174,6 @@ public class PostProcessStep {
     public static final PostProcessStep LimitBoneWeights =
     public static final PostProcessStep LimitBoneWeights =
             new PostProcessStep("LimitBoneWeights");
             new PostProcessStep("LimitBoneWeights");
 
 
-
     /**
     /**
      * Validates the aiScene data structure before it is returned.
      * Validates the aiScene data structure before it is returned.
      * This makes sure that all indices are valid, all animations and
      * This makes sure that all indices are valid, all animations and
@@ -187,19 +186,6 @@ public class PostProcessStep {
             new PostProcessStep("ValidateDataStructure");
             new PostProcessStep("ValidateDataStructure");
 
 
 
 
-    /** This step tries to determine which meshes have normal vectors
-	 * that are facing inwards. The algorithm is simple but effective:
-	 * the bounding box of all vertices + their normals is compared against
-	 * the volume of the bounding box of all vertices without their normals.
-	 * This works well for most objects, problems might occur with planar
-	 * surfaces. However the step tries to filter such cases out.
-	 * The step inverts all infacing normals. Generally it is recommended
-	 * to enable this step.
-	*/
-    public static final PostProcessStep FixInfacingNormals =
-            new PostProcessStep("FixInfacingNormals");
-
-
     /** Reorders triangles for vertex cache locality and thus better performance.
     /** Reorders triangles for vertex cache locality and thus better performance.
 	 * The step tries to improve the ACMR (average post-transform vertex cache
 	 * The step tries to improve the ACMR (average post-transform vertex cache
 	 * miss ratio) for all meshes. The step runs in O(n) and is roughly
 	 * miss ratio) for all meshes. The step runs in O(n) and is roughly
@@ -210,6 +196,46 @@ public class PostProcessStep {
             new PostProcessStep("ImproveVertexLocality");
             new PostProcessStep("ImproveVertexLocality");
 
 
 
 
+    /** Searches for redundant materials and removes them.
+	 *
+	 * This is especially useful in combination with the PretransformVertices
+	 * and OptimizeGraph steps. Both steps join small meshes, but they
+	 * can't do that if two meshes have different materials.
+	 */
+	 public static final PostProcessStep RemoveRedundantMaterials =
+            new PostProcessStep("RemoveRedundantMaterials");
+
+	/** This step tries to determine which meshes have normal vectors
+	 * that are facing inwards. The algorithm is simple but effective:
+	 * the bounding box of all vertices + their normals is compared against
+	 * the volume of the bounding box of all vertices without their normals.
+	 * This works well for most objects, problems might occur with planar
+	 * surfaces. However, the step tries to filter such cases.
+	 * The step inverts all infacing normals. Generally it is recommended
+	 * to enable this step, although the result is not always correct.
+	*/
+	 public static final PostProcessStep FixInfacingNormals =
+            new PostProcessStep("FixInfacingNormals");
+
+	/** This step performs some optimizations on the node graph.
+	 *
+	 * It is incompatible to the PreTransformVertices-Step. Some configuration
+	 * options exist, see aiConfig.h for more details.
+	 * Generally, two actions are available:<br>
+	 *   1. Remove animation nodes and data from the scene. This allows other
+	 *      steps for further optimizations.<br>
+	 *   2. Combine very small meshes to larger ones. Only if the meshes
+	 *      are used by the same node or by nodes on the same hierarchy (with
+	 *      equal local transformations). Unlike PreTransformVertices, the
+	 *      OptimizeGraph-step doesn't transform vertices from one space
+	 *      another.<br>
+	 *   3. Remove hierarchy levels<br>
+	 *
+	 *  It is recommended to have this step run with the default configuration.
+	 */
+	 public static final PostProcessStep OptimizeGraph =
+            new PostProcessStep("OptimizeGraph");
+
     private final String myName; // for debug only
     private final String myName; // for debug only
 
 
     private PostProcessStep(String name) {
     private PostProcessStep(String name) {

+ 107 - 0
test/unit/utImporter.cpp

@@ -0,0 +1,107 @@
+#include "utImporter.h"
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION (ImporterTest);
+
+#define AIUT_DEF_ERROR_TEXT "sorry, this is a test"
+
+
+bool TestPlugin :: CanRead( const std::string& pFile, 
+	IOSystem* pIOHandler) const
+{
+	std::string::size_type pos = pFile.find_last_of('.');
+	// no file extension - can't read
+	if( pos == std::string::npos)return false;
+	std::string extension = pFile.substr( pos);
+
+	// todo ... make case-insensitive
+	return (extension == ".apple" || extension == ".mac" ||
+		extension == ".linux" || extension == ".windows" );
+}
+
+void TestPlugin :: GetExtensionList(std::string& append)
+{
+	append.append("*.apple;*.mac;*.linux;*.windows");
+}
+
+void TestPlugin :: InternReadFile( const std::string& pFile, 
+	aiScene* pScene, IOSystem* pIOHandler)
+{
+	throw new ImportErrorException(AIUT_DEF_ERROR_TEXT);
+}
+
+
+void ImporterTest :: setUp (void)
+{
+	pImp = new Importer();
+}
+
+void ImporterTest :: tearDown (void)
+{
+	delete pImp;
+}
+
+void ImporterTest :: testIntProperty (void)
+{
+	bool b;
+	pImp->SetPropertyInteger("quakquak",1503,&b);
+	CPPUNIT_ASSERT(!b);
+	CPPUNIT_ASSERT(1503 == pImp->GetPropertyInteger("quakquak",0));
+	CPPUNIT_ASSERT(314159 == pImp->GetPropertyInteger("not_there",314159));
+
+	pImp->SetPropertyInteger("quakquak",1504,&b);
+	CPPUNIT_ASSERT(b);
+}
+
+void ImporterTest :: testFloatProperty (void)
+{
+	bool b;
+	pImp->SetPropertyFloat("quakquak",1503.f,&b);
+	CPPUNIT_ASSERT(!b);
+	CPPUNIT_ASSERT(1503.f == pImp->GetPropertyFloat("quakquak",0.f));
+	CPPUNIT_ASSERT(314159.f == pImp->GetPropertyFloat("not_there",314159.f));
+}
+
+void ImporterTest :: testStringProperty (void)
+{
+	bool b;
+	pImp->SetPropertyString("quakquak","test",&b);
+	CPPUNIT_ASSERT(!b);
+	CPPUNIT_ASSERT("test" == pImp->GetPropertyString("quakquak","weghwekg"));
+	CPPUNIT_ASSERT("ILoveYou" == pImp->GetPropertyString("not_there","ILoveYou"));
+}
+
+void ImporterTest :: testPluginInterface (void)
+{
+	pImp->RegisterLoader(new TestPlugin());
+	CPPUNIT_ASSERT(pImp->IsExtensionSupported(".apple"));
+	CPPUNIT_ASSERT(pImp->IsExtensionSupported(".mac"));
+	CPPUNIT_ASSERT(pImp->IsExtensionSupported(".linux"));
+	CPPUNIT_ASSERT(pImp->IsExtensionSupported(".windows"));
+
+	TestPlugin* p = (TestPlugin*) pImp->FindLoader(".windows");
+	CPPUNIT_ASSERT(NULL != p);
+
+	try {
+	p->InternReadFile("",0,NULL);
+	}
+	catch ( ImportErrorException* ex)
+	{
+		CPPUNIT_ASSERT(ex->GetErrorText() == AIUT_DEF_ERROR_TEXT);
+
+		// unregister the plugin and delete it
+		pImp->UnregisterLoader(p);
+		delete p;
+
+		return;
+	}
+	CPPUNIT_ASSERT(false); // control shouldn't reach this point
+}
+
+void ImporterTest :: testExtensionCheck (void)
+{
+	std::string s;
+	pImp->GetExtensionList(s);
+
+	// todo ..
+}

+ 57 - 0
test/unit/utImporter.h

@@ -0,0 +1,57 @@
+#ifndef TESTIMPORTER_H
+#define TESTIMPORTER_H
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <assimp.hpp>
+
+
+using namespace std;
+using namespace Assimp;
+
+class ImporterTest : public CPPUNIT_NS :: TestFixture
+{
+    CPPUNIT_TEST_SUITE (ImporterTest);
+    CPPUNIT_TEST (testIntProperty);
+	CPPUNIT_TEST (testFloatProperty);
+	CPPUNIT_TEST (testStringProperty);
+	CPPUNIT_TEST (testPluginInterface);
+	CPPUNIT_TEST (testExtensionCheck);
+    CPPUNIT_TEST_SUITE_END ();
+
+    public:
+        void setUp (void);
+        void tearDown (void);
+
+    protected:
+
+        void  testIntProperty (void);
+		void  testFloatProperty (void);
+		void  testStringProperty (void);
+		
+		void  testPluginInterface (void);
+		void  testExtensionCheck (void);
+
+	private:
+
+		Importer* pImp;
+};
+
+class TestPlugin : public BaseImporter
+{
+public:
+
+	// overriden
+	bool CanRead( const std::string& pFile, 
+		IOSystem* pIOHandler) const;
+
+	// overriden
+	void GetExtensionList(std::string& append);
+
+	// overriden
+	void InternReadFile( const std::string& pFile, 
+		aiScene* pScene, IOSystem* pIOHandler);
+};
+
+#endif 

+ 85 - 0
test/unit/utJoinVertices.cpp

@@ -0,0 +1,85 @@
+
+#include "utJoinVertices.h"
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION (JoinVerticesTest);
+
+void JoinVerticesTest :: setUp (void)
+{
+	// construct the process
+	this->piProcess = new JoinVerticesProcess();
+
+	// create a quite small mesh for testing purposes -
+	// the mesh itself is *something* but it has redundant vertices
+	this->pcMesh = new aiMesh();
+
+	pcMesh->mNumVertices = 900;
+	aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900];
+	for (unsigned int i = 0; i < 3;++i)
+	{
+		const unsigned int base = i*300;
+		for (unsigned int a = 0; a < 300;++a)
+		{
+			pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a;
+		}
+	}
+
+	// generate faces - each vertex is referenced once
+	pcMesh->mNumFaces = 300;
+	pcMesh->mFaces = new aiFace[300];
+	for (unsigned int i = 0,p = 0; i < 300;++i)
+	{
+		aiFace& face = pcMesh->mFaces[i];
+		face.mIndices = new unsigned int[ face.mNumIndices = 3 ];
+		for (unsigned int a = 0; a < 3;++a)
+			face.mIndices[a] = p++;
+	}
+
+	// generate extra members - set them to zero to make sure they're identical
+	pcMesh->mTextureCoords[0] = new aiVector3D[900];
+	for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = 0.f; 
+
+	pcMesh->mNormals = new aiVector3D[900];
+	for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = 0.f; 
+
+	pcMesh->mTangents = new aiVector3D[900];
+	for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = 0.f; 
+
+	pcMesh->mBitangents = new aiVector3D[900];
+	for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = 0.f; 
+}
+
+void JoinVerticesTest :: tearDown (void)
+{
+	delete this->pcMesh;
+	delete this->piProcess;
+}
+
+void JoinVerticesTest :: testProcess(void)
+{
+	// execute the step on the given data
+	this->piProcess->ProcessMesh(this->pcMesh,0);
+
+	// the number of faces shouldn't change
+	CPPUNIT_ASSERT(pcMesh->mNumFaces == 300);
+	CPPUNIT_ASSERT(pcMesh->mNumVertices == 300);
+
+	CPPUNIT_ASSERT(NULL != pcMesh->mNormals);
+	CPPUNIT_ASSERT(NULL != pcMesh->mTangents);
+	CPPUNIT_ASSERT(NULL != pcMesh->mBitangents);
+	CPPUNIT_ASSERT(NULL != pcMesh->mTextureCoords[0]);
+
+	// the order doesn't care
+	float fSum = 0.f;
+	for (unsigned int i = 0; i < 300;++i)
+	{
+		aiVector3D& v = pcMesh->mVertices[i];
+		fSum += v.x + v.y + v.z;
+
+		CPPUNIT_ASSERT(!pcMesh->mNormals[i].x);
+		CPPUNIT_ASSERT(!pcMesh->mTangents[i].x);
+		CPPUNIT_ASSERT(!pcMesh->mBitangents[i].x);
+		CPPUNIT_ASSERT(!pcMesh->mTextureCoords[0][i].x);
+	}
+	CPPUNIT_ASSERT(fSum == 150.f*299.f*3.f); // gaussian sum equation
+}

+ 36 - 0
test/unit/utJoinVertices.h

@@ -0,0 +1,36 @@
+#ifndef TESTLBW_H
+#define TESTLBW_H
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <aiScene.h>
+#include <JoinVerticesProcess.h>
+
+
+using namespace std;
+using namespace Assimp;
+
+class JoinVerticesTest : public CPPUNIT_NS :: TestFixture
+{
+    CPPUNIT_TEST_SUITE (JoinVerticesTest);
+    CPPUNIT_TEST (testProcess);
+    CPPUNIT_TEST_SUITE_END ();
+
+    public:
+        void setUp (void);
+        void tearDown (void);
+
+    protected:
+
+        void  testProcess (void);
+		
+   
+	private:
+
+		JoinVerticesProcess* piProcess;
+		aiMesh* pcMesh;
+
+};
+
+#endif 

+ 0 - 2
test/unit/utLimitBoneWeights.h

@@ -4,8 +4,6 @@
 #include <cppunit/TestFixture.h>
 #include <cppunit/TestFixture.h>
 #include <cppunit/extensions/HelperMacros.h>
 #include <cppunit/extensions/HelperMacros.h>
 
 
-#include <aiTypes.h>
-#include <aiMesh.h>
 #include <aiScene.h>
 #include <aiScene.h>
 #include <LimitBoneWeightsProcess.h>
 #include <LimitBoneWeightsProcess.h>
 
 

+ 0 - 0
test/unit/utOptimizeGraph.cpp


+ 35 - 0
test/unit/utOptimizeGraph.h

@@ -0,0 +1,35 @@
+#ifndef TESTOG_H
+#define TESTOG_H
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <aiScene.h>
+#include <OptimizeGraphProcess.h>
+
+
+using namespace std;
+using namespace Assimp;
+
+class OptimizeGraphProcessTest : public CPPUNIT_NS :: TestFixture
+{
+    CPPUNIT_TEST_SUITE (OptimizeGraphProcessTest);
+    CPPUNIT_TEST (testProcess);
+    CPPUNIT_TEST_SUITE_END ();
+
+    public:
+        void setUp (void);
+        void tearDown (void);
+
+    protected:
+
+        void  testProcess (void);
+		
+   
+	private:
+
+		OptimizeGraphProcess* piProcess;
+		aiScene* pcMesh;
+};
+
+#endif 

+ 1 - 1
tools/assimp_view/assimp_view.cpp

@@ -134,7 +134,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter)
 		aiProcess_ConvertToLeftHanded	| // convert everything to D3D left handed space
 		aiProcess_ConvertToLeftHanded	| // convert everything to D3D left handed space
 		aiProcess_SplitLargeMeshes      | // split large, unrenderable meshes into submeshes
 		aiProcess_SplitLargeMeshes      | // split large, unrenderable meshes into submeshes
 		aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality 
 		aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality 
-		| aiProcess_RemoveRedundantMaterials | aiProcess_FixInfacingNormals); // validate the output data structure
+		| aiProcess_RemoveRedundantMaterials ); // validate the output data structure
 
 
 	// get the end time of zje operation, calculate delta t
 	// get the end time of zje operation, calculate delta t
 	double fEnd = (double)timeGetTime();
 	double fEnd = (double)timeGetTime();

+ 1 - 1
workspaces/jidea5.1/jAssimp.ipr

@@ -178,7 +178,7 @@
   </component>
   </component>
   <component name="ProjectModuleManager">
   <component name="ProjectModuleManager">
     <modules>
     <modules>
-      <module fileurl="file://J:/Programmieren/ASSIMP/ASSIMP3/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/ASSIMP3/port/jAssimp/assimp.iml" />
+      <module fileurl="file://J:/Programmieren/ASSIMP/assimp3/trunk/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/assimp3/trunk/port/jAssimp/assimp.iml" />
     </modules>
     </modules>
   </component>
   </component>
   <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" />
   <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" />

+ 20 - 0
workspaces/vc8/UnitTest.vcproj

@@ -683,6 +683,10 @@
 				RelativePath="..\..\test\unit\utGenNormals.cpp"
 				RelativePath="..\..\test\unit\utGenNormals.cpp"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utImporter.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\test\unit\utImproveCacheLocality.cpp"
 				RelativePath="..\..\test\unit\utImproveCacheLocality.cpp"
 				>
 				>
@@ -699,6 +703,10 @@
 				RelativePath="..\..\test\unit\utMaterialSystem.cpp"
 				RelativePath="..\..\test\unit\utMaterialSystem.cpp"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utOptimizeGraph.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\test\unit\utPretransformVertices.cpp"
 				RelativePath="..\..\test\unit\utPretransformVertices.cpp"
 				>
 				>
@@ -733,6 +741,14 @@
 				RelativePath="..\..\test\unit\utGenNormals.h"
 				RelativePath="..\..\test\unit\utGenNormals.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utImporter.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\test\unit\utJoinVertices.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\test\unit\utLimitBoneWeights.h"
 				RelativePath="..\..\test\unit\utLimitBoneWeights.h"
 				>
 				>
@@ -741,6 +757,10 @@
 				RelativePath="..\..\test\unit\utMaterialSystem.h"
 				RelativePath="..\..\test\unit\utMaterialSystem.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utOptimizeGraph.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\test\unit\utPretransformVertices.h"
 				RelativePath="..\..\test\unit\utPretransformVertices.h"
 				>
 				>

+ 45 - 13
workspaces/vc8/assimp.vcproj

@@ -734,6 +734,10 @@
 				RelativePath="..\..\code\FixNormalsStep.h"
 				RelativePath="..\..\code\FixNormalsStep.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\GenericProperty.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\GenFaceNormalsProcess.h"
 				RelativePath="..\..\code\GenFaceNormalsProcess.h"
 				>
 				>
@@ -770,6 +774,10 @@
 				RelativePath="..\..\code\MaterialSystem.h"
 				RelativePath="..\..\code\MaterialSystem.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\OptimizeGraphProcess.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\ParsingUtils.h"
 				RelativePath="..\..\code\ParsingUtils.h"
 				>
 				>
@@ -790,6 +798,18 @@
 				RelativePath="..\..\code\RemoveRedundantMaterials.h"
 				RelativePath="..\..\code\RemoveRedundantMaterials.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\SGSpatialSort.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\code\SmoothingGroups.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\code\SmoothingGroups.inl"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\SpatialSort.h"
 				RelativePath="..\..\code\SpatialSort.h"
 				>
 				>
@@ -868,10 +888,6 @@
 						RelativePath="..\..\code\3DSLoader.h"
 						RelativePath="..\..\code\3DSLoader.h"
 						>
 						>
 					</File>
 					</File>
-					<File
-						RelativePath="..\..\code\3DSSpatialSort.h"
-						>
-					</File>
 				</Filter>
 				</Filter>
 				<Filter
 				<Filter
 					Name="ASE"
 					Name="ASE"
@@ -886,7 +902,7 @@
 					</File>
 					</File>
 				</Filter>
 				</Filter>
 				<Filter
 				<Filter
-					Name="HMPLoader"
+					Name="HMP"
 					>
 					>
 					<File
 					<File
 						RelativePath="..\..\code\HMPFileData.h"
 						RelativePath="..\..\code\HMPFileData.h"
@@ -1061,6 +1077,14 @@
 						>
 						>
 					</File>
 					</File>
 				</Filter>
 				</Filter>
+				<Filter
+					Name="DXF"
+					>
+					<File
+						RelativePath="..\..\code\DXFLoader.h"
+						>
+					</File>
+				</Filter>
 			</Filter>
 			</Filter>
 		</Filter>
 		</Filter>
 		<Filter
 		<Filter
@@ -1138,6 +1162,10 @@
 				RelativePath="..\..\code\MaterialSystem.cpp"
 				RelativePath="..\..\code\MaterialSystem.cpp"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\OptimizeGraphProcess.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\PretransformVertices.cpp"
 				RelativePath="..\..\code\PretransformVertices.cpp"
 				>
 				>
@@ -1150,6 +1178,10 @@
 				RelativePath="..\..\code\RemoveRedundantMaterials.cpp"
 				RelativePath="..\..\code\RemoveRedundantMaterials.cpp"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\SGSpatialSort.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\SpatialSort.cpp"
 				RelativePath="..\..\code\SpatialSort.cpp"
 				>
 				>
@@ -1196,18 +1228,10 @@
 						RelativePath="..\..\code\3DSConverter.cpp"
 						RelativePath="..\..\code\3DSConverter.cpp"
 						>
 						>
 					</File>
 					</File>
-					<File
-						RelativePath="..\..\code\3DSGenNormals.cpp"
-						>
-					</File>
 					<File
 					<File
 						RelativePath="..\..\code\3DSLoader.cpp"
 						RelativePath="..\..\code\3DSLoader.cpp"
 						>
 						>
 					</File>
 					</File>
-					<File
-						RelativePath="..\..\code\3DSSpatialSort.cpp"
-						>
-					</File>
 				</Filter>
 				</Filter>
 				<Filter
 				<Filter
 					Name="ASE"
 					Name="ASE"
@@ -1345,6 +1369,14 @@
 						>
 						>
 					</File>
 					</File>
 				</Filter>
 				</Filter>
+				<Filter
+					Name="DXF"
+					>
+					<File
+						RelativePath="..\..\code\DXFLoader.cpp"
+						>
+					</File>
+				</Filter>
 			</Filter>
 			</Filter>
 		</Filter>
 		</Filter>
 		<Filter
 		<Filter

+ 20 - 16
workspaces/vc8/jAssimp.vcproj

@@ -238,22 +238,6 @@
 			Filter="h;hpp;hxx;hm;inl;inc;xsd"
 			Filter="h;hpp;hxx;hm;inl;inc;xsd"
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
 			>
-			<File
-				RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
-				>
-			</File>
-			<File
-				RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOStream.h"
-				>
-			</File>
-			<File
-				RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOSystem.h"
-				>
-			</File>
-			<File
-				RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_Property.h"
-				>
-			</File>
 			<File
 			<File
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
 				>
 				>
@@ -262,6 +246,26 @@
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
 				>
 				>
 			</File>
 			</File>
+			<Filter
+				Name="javah"
+				>
+				<File
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOStream.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_DefaultIOSystem.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer_Property.h"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		</Filter>
 		<Filter
 		<Filter
 			Name="resources"
 			Name="resources"