2
0
Эх сурвалжийг харах

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 жил өмнө
parent
commit
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;
 
-#ifdef _MSC_VER
-#	define sprintf sprintf_s
-#endif
-
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ReplaceDefaultMaterial()
 {
@@ -130,70 +126,66 @@ void Dot3DSImporter::ReplaceDefaultMaterial()
 	}
 	return;
 }
+
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh)
+void Dot3DSImporter::CheckIndices(Dot3DS::Mesh& sMesh)
 {
 	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
-		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;
 }
+
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh)
+void Dot3DSImporter::MakeUnique(Dot3DS::Mesh& sMesh)
 {
 	unsigned int iBase = 0;
 
 	std::vector<aiVector3D> vNew;
 	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;
 
 		// positions
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[2]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[2]];
 		iTemp1 = iBase++;
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[1]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[1]];
 		iTemp2 = iBase++;
-		vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].mIndices[0]];
+		vNew[iBase]   = sMesh.mPositions[sMesh.mFaces[i].mIndices[0]];
 
 		// 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;
 }
+
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	MaterialHelper& mat)
@@ -201,14 +193,14 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	// NOTE: Pass the background image to the viewer by bypassing the
 	// material system. This is an evil hack, never do it  again!
 	if (0 != this->mBackgroundImage.length() && this->bHasBG)
-		{
+	{
 		aiString tex;
 		tex.Set( this->mBackgroundImage);
 		mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
 
 		// be sure this is only done for the first material
 		this->mBackgroundImage = std::string("");
-		}
+	}
 
 	// At first add the base ambient color of the
 	// scene to	the material
@@ -250,7 +242,7 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 	// two sided rendering?
 	if (oldMat.mTwoSided)
 	{
-		int i = 0;
+		int i = 1;
 		mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
 	}
 
@@ -268,8 +260,6 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
 			eShading = aiShadingMode_Gouraud; break;
 
 		// 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 :
 			eShading = aiShadingMode_Phong; break;
 
@@ -409,20 +399,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 			a =  (*i).mFaceMaterials.begin();
 			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
 		bool bFirst = true;
@@ -506,12 +483,9 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 	pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
 	pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
 	for (unsigned int a = 0; a < pcOut->mNumMeshes;++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
 	// set for each texture
@@ -519,6 +493,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 		TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
 	return;
 }
+
 // ------------------------------------------------------------------------------------------------
 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);
 
 			// 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);
-			}
 		}
 		if (!iArray.empty())
 		{
@@ -568,10 +540,11 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 						pvCurrent->y -= pivot.y;
 						pvCurrent->z -= pivot.z;
 						*pvCurrent = mTrafo * (*pvCurrent);
-						std::swap( pvCurrent->y, pvCurrent->z );
+						//std::swap( pvCurrent->y, pvCurrent->z );
 						++pvCurrent;
 					}
 				}
+#if 0
 				else
 				{
 					while (pvCurrent != pvEnd)
@@ -580,6 +553,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 						++pvCurrent;
 					}
 				}
+#endif
 				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 (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')
-	{
 		pcOut->mRootNode->mName.Set("<root>");
-	}
 }
 // ------------------------------------------------------------------------------------------------
 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 */
 
 #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 "SpatialSort.h"
+#include "SmoothingGroups.h"
 
 namespace Assimp	{
 namespace Dot3DS	{
 
 #include "./Compiler/pushpack1.h"
 
-#ifdef _MSC_VER
-#	define sprintf sprintf_s
-#endif
-
 // ---------------------------------------------------------------------------
 /** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
 *  and data structures.
 */
+// ---------------------------------------------------------------------------
 class Dot3DSFile
 {
 public:
@@ -91,32 +89,27 @@ public:
 	typedef enum
 	{
 		// translated to gouraud shading with wireframe active
-		Wire = 0,
+		Wire = 0x0,
 
 		// if this material is set, no vertex normals will
 		// be calculated for the model. Face normals + gouraud
-		Flat = 1,
+		Flat = 0x1,
 
 		// standard gouraud shading
-		Gouraud = 2,
+		Gouraud = 0x2,
 
 		// phong shading
-		Phong = 3,
+		Phong = 0x3,
 
 		// cooktorrance or anistropic phong shading ...
 		// the exact meaning is unknown, if you know it
 		// feel free to tell me ;-)
-		Metal = 4,
+		Metal = 0x4,
 
 		// required by the ASE loader
-		Blinn = 5
+		Blinn = 0x5
 	} shadetype3ds;
 
-	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-	// enum for all chunks in 3ds files. Unused
-	// ones are commented, list is not complete since
-	// there are many undocumented chunks
-	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	enum 
 	{
 
@@ -316,26 +309,10 @@ public:
 
 // ---------------------------------------------------------------------------
 /** 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;
 };
 // ---------------------------------------------------------------------------
@@ -442,7 +419,7 @@ struct Material
 };
 // ---------------------------------------------------------------------------
 /** Helper structure to represent a 3ds file mesh */
-struct Mesh
+struct Mesh : public MeshWithSmoothingGroups<Dot3DS::Face>
 {
 	//! Default constructor
 	Mesh()
@@ -457,21 +434,12 @@ struct Mesh
 	//! Name of the mesh
 	std::string mName;
 
-	//! Vertex positions
-	std::vector<aiVector3D> mPositions;
-
-	//! Face lists
-	std::vector<Face> mFaces;
-
 	//! Texture coordinates
 	std::vector<aiVector2D> mTexCoords;
 
 	//! Face materials
 	std::vector<unsigned int> mFaceMaterials;
 
-	//! Normal vectors
-	std::vector<aiVector3D> mNormals;
-
 	//! Local transformation matrix
 	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
 #include "../include/DefaultLogger.h"
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/assimp.hpp"
 
 // boost headers
 #include <boost/scoped_ptr.hpp>
 
 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
 
 // ------------------------------------------------------------------------------------------------
@@ -92,7 +117,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 		return false;
 	std::string extension = pFile.substr( pos);
 
-	// not brilliant but working ;-)
+	// not brillant but working ;-)
 	if( extension == ".3ds" || extension == ".3DS" || 
 		extension == ".3Ds" || extension == ".3dS")
 		return true;
@@ -103,7 +128,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 // Setup configuration properties
 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. 
@@ -151,11 +176,11 @@ void Dot3DSImporter::InternReadFile(
 		i != this->mScene->mMeshes.end();++i)
 	{
 		// TODO: see function body
-		this->CheckIndices(&(*i));
-		this->MakeUnique(&(*i));
+		this->CheckIndices(*i);
+		this->MakeUnique(*i);
 
 		// first generate normals for the mesh
-		this->GenNormals(&(*i));
+		ComputeNormalsWithSmoothingsGroups<Dot3DS::Face>(*i);
 	}
 
 	// Apply scaling and offsets to all texture coordinates
@@ -179,11 +204,7 @@ void Dot3DSImporter::InternReadFile(
 // ------------------------------------------------------------------------------------------------
 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;
 
 	// construct an uniform scaling matrix and multiply with it
@@ -200,9 +221,8 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
 
 	// read chunk
 	if (this->mCurrent >= this->mLast)
-	{
 		throw new ImportErrorException("Unexpected end of file, can't read chunk header");
-	}
+
 	const uintptr_t iDiff = this->mLast - this->mCurrent;
 	if (iDiff < sizeof(Dot3DSFile::Chunk)) 
 	{
@@ -211,23 +231,15 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
 	}
 	*p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent;
 	if ((**p_ppcOut).Size + this->mCurrent > this->mLast)
-	{
 		throw new ImportErrorException("Unexpected end of file, can't read chunk footer");
-	}
+
 	this->mCurrent += sizeof(Dot3DSFile::Chunk);
 	return;
 }
 // ------------------------------------------------------------------------------------------------
 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
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -237,27 +249,14 @@ void Dot3DSImporter::ParseMainChunk(int& piRemaining)
 		this->ParseEditorChunk(iRemaining);
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 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
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -291,27 +290,14 @@ void Dot3DSImporter::ParseEditorChunk(int& piRemaining)
 		}
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 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;
 	unsigned int iCnt = 0;
@@ -384,17 +370,8 @@ void Dot3DSImporter::ParseObjectChunk(int& piRemaining)
 		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);
 }
 // ------------------------------------------------------------------------------------------------
@@ -409,11 +386,7 @@ void Dot3DSImporter::SkipChunk()
 // ------------------------------------------------------------------------------------------------
 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
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -424,28 +397,14 @@ void Dot3DSImporter::ParseChunk(int& piRemaining)
 		this->ParseMeshChunk(iRemaining);
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 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
 	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
@@ -456,18 +415,8 @@ void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining)
 		this->ParseHierarchyChunk(iRemaining);
 		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);
 }
 // ------------------------------------------------------------------------------------------------
@@ -489,14 +438,7 @@ void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurr
 // ------------------------------------------------------------------------------------------------
 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
 	const unsigned char* sz = (unsigned char*)this->mCurrent;
@@ -548,6 +490,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 
 		// pivot = origin of rotation and scaling
 		this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent);
+		std::swap(this->mCurrentNode->vPivot.y,this->mCurrentNode->vPivot.z);
 		this->mCurrent += sizeof(aiVector3D);
 		break;
 
@@ -576,8 +519,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			this->mCurrent += sizeof(uint16_t);
 
-			aiVectorKey v;
-			v.mTime = (double)sNum;
+			aiVectorKey v;v.mTime = (double)sNum;
 
 			this->mCurrent += sizeof(uint32_t);
 			v.mValue =  *((const aiVector3D*)this->mCurrent);
@@ -591,7 +533,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 			}
 			// 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;
 
@@ -618,9 +561,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 			uint16_t sNum = *((const uint16_t*)mCurrent);
 			this->mCurrent += sizeof(uint16_t);
 
-			aiQuatKey v;
-			v.mTime = (double)sNum;
-
+			aiQuatKey v;v.mTime = (double)sNum;
 			this->mCurrent += sizeof(uint32_t);
 
 			float fRadians = *((const float*)this->mCurrent);
@@ -640,7 +581,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 				if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;}
 			}
 			// 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;
 
@@ -698,31 +640,16 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining)
 		break;
 #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);
 }
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
 {
-	const Dot3DSFile::Chunk* psChunk;
-
+	ASSIMP_3DS_BEGIN_CHUNK();
 	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
 	const unsigned char* sz = this->mCurrent;
 	uint32_t iCnt = 0,iTemp;
@@ -796,31 +723,16 @@ void Dot3DSImporter::ParseFaceChunk(int& piRemaining)
 
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 {
-	const Dot3DSFile::Chunk* psChunk;
-
+	ASSIMP_3DS_BEGIN_CHUNK();
 	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
 	const unsigned char* sz = this->mCurrent;
 	unsigned int iCnt = 0;
@@ -837,7 +749,7 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 		{
 			mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent));
 			aiVector3D& v = mMesh.mPositions.back();
-			//std::swap( v.y, v.z);
+			std::swap( v.y, v.z);
 			//v.y *= -1.0f;
 			this->mCurrent += sizeof(aiVector3D);
 		}
@@ -924,28 +836,14 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining)
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 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
 	const unsigned char* sz = this->mCurrent;
@@ -958,9 +856,8 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 	case Dot3DSFile::CHUNK_MAT_MATNAME:
 
 		// 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')
 		{
 			if (sz > pcCurNext-1)
@@ -1017,8 +914,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		pcf = &this->mScene->mMaterials.back().mTransparency;
 		*pcf = this->ParsePercentageChunk();
 		// 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;
 		break;
 
@@ -1037,16 +933,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 	case Dot3DSFile::CHUNK_MAT_SHININESS:
 		pcf = &this->mScene->mMaterials.back().mSpecularExponent;
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf *= (float)0xFFFF;
 		break;
 
 	case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT:
 		pcf = &this->mScene->mMaterials.back().mShininessStrength;
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf *= (float)0xffff / 100.0f;
 		break;
 
@@ -1054,8 +948,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		// TODO: need to multiply with emissive base color?
 		pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend;
 		*pcf = this->ParsePercentageChunk();
-		if (is_qnan(*pcf))
-			*pcf = 0.0f;
+		if (is_qnan(*pcf))*pcf = 0.0f;
 		else *pcf = *pcf * (float)0xFFFF / 100.0f;
 		break;
 
@@ -1085,31 +978,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining)
 		this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive);
 		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);
 }
 // ------------------------------------------------------------------------------------------------
 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
 	const unsigned char* sz = this->mCurrent;
@@ -1184,11 +1060,8 @@ void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut)
 		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);
 }
 // ------------------------------------------------------------------------------------------------
@@ -1226,20 +1099,22 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 
 	const Dot3DSFile::Chunk* psChunk;
 	this->ReadChunk(&psChunk);
-	if (NULL == psChunk)
+	if (!psChunk)
 	{
 		*p_pcOut = clrError;
 		return;
 	}
+	const unsigned int diff = psChunk->Size - sizeof(Dot3DSFile::Chunk);
+
 	const unsigned char* pcCur = this->mCurrent;
-	this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk);
+	this->mCurrent += diff;
 	bool bGamma = false;
 	switch(psChunk->Flag)
 	{
 	case Dot3DSFile::CHUNK_LINRGBF:
 		bGamma = true;
 	case Dot3DSFile::CHUNK_RGBF:
-		if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (sizeof(float) * 3 > diff)
 		{
 			*p_pcOut = clrError;
 			return;
@@ -1252,7 +1127,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 	case Dot3DSFile::CHUNK_LINRGBB:
 		bGamma = true;
 	case Dot3DSFile::CHUNK_RGBB:
-		if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		if (sizeof(char) * 3 > diff)
 		{
 			*p_pcOut = clrError;
 			return;
@@ -1265,7 +1140,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 	// percentage chunks: accepted to be compatible with various
 	// .3ds files with very curious content
 	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->g = *((float*)pcCur);
@@ -1275,7 +1150,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
 		*p_pcOut = clrError;
 		return;
 	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->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.
 		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)
 	{
 		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);
 
-	// -------------------------------------------------------------------
-	/** generate normal vectors for a given mesh
-	*/
-	void GenNormals(Dot3DS::Mesh* sMesh);
-
 	// -------------------------------------------------------------------
 	/** generate unique vertices for a mesh
 	*/
-	void MakeUnique(Dot3DS::Mesh* sMesh);
+	void MakeUnique(Dot3DS::Mesh& sMesh);
 
 	// -------------------------------------------------------------------
 	/** Add a node to the node graph
@@ -235,7 +230,7 @@ protected:
 	// -------------------------------------------------------------------
 	/** Clamp all indices in the file to a valid range
 	*/
-	void CheckIndices(Dot3DS::Mesh* sMesh);
+	void CheckIndices(Dot3DS::Mesh& sMesh);
 
 
 protected:

+ 2 - 73
code/ASELoader.cpp

@@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // internal headers
 #include "ASELoader.h"
-#include "3DSSpatialSort.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 #include "TextureTransform.h"
@@ -142,6 +141,7 @@ void ASEImporter::InternReadFile(
 		if ((*i).bSkip)continue;
 
 		this->TransformVertices(*i);
+
 		// now we need to create proper meshes from the import we need to 
 		// split them by materials, build valid vertex/face lists ...
 		this->BuildUniqueRepresentation(*i);
@@ -1135,9 +1135,7 @@ void ASEImporter::BuildMaterialIndices()
 	}
 	// prepare for the next step
 	for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans)
-	{
 		TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]);
-	}
 
 	// now we need to iterate through all meshes,
 	// generating correct texture coordinates and material uv indices
@@ -1182,75 +1180,6 @@ void ASEImporter::GenerateNormals(ASE::Mesh& mesh)
 		}
 	}
 	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;
 }

+ 6 - 57
code/ASEParser.cpp

@@ -64,7 +64,7 @@ using namespace Assimp::ASE;
 #define BLUBB(_message_) \
 	{this->LogError(_message_);return;}
 
-
+// ------------------------------------------------------------------------------------------------
 #define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('}' == *this->m_szFile) \
@@ -87,6 +87,7 @@ using namespace Assimp::ASE;
 	} else bLastWasEndLine = false; \
 	++this->m_szFile; 
 
+// ------------------------------------------------------------------------------------------------
 #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
 	else if ('{' == *this->m_szFile)iDepth++; \
 	else if ('}' == *this->m_szFile) \
@@ -1723,62 +1724,29 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
 	ai_assert(NULL != apOut);
 
 	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)
 {
 	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
-	rIndexOut = strtol10(this->m_szFile,&this->m_szFile);
+	ParseLV4MeshLong(rIndexOut);
 
 	// parse the three others
 	this->ParseLV4MeshLongTriple(apOut);
-	return;
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
 {
 	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
-	rIndexOut = strtol10(this->m_szFile,&this->m_szFile);
+	ParseLV4MeshLong(rIndexOut);
 	
 	// parse the three others
 	this->ParseLV4MeshFloatTriple(apOut);
-	return;
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloatTriple(float* apOut)
@@ -1786,20 +1754,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut)
 	ai_assert(NULL != apOut);
 
 	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)
@@ -1815,9 +1770,6 @@ void Parser::ParseLV4MeshFloat(float& fOut)
 	}
 	// parse the first float
 	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)
@@ -1833,7 +1785,4 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut)
 	}
 	// parse the value
 	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"
 
 // for some helper routines like IsSpace()
-#include "PlyParser.h"
+#include "ParsingUtils.h"
 #include "qnan.h"
 
 // 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 */
-struct Face : public Dot3DS::Face
+struct Face : public FaceWithSmoothingGroup
 {
 	//! Default constructor. Initializes everything with 0
 	Face()
@@ -224,13 +224,13 @@ struct DecompTransform
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file mesh */
-struct Mesh
+struct Mesh : public MeshWithSmoothingGroups<ASE::Face>
 {
 	//! Constructor. Creates a default name for the mesh
 	Mesh() : bSkip(false)
 	{
 		static int iCnt = 0;
-		char szTemp[128];
+		char szTemp[128]; // should be sufficiently large
 		::sprintf(szTemp,"UNNAMED_%i",iCnt++);
 		mName = szTemp;
 
@@ -249,21 +249,12 @@ struct Mesh
 	//! "" if there is no parent ...
 	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
 	std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 
 	//! List of all vertex color sets.
 	std::vector<aiColor4D> mVertexColors;
 
-	//! List of normal vectors
-	std::vector<aiVector3D> mNormals;
-
 	//! List of all bone vertices
 	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 */
 
-// CRT headers
-#include <map>
 
 // public ASSIMP headers
 #include "../include/assimp.h"
+#include "../include/aiFileIO.h"
 #include "../include/assimp.hpp"
 #include "../include/DefaultLogger.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+
+#include "GenericProperty.h"
 
 // boost headers
 #define AI_C_THREADSAFE
@@ -67,19 +70,158 @@ static ImporterMap gActiveImports;
 /** Error message of the last failed import process */
 static std::string gLastErrorString;
 
+/** Configuration properties */
+static Importer::IntPropertyMap			gIntProperties;
+static Importer::FloatPropertyMap		gFloatProperties;
+static Importer::StringPropertyMap		gStringProperties;
+
 #if (defined AI_C_THREADSAFE)
 /** Global mutex to manage the access to the importer map */
 static boost::mutex gMutex;
 #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. 
 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);
 
 	// create an Importer for this file
 	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
 	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
 	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;
 	}
 
@@ -196,11 +337,31 @@ void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
 	// it should be there... else the user is playing fools with us
 	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;
 	}
 	// get memory statistics
 	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)
 {
 	// 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 = 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)
 {
 	// 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 = AI_DEG_TO_RAD(this->configMaxAngle);
 }
@@ -145,8 +145,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
 	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
 	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];
 	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
 #define AI_HASH_H_INCLUDED
@@ -19,11 +58,12 @@
 #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;
 int rem;
 
-    if (len <= 0 || data == NULL) return 0;
+    if (!data) return 0;
+	if (!len)len = (unsigned int)::strlen(data);
 
     rem = len & 3;
     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 "DefaultIOStream.h"
 #include "DefaultIOSystem.h"
+#include "GenericProperty.h"
 
 // Importers
 #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)
 #	include "RemoveRedundantMaterials.h"
 #endif
+#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+#	include "OptimizeGraphProcess.h"
+#endif
 
 
 using namespace Assimp;
@@ -168,6 +172,9 @@ Importer::Importer() :
 	bExtraVerbose = false; // disable extra verbose mode by default
 
 	// 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)
 	mImporter.push_back( new XFileImporter());
 #endif
@@ -217,13 +224,18 @@ Importer::Importer() :
 #endif
 
 	// 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)
 	mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first
 #endif
 #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
 	mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
 #endif
+#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+	mPostProcessingSteps.push_back( new OptimizeGraphProcess());
+#endif
 #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
 	mPostProcessingSteps.push_back( new TriangulateProcess());
 #endif
@@ -379,8 +391,16 @@ bool ValidateFlags(unsigned int pFlags)
 	if (pFlags & aiProcess_GenSmoothNormals &&
 		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;
 	}
 
@@ -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
 bool Importer::IsExtensionSupported(const std::string& szExtension)
+{
+	return NULL != FindLoader(szExtension);
+}
+
+// ------------------------------------------------------------------------------------------------
+BaseImporter* Importer::FindLoader (const std::string& szExtension)
 {
 	for (std::vector<BaseImporter*>::const_iterator
 		i =  this->mImporter.begin();
 		i != this->mImporter.end();++i)
 	{
 		// 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
-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
-int Importer::GetProperty(const char* szName, 
+int Importer::GetPropertyInteger(const char* szName, 
 	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)
 {

+ 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"
 
 struct aiMesh;
+class JoinVerticesTest;
 
 namespace Assimp
 {
@@ -62,6 +63,7 @@ namespace Assimp
 class ASSIMP_API JoinVerticesProcess : public BaseProcess
 {
 	friend class Importer;
+	friend class ::JoinVerticesTest;
 
 protected:
 	/** 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
 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. 
@@ -174,10 +174,10 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 	std::vector<aiMesh*> apcMeshes;
 	std::vector<aiNode*> apcNodes;
 	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
-	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)
 	{
 		MaterialHelper* pcMat = new MaterialHelper();
@@ -192,7 +192,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 		const LWO::Layer& layer = *lit;
 
 		// 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())
 		{
 			// 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())
 				{
 					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 == iDefaultSurface)
 					{
-						iDefaultSurface = mSurfaces->size();
+						iDefaultSurface = (unsigned int)mSurfaces->size();
 						mSurfaces->push_back(LWO::Surface());
 						LWO::Surface& surf = mSurfaces->back();
 						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 
 				aiMesh* mesh = new aiMesh();
 				apcMeshes.push_back(mesh);
-				mesh->mNumFaces = sorted.size();
+				mesh->mNumFaces = (unsigned int)sorted.size();
 
 				for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
 					it != end;++it)
@@ -281,7 +281,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 		apcNodes.push_back(pcNode);
 		pcNode->mName.Set(layer.mName);
 		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];
 		for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
 			pcNode->mMeshes[p] = p + meshStart;
@@ -292,7 +292,7 @@ void LWOImporter::InternReadFile( const std::string& pFile,
 	// copy the meshes to the output structure
 	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*));
 	}
 }
@@ -414,7 +414,7 @@ void LWOImporter::CopyFaceIndices(FaceList::iterator& it,
 				if (mi > mCurLayer->mTempPoints.size())
 				{
 					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)
 {
 	// 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 "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/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/DefaultLogger.h"
-#include "../include/assimp.hpp"
 
+// boost headers
 #include <boost/scoped_ptr.hpp>
 
 using namespace Assimp;
@@ -117,10 +118,10 @@ void MD2Importer::SetupProperties(const Importer* pImp)
 {
 	// The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		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))
 		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
 
-		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
 
-		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
-		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
 
-		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
 		{
-			// 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
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		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;
 
-// 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
 MD5Importer::MD5Importer()
@@ -122,9 +114,8 @@ void MD5Importer::InternReadFile(
 
 	// make sure we return no incomplete data
 	if (!bHadMD5Mesh && !bHadMD5Anim)
-	{
 		throw new ImportErrorException("Failed to read valid data from this MD5");
-	}
+	
 	if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY;
 }
 // ------------------------------------------------------------------------------------------------
@@ -285,30 +276,48 @@ void MD5Importer::LoadMD5MeshFile ()
 	pScene->mRootNode->mNumChildren = 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
-	pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
+	aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
 	pcNode->mName.Set("MD5Anim");
 	pcNode->mParent = pScene->mRootNode;
 	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
-	pScene->mNumMeshes = pScene->mNumMaterials = (unsigned int)meshParser.mMeshes.size();
+	pScene->mNumMeshes = pScene->mNumMaterials;
 	pScene->mMeshes = new aiMesh*[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
 		MakeDataUnique(meshSrc);
@@ -424,9 +433,9 @@ void MD5Importer::LoadMD5MeshFile ()
 
 		// generate a material for the mesh
 		MaterialHelper* mat = new MaterialHelper();
-		pScene->mMaterials[i] = mat;
+		pScene->mMaterials[n] = mat;
 		mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
-		mesh->mMaterialIndex = i;
+		mesh->mMaterialIndex = n++;
 	}
 
 	// 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
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		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;
 
 	// create materials
-	pScene->mNumMaterials = aszShaders.size();
+	pScene->mNumMaterials = (unsigned int)aszShaders.size();
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
 	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
 #include "../include/DefaultLogger.h"
-#include "../include/IOStream.h"
-#include "../include/IOSystem.h"
-#include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 #include "../include/assimp.hpp"
 
 // boost headers
@@ -119,11 +118,12 @@ void MDLImporter::SetupProperties(const Importer* pImp)
 {
 	// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		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. 

+ 7 - 8
code/MDLLoader.h

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

+ 1 - 1
code/MDLMaterialLoader.cpp

@@ -61,7 +61,7 @@ using namespace Assimp;
 void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
 {
 	// 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;
 	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
 		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);
 		}
 	}
@@ -371,8 +371,8 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput,
 	const char* pKey)
 {
 	// 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);
 }
 // ------------------------------------------------------------------------------------------------

+ 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 "ObjFileParser.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/aiAssert.h"
 #include "../include/DefaultLogger.h"
-#include "MaterialSystem.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/format.hpp>
@@ -237,7 +237,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
 	if ( meshSizeDiff > 0 )
 	{
 		pNode->mMeshes = new unsigned int[ meshSizeDiff ];
-		pNode->mNumMeshes = meshSizeDiff;
+		pNode->mNumMeshes = (unsigned int)meshSizeDiff;
 		size_t index = 0;
 		for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
 		{

+ 3 - 3
code/ObjFileParser.cpp

@@ -306,8 +306,8 @@ void ObjFileParser::getFace()
 	
 	// Store the 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
 	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 ])
 			{
-				mat_index = index;
+				mat_index = (int)index;
 				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 */
+
+// internal headers
 #include "PlyLoader.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 
+// public ASSIMP headers
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
+// boost headeers
 #include <boost/scoped_ptr.hpp>
 
 using namespace Assimp;
@@ -95,24 +100,19 @@ void PLYImporter::InternReadFile(
 
 	// Check whether we can read from the file
 	if( file.get() == NULL)
-	{
 		throw new ImportErrorException( "Failed to open PLY file " + pFile + ".");
-	}
 
 	// check whether the ply file is large enough to contain
 	// at least the file header
 	size_t fileSize = file->FileSize();
 	if( fileSize < 10)
-	{
 		throw new ImportErrorException( "PLY File is too small.");
-	}
 
 	// allocate storage and copy the contents of the file to a memory buffer
 	// (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';
 
 	// 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[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");
 	}
 	char* szMe = (char*)&this->mBuffer[3];
@@ -137,11 +135,7 @@ void PLYImporter::InternReadFile(
 			szMe += 6;
 			SkipLine(szMe,(const char**)&szMe);
 			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)");
-			}
 		}
 		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
 			SkipLine(szMe,(const char**)&szMe);
 			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)");
-			}
-		}
-		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
 	{
@@ -185,12 +170,8 @@ void PLYImporter::InternReadFile(
 	this->LoadVertices(&avPositions,false);
 
 	if (avPositions.empty())
-	{
-		delete[] this->mBuffer;
-		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		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. 
 	std::vector<aiVector3D> avNormals;
@@ -206,10 +187,8 @@ void PLYImporter::InternReadFile(
 	{
 		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;
@@ -244,11 +223,7 @@ void PLYImporter::InternReadFile(
 		&avColors,&avTexCoords,&avMaterials,&avMeshes);
 
 	if (avMeshes.empty())
-	{
-		delete[] this->mBuffer;
-		AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
 		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
-	}
 
 	// now generate the output scene object. Fill the material list
 	pScene->mNumMaterials = (unsigned int)avMaterials.size();
@@ -269,12 +244,6 @@ void PLYImporter::InternReadFile(
 
 	for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++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,
@@ -819,9 +788,8 @@ void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
 
 					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;
 					}
 

+ 6 - 6
code/PretransformVertices.cpp

@@ -52,17 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
+// ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 PretransformVertices::PretransformVertices()
-	{
-	}
-
+{
+}
+// ------------------------------------------------------------------------------------------------
 // Destructor, private as well
 PretransformVertices::~PretransformVertices()
-	{
+{
 	// nothing to do here
-	}
-
+}
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
 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:
 
 * 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
-  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
-  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 
 "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 
- 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 "3DSSpatialSort.h"
+#include "SGSpatialSort.h"
 
 #include "../include/aiAssert.h"
 
 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 
 	// in the hope that no model spreads all its vertices along this plane.
 	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
 	mPlaneNormal.Normalize();
-	}
+}
 // ------------------------------------------------------------------------------------------------
 // Destructor
-D3DSSpatialSorter::~D3DSSpatialSorter()
-	{
+SGSpatialSort::~SGSpatialSort()
+{
 	// 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
-	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.
 	std::sort( this->mPositions.begin(), this->mPositions.end());
 }
 // ------------------------------------------------------------------------------------------------
 // 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 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 binaryStepSize = (unsigned int)mPositions.size() / 4;
 	while( binaryStepSize > 1)
-		{
+	{
 		if( mPositions[index].mDistance < minDist)
 			index += binaryStepSize;
 		else
 			index -= binaryStepSize;
 
 		binaryStepSize /= 2;
-		}
+	}
 
 	// 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
@@ -140,33 +127,16 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition,
 
 	float squareEpsilon = pRadius * pRadius;
 	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
 
 #include <vector>
-#include "../include/aiVector3D.h"
-
-
-#if (!defined AI_BUILD_NO_ASE_IMPORTER)
-#	include "3DSHelper.h"
-#endif
+#include "../include/aiTypes.h"
 
 namespace Assimp
 {
 
-using namespace Dot3DS;
-
 // ------------------------------------------------------------------------------------------------
 /** Specialized version of SpatialSort to support smoothing groups
  *  This is used in the .3ds loader
  */
-class D3DSSpatialSorter
+class SGSpatialSort
 {
 public:
 
-	D3DSSpatialSorter();
+	SGSpatialSort();
 
 	// -------------------------------------------------------------------
 	/** 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
@@ -85,7 +79,7 @@ public:
 	void Prepare();
 
 	/** Destructor */
-	~D3DSSpatialSorter();
+	~SGSpatialSort();
 
 	// -------------------------------------------------------------------
 	/** 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
 	// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
-	if(0xffffffff == (this->configFrameID = pImp->GetProperty(
+	if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
 		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 "../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;
 
 // ------------------------------------------------------------------------------------------------
-void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
 {
 	// 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
-		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;
 
-		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 
 	// check position differences against 
 	// @Schrompf: This is the 6th time this snippet is repeated!
 	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;
 	std::vector<aiVector3D> avNormals;
-	avNormals.resize(sMesh->mNormals.size());
+	avNormals.resize(sMesh.mNormals.size());
 	
 	// 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();
 
-	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;
 
 		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);
 
 			aiVector3D vNormals;
@@ -121,21 +122,16 @@ void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
 				a =  poResult.begin();
 				a != poResult.end();++a)
 			{
-				vNormals += sMesh->mNormals[(*a)];
+				vNormals += sMesh.mNormals[(*a)];
 				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;
-			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)
 {
     // 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
@@ -373,7 +373,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene)
 // Setup properties
 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.

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

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

+ 1 - 0
include/IOStream.h

@@ -85,6 +85,7 @@ public:
 	// -------------------------------------------------------------------
     virtual size_t Tell(void) const = 0;
 
+
 	// -------------------------------------------------------------------
 	/**	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.
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  *       the internal header file SplitLargeMeshes.h
+ * Property type: integer.
  */
 #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.
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  *       the internal header file SplitLargeMeshes.h
+ * Property type: integer.
  */
 #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.
  * \note The default value is AI_LBW_MAX_WEIGHTS, defined in
  *       the internal header file LimitBoneWeightsProcess.h
+ * Property type: integer.
  */
 #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
  *   options (where XXX is a placeholder for the file format for which you
  *   want to override the global setting).
+ * Property type: integer.
  */
 #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
  *
  * 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"
 
@@ -112,6 +117,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * LightWave represents the gradients with infinite detail,
  * but for use in realtime the loader computes replacement textures.
  * 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_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.
  *
  * 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"
 
@@ -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.
  *
  * 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_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

+ 32 - 17
include/aiFileIO.h

@@ -53,11 +53,13 @@ extern "C" {
 
 
 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.
@@ -70,41 +72,54 @@ enum aiOrigin
 	aiOrigin_END = 0x2		//!< End of file
 };
 
-typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin);
-
+typedef aiReturn (*aiFileSeek)(aiFile*, size_t, aiOrigin);
 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
 {
-	//! User data assigned to the structure
-	aiUserData UserData;
-
 	//! Function used to open a new file
-	aiFileOpenProc OpenFunc;
+	aiFileOpenProc OpenProc;
 
 	//! 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
-	aiFileReadWriteProc ReadFunc;
+	aiFileReadProc ReadProc;
 
 	//! Function used to write to a file
-	aiFileReadWriteProc WriteFunc;
+	aiFileWriteProc WriteProc;
 
 	//! Function used to retrieve the current
 	//! position of the file cursor (ftell())
 	aiFileTellProc TellProc;
 
+	//! Function used to retrieve the size of the file, in bytes
+	aiFileTellProc FileSizeProc;
+
 	//! Function used to set the current position
 	//! of the file cursor (fseek())
 	aiFileSeek SeekProc;
+
+	//! User-defined data
+	aiUserData UserData;
 };
 
 

+ 0 - 11
include/aiMesh.h

@@ -356,16 +356,6 @@ struct aiMesh
 	 */
 	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
 
 	//! Default constructor. Initializes all members to 0
@@ -384,7 +374,6 @@ struct aiMesh
 			mColors[a] = NULL;
 		mNumBones = 0; mBones = NULL;
 		mMaterialIndex = 0;
-		mMaxSmoothingAngle = AI_MESH_SMOOTHING_ANGLE_NOT_SET;
 	}
 
 	//! 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 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.
+	 * surfaces. However, the step tries to filter such cases.
 	 * 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
 } // end of extern "C"
 #endif

+ 3 - 0
include/aiVector3D.h

@@ -75,6 +75,9 @@ struct aiVector3D
 	inline bool operator!= (const aiVector3D& other) const
 		{return x != other.x || y != other.y || z != other.z;}
 
+	inline aiVector3D& operator= (float f)
+		{x = y = z = f;return *this;}
+
 #endif // __cplusplus
 
 	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, 
 *   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.
+*   a successful import. Provide a bitwise combination of the 
+*   #aiPostProcessSteps flags.
 * @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 
 * this file. If the import fails, NULL is returned instead. Call 
 * 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( 
-	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,
 	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
 }
 #endif

+ 83 - 35
include/assimp.hpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // STL headers
 #include <string>
+#include <map>
 #include <vector>
 
 // public ASSIMP headers
@@ -67,6 +68,8 @@ namespace Assimp
 #define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff
 
 struct aiScene;
+struct aiFileIO;
+const aiScene* aiImportFileEx( const char*, unsigned int, aiFileIO*);
 
 namespace Assimp
 {
@@ -101,28 +104,14 @@ class ASSIMP_API Importer
 {
 	// used internally
 	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:
 
@@ -188,28 +177,66 @@ public:
 #endif
 
 	// -------------------------------------------------------------------
-	/** Set a configuration property.
+	/** Set an integer configuration property.
 	 * @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).
 	 * @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.
 	 * @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
+	 * @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.
-	*   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
 	*/
 	bool IsExtensionSupported(const std::string& szExtension);
@@ -298,6 +326,18 @@ public:
 	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()
 	*
@@ -351,9 +391,17 @@ protected:
 	std::string mErrorString;
 
 	/** 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;
 };
 

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

@@ -44,7 +44,7 @@ package assimp;
 
 /**
  * Defines configuration properties.
- *
+ * <p/>
  * Static helper class, can't be instanced. It defines configuration
  * 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>
      * configuration property.
@@ -79,15 +78,13 @@ public class ConfigProperty {
     public static final int DEFAULT_LBW_MAX_WEIGHTS = 4;
 
 
-
-
-
     /**
      * Set the maximum number of vertices in a mesh.
      * <p/>
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * 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
             = "pp.slm.triangle_limit";
@@ -98,7 +95,8 @@ public class ConfigProperty {
      * <p/>
      * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
      * 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
             = "pp.slm.vertex_limit";
@@ -108,7 +106,8 @@ public class ConfigProperty {
      * Set the maximum number of bones affecting a single vertex
      * <p/>
      * 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
             = "pp.lbw.weights_limit";
@@ -126,6 +125,7 @@ public class ConfigProperty {
      * <code>CONFIG_IMPORT_XXX_KEYFRAME</code> options (where XXX is a
      * placeholder for the file format for which you want to override the
      * global setting).
+     * The type of the property is int.
      */
     public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME
             = "imp.global.kf";
@@ -144,11 +144,60 @@ public class ConfigProperty {
      * There are some faulty 3DS files on the internet which look
      * only correctly with pivot points disabled. By default,
      * 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
             = "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;
     }
 
+
+    /**
+     * 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>.
      * <br>
@@ -158,12 +190,14 @@ public class Importer {
     /**
      * 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.
      * @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 {
 
-        // allocate a default I/O system
-        ioSystem = new DefaultIOSystem();
-
         if (!bLibInitialized) {
 
             /** 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
      * @return null if the import failed, otherwise a valid Scene instance
      * @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 {
         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.LimitBoneWeights)) flags |= 0x200;
             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
@@ -418,30 +451,39 @@ public class Importer {
      *
      * @param prop Name of 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) {
 
-        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
      */
     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 =
             new PostProcessStep("LimitBoneWeights");
 
-
     /**
      * Validates the aiScene data structure before it is returned.
      * This makes sure that all indices are valid, all animations and
@@ -187,19 +186,6 @@ public class PostProcessStep {
             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.
 	 * 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
@@ -210,6 +196,46 @@ public class PostProcessStep {
             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 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/extensions/HelperMacros.h>
 
-#include <aiTypes.h>
-#include <aiMesh.h>
 #include <aiScene.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_SplitLargeMeshes      | // split large, unrenderable meshes into submeshes
 		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
 	double fEnd = (double)timeGetTime();

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

@@ -178,7 +178,7 @@
   </component>
   <component name="ProjectModuleManager">
     <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>
   </component>
   <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"
 				>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utImporter.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\..\test\unit\utImproveCacheLocality.cpp"
 				>
@@ -699,6 +703,10 @@
 				RelativePath="..\..\test\unit\utMaterialSystem.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utOptimizeGraph.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\..\test\unit\utPretransformVertices.cpp"
 				>
@@ -733,6 +741,14 @@
 				RelativePath="..\..\test\unit\utGenNormals.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utImporter.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\test\unit\utJoinVertices.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\test\unit\utLimitBoneWeights.h"
 				>
@@ -741,6 +757,10 @@
 				RelativePath="..\..\test\unit\utMaterialSystem.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utOptimizeGraph.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\test\unit\utPretransformVertices.h"
 				>

+ 45 - 13
workspaces/vc8/assimp.vcproj

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

+ 20 - 16
workspaces/vc8/jAssimp.vcproj

@@ -238,22 +238,6 @@
 			Filter="h;hpp;hxx;hm;inl;inc;xsd"
 			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
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
 				>
@@ -262,6 +246,26 @@
 				RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
 				>
 			</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
 			Name="resources"