Sfoglia il codice sorgente

Finished loading of MD2, MD3, MDL2, MDL3, MDL4, MDL5, MDL7, MDL. First WIP version of the SMD loader. Additionals checks added to the validation step.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@60 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
aramis_acg 17 anni fa
parent
commit
b422d4e303
90 ha cambiato i file con 6258 aggiunte e 1299 eliminazioni
  1. 26 23
      code/3DSConverter.cpp
  2. 26 30
      code/3DSHelper.h
  3. 21 29
      code/3DSLoader.cpp
  4. 6 0
      code/ASELoader.cpp
  5. 10 2
      code/BaseImporter.h
  6. 10 6
      code/DefaultIOStream.cpp
  7. 3 3
      code/DefaultIOSystem.cpp
  8. 449 0
      code/HMPLoader.cpp
  9. 208 0
      code/HMPLoader.h
  10. 87 13
      code/Importer.cpp
  11. 62 2
      code/LimitBoneWeightsProcess.cpp
  12. 55 1
      code/LimitBoneWeightsProcess.h
  13. 26 24
      code/MD2FileData.h
  14. 150 91
      code/MD2Loader.cpp
  15. 9 0
      code/MD2Loader.h
  16. 23 23
      code/MD3FileData.h
  17. 90 39
      code/MD3Loader.cpp
  18. 10 0
      code/MD3Loader.h
  19. 1 1
      code/MD4Loader.cpp
  20. 341 78
      code/MDLFileData.h
  21. 280 499
      code/MDLLoader.cpp
  22. 252 53
      code/MDLLoader.h
  23. 774 0
      code/MDLMaterialLoader.cpp
  24. 1 0
      code/MaterialSystem.cpp
  25. 2 1
      code/MaterialSystem.h
  26. 115 0
      code/ParsingUtils.h
  27. 7 0
      code/PlyLoader.cpp
  28. 2 42
      code/PlyParser.h
  29. 1070 0
      code/SMDLoader.cpp
  30. 408 0
      code/SMDLoader.h
  31. 8 4
      code/SplitLargeMeshes.h
  32. 65 42
      code/TextureTransform.cpp
  33. 61 2
      code/ValidateDataStructure.cpp
  34. 7 0
      code/ValidateDataStructure.h
  35. 89 75
      code/XFileImporter.cpp
  36. 15 8
      code/XFileParser.cpp
  37. 4 5
      code/extra/MakeVerboseFormat.cpp
  38. 42 0
      code/qnan.h
  39. 34 16
      doc/dox.h
  40. 1 1
      include/IOStream.h
  41. 3 1
      include/IOSystem.h
  42. 1 1
      include/LogStream.h
  43. 42 1
      include/Logger.h
  44. 97 0
      include/aiDefines.h
  45. 104 26
      include/aiMaterial.h
  46. 18 43
      include/aiMaterial.inl
  47. 3 2
      include/aiMatrix3x3.h
  48. 1 0
      include/aiMatrix3x3.inl
  49. 31 1
      include/aiMatrix4x4.h
  50. 93 0
      include/aiMatrix4x4.inl
  51. 16 5
      include/aiPostProcess.h
  52. 24 0
      include/aiQuaternion.h
  53. 16 1
      include/aiScene.h
  54. 15 0
      include/aiTexture.h
  55. 8 17
      include/aiTypes.h
  56. 8 7
      include/assimp.h
  57. 37 9
      include/assimp.hpp
  58. 1 1
      port/jAssimp/assimp.iml
  59. 0 0
      port/jAssimp/jni_bridge/BuildHeader.bat
  60. 0 0
      port/jAssimp/jni_bridge/JNICalls.cpp
  61. 0 0
      port/jAssimp/jni_bridge/JNIEnvironment.cpp
  62. 0 0
      port/jAssimp/jni_bridge/JNIEnvironment.h
  63. 0 0
      port/jAssimp/jni_bridge/JNILogger.cpp
  64. 0 0
      port/jAssimp/jni_bridge/JNILogger.h
  65. 0 0
      port/jAssimp/jni_bridge/assimp_Animation.h
  66. 0 0
      port/jAssimp/jni_bridge/assimp_Importer.h
  67. 0 0
      port/jAssimp/jni_bridge/assimp_Material.h
  68. 0 0
      port/jAssimp/jni_bridge/assimp_Mesh.h
  69. 0 0
      port/jAssimp/jni_bridge/assimp_Node.h
  70. 0 0
      port/jAssimp/jni_bridge/assimp_PostProcessStep.h
  71. 0 0
      port/jAssimp/jni_bridge/assimp_Scene.h
  72. 0 0
      port/jAssimp/jni_bridge/assimp_Texture.h
  73. 11 0
      port/jAssimp/src/assimp/Bone.java
  74. 14 0
      port/jAssimp/src/assimp/IOStream.java
  75. 73 0
      port/jAssimp/src/assimp/IOSystem.java
  76. 109 7
      port/jAssimp/src/assimp/Importer.java
  77. 69 0
      port/jAssimp/src/assimp/Material.java
  78. 5 31
      port/jAssimp/src/assimp/Mesh.java
  79. 217 5
      port/jAssimp/src/assimp/Node.java
  80. 56 1
      port/jAssimp/src/assimp/PostProcessStep.java
  81. 116 0
      port/jAssimp/src/assimp/ShadingMode.java
  82. 52 0
      port/jAssimp/src/assimp/TextureMapMode.java
  83. 71 0
      port/jAssimp/src/assimp/TextureOp.java
  84. BIN
      test/HMP/planar.hmp
  85. BIN
      test/HMP/terrain.hmp
  86. BIN
      test/HMP/terrain_withtexture.hmp
  87. 15 0
      test/SMD/triangle.smd
  88. 6 4
      tools/assimp_view/Material.cpp
  89. 2 2
      workspaces/jidea5.1/jAssimp.ipr
  90. 74 21
      workspaces/vc8/assimp.vcproj

+ 26 - 23
code/3DSConverter.cpp

@@ -40,11 +40,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 /** @file Implementation of the 3ds importer class */
 /** @file Implementation of the 3ds importer class */
+
+// internal headers
 #include "3DSLoader.h"
 #include "3DSLoader.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
 #include "TextureTransform.h"
 #include "TextureTransform.h"
 #include "StringComparison.h"
 #include "StringComparison.h"
+#include "qnan.h"
 
 
+// public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
 #include "../include/IOStream.h"
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/IOSystem.h"
@@ -52,7 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
 
 
-#include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
@@ -427,10 +430,12 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 			a != (*i).mFaceMaterials.end();++a,++iNum)
 			a != (*i).mFaceMaterials.end();++a,++iNum)
 		{
 		{
 		// check range
 		// check range
-		if ((*a) >= this->mScene->mMaterials.size())
+			if ((*a) >= this->mScene->mMaterials.size())
 			{
 			{
-			// use the last material instead
-			aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
+				DefaultLogger::get()->error("Face material index is out of range");
+
+				// use the last material instead
+				aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
 			}
 			}
 		else aiSplit[*a].push_back(iNum);
 		else aiSplit[*a].push_back(iNum);
 		}
 		}
@@ -450,15 +455,15 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 				p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName);
 				p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName);
 				avOutMeshes.push_back(p_pcOut);
 				avOutMeshes.push_back(p_pcOut);
 
 
-
+// (code for keyframe animation. however, this is currently not supported by Assimp)
+#if 0
 				if (bFirst)
 				if (bFirst)
 				{
 				{
 					p_pcOut->mColors[1] = (aiColor4D*)new aiMatrix4x4();
 					p_pcOut->mColors[1] = (aiColor4D*)new aiMatrix4x4();
-
 					*((aiMatrix4x4*)p_pcOut->mColors[1]) = (*i).mMat;
 					*((aiMatrix4x4*)p_pcOut->mColors[1]) = (*i).mMat;
 					bFirst = false;
 					bFirst = false;
 				}
 				}
-
+#endif
 
 
 				// convert vertices
 				// convert vertices
 				p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
 				p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
@@ -519,16 +524,6 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 					// apply texture coordinate scalings
 					// apply texture coordinate scalings
 					TextureTransform::BakeScaleNOffset ( p_pcOut, &this->mScene->mMaterials[
 					TextureTransform::BakeScaleNOffset ( p_pcOut, &this->mScene->mMaterials[
 						p_pcOut->mMaterialIndex] );
 						p_pcOut->mMaterialIndex] );
-					
-					// setup bitflags to indicate which texture coordinate
-					// channels are used
-					p_pcOut->mNumUVComponents[0] = 2;
-					if (p_pcOut->HasTextureCoords(1))
-						p_pcOut->mNumUVComponents[1] = 2;
-					if (p_pcOut->HasTextureCoords(2))
-						p_pcOut->mNumUVComponents[2] = 2;
-					if (p_pcOut->HasTextureCoords(3))
-						p_pcOut->mNumUVComponents[3] = 2;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -579,6 +574,8 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 	{
 	{
 		const unsigned int iIndex = iArray[i];
 		const unsigned int iIndex = iArray[i];
 
 
+// (code for keyframe animation. however, this is currently not supported by Assimp)
+#if 0
 		if (NULL != pcSOut->mMeshes[iIndex]->mColors[1])
 		if (NULL != pcSOut->mMeshes[iIndex]->mColors[1])
 		{
 		{
 			pcOut->mTransformation = *((aiMatrix4x4*)
 			pcOut->mTransformation = *((aiMatrix4x4*)
@@ -587,7 +584,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 			delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1];
 			delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1];
 			pcSOut->mMeshes[iIndex]->mColors[1] = NULL;
 			pcSOut->mMeshes[iIndex]->mColors[1] = NULL;
 		}
 		}
-
+#endif
 		pcOut->mMeshes[i] = iIndex;
 		pcOut->mMeshes[i] = iIndex;
 	}
 	}
 
 
@@ -681,11 +678,17 @@ void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
 			pcNode->mMeshes[0] = i;
 			pcNode->mMeshes[0] = i;
 			pcNode->mNumMeshes = 1;
 			pcNode->mNumMeshes = 1;
 
 
-			std::string s;
-			std::stringstream ss(s);
-			ss << "UNNAMED[" << i << + "]"; 
-
-			pcNode->mName.Set(s);
+			char szBuffer[128];
+			int iLen;
+#if _MSC_VER >= 1400
+			iLen = sprintf_s(szBuffer,"UNNAMED_%i",i);
+#else
+			iLen = sprintf(szBuffer,"UNNAMED_%i",i);
+#endif
+			ai_assert(0 < iLen);
+			::memcpy(pcNode->mName.data,szBuffer,iLen);
+			pcNode->mName.data[iLen] = '\0';
+			pcNode->mName.length = iLen;
 
 
 			// add the new child to the parent node
 			// add the new child to the parent node
 			pcOut->mRootNode->mChildren[i] = pcNode;
 			pcOut->mRootNode->mChildren[i] = pcNode;

+ 26 - 30
code/3DSHelper.h

@@ -93,10 +93,22 @@ public:
 	//! From AutoDesk 3ds SDK
 	//! From AutoDesk 3ds SDK
 	typedef enum
 	typedef enum
 	{
 	{
+		// translated to gouraud shading with wireframe active
 		Wire = 0,
 		Wire = 0,
+
+		// if this material is set, no vertex normals will
+		// be calculated for the model. Face normals + gouraud
 		Flat = 1,
 		Flat = 1,
+
+		// standard gouraud shading
 		Gouraud = 2,
 		Gouraud = 2,
+
+		// phong shading
 		Phong = 3,
 		Phong = 3,
+
+		// cooktorrance or anistropic phong shading ...
+		// the exact meaning is unknown, if you know it
+		// feel free to tell me ;-)
 		Metal = 4,
 		Metal = 4,
 
 
 		// required by the ASE loader
 		// required by the ASE loader
@@ -127,7 +139,8 @@ public:
 		CHUNK_PERCENTF	= 0x0031,		// float4  percentage
 		CHUNK_PERCENTF	= 0x0031,		// float4  percentage
 		// **************************************************************
 		// **************************************************************
 
 
-		// Unknown and ignored
+		// Unknown and ignored. Possibly a chunk used by PROJ (
+		// Discreet 3DS max Project File)?
 		CHUNK_PRJ       = 0xC23D,
 		CHUNK_PRJ       = 0xC23D,
 
 
 		// Unknown. Possibly a reference to an external .mli file?
 		// Unknown. Possibly a reference to an external .mli file?
@@ -387,9 +400,10 @@ struct Material
 	mTwoSided			(false)
 	mTwoSided			(false)
 	{
 	{
 		static int iCnt = 0;
 		static int iCnt = 0;
-		std::stringstream ss;
-		ss << "$$_UNNAMED_" << iCnt++ << "_$$"; 
-		ss >> mName;
+		
+		char szTemp[128];
+		sprintf(szTemp,"$$_UNNAMED_%i_$$",iCnt++);
+		mName = szTemp;
 	}
 	}
 
 
 	//! Name of the material
 	//! Name of the material
@@ -442,9 +456,10 @@ struct Mesh
 	Mesh()
 	Mesh()
 	{
 	{
 		static int iCnt = 0;
 		static int iCnt = 0;
-		std::stringstream ss;
-		ss << "$$_UNNAMED_" << iCnt++ << "_$$"; 
-		ss >> mName;
+		
+		char szTemp[128];
+		sprintf(szTemp,"$$_UNNAMED_%i_$$",iCnt++);
+		mName = szTemp;
 	}
 	}
 
 
 	//! Name of the mesh
 	//! Name of the mesh
@@ -481,9 +496,10 @@ struct Node
 
 
 	{
 	{
 		static int iCnt = 0;
 		static int iCnt = 0;
-		std::stringstream ss;
-		ss << "$$_UNNAMED_" << iCnt++ << "_$$"; 
-		ss >> mName;
+		
+		char szTemp[128];
+		sprintf(szTemp,"$$_UNNAMED_%i_$$",iCnt++);
+		mName = szTemp;
 
 
 		mHierarchyPos = 0;
 		mHierarchyPos = 0;
 		mHierarchyIndex = 0;
 		mHierarchyIndex = 0;
@@ -538,26 +554,6 @@ struct Scene
 	Node* pcRootNode;
 	Node* pcRootNode;
 };
 };
 
 
-// ---------------------------------------------------------------------------
-inline bool is_qnan(float p_fIn)
-{
-	// NOTE: Comparison against qnan is generally problematic
-	// because qnan == qnan is false AFAIK
-	union FTOINT
-	{
-		float fFloat;
-		int32_t iInt;
-	} one, two;
-	one.fFloat = std::numeric_limits<float>::quiet_NaN();
-	two.fFloat = p_fIn;
-
-	return (one.iInt == two.iInt);
-}
-// ---------------------------------------------------------------------------
-inline bool is_not_qnan(float p_fIn)
-{
-	return !is_qnan(p_fIn);
-}
 
 
 } // end of namespace Dot3DS
 } // end of namespace Dot3DS
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 21 - 29
code/3DSLoader.cpp

@@ -40,11 +40,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 /** @file Implementation of the 3ds importer class */
 /** @file Implementation of the 3ds importer class */
+
+// internal headers
 #include "3DSLoader.h"
 #include "3DSLoader.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
 #include "TextureTransform.h"
 #include "TextureTransform.h"
 #include "StringComparison.h"
 #include "StringComparison.h"
+#include "qnan.h"
 
 
+// public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
 #include "../include/IOStream.h"
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/IOSystem.h"
@@ -52,14 +56,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
 
 
+// boost headers
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-#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 higher-level chunk header."	\
+#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."			
+#endif
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
@@ -92,22 +99,6 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 	return false;
 	return false;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-// recursively delete a given node
-void DeleteNodeRecursively (aiNode* p_piNode)
-{
-	if (!p_piNode)return;
-
-	if (p_piNode->mChildren)
-	{
-		for (unsigned int i = 0 ; i < p_piNode->mNumChildren;++i)
-		{
-			DeleteNodeRecursively(p_piNode->mChildren[i]);
-		}
-	}
-	delete p_piNode;
-	return;
-}
-// ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 
 void Dot3DSImporter::InternReadFile( 
 void Dot3DSImporter::InternReadFile( 
 	const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
 	const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
@@ -125,7 +116,7 @@ void Dot3DSImporter::InternReadFile(
 	size_t fileSize = file->FileSize();
 	size_t fileSize = file->FileSize();
 	if( fileSize < 16)
 	if( fileSize < 16)
 	{
 	{
-		throw new ImportErrorException( ".3ds File is too small.");
+		throw new ImportErrorException( "3DS File is too small.");
 	}
 	}
 
 
 	this->mScene = new Dot3DS::Scene();
 	this->mScene = new Dot3DS::Scene();
@@ -780,8 +771,9 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 	case Dot3DSFile::CHUNK_FACEMAT:
 	case Dot3DSFile::CHUNK_FACEMAT:
 
 
 		// at fist an asciiz with the material name
 		// at fist an asciiz with the material name
-		while (*sz++ != '\0')
+		while (*sz++)
 		{
 		{
+			// make sure we don't run over the end of the chunk
 			if (sz > pcCurNext-1)break;
 			if (sz > pcCurNext-1)break;
 		}
 		}
 
 
@@ -800,7 +792,7 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 			break;
 			break;
 			}
 			}
 		}
 		}
-		if (iIndex == 0xFFFFFFFF)
+		if (0xFFFFFFFF == iIndex)
 		{
 		{
 			// this material is not known. Ignore this. We will later
 			// this material is not known. Ignore this. We will later
 			// assign the default material to all faces using *this*
 			// assign the default material to all faces using *this*
@@ -818,13 +810,14 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 
 
 			// check range
 			// check range
 			if (iTemp >= mMesh.mFaceMaterials.size())
 			if (iTemp >= mMesh.mFaceMaterials.size())
-				{
+			{
+				DefaultLogger::get()->error("Invalid face index in face material list");
 				mMesh.mFaceMaterials[mMesh.mFaceMaterials.size()-1] = iIndex;
 				mMesh.mFaceMaterials[mMesh.mFaceMaterials.size()-1] = iIndex;
-				}
+			}
 			else
 			else
-				{
+			{
 				mMesh.mFaceMaterials[iTemp] = iIndex;
 				mMesh.mFaceMaterials[iTemp] = iIndex;
-				}
+			}
 			this->mCurrent += sizeof(uint16_t);
 			this->mCurrent += sizeof(uint16_t);
 		}
 		}
 
 
@@ -959,8 +952,7 @@ void Dot3DSImporter::ParseMeshChunk(int* piRemaining)
 		}
 		}
 		break;
 		break;
 
 
-#if (defined _DEBUG)
-
+#if 0
 	case Dot3DSFile::CHUNK_TXTINFO:
 	case Dot3DSFile::CHUNK_TXTINFO:
 
 
 		// for debugging purposes. Read two bytes to determine the mapping type
 		// for debugging purposes. Read two bytes to determine the mapping type

+ 6 - 0
code/ASELoader.cpp

@@ -40,13 +40,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 /** @file Implementation of the ASE importer class */
 /** @file Implementation of the ASE importer class */
+
+// internal headers
 #include "ASELoader.h"
 #include "ASELoader.h"
 #include "3DSSpatialSort.h"
 #include "3DSSpatialSort.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 #include "StringComparison.h"
 #include "TextureTransform.h"
 #include "TextureTransform.h"
+
+// utilities
 #include "fast_atof.h"
 #include "fast_atof.h"
+#include "qnan.h"
 
 
+// ASSIMP public headers
 #include "../include/IOStream.h"
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"

+ 10 - 2
code/BaseImporter.h

@@ -111,7 +111,7 @@ public:
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Imports the given file and returns the imported data.
 	/** Imports the given file and returns the imported data.
 	* If the import succeeds, ownership of the data is transferred to 
 	* If the import succeeds, ownership of the data is transferred to 
-	* the caller. If the import failes, NULL is returned. The function
+	* the caller. If the import fails, NULL is returned. The function
 	* takes care that any partially constructed data is destroyed
 	* takes care that any partially constructed data is destroyed
 	* beforehand.
 	* beforehand.
 	*
 	*
@@ -154,7 +154,15 @@ protected:
 	 * an error. If it terminates normally, the data in aiScene is 
 	 * an error. If it terminates normally, the data in aiScene is 
 	 * expected to be correct. Override this function to implement the 
 	 * expected to be correct. Override this function to implement the 
 	 * actual importing.
 	 * actual importing.
-	 * 
+	 * <br>
+	 * The output scene must meet the following conditions:<br>
+	 * - at least one mesh must be there<br>
+	 * - at least a root node must be there<br>
+	 * - at least one material must be there<br>
+	 * - there may be no meshes with 0 vertices or faces<br>
+	 * This won't be checked (except by the validation step), Assimp will
+	 * crash if one of the conditions is not met!
+	 *
 	 * @param pFile Path of the file to be imported.
 	 * @param pFile Path of the file to be imported.
 	 * @param pScene The scene object to hold the imported data.
 	 * @param pScene The scene object to hold the imported data.
 	 * NULL is not a valid parameter.
 	 * NULL is not a valid parameter.

+ 10 - 6
code/DefaultIOStream.cpp

@@ -53,7 +53,7 @@ DefaultIOStream::~DefaultIOStream()
 {
 {
 	if (this->mFile)
 	if (this->mFile)
 	{
 	{
-		fclose(this->mFile);
+		::fclose(this->mFile);
 	}	
 	}	
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -61,20 +61,24 @@ size_t DefaultIOStream::Read(void* pvBuffer,
 								size_t pSize, 
 								size_t pSize, 
 								size_t pCount)
 								size_t pCount)
 {
 {
+	ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+
 	if (!this->mFile)
 	if (!this->mFile)
 		return 0;
 		return 0;
 
 
-	return fread(pvBuffer, pSize, pCount, this->mFile);
+	return ::fread(pvBuffer, pSize, pCount, this->mFile);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 size_t DefaultIOStream::Write(const void* pvBuffer, 
 size_t DefaultIOStream::Write(const void* pvBuffer, 
 								 size_t pSize,
 								 size_t pSize,
 								 size_t pCount)
 								 size_t pCount)
 {
 {
+	ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+
 	if (!this->mFile)return 0;
 	if (!this->mFile)return 0;
 
 
-	fseek(mFile, 0, SEEK_SET);
-	return fwrite(pvBuffer, pSize, pCount, this->mFile);
+	::fseek(mFile, 0, SEEK_SET);
+	return ::fwrite(pvBuffer, pSize, pCount, this->mFile);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 aiReturn DefaultIOStream::Seek(size_t pOffset,
 aiReturn DefaultIOStream::Seek(size_t pOffset,
@@ -82,7 +86,7 @@ aiReturn DefaultIOStream::Seek(size_t pOffset,
 {
 {
 	if (!this->mFile)return AI_FAILURE;
 	if (!this->mFile)return AI_FAILURE;
 
 
-	return (0 == fseek(this->mFile, (long)pOffset,
+	return (0 == ::fseek(this->mFile, (long)pOffset,
 		(aiOrigin_CUR == pOrigin ? SEEK_CUR :
 		(aiOrigin_CUR == pOrigin ? SEEK_CUR :
 		(aiOrigin_END == pOrigin ? SEEK_END : SEEK_SET))) 
 		(aiOrigin_END == pOrigin ? SEEK_END : SEEK_SET))) 
 		? AI_SUCCESS : AI_FAILURE);
 		? AI_SUCCESS : AI_FAILURE);
@@ -92,7 +96,7 @@ size_t DefaultIOStream::Tell() const
 {
 {
 	if (!this->mFile)return 0;
 	if (!this->mFile)return 0;
 
 
-	return ftell(this->mFile);
+	return ::ftell(this->mFile);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 size_t DefaultIOStream::FileSize() const
 size_t DefaultIOStream::FileSize() const

+ 3 - 3
code/DefaultIOSystem.cpp

@@ -65,11 +65,11 @@ DefaultIOSystem::~DefaultIOSystem()
 // Tests for the existence of a file at the given path.
 // Tests for the existence of a file at the given path.
 bool DefaultIOSystem::Exists( const std::string& pFile) const
 bool DefaultIOSystem::Exists( const std::string& pFile) const
 {
 {
-	FILE* file = fopen( pFile.c_str(), "rb");
+	FILE* file = ::fopen( pFile.c_str(), "rb");
 	if( !file)
 	if( !file)
 		return false;
 		return false;
 
 
-	fclose( file);
+	::fclose( file);
 	return true;
 	return true;
 }
 }
 
 
@@ -77,7 +77,7 @@ bool DefaultIOSystem::Exists( const std::string& pFile) const
 // Open a new file with a given path.
 // Open a new file with a given path.
 IOStream* DefaultIOSystem::Open( const std::string& strFile, const std::string& strMode)
 IOStream* DefaultIOSystem::Open( const std::string& strFile, const std::string& strMode)
 {
 {
-	FILE* file = fopen( strFile.c_str(), strMode.c_str());
+	FILE* file = ::fopen( strFile.c_str(), strMode.c_str());
 	if( NULL == file) 
 	if( NULL == file) 
 		return NULL;
 		return NULL;
 
 

+ 449 - 0
code/HMPLoader.cpp

@@ -0,0 +1,449 @@
+/*
+---------------------------------------------------------------------------
+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 MDL importer class */
+
+#include "MaterialSystem.h"
+#include "HMPLoader.h"
+
+#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 <boost/scoped_ptr.hpp>
+
+using namespace Assimp;
+
+extern float g_avNormals[162][3];
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+HMPImporter::HMPImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+HMPImporter::~HMPImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool HMPImporter::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);
+
+	if (extension.length() < 4)return false;
+	if (extension[0] != '.')return false;
+	if (extension[1] != 'h' && extension[1] != 'H')return false;
+	if (extension[2] != 'm' && extension[2] != 'M')return false;
+	if (extension[3] != 'p' && extension[3] != 'P')return false;
+
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void HMPImporter::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 HMP file " + pFile + ".");
+	}
+
+	// check whether the ply file is large enough to contain
+	// at least the file header
+	size_t fileSize = file->FileSize();
+	if( fileSize < 50)
+	{
+		throw new ImportErrorException( ".hmp File is too small.");
+	}
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	this->pScene = pScene;
+	this->pIOHandler = pIOHandler;
+	this->mBuffer = new unsigned char[fileSize+1];
+	file->Read( (void*)mBuffer, 1, fileSize);
+
+	this->iFileSize = (unsigned int)fileSize;
+
+	// determine the file subtype and call the appropriate member function
+	uint32_t iMagic = *((uint32_t*)this->mBuffer);
+
+	try {
+
+	// HMP4 format
+	if (AI_HMP_MAGIC_NUMBER_LE_4 == iMagic ||
+		AI_HMP_MAGIC_NUMBER_BE_4 == iMagic)
+	{
+		DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A4, magic word is HMP4");
+		this->InternReadFile_HMP4();
+	}
+	// HMP5 format
+	else if (AI_HMP_MAGIC_NUMBER_LE_5 == iMagic ||
+			 AI_HMP_MAGIC_NUMBER_BE_5 == iMagic)
+	{
+		DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A5, magic word is HMP5");
+		this->InternReadFile_HMP5();
+	}
+	// HMP7 format
+	else if (AI_HMP_MAGIC_NUMBER_LE_7 == iMagic ||
+			 AI_HMP_MAGIC_NUMBER_BE_7 == iMagic)
+	{
+		DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A7, magic word is HMP7");
+		this->InternReadFile_HMP7();
+	}
+	else
+	{
+		// we're definitely unable to load this file
+		throw new ImportErrorException( "Unknown HMP subformat " + pFile +
+			". Magic word is not known");
+	}
+
+	} catch (ImportErrorException* ex) {
+		delete[] this->mBuffer;
+		throw ex;
+	}
+
+	// delete the file buffer
+	delete[] this->mBuffer;
+	return;
+}
+
+// ------------------------------------------------------------------------------------------------ 
+void HMPImporter::InternReadFile_HMP4( )
+{
+	throw new ImportErrorException("HMP4 is currently not supported");
+}
+
+// ------------------------------------------------------------------------------------------------ 
+void HMPImporter::InternReadFile_HMP5( )
+{
+	throw new ImportErrorException("HMP4 is currently not supported");
+}
+
+// ------------------------------------------------------------------------------------------------ 
+void HMPImporter::InternReadFile_HMP7( )
+{
+	if (120 > this->iFileSize)
+	{
+		throw new ImportErrorException("HMP7 file is too small (header size is "
+			"120 bytes, this file is smaller)");
+	}
+
+	// read the file header and skip everything to byte 84
+	const HMP::Header_HMP5* pcHeader = (const HMP::Header_HMP5*)this->mBuffer;
+	const unsigned char* szCurrent = (const unsigned char*)(this->mBuffer+84);
+
+	if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y ||
+		pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f ||
+		!pcHeader->numframes)
+	{
+		throw new ImportErrorException("One or more of the values in the HMP7 "
+			"file header are invalid.");
+	}
+
+	// generate an output mesh
+	this->pScene->mNumMeshes = 1;
+	this->pScene->mMeshes = new aiMesh*[1];
+	aiMesh* pcMesh = this->pScene->mMeshes[0] = new aiMesh();
+
+	pcMesh->mMaterialIndex = 0;
+	pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
+	pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
+
+	const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
+	const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
+
+	// we don't need to generate texture coordinates if
+	// we have no textures in the file ...
+	if (pcHeader->numskins)
+	{
+		pcMesh->mTextureCoords[0] = new aiVector3D[pcHeader->numverts];
+		pcMesh->mNumUVComponents[0] = 2;
+
+		// now read the first skin and skip all others
+		this->ReadFirstSkin(pcHeader->numskins,szCurrent,&szCurrent);
+	}
+	else
+	{
+		// generate a default material
+		const int iMode = (int)aiShadingMode_Gouraud;
+		MaterialHelper* pcHelper = new MaterialHelper();
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.7f;
+		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);
+
+		// add the material to the scene
+		this->pScene->mNumMaterials = 1;
+		this->pScene->mMaterials = new aiMaterial*[1];
+		this->pScene->mMaterials[0] = pcHelper;
+	}
+
+	// goto offset 120, I don't know why ...
+	// (fixme) is this the frame header? I assume yes since it starts
+	// with 2. 
+	szCurrent += 36;
+
+	this->SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
+
+	// now load all vertices from the file
+	aiVector3D* pcVertOut = pcMesh->mVertices;
+	aiVector3D* pcNorOut = pcMesh->mNormals;
+	const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
+	for (unsigned int y = 0; y < height;++y)
+	{
+		for (unsigned int x = 0; x < width;++x)
+		{
+			pcVertOut->x = x * pcHeader->ftrisize_x;
+			pcVertOut->y = y * pcHeader->ftrisize_y;
+			// FIXME: What exctly is the correct scaling factor to use?
+			// possibly pcHeader->scale_origin[2] in combination with a
+			// signed interpretation of src->z?
+			pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f; 
+
+			pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
+			pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
+			pcNorOut->z = 1.0f;
+			pcNorOut->Normalize();
+			
+			++pcVertOut;++pcNorOut;++src;
+		}
+	}
+
+	// generate texture coordinates if necessary
+	if (pcHeader->numskins)
+	{
+		this->GenerateTextureCoords(width,height);
+	}
+
+	// now build a list of faces
+	const unsigned int iNumSquares = (width-1) * (height-1);
+	pcMesh->mNumFaces = iNumSquares << 1;
+	pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+	pcMesh->mNumVertices = pcMesh->mNumFaces*3;
+	aiVector3D* pcVertices = new aiVector3D[pcMesh->mNumVertices];
+	aiVector3D* pcNormals = new aiVector3D[pcMesh->mNumVertices];
+
+	aiFace* pcFaceOut(pcMesh->mFaces);
+	pcVertOut = pcVertices;
+	pcNorOut = pcNormals;
+
+	aiVector3D* pcUVs = pcMesh->mTextureCoords[0] ? new aiVector3D[pcMesh->mNumVertices] : NULL;
+	aiVector3D* pcUVOut(pcUVs);
+
+	unsigned int iCurrent = 0;
+	for (unsigned int y = 0; y < height-1;++y)
+	{
+		for (unsigned int x = 0; x < width-1;++x)
+		{
+			// first triangle of the square
+			pcFaceOut->mNumIndices = 3;
+			pcFaceOut->mIndices = new unsigned int[3];
+
+			*pcVertOut++ = pcMesh->mVertices[y*width+x];
+			*pcVertOut++ = pcMesh->mVertices[y*width+x+1];
+			*pcVertOut++ = pcMesh->mVertices[(y+1)*width+x];
+
+			*pcNorOut++ = pcMesh->mNormals[y*width+x];
+			*pcNorOut++ = pcMesh->mNormals[y*width+x+1];
+			*pcNorOut++ = pcMesh->mNormals[(y+1)*width+x];
+
+			if (pcMesh->mTextureCoords[0])
+			{
+				*pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x];
+				*pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x+1];
+				*pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x];
+			}
+			
+			pcFaceOut->mIndices[2] = iCurrent++;
+			pcFaceOut->mIndices[1] = iCurrent++;
+			pcFaceOut->mIndices[0] = iCurrent++;
+			++pcFaceOut;
+
+			// second triangle of the square
+			pcFaceOut->mNumIndices = 3;
+			pcFaceOut->mIndices = new unsigned int[3];
+
+			*pcVertOut++ = pcMesh->mVertices[(y+1)*width+x];
+			*pcVertOut++ = pcMesh->mVertices[y*width+x+1];
+			*pcVertOut++ = pcMesh->mVertices[(y+1)*width+x+1];
+
+			*pcNorOut++ = pcMesh->mNormals[(y+1)*width+x];
+			*pcNorOut++ = pcMesh->mNormals[y*width+x+1];
+			*pcNorOut++ = pcMesh->mNormals[(y+1)*width+x+1];
+
+			if (pcMesh->mTextureCoords[0])
+			{
+				*pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x];
+				*pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x+1];
+				*pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x+1];
+			}
+			
+			pcFaceOut->mIndices[2] = iCurrent++;
+			pcFaceOut->mIndices[1] = iCurrent++;
+			pcFaceOut->mIndices[0] = iCurrent++;
+			++pcFaceOut;
+		}
+	}
+	delete[] pcMesh->mVertices;
+	pcMesh->mVertices = pcVertices;
+
+	delete[] pcMesh->mNormals;
+	pcMesh->mNormals = pcNormals;
+
+	if (pcMesh->mTextureCoords[0])
+	{
+		delete[] pcMesh->mTextureCoords[0];
+		pcMesh->mTextureCoords[0] = pcUVs;
+	}
+
+	// there is no nodegraph in HMP files. Simply assign the one mesh
+	// (no, not the one ring) to the root node
+	this->pScene->mRootNode = new aiNode();
+	this->pScene->mRootNode->mName.Set("terrain_root");
+	this->pScene->mRootNode->mNumMeshes = 1;
+	this->pScene->mRootNode->mMeshes = new unsigned int[1];
+	this->pScene->mRootNode->mMeshes[0] = 0;
+
+	return;
+}
+// ------------------------------------------------------------------------------------------------ 
+void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+	const unsigned char** szCursorOut)
+{
+	ai_assert(0 != iNumSkins && NULL != szCursor);
+
+	// read the type of the skin ...
+	// sometimes we need to skip 12 bytes here, I don't know why ...
+	uint32_t iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+	if (0 == iType)
+	{
+		DefaultLogger::get()->warn("Skin type is 0. Skipping 12 bytes to "
+			"the next valid value, which seems to be the real skin type. "
+			"However, it is not known whether or not this is correct.");
+		szCursor += sizeof(uint32_t) * 2;
+		iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+		if (0 == iType)
+		{
+			throw new ImportErrorException("Unable to read HMP7 skin chunk");
+		}
+	}
+	// read width and height
+	uint32_t iWidth = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+	uint32_t iHeight = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+
+	// allocate an output material
+	MaterialHelper* pcMat = new MaterialHelper();
+
+	// read the skin, this works exactly as for MDL7
+	this->ParseSkinLump_3DGS_MDL7(szCursor,&szCursor,
+		pcMat,iType,iWidth,iHeight);
+
+	// now we need to skip any other skins ... 
+	for (unsigned int i = 1; i< iNumSkins;++i)
+	{
+		iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+		iWidth = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+		iHeight = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+
+		this->SkipSkinLump_3DGS_MDL7(szCursor,&szCursor,
+			iType,iWidth,iHeight);
+
+		this->SizeCheck(szCursor);
+	}
+
+	// setup the material ...
+	this->pScene->mNumMaterials = 1;
+	this->pScene->mMaterials = new aiMaterial*[1];
+	this->pScene->mMaterials[0] = pcMat;
+
+	*szCursorOut = szCursor;
+	return;
+}
+// ------------------------------------------------------------------------------------------------ 
+void HMPImporter::GenerateTextureCoords(
+	const unsigned int width, const unsigned int height)
+{
+	ai_assert(NULL != this->pScene->mMeshes && NULL != this->pScene->mMeshes[0] &&
+		NULL != this->pScene->mMeshes[0]->mTextureCoords[0]);
+
+	aiVector3D* uv = this->pScene->mMeshes[0]->mTextureCoords[0];
+
+	const float fX = (1.0f / height) + (1.0f / height) / (height-1);
+	const float fY = (1.0f / width) + (1.0f / width) / (width-1);
+
+	for (unsigned int y = 0; y < height;++y)
+	{
+		for (unsigned int x = 0; x < width;++x)
+		{
+			uv->x = fX*x;
+			uv->y = 1.0f-fY*y;
+			++uv;
+		}
+	}
+	return;
+}

+ 208 - 0
code/HMPLoader.h

@@ -0,0 +1,208 @@
+/*
+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 Definition of HMP importer class
+//!
+
+#ifndef AI_HMPLOADER_H_INCLUDED
+#define AI_HMPLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiMaterial.h"
+
+struct aiNode;
+#include "MDLLoader.h"
+
+namespace Assimp
+{
+class MaterialHelper;
+
+#define AI_HMP_MAGIC_NUMBER_BE_4	'HMP4'
+#define AI_HMP_MAGIC_NUMBER_LE_4	'4PMH'
+
+#define AI_HMP_MAGIC_NUMBER_BE_5	'HMP5'
+#define AI_HMP_MAGIC_NUMBER_LE_5	'5PMH'
+
+#define AI_HMP_MAGIC_NUMBER_BE_7	'HMP7'
+#define AI_HMP_MAGIC_NUMBER_LE_7	'7PMH'
+
+namespace HMP
+{
+
+// ---------------------------------------------------------------------------
+/** Data structure for the header of a HMP5 file.
+ *  This is also used by HMP4 and HMP7, but with modifications
+*/
+struct Header_HMP5
+{
+	int8_t	ident[4]; // "HMP5"
+	int32_t		version;
+	
+	// ignored
+	float	scale[3];
+	float	scale_origin[3];
+	float	boundingradius;
+	
+	//! Size of one triangle in x direction
+	float	ftrisize_x;		
+	//! Size of one triangle in y direction
+	float	ftrisize_y;		
+	//! Number of vertices in x direction
+	float	fnumverts_x;	
+							
+	//! Number of skins in the file
+	int32_t		numskins;
+
+	// can ignore this?
+	int32_t		skinwidth;
+	int32_t		skinheight;
+
+	//!Number of vertices in the file
+	int32_t		numverts;
+
+	// ignored and zero
+	int32_t		numtris;
+
+	//! only one supported ...
+	int32_t		numframes;		
+
+	//! Always 0 ...
+	int32_t		num_stverts;	
+	int32_t		flags;
+	float	size;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP7 file 
+*/
+struct Vertex_HMP7
+{
+	uint16_t	 z;				
+	int8_t normal_x,normal_y;
+};
+
+}; //! namespace HMP
+
+// ---------------------------------------------------------------------------
+/** Used to load 3D GameStudio HMP files (terrains)
+*/
+class HMPImporter : public MDLImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	HMPImporter();
+
+	/** Destructor, private as well */
+	~HMPImporter();
+
+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("*.hmp");
+	}
+
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. 
+	* See BaseImporter::InternReadFile() for details
+	*/
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+protected:
+
+	// -------------------------------------------------------------------
+	/** Import a HMP4 file
+	*/
+	void InternReadFile_HMP4( );
+
+	// -------------------------------------------------------------------
+	/** Import a HMP5 file
+	*/
+	void InternReadFile_HMP5( );
+
+	// -------------------------------------------------------------------
+	/** Import a HMP7 file
+	*/
+	void InternReadFile_HMP7( );
+
+
+	// -------------------------------------------------------------------
+	/** Generate planar texture coordinates for a terrain
+	 * \param width Width of the terrain, in vertices
+	 * \param height Height of the terrain, in vertices
+	*/
+	void GenerateTextureCoords(const unsigned int width, 
+		const unsigned int height);
+
+	// -------------------------------------------------------------------
+	/** Read the first skin from the file and skip all others ...
+	 *  \param iNumSkins Number of skins in the file
+	 *  \param szCursor Position of the first skin (offset 84)
+	*/
+	void ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+		const unsigned char** szCursorOut);
+
+private:
+
+
+};
+}; // end of namespace Assimp
+
+#endif // AI_HMPIMPORTER_H_INC

+ 87 - 13
code/Importer.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "DefaultIOStream.h"
 #include "DefaultIOStream.h"
 #include "DefaultIOSystem.h"
 #include "DefaultIOSystem.h"
 
 
+// Importers
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #	include "XFileImporter.h"
 #	include "XFileImporter.h"
 #endif
 #endif
@@ -81,18 +82,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #if (!defined AI_BUILD_NO_OBJ_IMPORTER)
 #if (!defined AI_BUILD_NO_OBJ_IMPORTER)
 #	include "ObjFileImporter.h"
 #	include "ObjFileImporter.h"
 #endif
 #endif
+#if (!defined AI_BUILD_NO_HMP_IMPORTER)
+#	include "HMPLoader.h"
+#endif
+#if (!defined AI_BUILD_NO_SMD_IMPORTER)
+#	include "SMDLoader.h"
+#endif
 
 
-#include "CalcTangentsProcess.h"
-#include "JoinVerticesProcess.h"
-#include "ConvertToLHProcess.h"
-#include "TriangulateProcess.h"
-#include "GenFaceNormalsProcess.h"
-#include "GenVertexNormalsProcess.h"
-#include "KillNormalsProcess.h"
-#include "SplitLargeMeshes.h"
-#include "PretransformVertices.h"
-#include "LimitBoneWeightsProcess.h"
-#include "ValidateDataStructure.h"
+// PostProcess-Steps
+#if (!defined AI_BUILD_NO_CALCTANGENTS_PROCESS)
+#	include "CalcTangentsProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_JOINVERTICES_PROCESS)
+#	include "JoinVerticesProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_CONVERTTOLH_PROCESS)
+#	include "ConvertToLHProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
+#	include "TriangulateProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_GENFACENORMALS_PROCESS)
+#	include "GenFaceNormalsProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_GENVERTEXNORMALS_PROCESS)
+#	include "GenVertexNormalsProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_KILLNORMALS_PROCESS)
+#	include "KillNormalsProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS)
+#	include "SplitLargeMeshes.h"
+#endif
+#if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
+#	include "PretransformVertices.h"
+#endif
+#if (!defined AI_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
+#	include "LimitBoneWeightsProcess.h"
+#endif
+#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
+#	include "ValidateDataStructure.h"
+#endif
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
@@ -105,6 +135,7 @@ Importer::Importer() :
 {
 {
 	// allocate a default IO handler
 	// allocate a default IO handler
 	mIOHandler = new DefaultIOSystem;
 	mIOHandler = new DefaultIOSystem;
+	mIsDefaultHandler = true;
 
 
 	// add an instance of each worker class here
 	// add an instance of each worker class here
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #if (!defined AI_BUILD_NO_X_IMPORTER)
@@ -134,20 +165,51 @@ Importer::Importer() :
 #if (!defined AI_BUILD_NO_ASE_IMPORTER)
 #if (!defined AI_BUILD_NO_ASE_IMPORTER)
 	mImporter.push_back( new ASEImporter());
 	mImporter.push_back( new ASEImporter());
 #endif
 #endif
+	#if (!defined AI_BUILD_NO_HMP_IMPORTER)
+	mImporter.push_back( new HMPImporter());
+#endif
+	#if (!defined AI_BUILD_NO_SMD_IMPORTER)
+	mImporter.push_back( new SMDImporter());
+#endif
 
 
-	// add an instance of each post processing step here in the order of sequence it is executed
+	// add an instance of each post processing step here in the order 
+	// of sequence it is executed
+#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
 	mPostProcessingSteps.push_back( new ValidateDSProcess());
 	mPostProcessingSteps.push_back( new ValidateDSProcess());
+#endif
+#if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
 	mPostProcessingSteps.push_back( new TriangulateProcess());
 	mPostProcessingSteps.push_back( new TriangulateProcess());
+#endif
+#if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
 	mPostProcessingSteps.push_back( new PretransformVertices());
 	mPostProcessingSteps.push_back( new PretransformVertices());
+#endif
+#if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS)
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
+#endif
+#if (!defined AI_BUILD_NO_KILLNORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new KillNormalsProcess());
 	mPostProcessingSteps.push_back( new KillNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_GENFACENORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
 	mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_GENVERTEXNORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
 	mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_CALCTANGENTS_PROCESS)
 	mPostProcessingSteps.push_back( new CalcTangentsProcess());
 	mPostProcessingSteps.push_back( new CalcTangentsProcess());
+#endif
+#if (!defined AI_BUILD_NO_JOINVERTICES_PROCESS)
 	mPostProcessingSteps.push_back( new JoinVerticesProcess());
 	mPostProcessingSteps.push_back( new JoinVerticesProcess());
+#endif
+#if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS)
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
+#endif
+#if (!defined AI_BUILD_NO_CONVERTTOLH_PROCESS)
 	mPostProcessingSteps.push_back( new ConvertToLHProcess());
 	mPostProcessingSteps.push_back( new ConvertToLHProcess());
+#endif
+#if (!defined AI_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
 	mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
 	mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
+#endif
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -170,19 +232,31 @@ Importer::~Importer()
 // Supplies a custom IO handler to the importer to open and access files.
 // Supplies a custom IO handler to the importer to open and access files.
 void Importer::SetIOHandler( IOSystem* pIOHandler)
 void Importer::SetIOHandler( IOSystem* pIOHandler)
 {
 {
-	if (NULL == pIOHandler)
+	if (!pIOHandler)
 	{
 	{
 		delete mIOHandler;
 		delete mIOHandler;
 		mIOHandler = new DefaultIOSystem();
 		mIOHandler = new DefaultIOSystem();
+		mIsDefaultHandler = true;
 	}
 	}
 	else if (mIOHandler != pIOHandler)
 	else if (mIOHandler != pIOHandler)
 	{
 	{
 		delete mIOHandler;
 		delete mIOHandler;
 		mIOHandler = pIOHandler;
 		mIOHandler = pIOHandler;
+		mIsDefaultHandler = false;
 	}
 	}
 	return;
 	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
+IOSystem* Importer::GetIOHandler()
+{
+	return mIOHandler;
+}
+// ------------------------------------------------------------------------------------------------
+bool Importer::IsDefaultIOHandler()
+{
+	return mIsDefaultHandler;
+}
+// ------------------------------------------------------------------------------------------------
 // Validate post process step flags 
 // Validate post process step flags 
 bool ValidateFlags(unsigned int pFlags)
 bool ValidateFlags(unsigned int pFlags)
 {
 {

+ 62 - 2
code/LimitBoneWeightsProcess.cpp

@@ -1,20 +1,80 @@
+/*
+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 LimitBoneWeightsProcess post processing step */
 /** Implementation of the LimitBoneWeightsProcess post processing step */
 
 
 #include <vector>
 #include <vector>
 #include <assert.h>
 #include <assert.h>
+
 #include "LimitBoneWeightsProcess.h"
 #include "LimitBoneWeightsProcess.h"
+
 #include "../include/aiPostProcess.h"
 #include "../include/aiPostProcess.h"
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
+#include "../include/DefaultLogger.h"
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
+
+/*static*/ unsigned int LimitBoneWeightsProcess::mMaxWeights = AI_LMW_MAX_WEIGHTS;
+
+extern "C" {
+// ------------------------------------------------------------------------------------------------
+aiReturn aiSetBoneWeightLimit(unsigned int pLimit)
+{
+	if (0 == pLimit)
+	{
+		LimitBoneWeightsProcess::mMaxWeights = 0xFFFFFFFF;
+		return AI_FAILURE;
+	}
+
+	LimitBoneWeightsProcess::mMaxWeights = pLimit;
+	DefaultLogger::get()->debug("aiSetBoneWeightLimit() - bone weight limit was changed");
+	return AI_SUCCESS;
+}
+};
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 LimitBoneWeightsProcess::LimitBoneWeightsProcess()
 LimitBoneWeightsProcess::LimitBoneWeightsProcess()
 {
 {
-	// TODO: (thom) make this configurable from somewhere?
-	mMaxWeights = 4;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 55 - 1
code/LimitBoneWeightsProcess.h

@@ -1,3 +1,43 @@
+/*
+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 limit the number of bones affecting a single vertex. */
 /** Defines a post processing step to limit the number of bones affecting a single vertex. */
 #ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
 #ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
 #define AI_LIMITBONEWEIGHTSPROCESS_H_INC
 #define AI_LIMITBONEWEIGHTSPROCESS_H_INC
@@ -9,6 +49,18 @@ struct aiMesh;
 namespace Assimp
 namespace Assimp
 {
 {
 
 
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: PostProcessStep.java, 
+//  PostProcessStep.DEFAULT_BONE_WEIGHT_LIMIT
+// **********************************************************
+
+#if (!defined AI_LMW_MAX_WEIGHTS)
+#	define AI_LMW_MAX_WEIGHTS	0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** This post processing step limits the number of bones affecting a vertex
 /** This post processing step limits the number of bones affecting a vertex
 * to a certain maximum value. If a vertex is affected by more than that number
 * to a certain maximum value. If a vertex is affected by more than that number
@@ -58,12 +110,14 @@ protected:
 		float mWeight;      ///< Weight of that bone on this vertex
 		float mWeight;      ///< Weight of that bone on this vertex
 		Weight() { }
 		Weight() { }
 		Weight( unsigned int pBone, float pWeight) { mBone = pBone; mWeight = pWeight; }
 		Weight( unsigned int pBone, float pWeight) { mBone = pBone; mWeight = pWeight; }
+
 		/** Comparision operator to sort bone weights by descending weight */
 		/** Comparision operator to sort bone weights by descending weight */
 		bool operator < (const Weight& pWeight) const { return mWeight > pWeight.mWeight; }
 		bool operator < (const Weight& pWeight) const { return mWeight > pWeight.mWeight; }
 	};
 	};
 
 
+public:
 	/** Maximum number of bones influencing any single vertex. */
 	/** Maximum number of bones influencing any single vertex. */
-	unsigned int mMaxWeights;
+	static unsigned int mMaxWeights;
 };
 };
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 26 - 24
code/MD2FileData.h

@@ -80,34 +80,33 @@ namespace MD2
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for the MD2 main header
 /**	\brief Data structure for the MD2 main header
  */
  */
-// ---------------------------------------------------------------------------
 struct Header
 struct Header
 {
 {
-	int32_t magic; 
-	int32_t version; 
-	int32_t skinWidth; 
-	int32_t skinHeight; 
-	int32_t frameSize; 
-	int32_t numSkins; 
-	int32_t numVertices; 
-	int32_t numTexCoords; 
-	int32_t numTriangles; 
-	int32_t numGlCommands; 
-	int32_t numFrames; 
-	int32_t offsetSkins; 
-	int32_t offsetTexCoords; 
-	int32_t offsetTriangles; 
-	int32_t offsetFrames; 
-	int32_t offsetGlCommands; 
-	int32_t offsetEnd; 
+	uint32_t magic; 
+	uint32_t version; 
+	uint32_t skinWidth; 
+	uint32_t skinHeight; 
+	uint32_t frameSize; 
+	uint32_t numSkins; 
+	uint32_t numVertices; 
+	uint32_t numTexCoords; 
+	uint32_t numTriangles; 
+	uint32_t numGlCommands; 
+	uint32_t numFrames; 
+	uint32_t offsetSkins; 
+	uint32_t offsetTexCoords; 
+	uint32_t offsetTriangles; 
+	uint32_t offsetFrames; 
+	uint32_t offsetGlCommands; 
+	uint32_t offsetEnd; 
 
 
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 OpenGl draw command
 /**	\brief Data structure for a MD2 OpenGl draw command
  */
  */
-// ---------------------------------------------------------------------------
 struct GLCommand
 struct GLCommand
 {
 {
    float s, t;
    float s, t;
@@ -117,7 +116,6 @@ struct GLCommand
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 triangle
 /**	\brief Data structure for a MD2 triangle
  */
  */
-// ---------------------------------------------------------------------------
 struct Triangle
 struct Triangle
 {
 {
 	uint16_t vertexIndices[3];
 	uint16_t vertexIndices[3];
@@ -127,7 +125,6 @@ struct Triangle
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 vertex
 /**	\brief Data structure for a MD2 vertex
  */
  */
-// ---------------------------------------------------------------------------
 struct Vertex
 struct Vertex
 {
 {
 	uint8_t vertex[3];
 	uint8_t vertex[3];
@@ -137,7 +134,6 @@ struct Vertex
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 frame
 /**	\brief Data structure for a MD2 frame
  */
  */
-// ---------------------------------------------------------------------------
 struct Frame
 struct Frame
 {
 {
 	float scale[3];
 	float scale[3];
@@ -149,7 +145,6 @@ struct Frame
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 texture coordinate
 /**	\brief Data structure for a MD2 texture coordinate
  */
  */
-// ---------------------------------------------------------------------------
 struct TexCoord
 struct TexCoord
 {
 {
 	int16_t s;
 	int16_t s;
@@ -159,7 +154,6 @@ struct TexCoord
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 skin
 /**	\brief Data structure for a MD2 skin
  */
  */
-// ---------------------------------------------------------------------------
 struct Skin
 struct Skin
 {
 {
 	char name[AI_MD2_MAXQPATH];              /* texture file name */
 	char name[AI_MD2_MAXQPATH];              /* texture file name */
@@ -171,6 +165,14 @@ struct Skin
 #endif
 #endif
 #undef PACK_STRUCT
 #undef PACK_STRUCT
 
 
+
+// ---------------------------------------------------------------------------
+//! Lookup a normal vector from Quake's normal lookup table
+//! \param index Input index (0-161)
+//! \param vOut Receives the output normal
+void LookupNormalIndex(uint8_t index,aiVector3D& vOut);
+
+
 };
 };
 };
 };
 
 

+ 150 - 91
code/MD2Loader.cpp

@@ -42,40 +42,42 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file Implementation of the MD2 importer class */
 /** @file Implementation of the MD2 importer class */
 #include "MD2Loader.h"
 #include "MD2Loader.h"
 #include "MaterialSystem.h"
 #include "MaterialSystem.h"
-
-#include "MD2NormalTable.h"
+#include "MD2NormalTable.h" // shouldn't be included by other units
 
 
 #include "../include/IOStream.h"
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
 using namespace Assimp;
 using namespace Assimp;
+using namespace Assimp::MD2;
+
+
+// helper macro to determine the size of an array
+#if (!defined ARRAYSIZE)
+#	define ARRAYSIZE(_array) (int(sizeof(_array) / sizeof(_array[0])))
+#endif 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-inline bool is_qnan(float p_fIn)
+// Helper function to lookup a normal in Quake 2's precalculated table
+void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
 {
 {
-	// NOTE: Comparison against qnan is generally problematic
-	// because qnan == qnan is false AFAIK
-	union FTOINT
+	// make sure the normal index has a valid value
+	if (iNormalIndex >= ARRAYSIZE(g_avNormals))
 	{
 	{
-		float fFloat;
-		int32_t iInt;
-	} one, two;
-	one.fFloat = std::numeric_limits<float>::quiet_NaN();
-	two.fFloat = p_fIn;
+		DefaultLogger::get()->warn("Index overflow in MDL7 normal vector list (the "
+			" LUT has only 162 entries). ");
 
 
-	return (one.iInt == two.iInt);
-}
-// ------------------------------------------------------------------------------------------------
-inline bool is_not_qnan(float p_fIn)
-{
-	return !is_qnan(p_fIn);
+		iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
+	}
+	vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
 }
 }
 
 
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 MD2Importer::MD2Importer()
 MD2Importer::MD2Importer()
@@ -99,12 +101,43 @@ bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 		return false;
 		return false;
 	std::string extension = pFile.substr( pos);
 	std::string extension = pFile.substr( pos);
 
 
-	// not brilliant but working ;-)
-	if( extension == ".md2" || extension == ".MD2" || 
-		extension == ".mD2" || extension == ".Md2")
-		return true;
+	if (extension.length() < 4)return false;
+	if (extension[0] != '.')return false;
+	if (extension[1] != 'm' && extension[1] != 'M')return false;
+	if (extension[2] != 'd' && extension[2] != 'D')return false;
+	if (extension[3] != '2')return false;
 
 
-	return false;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Validate the file header
+void MD2Importer::ValidateHeader( )
+{
+	/* to be validated:
+	int32_t offsetSkins; 
+	int32_t offsetTexCoords; 
+	int32_t offsetTriangles; 
+	int32_t offsetFrames; 
+	//int32_t offsetGlCommands; 
+	int32_t offsetEnd; 
+	*/
+
+	if (this->m_pcHeader->offsetSkins	+ this->m_pcHeader->numSkins * sizeof (MD2::Skin)			>= this->fileSize ||
+		this->m_pcHeader->offsetTexCoords + this->m_pcHeader->numTexCoords * sizeof (MD2::TexCoord) >= this->fileSize ||
+		this->m_pcHeader->offsetTriangles + this->m_pcHeader->numTriangles * sizeof (MD2::Triangle) >= this->fileSize ||
+		this->m_pcHeader->offsetFrames	  + this->m_pcHeader->numFrames * sizeof (MD2::Frame)		>= this->fileSize ||
+		this->m_pcHeader->offsetEnd			> this->fileSize)
+	{
+		throw new ImportErrorException("Invalid MD2 header: some offsets are outside the file");
+		delete[] this->mBuffer;
+	}
+
+	if (this->m_pcHeader->numSkins > AI_MD2_MAX_SKINS)
+		DefaultLogger::get()->warn("The model contains more skins than Quake 2 supports");
+	if ( this->m_pcHeader->numFrames > AI_MD2_MAX_FRAMES)
+		DefaultLogger::get()->warn("The model contains more frames than Quake 2 supports");
+	if (this->m_pcHeader->numVertices > AI_MD2_MAX_VERTS)
+		DefaultLogger::get()->warn("The model contains more vertices than Quake 2 supports");
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 
@@ -121,10 +154,10 @@ void MD2Importer::InternReadFile(
 
 
 	// check whether the md3 file is large enough to contain
 	// check whether the md3 file is large enough to contain
 	// at least the file header
 	// at least the file header
-	size_t fileSize = file->FileSize();
+	fileSize = (unsigned int)file->FileSize();
 	if( fileSize < sizeof(MD2::Header))
 	if( fileSize < sizeof(MD2::Header))
 	{
 	{
-		throw new ImportErrorException( ".md2 File is too small.");
+		throw new ImportErrorException( "md2 File is too small.");
 	}
 	}
 
 
 	// allocate storage and copy the contents of the file to a memory buffer
 	// allocate storage and copy the contents of the file to a memory buffer
@@ -137,22 +170,26 @@ void MD2Importer::InternReadFile(
 	if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
 	if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
 		this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
 		this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
 		throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
 	}
 	}
 
 
 	// check file format version
 	// check file format version
 	if (this->m_pcHeader->version != 8)
 	if (this->m_pcHeader->version != 8)
 	{
 	{
-		throw new ImportErrorException( "Unsupported md3 file version");
+		DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ...");
 	}
 	}
+	this->ValidateHeader();
 
 
 	// check some values whether they are valid
 	// check some values whether they are valid
 	if (0 == this->m_pcHeader->numFrames)
 	if (0 == this->m_pcHeader->numFrames)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
 		throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
 	}
 	}
 	if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
 	if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: File is too small");
 		throw new ImportErrorException( "Invalid md2 file: File is too small");
 	}
 	}
 
 
@@ -166,39 +203,35 @@ void MD2Importer::InternReadFile(
 	pScene->mMaterials[0] = new MaterialHelper();
 	pScene->mMaterials[0] = new MaterialHelper();
 	pScene->mNumMeshes = 1;
 	pScene->mNumMeshes = 1;
 	pScene->mMeshes = new aiMesh*[1];
 	pScene->mMeshes = new aiMesh*[1];
-	pScene->mMeshes[0] = new aiMesh();
+	aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
 
 
 	// navigate to the begin of the frame data
 	// navigate to the begin of the frame data
-	const MD2::Frame* pcFrame = (const MD2::Frame*) ((unsigned char*)this->m_pcHeader + 
-		this->m_pcHeader->offsetFrames);
+	const MD2::Frame* pcFrame = (const MD2::Frame*) (
+		(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetFrames);
 
 
 	// navigate to the begin of the triangle data
 	// navigate to the begin of the triangle data
-	MD2::Triangle* pcTriangles = (MD2::Triangle*) ((unsigned char*)this->m_pcHeader + 
-		this->m_pcHeader->offsetTriangles);
+	MD2::Triangle* pcTriangles = (MD2::Triangle*) (
+		(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTriangles);
 
 
 	// navigate to the begin of the tex coords data
 	// navigate to the begin of the tex coords data
-	const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((unsigned char*)this->m_pcHeader + 
-		this->m_pcHeader->offsetTexCoords);
+	const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) (
+		(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
 
 
 	// navigate to the begin of the vertex data
 	// navigate to the begin of the vertex data
 	const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
 	const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
 
 
-	pScene->mMeshes[0]->mNumFaces = this->m_pcHeader->numTriangles;
-	pScene->mMeshes[0]->mFaces = new aiFace[this->m_pcHeader->numTriangles];
-
-	// temporary vectors for position/texture coordinates/normals
-	std::vector<aiVector3D> vPositions;
-	std::vector<aiVector3D> vTexCoords;
-	std::vector<aiVector3D> vNormals;
+	pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
+	pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
 
 
-	vPositions.resize(pScene->mMeshes[0]->mNumFaces*3,aiVector3D());
-	vTexCoords.resize(pScene->mMeshes[0]->mNumFaces*3,aiVector3D(
-		std::numeric_limits<float>::quiet_NaN(),
-		std::numeric_limits<float>::quiet_NaN(),0.0f));
-	vNormals.resize(pScene->mMeshes[0]->mNumFaces*3,aiVector3D());
+	// 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
 	// not sure whether there are MD2 files without texture coordinates
-	if (0 != this->m_pcHeader->numTexCoords && 0 != this->m_pcHeader->numSkins)
+	// 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
 		// navigate to the first texture associated with the mesh
 		const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + 
 		const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + 
@@ -216,12 +249,20 @@ void MD2Importer::InternReadFile(
 		clr.b = clr.g = clr.r = 0.05f;
 		clr.b = clr.g = clr.r = 0.05f;
 		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 
 
-		aiString szString;
-		const size_t iLen = strlen(pcSkins->name);
-		memcpy(szString.data,pcSkins->name,iLen+1);
-		szString.length = iLen-1;
+		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));
+			pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+		}
+		else
+		{
+			DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
+		}
 	}
 	}
 	else
 	else
 	{
 	{
@@ -237,13 +278,42 @@ void MD2Importer::InternReadFile(
 
 
 		clr.b = clr.g = clr.r = 0.05f;
 		clr.b = clr.g = clr.r = 0.05f;
 		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 		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
 	// now read all triangles of the first frame, apply scaling and translation
 	unsigned int iCurrent = 0;
 	unsigned int iCurrent = 0;
-	if (0 != this->m_pcHeader->numTexCoords)
+	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)
+		float fDivisorU;
+		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;
+
+		float fDivisorV;
+		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)
 		{
 		{
@@ -259,13 +329,16 @@ void MD2Importer::InternReadFile(
 			{
 			{
 				// validate vertex indices
 				// validate vertex indices
 				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
 				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;
 					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+				}
 
 
 				// copy face indices
 				// copy face indices
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 
 
 				// read x,y, and z component of the vertex
 				// read x,y, and z component of the vertex
-				aiVector3D& vec = vPositions[iCurrent];
+				aiVector3D& vec = pcMesh->mVertices[iCurrent];
 
 
 				vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
 				vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
 				vec.x += pcFrame->translate[0];
 				vec.x += pcFrame->translate[0];
@@ -278,23 +351,28 @@ void MD2Importer::InternReadFile(
 				vec.y += pcFrame->translate[2];
 				vec.y += pcFrame->translate[2];
 
 
 				// read the normal vector from the precalculated normal table
 				// read the normal vector from the precalculated normal table
-				vNormals[iCurrent] = *((const aiVector3D*)(&g_avNormals[std::min(
-					int(pcVerts[iIndex].lightNormalIndex),
-					int(sizeof(g_avNormals) / sizeof(g_avNormals[0]))-1)]));
-
-				std::swap ( vNormals[iCurrent].y,vNormals[iCurrent].z );
+				aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
+				LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
+				std::swap ( vNormal.y,vNormal.z );
 
 
 				// validate texture coordinates
 				// validate texture coordinates
 				if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
 				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;
 					pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
+				}
 
 
-				aiVector3D* pcOut = &vTexCoords[iCurrent];
+				aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
 				float u,v;
 				float u,v;
-				u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / this->m_pcHeader->skinWidth;
-				v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / this->m_pcHeader->skinHeight;
-				pcOut->x = u;
-				pcOut->y = 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[0] = iTemp+2;
 			pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
 			pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
 			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
 			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
@@ -316,13 +394,16 @@ void MD2Importer::InternReadFile(
 			{
 			{
 				// validate vertex indices
 				// validate vertex indices
 				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
 				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;
 					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+				}
 
 
 				// copy face indices
 				// copy face indices
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 
 
 				// read x,y, and z component of the vertex
 				// read x,y, and z component of the vertex
-				aiVector3D& vec = vPositions[iCurrent];
+				aiVector3D& vec = pcMesh->mVertices[iCurrent];
 
 
 				vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
 				vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
 				vec.x += pcFrame->translate[0];
 				vec.x += pcFrame->translate[0];
@@ -335,39 +416,17 @@ void MD2Importer::InternReadFile(
 				vec.y += pcFrame->translate[2];
 				vec.y += pcFrame->translate[2];
 
 
 				// read the normal vector from the precalculated normal table
 				// read the normal vector from the precalculated normal table
-				vNormals[iCurrent] = *((const aiVector3D*)(&g_avNormals[std::min(
-					int(pcVerts[iIndex].lightNormalIndex),
-					int(sizeof(g_avNormals) / sizeof(g_avNormals[0]))-1)]));
-
-				std::swap ( vNormals[iCurrent].y,vNormals[iCurrent].z );
-			
-				aiVector3D* pcOut = &vTexCoords[iCurrent];
-				pcOut->x = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / this->m_pcHeader->skinWidth;
-				pcOut->y = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / this->m_pcHeader->skinHeight;
+				aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
+				LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
+				std::swap ( vNormal.y,vNormal.z );
 			}
 			}
+			// FIX: flip the face order for use with OpenGL
 			pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
 			pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
 			pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
 			pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
 			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
 			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
 		}
 		}
 	}
 	}
-
-	// allocate output storage
-	pScene->mMeshes[0]->mNumVertices = (unsigned int)vPositions.size();
-	pScene->mMeshes[0]->mVertices = new aiVector3D[vPositions.size()];
-	pScene->mMeshes[0]->mNormals = new aiVector3D[vPositions.size()];
-	pScene->mMeshes[0]->mTextureCoords[0] = new aiVector3D[vPositions.size()];
-
-	// memcpy() the data to the c-syle arrays
-	memcpy(pScene->mMeshes[0]->mVertices,	&vPositions[0],	
-		vPositions.size() * sizeof(aiVector3D));
-	memcpy(pScene->mMeshes[0]->mNormals,	&vNormals[0],	
-		vPositions.size() * sizeof(aiVector3D));
-	
-	if (0 != this->m_pcHeader->numTexCoords)
-	{
-		memcpy(pScene->mMeshes[0]->mTextureCoords[0],	&vTexCoords[0],	
-			vPositions.size() * sizeof(aiVector3D));
-	}
-
+	// delete the file buffer and return
+	delete[] this->mBuffer; 
 	return;
 	return;
 }
 }

+ 9 - 0
code/MD2Loader.h

@@ -93,6 +93,12 @@ protected:
 	void InternReadFile( const std::string& pFile, aiScene* pScene, 
 	void InternReadFile( const std::string& pFile, aiScene* pScene, 
 		IOSystem* pIOHandler);
 		IOSystem* pIOHandler);
 
 
+
+	// -------------------------------------------------------------------
+	/** Validate the header of the file
+	*/
+	void ValidateHeader();
+
 protected:
 protected:
 
 
 	/** Header of the MD2 file */
 	/** Header of the MD2 file */
@@ -100,6 +106,9 @@ protected:
 
 
 	/** Buffer to hold the loaded file */
 	/** Buffer to hold the loaded file */
 	const unsigned char* mBuffer;
 	const unsigned char* mBuffer;
+
+	/** Size of the file, in bytes */
+	unsigned int fileSize;
 };
 };
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 23 - 23
code/MD3FileData.h

@@ -89,10 +89,10 @@ namespace MD3
 struct Header
 struct Header
 {
 {
 	//! magic number
 	//! magic number
-	int32_t IDENT;
+	uint32_t IDENT;
 
 
 	//! file format version
 	//! file format version
-	int32_t VERSION;
+	uint32_t VERSION;
 
 
 	//! original name in .pak archive
 	//! original name in .pak archive
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
@@ -101,28 +101,28 @@ struct Header
 	int32_t FLAGS;
 	int32_t FLAGS;
 
 
 	//! number of frames in the file
 	//! number of frames in the file
-	int32_t NUM_FRAMES;
+	uint32_t NUM_FRAMES;
 
 
 	//! number of tags in the file
 	//! number of tags in the file
-	int32_t NUM_TAGS;
+	uint32_t NUM_TAGS;
 
 
 	//! number of surfaces in the file
 	//! number of surfaces in the file
-	int32_t NUM_SURFACES;
+	uint32_t NUM_SURFACES;
 
 
 	//! number of skins in the file
 	//! number of skins in the file
-	int32_t NUM_SKINS;
+	uint32_t NUM_SKINS;
 
 
 	//! offset of the first frame
 	//! offset of the first frame
-	int32_t OFS_FRAMES;
+	uint32_t OFS_FRAMES;
 
 
 	//! offset of the first tag
 	//! offset of the first tag
-	int32_t OFS_TAGS;
+	uint32_t OFS_TAGS;
 
 
 	//! offset of the first surface
 	//! offset of the first surface
-	int32_t OFS_SURFACES;
+	uint32_t OFS_SURFACES;
 
 
 	//! end of file
 	//! end of file
-	int32_t OFS_EOF;
+	uint32_t OFS_EOF;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
@@ -162,29 +162,29 @@ struct Surface
 	int32_t FLAGS;
 	int32_t FLAGS;
 
 
 	//! number of frames in the surface
 	//! number of frames in the surface
-	int32_t NUM_FRAMES;
+	uint32_t NUM_FRAMES;
 
 
 	//! number of shaders in the surface
 	//! number of shaders in the surface
-	int32_t NUM_SHADER;
+	uint32_t NUM_SHADER;
 
 
 	//! number of vertices in the surface
 	//! number of vertices in the surface
-	int32_t NUM_VERTICES;
+	uint32_t NUM_VERTICES;
 
 
 	//! number of triangles in the surface
 	//! number of triangles in the surface
-	int32_t NUM_TRIANGLES;
+	uint32_t NUM_TRIANGLES;
 
 
 
 
 	//! offset to the triangle data 
 	//! offset to the triangle data 
-	int32_t OFS_TRIANGLES;
+	uint32_t OFS_TRIANGLES;
 
 
 	//! offset to the shader data
 	//! offset to the shader data
-	int32_t OFS_SHADERS;
+	uint32_t OFS_SHADERS;
 
 
 	//! offset to the texture coordinate data
 	//! offset to the texture coordinate data
-	int32_t OFS_ST;
+	uint32_t OFS_ST;
 
 
 	//! offset to the vertex/normal data
 	//! offset to the vertex/normal data
-	int32_t OFS_XYZNORMAL;
+	uint32_t OFS_XYZNORMAL;
 
 
 	//! offset to the end of the Surface object
 	//! offset to the end of the Surface object
 	int32_t OFS_END;
 	int32_t OFS_END;
@@ -200,7 +200,7 @@ struct Shader
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
 
 
 	//! index of the shader
 	//! index of the shader
-	int32_t SHADER_INDEX;
+	uint32_t SHADER_INDEX;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
@@ -211,7 +211,7 @@ struct Shader
 struct Triangle
 struct Triangle
 {
 {
 	//! triangle indices
 	//! triangle indices
-	int32_t INDEXES[3];
+	uint32_t INDEXES[3];
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
@@ -236,7 +236,7 @@ struct Vertex
 	int16_t X,Y,Z;
 	int16_t X,Y,Z;
 
 
 	//! encoded normal vector
 	//! encoded normal vector
-	int16_t  NORMAL;
+	uint16_t  NORMAL;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 // reset packing to the original value
 // reset packing to the original value
@@ -254,9 +254,9 @@ struct Vertex
  *	\note This has been taken from q3 source (misc_model.c)
  *	\note This has been taken from q3 source (misc_model.c)
  */
  */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-inline void LatLngNormalToVec3(int16_t p_iNormal, float* p_afOut)
+inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
 {
 {
-	float lat = (float)(( p_iNormal >> 8 ) & 0xff);
+	float lat = (float)(( p_iNormal >> 8u ) & 0xff);
 	float lng = (float)(( p_iNormal & 0xff ));
 	float lng = (float)(( p_iNormal & 0xff ));
 	lat *= 3.141926f/128.0f;
 	lat *= 3.141926f/128.0f;
 	lng *= 3.141926f/128.0f;
 	lng *= 3.141926f/128.0f;

+ 90 - 39
code/MD3Loader.cpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 
 
@@ -78,12 +79,48 @@ bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 		return false;
 		return false;
 	std::string extension = pFile.substr( pos);
 	std::string extension = pFile.substr( pos);
 
 
-	// not brilliant but working ;-)
-	if( extension == ".md3" || extension == ".MD3" || 
-		extension == ".mD3" || extension == ".Md3")
-		return true;
+	if (extension.length() < 4)return false;
+	if (extension[0] != '.')return false;
+	if (extension[1] != 'm' && extension[1] != 'M')return false;
+	if (extension[2] != 'd' && extension[2] != 'D')return false;
+	if (extension[3] != '3')return false;
 
 
-	return false;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateHeaderOffsets()
+{
+	if (this->m_pcHeader->OFS_FRAMES	>= this->fileSize ||
+		this->m_pcHeader->OFS_SURFACES	>= this->fileSize || 
+		this->m_pcHeader->OFS_EOF		> this->fileSize)
+	{
+		delete[] this->mBuffer;
+		throw new ImportErrorException("Invalid MD3 header: some offsets are outside the file");
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
+{
+	// calculate the relative offset of the surface
+	int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
+
+	if (pcSurf->OFS_TRIANGLES	+ ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle)	> this->fileSize ||
+		pcSurf->OFS_SHADERS		+ ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader)		> this->fileSize ||
+		pcSurf->OFS_ST			+ ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord)	> this->fileSize ||
+		pcSurf->OFS_XYZNORMAL	+ ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex)		> this->fileSize)
+	{
+		delete[] this->mBuffer;
+		throw new ImportErrorException("Invalid MD3 surface header: some offsets are outside the file");
+	}
+
+	if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES)
+		DefaultLogger::get()->warn("The model contains more triangles than Quake 3 supports");
+	if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS)
+		DefaultLogger::get()->warn("The model contains more shaders than Quake 3 supports");
+	if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS)
+		DefaultLogger::get()->warn("The model contains more vertices than Quake 3 supports");
+	if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES)
+		DefaultLogger::get()->warn("The model contains more frames than Quake 3 supports");
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure. 
 // Imports the given file into the given scene structure. 
@@ -100,7 +137,7 @@ void MD3Importer::InternReadFile(
 
 
 	// check whether the md3 file is large enough to contain
 	// check whether the md3 file is large enough to contain
 	// at least the file header
 	// at least the file header
-	size_t fileSize = file->FileSize();
+	fileSize = (unsigned int)file->FileSize();
 	if( fileSize < sizeof(MD3::Header))
 	if( fileSize < sizeof(MD3::Header))
 	{
 	{
 		throw new ImportErrorException( ".md3 File is too small.");
 		throw new ImportErrorException( ".md3 File is too small.");
@@ -116,28 +153,28 @@ void MD3Importer::InternReadFile(
 	if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
 	if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
 		this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
 		this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
 		throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
 	}
 	}
 
 
 	// check file format version
 	// check file format version
 	if (this->m_pcHeader->VERSION > 15)
 	if (this->m_pcHeader->VERSION > 15)
 	{
 	{
-		throw new ImportErrorException( "Unsupported md3 file version");
+		DefaultLogger::get()->warn( "Unsupported md3 file version. Continuing happily ...");
 	}
 	}
 
 
 	// check some values whether they are valid
 	// check some values whether they are valid
 	if (0 == this->m_pcHeader->NUM_FRAMES)
 	if (0 == this->m_pcHeader->NUM_FRAMES)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
 		throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
 	}
 	}
 	if (0 == this->m_pcHeader->NUM_SURFACES)
 	if (0 == this->m_pcHeader->NUM_SURFACES)
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
 		throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
 	}
 	}
-	if (this->m_pcHeader->OFS_EOF > (int32_t)fileSize)
-	{
-		throw new ImportErrorException( "Invalid md3 file: File is too small");
-	}
+	this->ValidateHeaderOffsets();
 
 
 	// now navigate to the list of surfaces
 	// now navigate to the list of surfaces
 	const MD3::Surface* pcSurfaces = (const MD3::Surface*)
 	const MD3::Surface* pcSurfaces = (const MD3::Surface*)
@@ -150,11 +187,19 @@ void MD3Importer::InternReadFile(
 	pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
 	pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
 	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
 
 
+	// if an exception is thrown before the meshes are allocated ->
+	// otherwise the pointer value would be invalid and delete would crash
+	::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
+	::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
+
 	unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
 	unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
 	unsigned int iNumMaterials = 0;
 	unsigned int iNumMaterials = 0;
 	unsigned int iDefaultMatIndex = 0xFFFFFFFF;
 	unsigned int iDefaultMatIndex = 0xFFFFFFFF;
 	while (iNum-- > 0)
 	while (iNum-- > 0)
 	{
 	{
+		// validate the surface
+		this->ValidateSurfaceHeaderOffsets(pcSurfaces);
+
 		// navigate to the vertex list of the surface
 		// navigate to the vertex list of the surface
 		const MD3::Vertex* pcVertices = (const MD3::Vertex*)
 		const MD3::Vertex* pcVertices = (const MD3::Vertex*)
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
@@ -171,7 +216,6 @@ void MD3Importer::InternReadFile(
 		const MD3::Shader* pcShaders = (const MD3::Shader*)
 		const MD3::Shader* pcShaders = (const MD3::Shader*)
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
 
 
-
 		// if the submesh is empty ignore it
 		// if the submesh is empty ignore it
 		if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
 		if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
 		{
 		{
@@ -185,14 +229,14 @@ void MD3Importer::InternReadFile(
 		aiMesh* pcMesh = pScene->mMeshes[iNum];
 		aiMesh* pcMesh = pScene->mMeshes[iNum];
 
 
 		pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
 		pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
-		pcMesh->mNumBones = 0;
-		pcMesh->mColors[0] = pcMesh->mColors[1] = pcMesh->mColors[2] = pcMesh->mColors[3] = NULL;
+		//pcMesh->mNumBones = 0;
+		//pcMesh->mColors[0] = pcMesh->mColors[1] = pcMesh->mColors[2] = pcMesh->mColors[3] = NULL;
 		pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
 		pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
 		pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
 		pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
 		pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
-		pcMesh->mTextureCoords[1] = pcMesh->mTextureCoords[2] = pcMesh->mTextureCoords[3] = NULL;
+		//pcMesh->mTextureCoords[1] = pcMesh->mTextureCoords[2] = pcMesh->mTextureCoords[3] = NULL;
 		pcMesh->mNumUVComponents[0] = 2;
 		pcMesh->mNumUVComponents[0] = 2;
 
 
 		// fill in all triangles
 		// fill in all triangles
@@ -221,6 +265,7 @@ void MD3Importer::InternReadFile(
 				pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
 				pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
 				pcMesh->mTextureCoords[0][iCurrent].y = 1.0f - pcUVs[ pcTriangles->INDEXES[c]].V;
 				pcMesh->mTextureCoords[0][iCurrent].y = 1.0f - pcUVs[ pcTriangles->INDEXES[c]].V;
 			}
 			}
+			// FIX: flip the face ordering for use with OpenGL
 			pcMesh->mFaces[i].mIndices[0] = iTemp+2;
 			pcMesh->mFaces[i].mIndices[0] = iTemp+2;
 			pcMesh->mFaces[i].mIndices[1] = iTemp+1;
 			pcMesh->mFaces[i].mIndices[1] = iTemp+1;
 			pcMesh->mFaces[i].mIndices[2] = iTemp+0;
 			pcMesh->mFaces[i].mIndices[2] = iTemp+0;
@@ -233,11 +278,11 @@ void MD3Importer::InternReadFile(
 			// make a relative path.
 			// make a relative path.
 			// if the MD3's internal path itself and the given path are using
 			// if the MD3's internal path itself and the given path are using
 			// the same directory remove it
 			// the same directory remove it
-			const char* szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'\\');
-			if (!szEndDir1)szEndDir1 = strrchr((const char*)this->m_pcHeader->NAME,'/');
+			const char* szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'\\');
+			if (!szEndDir1)szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'/');
 
 
-			const char* szEndDir2 = strrchr((const char*)pcShaders->NAME,'\\');
-			if (!szEndDir2)szEndDir2 = strrchr((const char*)pcShaders->NAME,'/');
+			const char* szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'\\');
+			if (!szEndDir2)szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'/');
 
 
 			if (szEndDir1 && szEndDir2)
 			if (szEndDir1 && szEndDir2)
 			{
 			{
@@ -276,7 +321,7 @@ void MD3Importer::InternReadFile(
 
 
 				aiString szOut;
 				aiString szOut;
 				if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
 				if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
-					AI_MATKEY_TEXBLEND_DIFFUSE(0),&szOut))
+					AI_MATKEY_TEXTURE_DIFFUSE(0),&szOut))
 				{
 				{
 					if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
 					if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
 					{
 					{
@@ -294,25 +339,35 @@ void MD3Importer::InternReadFile(
 
 
 				if (szEndDir2)
 				if (szEndDir2)
 				{
 				{
-					aiString szString;
-					const size_t iLen = strlen(szEndDir2);
-					memcpy(szString.data,szEndDir2,iLen+1);
-					szString.length = iLen-1;
+					if (szEndDir2[0])
+					{
+						aiString szString;
+						const size_t iLen = ::strlen(szEndDir2);
+						::memcpy(szString.data,szEndDir2,iLen);
+						szString.data[iLen] = '\0';
+						szString.length = iLen;
 
 
-					pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+						pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+					}
+					else 
+					{
+						DefaultLogger::get()->warn("Texture file name has zero length. "
+							"It will be skipped.");
+					}
 				}
 				}
 
 
 				int iMode = (int)aiShadingMode_Gouraud;
 				int iMode = (int)aiShadingMode_Gouraud;
 				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
 				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
 
 
+				// add a small ambient color value - Quake 3 seems to have one
 				aiColor3D clr;
 				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;
 				clr.b = clr.g = clr.r = 0.05f;
 				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 
 
+				aiString szName;
+				szName.Set(AI_DEFAULT_MATERIAL_NAME);
+				pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
 				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
 				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
 				pcMesh->mMaterialIndex = iNumMaterials++;
 				pcMesh->mMaterialIndex = iNumMaterials++;
 			}
 			}
@@ -343,31 +398,27 @@ void MD3Importer::InternReadFile(
 				pcMesh->mMaterialIndex = iNumMaterials++;
 				pcMesh->mMaterialIndex = iNumMaterials++;
 			}
 			}
 		}
 		}
+		// go to the next surface
 		pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
 		pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
 	}
 	}
 
 
 	if (0 == pScene->mNumMeshes)
 	if (0 == pScene->mNumMeshes)
 	{
 	{
 		// cleanup before returning
 		// cleanup before returning
-		delete pScene;
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
 		throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
 	}
 	}
 	pScene->mNumMaterials = iNumMaterials;
 	pScene->mNumMaterials = iNumMaterials;
 
 
 	// now we need to generate an empty node graph
 	// now we need to generate an empty node graph
 	pScene->mRootNode = new aiNode();
 	pScene->mRootNode = new aiNode();
-	pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
-	pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
+	pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+	pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
 
 
 	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
 	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-	{
-		pScene->mRootNode->mChildren[i] = new aiNode();
-		pScene->mRootNode->mChildren[i]->mParent = pScene->mRootNode;
-		pScene->mRootNode->mChildren[i]->mNumMeshes = 1;
-		pScene->mRootNode->mChildren[i]->mMeshes = new unsigned int[1];
-		pScene->mRootNode->mChildren[i]->mMeshes[0] = i;
-	}
+		pScene->mRootNode->mMeshes[i] = i;
 
 
+	// delete the file buffer and return
 	delete[] this->mBuffer;
 	delete[] this->mBuffer;
 	return;
 	return;
 }
 }

+ 10 - 0
code/MD3Loader.h

@@ -96,6 +96,13 @@ protected:
 	void InternReadFile( const std::string& pFile, aiScene* pScene, 
 	void InternReadFile( const std::string& pFile, aiScene* pScene, 
 		IOSystem* pIOHandler);
 		IOSystem* pIOHandler);
 
 
+
+	// -------------------------------------------------------------------
+	/** Validate offsets in the header
+	*/
+	void ValidateHeaderOffsets();
+	void ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurfHeader);
+
 protected:
 protected:
 
 
 	/** Header of the MD3 file */
 	/** Header of the MD3 file */
@@ -103,6 +110,9 @@ protected:
 
 
 	/** Buffer to hold the loaded file */
 	/** Buffer to hold the loaded file */
 	const unsigned char* mBuffer;
 	const unsigned char* mBuffer;
+
+	/** Size of the file, in bytes */
+	unsigned int fileSize;
 	};
 	};
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 1 - 1
code/MD4Loader.cpp

@@ -109,7 +109,7 @@ void MD4Importer::InternReadFile(
 	size_t fileSize = file->FileSize();
 	size_t fileSize = file->FileSize();
 	if( fileSize < sizeof(MD4::Header))
 	if( fileSize < sizeof(MD4::Header))
 	{
 	{
-		throw new ImportErrorException( ".mdd File is too small.");
+		throw new ImportErrorException( ".md4 File is too small.");
 	}
 	}
 	return;
 	return;
 }
 }

+ 341 - 78
code/MDLFileData.h

@@ -43,8 +43,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //! @file Definition of in-memory structures for the MDL file format. 
 //! @file Definition of in-memory structures for the MDL file format. 
 //
 //
 // The specification has been taken from various sources on the internet.
 // The specification has been taken from various sources on the internet.
-// http://tfc.duke.free.fr/coding/mdl-specs-en.html
-
+// - http://tfc.duke.free.fr/coding/mdl-specs-en.html
+// - Conitec's MED SDK
+// - Many quite long HEX-editor sessions
 
 
 #ifndef AI_MDLFILEHELPER_H_INC
 #ifndef AI_MDLFILEHELPER_H_INC
 #define AI_MDLFILEHELPER_H_INC
 #define AI_MDLFILEHELPER_H_INC
@@ -67,15 +68,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	error Compiler not supported. Never do this again.
 #	error Compiler not supported. Never do this again.
 #endif
 #endif
 
 
-namespace Assimp
-{
-namespace MDL
-{
+namespace Assimp	{
+namespace MDL	{
 
 
 // magic bytes used in Quake 1 MDL meshes
 // magic bytes used in Quake 1 MDL meshes
 #define AI_MDL_MAGIC_NUMBER_BE	'IDPO'
 #define AI_MDL_MAGIC_NUMBER_BE	'IDPO'
 #define AI_MDL_MAGIC_NUMBER_LE	'OPDI'
 #define AI_MDL_MAGIC_NUMBER_LE	'OPDI'
 
 
+// magic bytes used in GameStudio A<very  low> MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS3	'MDL2'
+#define AI_MDL_MAGIC_NUMBER_LE_GS3	'2LDM'
+
 // magic bytes used in GameStudio A4 MDL meshes
 // magic bytes used in GameStudio A4 MDL meshes
 #define AI_MDL_MAGIC_NUMBER_BE_GS4	'MDL3'
 #define AI_MDL_MAGIC_NUMBER_BE_GS4	'MDL3'
 #define AI_MDL_MAGIC_NUMBER_LE_GS4	'3LDM'
 #define AI_MDL_MAGIC_NUMBER_LE_GS4	'3LDM'
@@ -96,7 +99,7 @@ namespace MDL
 
 
 
 
 // common limitations for Quake1 meshes. The loader does not check them,
 // common limitations for Quake1 meshes. The loader does not check them,
-// but models should not exceed these limits.
+// (however it warns) but models should not exceed these limits.
 #if (!defined AI_MDL_VERSION)
 #if (!defined AI_MDL_VERSION)
 #	define AI_MDL_VERSION				6
 #	define AI_MDL_VERSION				6
 #endif
 #endif
@@ -113,11 +116,25 @@ namespace MDL
 #	define AI_MDL_MAX_TRIANGLES			2048	
 #	define AI_MDL_MAX_TRIANGLES			2048	
 #endif
 #endif
 
 
+// helper macro that sets a pointer to NULL in debug builds
+#if (!defined DEBUG_INVALIDATE_PTR)
+#	if (defined _DEBUG)
+#		define DEBUG_INVALIDATE_PTR(x) x = NULL;
+#	else
+#		define DEBUG_INVALIDATE_PTR(x)
+#	endif
+#endif 
+
+// material key that is set for dummy materials that are
+// just referencing another material
+#if (!defined AI_MDL7_REFERRER_MATERIAL)
+#	define AI_MDL7_REFERRER_MATERIAL "&&&referrer&&&"
+#endif
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Header
 /** \struct Header
  *  \brief Data structure for the MDL main header
  *  \brief Data structure for the MDL main header
  */
  */
-// ---------------------------------------------------------------------------
 struct Header
 struct Header
 {
 {
 	//! magic number: "IDPO"
 	//! magic number: "IDPO"
@@ -157,12 +174,13 @@ struct Header
 	int32_t num_frames;      
 	int32_t num_frames;      
 
 
 	//! 0 = synchron, 1 = random . Ignored
 	//! 0 = synchron, 1 = random . Ignored
+	//! (MDLn formats: number of texture coordinates)
 	int32_t synctype;         
 	int32_t synctype;         
 
 
 	//! State flag
 	//! State flag
 	int32_t flags;     
 	int32_t flags;     
 
 
-	//! ???
+	//! Could be the total size of the file (and not a float)
 	float size;
 	float size;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
@@ -171,7 +189,6 @@ struct Header
 /** \struct Header_MDL7
 /** \struct Header_MDL7
  *  \brief Data structure for the MDL 7 main header
  *  \brief Data structure for the MDL 7 main header
  */
  */
-// ---------------------------------------------------------------------------
 struct Header_MDL7
 struct Header_MDL7
 {
 {
 	//! magic number: "MDL7"
 	//! magic number: "MDL7"
@@ -181,13 +198,13 @@ struct Header_MDL7
 	int32_t	version;		
 	int32_t	version;		
 
 
 	//! Number of bones in file
 	//! Number of bones in file
-	int32_t	bones_num;
+	uint32_t	bones_num;
 
 
 	//! Number of groups in file
 	//! Number of groups in file
-	int32_t	groups_num;
+	uint32_t	groups_num;
 
 
 	//! Size of data in the file
 	//! Size of data in the file
-	int32_t	data_size;	
+	uint32_t	data_size;	
 
 
 	//! Ignored. Used to store entity specific information
 	//! Ignored. Used to store entity specific information
 	int32_t	entlump_size;	
 	int32_t	entlump_size;	
@@ -195,45 +212,77 @@ struct Header_MDL7
 	//! Ignored. Used to store MED related data
 	//! Ignored. Used to store MED related data
 	int32_t	medlump_size;	
 	int32_t	medlump_size;	
 
 
-	// -------------------------------------------------------
-	// Sizes of some file parts
-
+	//! Size of the Bone_MDL7 data structure used in the file
 	uint16_t bone_stc_size;
 	uint16_t bone_stc_size;
+
+	//! Size of the Skin_MDL 7 data structure used in the file
 	uint16_t skin_stc_size;
 	uint16_t skin_stc_size;
+
+	//! Size of a single color (e.g. in a material)
 	uint16_t colorvalue_stc_size;
 	uint16_t colorvalue_stc_size;
+
+	//! Size of the Material_MDL7 data structure used in the file
 	uint16_t material_stc_size;
 	uint16_t material_stc_size;
+
+	//! Size of a texture coordinate set in the file
 	uint16_t skinpoint_stc_size;
 	uint16_t skinpoint_stc_size;
+
+	//! Size of a triangle in the file
 	uint16_t triangle_stc_size;
 	uint16_t triangle_stc_size;
+
+	//! Size of a normal vertex in the file
 	uint16_t mainvertex_stc_size;
 	uint16_t mainvertex_stc_size;
+
+	//! Size of a per-frame animated vertex in the file
+	//! (this is not supported)
 	uint16_t framevertex_stc_size;
 	uint16_t framevertex_stc_size;
+
+	//! Size of a bone animation matrix
 	uint16_t bonetrans_stc_size;
 	uint16_t bonetrans_stc_size;
+
+	//! Size of the Frame_MDL7 data structure used in the file
 	uint16_t frame_stc_size;
 	uint16_t frame_stc_size;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
-
-#define AI_MDL7_MAX_BONENAMESIZE	20 
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Bone_MDL7
 /** \struct Bone_MDL7
- *  \brief Bone in a MDL7 file
+ *  \brief Data structure for a bone in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct Bone_MDL7
 struct Bone_MDL7
 {
 {
+	//! Index of the parent bone of *this* bone. 0xffff means:
+	//! "hey, I have no parent, I'm an orphan"
 	uint16_t parent_index;
 	uint16_t parent_index;
-	uint8_t _unused_[2]; // 
+	uint8_t _unused_[2]; 
+
+	//! Relative position of the bone (relative to the
+	//! parent bone)
 	float x,y,z;
 	float x,y,z;
 
 
-	char name[AI_MDL7_MAX_BONENAMESIZE];
-};
+	//! Optional name of the bone
+	char name[1 /* DUMMY SIZE */];
+} PACK_STRUCT;
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS)
+#	define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS (16 + 20)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS)
+#	define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS (16 + 32)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE)
+#	define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE (16)
+#endif
 
 
-#define AI_MDL7_MAX_GROUPNAMESIZE	16
+#if (!defined AI_MDL7_MAX_GROUPNAMESIZE)
+#	define AI_MDL7_MAX_GROUPNAMESIZE	16
+#endif // ! AI_MDL7_MAX_GROUPNAMESIZE
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Group_MDL7
 /** \struct Group_MDL7
  *  \brief Group in a MDL7 file
  *  \brief Group in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct Group_MDL7
 struct Group_MDL7
 {
 {
 	//! = '1' -> triangle based Mesh
 	//! = '1' -> triangle based Mesh
@@ -268,14 +317,14 @@ struct Group_MDL7
 #define	AI_MDL7_SKINTYPE_MATERIAL_ASCDEF		0x20
 #define	AI_MDL7_SKINTYPE_MATERIAL_ASCDEF		0x20
 #define	AI_MDL7_SKINTYPE_RGBFLAG				0x80
 #define	AI_MDL7_SKINTYPE_RGBFLAG				0x80
 
 
-
-#define AI_MDL7_MAX_BONENAMESIZE 20
+#if (!defined AI_MDL7_MAX_BONENAMESIZE)
+#	define AI_MDL7_MAX_BONENAMESIZE 20
+#endif // !! AI_MDL7_MAX_BONENAMESIZE
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Deformer_MDL7
 /** \struct Deformer_MDL7
  *  \brief Deformer in a MDL7 file
  *  \brief Deformer in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct Deformer_MDL7
 struct Deformer_MDL7
 {
 {
 	int8_t	deformer_version;		// 0
 	int8_t	deformer_version;		// 0
@@ -291,7 +340,6 @@ struct Deformer_MDL7
 /** \struct DeformerElement_MDL7
 /** \struct DeformerElement_MDL7
  *  \brief Deformer element in a MDL7 file
  *  \brief Deformer element in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct DeformerElement_MDL7
 struct DeformerElement_MDL7
 {
 {
 	//! bei deformer_typ==0 (==bones) element_index == bone index
 	//! bei deformer_typ==0 (==bones) element_index == bone index
@@ -305,7 +353,6 @@ struct DeformerElement_MDL7
 /** \struct DeformerWeight_MDL7
 /** \struct DeformerWeight_MDL7
  *  \brief Deformer weight in a MDL7 file
  *  \brief Deformer weight in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct DeformerWeight_MDL7
 struct DeformerWeight_MDL7
 {
 {
 	//! for deformer_typ==0 (==bones) index == vertex index
 	//! for deformer_typ==0 (==bones) index == vertex index
@@ -313,27 +360,14 @@ struct DeformerWeight_MDL7
 	float	weight;
 	float	weight;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
-// maximum length of texture file name
-#define AI_MDL7_MAX_TEXNAMESIZE		0x10
 
 
 // don't know why this was in the original headers ...
 // don't know why this was in the original headers ...
-// to be removed in future versions
 typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
 typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** \struct Skin_MDL7
- *  \brief Skin in a MDL7 file
+/** \struct ColorValue_MDL7
+ *  \brief Data structure for a color value in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
-struct Skin_MDL7
-{
-	uint8_t			typ;
-	int8_t			_unused_[3];
-	int32_t			width;
-	int32_t			height;
-	char			texture_name[AI_MDL7_MAX_TEXNAMESIZE];	
-} PACK_STRUCT;
-
 struct ColorValue_MDL7
 struct ColorValue_MDL7
 {
 {
 	float r,g,b,a;
 	float r,g,b,a;
@@ -341,9 +375,8 @@ struct ColorValue_MDL7
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Material_MDL7
 /** \struct Material_MDL7
- *  \brief Material in a MDL7 file
+ *  \brief Data structure for a Material in a MDL7 file
  */
  */
-// ---------------------------------------------------------------------------
 struct Material_MDL7
 struct Material_MDL7
 {
 {
 	//! Diffuse base color of the material
 	//! Diffuse base color of the material
@@ -365,9 +398,8 @@ struct Material_MDL7
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Skin
 /** \struct Skin
- *  \brief Skin data structure #1
+ *  \brief Skin data structure #1 - used by Quake1, MDL2, MDL3 and MDL4
  */
  */
-// ---------------------------------------------------------------------------
 struct Skin
 struct Skin
 {
 {
 	//! 0 = single (Skin), 1 = group (GroupSkin)
 	//! 0 = single (Skin), 1 = group (GroupSkin)
@@ -387,12 +419,40 @@ struct Skin
 	uint8_t *data;  
 	uint8_t *data;  
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
+
+// ---------------------------------------------------------------------------
+/** \struct Skin
+ *  \brief Skin data structure #2 - used by MDL5, MDL6 and MDL7
+ *  \see Skin
+ */
 struct Skin_MDL5
 struct Skin_MDL5
 {
 {
 	int32_t size, width, height;      
 	int32_t size, width, height;      
 	uint8_t *data;  
 	uint8_t *data;  
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
+// maximum length of texture file name
+#if (!defined AI_MDL7_MAX_TEXNAMESIZE)
+#	define AI_MDL7_MAX_TEXNAMESIZE		0x10
+#endif
+
+// ---------------------------------------------------------------------------
+/** \struct Skin_MDL7
+ *  \brief Skin data structure #3 - used by MDL7 and HMP7
+ */
+struct Skin_MDL7
+{
+	uint8_t			typ;
+	int8_t			_unused_[3];
+	int32_t			width;
+	int32_t			height;
+	char			texture_name[AI_MDL7_MAX_TEXNAMESIZE];	
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \struct RGB565
+ *  \brief Data structure for a RGB565 pixel in a texture
+ */
 struct RGB565
 struct RGB565
 {
 {
 	uint16_t r : 5;
 	uint16_t r : 5;
@@ -400,6 +460,10 @@ struct RGB565
 	uint16_t b : 5;
 	uint16_t b : 5;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
+// ---------------------------------------------------------------------------
+/** \struct ARGB4
+ *  \brief Data structure for a ARGB4444 pixel in a texture
+ */
 struct ARGB4
 struct ARGB4
 {
 {
 	uint16_t a : 4;
 	uint16_t a : 4;
@@ -412,7 +476,6 @@ struct ARGB4
 /** \struct GroupSkin
 /** \struct GroupSkin
  *  \brief Skin data structure #2 (group of pictures)
  *  \brief Skin data structure #2 (group of pictures)
  */
  */
-// ---------------------------------------------------------------------------
 struct GroupSkin
 struct GroupSkin
 {
 {
 	//! 0 = single (Skin), 1 = group (GroupSkin)
 	//! 0 = single (Skin), 1 = group (GroupSkin)
@@ -430,9 +493,8 @@ struct GroupSkin
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct TexCoord
 /** \struct TexCoord
- *  \brief Texture coordinate data structure
+ *  \brief Texture coordinate data structure used by the Quake1 MDL format
  */
  */
-// ---------------------------------------------------------------------------
 struct TexCoord
 struct TexCoord
 {
 {
 	//! Is the vertex on the noundary between front and back piece?
 	//! Is the vertex on the noundary between front and back piece?
@@ -445,7 +507,10 @@ struct TexCoord
 	int32_t t;
 	int32_t t;
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
-
+// ---------------------------------------------------------------------------
+/** \struct TexCoord_MDL3
+ *  \brief Data structure for an UV coordinate in the 3DGS MDL3 format
+ */
 struct TexCoord_MDL3
 struct TexCoord_MDL3
 {
 {
 	//! position, horizontally in range 0..skinwidth-1
 	//! position, horizontally in range 0..skinwidth-1
@@ -455,6 +520,10 @@ struct TexCoord_MDL3
 	int16_t v; 
 	int16_t v; 
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
+// ---------------------------------------------------------------------------
+/** \struct TexCoord_MDL7
+ *  \brief Data structure for an UV coordinate in the 3DGS MDL7 format
+ */
 struct TexCoord_MDL7
 struct TexCoord_MDL7
 {
 {
 	//! position, horizontally in range 0..1
 	//! position, horizontally in range 0..1
@@ -464,12 +533,26 @@ struct TexCoord_MDL7
 	float v; 
 	float v; 
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
+// ---------------------------------------------------------------------------
+/** \struct SkinSet_MDL7
+ *  \brief Skin set data structure for the 3DGS MDL7 format
+ * MDL7 references UV coordinates per face via an index list.
+ * This allows the use of multiple skins per face with just one
+ * UV coordinate set.
+ */
+struct SkinSet_MDL7
+{
+	//! Index into the UV coordinate list
+	uint16_t	st_index[3]; // size 6	
+
+	//! Material index
+	int32_t		material;	 // size 4				
+} PACK_STRUCT;
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \struct Triangle
 /** \struct Triangle
- *  \brief Triangle data structure
+ *  \brief Triangle data structure for the Quake1 MDL format
  */
  */
-// ---------------------------------------------------------------------------
 struct Triangle
 struct Triangle
 {
 {
 	//! 0 = backface, 1 = frontface
 	//! 0 = backface, 1 = frontface
@@ -479,7 +562,10 @@ struct Triangle
 	int32_t vertex[3];   
 	int32_t vertex[3];   
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
-
+// ---------------------------------------------------------------------------
+/** \struct Triangle_MDL3
+ *  \brief Triangle data structure for the 3DGS MDL3 format
+ */
 struct Triangle_MDL3
 struct Triangle_MDL3
 {
 {
 	//!  Index of 3 3D vertices in range 0..numverts
 	//!  Index of 3 3D vertices in range 0..numverts
@@ -489,17 +575,10 @@ struct Triangle_MDL3
 	uint16_t index_uv[3]; 
 	uint16_t index_uv[3]; 
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
-
-struct SkinSet_MDL7
-{
-	//! Index into the UV coordinate list
-	uint16_t	st_index[3]; // size 6	
-
-	//! Material index
-	int32_t		material;	 // size 4				
-} PACK_STRUCT;
-
-
+// ---------------------------------------------------------------------------
+/** \struct Triangle_MDL7
+ *  \brief Triangle data structure for the 3DGS MDL7 format
+ */
 struct Triangle_MDL7
 struct Triangle_MDL7
 {
 {
 	//! Vertex indices
 	//! Vertex indices
@@ -509,6 +588,15 @@ struct Triangle_MDL7
 	SkinSet_MDL7  skinsets[2];
 	SkinSet_MDL7  skinsets[2];
 } PACK_STRUCT; 
 } PACK_STRUCT; 
 
 
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV)
+#	define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV (6+sizeof(SkinSet_MDL7)-sizeof(uint32_t))
+#endif 
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX)
+#	define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX (6+sizeof(SkinSet_MDL7))
+#endif 
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV)
+#	define AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV (6+2*sizeof(SkinSet_MDL7))
+#endif 
 
 
 // Helper constants for Triangle::facesfront
 // Helper constants for Triangle::facesfront
 #if (!defined AI_MDL_BACKFACE)
 #if (!defined AI_MDL_BACKFACE)
@@ -522,7 +610,6 @@ struct Triangle_MDL7
 /** \struct Vertex
 /** \struct Vertex
  *  \brief Vertex data structure
  *  \brief Vertex data structure
  */
  */
-// ---------------------------------------------------------------------------
 struct Vertex
 struct Vertex
 {
 {
 	uint8_t v[3];
 	uint8_t v[3];
@@ -530,6 +617,7 @@ struct Vertex
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
+// ---------------------------------------------------------------------------
 struct Vertex_MDL4
 struct Vertex_MDL4
 {
 {
 	uint16_t v[3];
 	uint16_t v[3];
@@ -544,13 +632,12 @@ struct Vertex_MDL4
 /** \struct Vertex_MDL7
 /** \struct Vertex_MDL7
  *  \brief Vertex data structure used in MDL7 files
  *  \brief Vertex data structure used in MDL7 files
  */
  */
-// ---------------------------------------------------------------------------
 struct Vertex_MDL7
 struct Vertex_MDL7
 {
 {
 	float	x,y,z;
 	float	x,y,z;
 	uint16_t vertindex;	// = bone index
 	uint16_t vertindex;	// = bone index
 	union {
 	union {
-		uint16_t norm162index;
+		uint8_t norm162index;
 		float norm[3];
 		float norm[3];
 	};
 	};
 } PACK_STRUCT;
 } PACK_STRUCT;
@@ -560,7 +647,6 @@ struct Vertex_MDL7
 /** \struct BoneTransform_MDL7
 /** \struct BoneTransform_MDL7
  *  \brief bone transformation matrix structure used in MDL7 files
  *  \brief bone transformation matrix structure used in MDL7 files
  */
  */
-// ---------------------------------------------------------------------------
 struct BoneTransform_MDL7
 struct BoneTransform_MDL7
 {
 {
 	//! 4*3
 	//! 4*3
@@ -582,7 +668,6 @@ struct BoneTransform_MDL7
 /** \struct Frame_MDL7
 /** \struct Frame_MDL7
  *  \brief Frame data structure used by MDL7 files
  *  \brief Frame data structure used by MDL7 files
  */
  */
-// ---------------------------------------------------------------------------
 struct Frame_MDL7
 struct Frame_MDL7
 {
 {
 	char	frame_name[AI_MDL7_MAX_FRAMENAMESIZE];
 	char	frame_name[AI_MDL7_MAX_FRAMENAMESIZE];
@@ -595,7 +680,6 @@ struct Frame_MDL7
 /** \struct SimpleFrame
 /** \struct SimpleFrame
  *  \brief Data structure for a simple frame
  *  \brief Data structure for a simple frame
  */
  */
-// ---------------------------------------------------------------------------
 struct SimpleFrame
 struct SimpleFrame
 {
 {
 	//! Minimum vertex of the bounding box
 	//! Minimum vertex of the bounding box
@@ -615,7 +699,6 @@ struct SimpleFrame
 /** \struct Frame
 /** \struct Frame
  *  \brief Model frame data structure
  *  \brief Model frame data structure
  */
  */
-// ---------------------------------------------------------------------------
 struct Frame
 struct Frame
 {
 {
 	//! 0 = simple frame, !0 = group frame
 	//! 0 = simple frame, !0 = group frame
@@ -626,6 +709,7 @@ struct Frame
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 
 
+// ---------------------------------------------------------------------------
 struct SimpleFrame_MDLn_SP
 struct SimpleFrame_MDLn_SP
 {
 {
 	//! Minimum vertex of the bounding box
 	//! Minimum vertex of the bounding box
@@ -645,7 +729,6 @@ struct SimpleFrame_MDLn_SP
 /** \struct GroupFrame
 /** \struct GroupFrame
  *  \brief Data structure for a group of frames
  *  \brief Data structure for a group of frames
  */
  */
-// ---------------------------------------------------------------------------
 struct GroupFrame
 struct GroupFrame
 {
 {
 	//! 0 = simple frame, !0 = group frame
 	//! 0 = simple frame, !0 = group frame
@@ -675,7 +758,6 @@ struct GroupFrame
 /** \struct IntFace_MDL7
 /** \struct IntFace_MDL7
  *  \brief Internal data structure to temporarily represent a face
  *  \brief Internal data structure to temporarily represent a face
  */
  */
-// ---------------------------------------------------------------------------
 struct IntFace_MDL7
 struct IntFace_MDL7
 {
 {
 	// provide a constructor for our own convenience
 	// provide a constructor for our own convenience
@@ -700,7 +782,6 @@ struct IntFace_MDL7
  *  which has been created from two single materials along with the
  *  which has been created from two single materials along with the
  *  original material indices.
  *  original material indices.
  */
  */
-// ---------------------------------------------------------------------------
 struct IntMaterial_MDL7
 struct IntMaterial_MDL7
 {
 {
 	// provide a constructor for our own convenience
 	// provide a constructor for our own convenience
@@ -717,6 +798,188 @@ struct IntMaterial_MDL7
 	unsigned int iOldMatIndices[2];
 	unsigned int iOldMatIndices[2];
 };
 };
 
 
+
+// ---------------------------------------------------------------------------
+/** \struct IntBone_MDL7
+ *  \brief Internal data structure to represent a bone in a MDL7 file with
+ *  all of its animation channels assigned to it.
+ */
+struct IntBone_MDL7 : aiBone
+{
+	//! Default constructor
+	IntBone_MDL7() : iParent (0xffff)
+	{
+		pkeyPositions.reserve(30);
+		pkeyScalings.reserve(30);
+		pkeyRotations.reserve(30);
+	}
+
+	//! Parent bone of the bone
+	uint64_t iParent;
+
+	//! Relative position of the bone
+	aiVector3D vPosition;
+
+	//! Array of position keys
+	std::vector<aiVectorKey> pkeyPositions;
+
+	//! Array of scaling keys
+	std::vector<aiVectorKey> pkeyScalings;
+
+	//! Array of rotation keys
+	std::vector<aiQuatKey> pkeyRotations;
+};
+
+// ---------------------------------------------------------------------------
+//! Describes a MDL7 frame
+struct IntFrameInfo_MDL7
+{
+	//! Construction from an existing frame header
+	IntFrameInfo_MDL7(const MDL::Frame_MDL7* _pcFrame,unsigned int _iIndex) 
+		: pcFrame(_pcFrame), iIndex(_iIndex)
+	{}
+
+	//! Index of the frame
+	unsigned int iIndex;
+
+	//! Points to the header of the frame
+	const MDL::Frame_MDL7*	pcFrame; 
+};
+
+// ---------------------------------------------------------------------------
+//! Describes a MDL7 mesh group
+struct IntGroupInfo_MDL7
+{
+	//! Default constructor
+	IntGroupInfo_MDL7()		:	
+		iIndex(0),
+		pcGroup(NULL),		pcGroupUVs(NULL),
+		pcGroupTris(NULL),	pcGroupVerts(NULL)
+		{}
+
+	//! Construction from an existing group header
+	IntGroupInfo_MDL7(const MDL::Group_MDL7* _pcGroup,unsigned int _iIndex)
+		:
+		pcGroup(_pcGroup),iIndex(_iIndex)
+	{}
+
+	//! Index of the group
+	unsigned int iIndex;
+
+	//! Points to the header of the group
+	const MDL::Group_MDL7*		pcGroup; 
+
+	//! Points to the beginning of the uv coordinate section
+	const MDL::TexCoord_MDL7*	pcGroupUVs;		
+
+	//! Points to the beginning of the triangle section
+	const MDL::Triangle_MDL7*	pcGroupTris;		
+
+	//! Points to the beginning of the vertex section
+	const MDL::Vertex_MDL7*		pcGroupVerts;
+};
+
+// ---------------------------------------------------------------------------
+//! Holds the data that belongs to a MDL7 mesh group
+struct IntGroupData_MDL7
+{
+	IntGroupData_MDL7()
+		: pcFaces(NULL), bNeed2UV(false)
+	{}
+
+	//! Array of faces that belong to the group
+	MDL::IntFace_MDL7* pcFaces;		
+
+	//! Array of vertex positions
+	std::vector<aiVector3D>		vPositions;			
+
+	//! Array of vertex normals
+	std::vector<aiVector3D>		vNormals;	
+
+	//! Array of bones indices
+	std::vector<unsigned int>	aiBones;	
+
+	//! First UV coordinate set
+	std::vector<aiVector3D>		vTextureCoords1;
+
+	//! Optional second UV coordinate set
+	std::vector<aiVector3D>		vTextureCoords2;
+
+	//! Specifies whether there are two texture
+	//! coordinate sets required
+	bool bNeed2UV;
+};
+
+// ---------------------------------------------------------------------------
+//! Holds data from an MDL7 file that is shared by all mesh groups
+struct IntSharedData_MDL7
+{
+	//! Default constructor
+	IntSharedData_MDL7() 
+	{
+		abNeedMaterials.reserve(10);
+	}
+
+	//! Destruction: properly delete all allocated resources
+	~IntSharedData_MDL7()
+	{
+		// kill all bones
+		if (this->apcOutBones)
+		{
+			for (unsigned int m = 0; m < iNum;++m)
+				delete this->apcOutBones[m];
+			delete[] this->apcOutBones;
+		}
+	}
+
+	//! Specifies which materials are used
+	std::vector<bool> abNeedMaterials;
+
+	//! List of all materials
+	std::vector<MaterialHelper*> pcMats;
+
+	//! List of all bones
+	IntBone_MDL7** apcOutBones;
+
+	//! number of bones
+	unsigned int iNum;
+};
+
+// ---------------------------------------------------------------------------
+//! Contains input data for GenerateOutputMeshes_3DGS_MDL7
+struct IntSplittedGroupData_MDL7
+{
+	//! Construction from a given shared data set 
+	IntSplittedGroupData_MDL7(IntSharedData_MDL7& _shared,
+		std::vector<aiMesh*>& _avOutList)
+
+		: shared(_shared), avOutList(_avOutList)
+	{
+	}
+
+	//! Destruction: properly delete all allocated resources
+	~IntSplittedGroupData_MDL7()
+	{
+		// kill all face lists
+		if(this->aiSplit)
+		{
+			for (unsigned int m = 0; m < shared.pcMats.size();++m)
+				delete this->aiSplit[m];
+			delete[] this->aiSplit;
+		}
+	}
+
+	//! Contains a list of all faces per material
+	std::vector<unsigned int>** aiSplit;
+
+	//! Shared data for all groups of the model
+	IntSharedData_MDL7& shared;
+
+	//! List of meshes 
+	std::vector<aiMesh*>& avOutList;
+};
+
+
 };}; // end namespaces
 };}; // end namespaces
 
 
 #endif // !! AI_MDLFILEHELPER_H_INC
 #endif // !! AI_MDLFILEHELPER_H_INC

File diff suppressed because it is too large
+ 280 - 499
code/MDLLoader.cpp


+ 252 - 53
code/MDLLoader.h

@@ -61,6 +61,11 @@ class MaterialHelper;
 
 
 using namespace MDL;
 using namespace MDL;
 
 
+
+#if (!defined VALIDATE_FILE_SIZE)
+#	define VALIDATE_FILE_SIZE(msg) this->SizeCheck(msg,__FILE__,__LINE__)
+#endif
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Used to load MDL files
 /** Used to load MDL files
 */
 */
@@ -104,38 +109,82 @@ protected:
 protected:
 protected:
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Import a quake 1 MDL file
+	/** Import a quake 1 MDL file (IDPO)
 	*/
 	*/
 	void InternReadFile_Quake1( );
 	void InternReadFile_Quake1( );
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Import a GameStudio A4/A5 file
+	/** Import a GameStudio A4/A5 file (MDL 3,4,5)
 	*/
 	*/
-	void InternReadFile_GameStudio( );
+	void InternReadFile_3DGS_MDL345( );
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Import a GameStudio A7 file
+	/** Import a GameStudio A7 file (MDL 7)
 	*/
 	*/
-	void InternReadFile_GameStudioA7( );
+	void InternReadFile_3DGS_MDL7( );
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Import a CS:S/HL2 MDL file
+	/** Import a CS:S/HL2 MDL file (not fully implemented)
 	*/
 	*/
 	void InternReadFile_HL2( );
 	void InternReadFile_HL2( );
 
 
+
+	// *******************************************************************
+	// Debugging/validation functions
+
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Load a paletized texture from the file and convert it to 32bpp
+	/** Check whether a given position is inside the valid range
+	 *  Throw a new ImportErrorException if it is not
+	 * \param szPos Cursor position
+	 * \param szFile Name of the source file from which the function was called
+	 * \param iLine Source code line from which the function was called
 	*/
 	*/
-	void CreateTextureARGB8(const unsigned char* szData);
+	void SizeCheck(const void* szPos);
+	void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
+
 
 
+	// -------------------------------------------------------------------
+	/** Validate the header data structure of a game studio MDL7 file
+	 * \param pcHeader Input header to be validated
+	 */
+	void ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader);
+
+	// -------------------------------------------------------------------
+	/** Validate the header data structure of a Quake 1 model
+	 * \param pcHeader Input header to be validated
+	 */
+	void ValidateHeader_Quake1(const MDL::Header* pcHeader);
+
+
+	// *******************************************************************
+	// Material import
+
+
+	// -------------------------------------------------------------------
+	/** Try to load a  palette from the current directory (colormap.lmp)
+	 *  If it is not found the default palette of Quake1 is returned
+	 */
+	void SearchPalette(const unsigned char** pszColorMap);
+
+	// -------------------------------------------------------------------
+	/** Free a palette created with a previous call to SearchPalette()
+	 */
+	void FreePalette(const unsigned char* pszColorMap);
+
+
+	// -------------------------------------------------------------------
+	/** Load a paletized texture from the file and convert it to 32bpp
+	*/
+	void CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Used to load textures from MDL3/4
 	/** Used to load textures from MDL3/4
 	 * \param szData Input data
 	 * \param szData Input data
 	 * \param iType Color data type
 	 * \param iType Color data type
-	 * \param piSkip Receive: Size to skip
+	 * \param piSkip Receive: Size to skip, in bytes
 	*/
 	*/
-	void CreateTextureARGB8_GS4(const unsigned char* szData, 
+	void CreateTexture_3DGS_MDL4(const unsigned char* szData, 
 		unsigned int iType,
 		unsigned int iType,
 		unsigned int* piSkip);
 		unsigned int* piSkip);
 
 
@@ -143,92 +192,241 @@ protected:
 	/** Used to load textures from MDL5
 	/** Used to load textures from MDL5
 	 * \param szData Input data
 	 * \param szData Input data
 	 * \param iType Color data type
 	 * \param iType Color data type
-	 * \param piSkip Receive: Size to skip
+	 * \param piSkip Receive: Size to skip, in bytes
 	*/
 	*/
-	void CreateTextureARGB8_GS5(const unsigned char* szData, 
+	void CreateTexture_3DGS_MDL5(const unsigned char* szData, 
 		unsigned int iType,
 		unsigned int iType,
 		unsigned int* piSkip);
 		unsigned int* piSkip);
 
 
+
+	// -------------------------------------------------------------------
+	/** Checks whether a texture can be replaced with a single color
+	 * This is useful for all file formats before MDL7 (all those
+	 * that are not containing material colors separate from textures).
+	 * MED seems to write dummy 8x8 monochrome images instead.
+	 * \param pcTexture Input texture
+	 * \return aiColor.r is set to qnan if the function fails and no
+	 *   color can be found.
+	*/
+	aiColor4D ReplaceTextureWithColor(const aiTexture* pcTexture);
+
+
+	// *******************************************************************
+	// Quake1, MDL 3,4,5 import
+
+
+	// -------------------------------------------------------------------
+	/** Converts the absolute texture coordinates in MDL5 files to
+	 *  relative in a range between 0 and 1
+	*/
+	void CalculateUVCoordinates_MDL5();
+
+	// -------------------------------------------------------------------
+	/** Read an UV coordinate from the file. If the file format is not
+	 * MDL5, the function calculates relative texture coordinates
+	 * \param vOut Receives the output UV coord
+	 * \param pcSrc UV coordinate buffer
+	 * \param UV coordinate index
+	*/
+	void ImportUVCoordinate_3DGS_MDL345( aiVector3D& vOut,
+		const MDL::TexCoord_MDL3* pcSrc, 
+		unsigned int iIndex);
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Parse a skin lump in a MDL7 file with all of its features
+	/** Setup the material properties for Quake and MDL<7 models.
+	 * These formats don't support more than one material per mesh,
+	 * therefore the method processes only ONE skin and removes
+	 * all others.
+	 */
+	void SetupMaterialProperties_3DGS_MDL5_Quake1( );
+
+
+	// *******************************************************************
+	// MDL7 import
+
+	// -------------------------------------------------------------------
+	/** Parse a skin lump in a MDL7/HMP7 file with all of its features
+	 *  variant 1: Current cursor position is the beginning of the skin header 
 	 * \param szCurrent Current data pointer
 	 * \param szCurrent Current data pointer
 	 * \param szCurrentOut Output data pointer
 	 * \param szCurrentOut Output data pointer
 	 * \param pcMats Material list for this group. To be filled ...
 	 * \param pcMats Material list for this group. To be filled ...
 	 */
 	 */
-	void ParseSkinLump_GameStudioA7(
+	void ParseSkinLump_3DGS_MDL7(
 		const unsigned char* szCurrent,
 		const unsigned char* szCurrent,
 		const unsigned char** szCurrentOut,
 		const unsigned char** szCurrentOut,
 		std::vector<MaterialHelper*>& pcMats);
 		std::vector<MaterialHelper*>& pcMats);
 
 
+	// -------------------------------------------------------------------
+	/** Parse a skin lump in a MDL7/HMP7 file with all of its features
+	 *  variant 2: Current cursor position is the beginning of the skin data
+	 * \param szCurrent Current data pointer
+	 * \param szCurrentOut Output data pointer
+	 * \param pcMatOut Output material
+	 * \param iType header.typ
+	 * \param iWidth header.width
+	 * \param iHeight header.height
+	 */
+	void ParseSkinLump_3DGS_MDL7(
+		const unsigned char* szCurrent,
+		const unsigned char** szCurrentOut,
+		MaterialHelper* pcMatOut,
+		unsigned int iType,
+		unsigned int iWidth,
+		unsigned int iHeight);
+
+	// -------------------------------------------------------------------
+	/** Skip a skin lump in a MDL7/HMP7 file 
+	 * \param szCurrent Current data pointer
+	 * \param szCurrentOut Output data pointer. Points to the byte just
+	 * behind the last byte of the skin.
+	 * \param iType header.typ
+	 * \param iWidth header.width
+	 * \param iHeight header.height
+	 */
+	void SkipSkinLump_3DGS_MDL7(const unsigned char* szCurrent,
+		const unsigned char** szCurrentOut,
+		unsigned int iType,
+		unsigned int iWidth,
+		unsigned int iHeight);
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Parse texture color data for MDL5, MDL6 and MDL7 formats
 	/** Parse texture color data for MDL5, MDL6 and MDL7 formats
 	 * \param szData Current data pointer
 	 * \param szData Current data pointer
 	 * \param iType type of the texture data. No DDS or external
 	 * \param iType type of the texture data. No DDS or external
 	 * \param piSkip Receive the number of bytes to skip
 	 * \param piSkip Receive the number of bytes to skip
 	 * \param pcNew Must point to fully initialized data. Width and 
 	 * \param pcNew Must point to fully initialized data. Width and 
-	 *        height must be set.
+	 *        height must be set. If pcNew->pcData is set to 0xffffffff,
+	 *        piSkip will receive the size of the texture, in bytes, but no
+	 *        color data will be read.
 	 */
 	 */
 	void ParseTextureColorData(const unsigned char* szData, 
 	void ParseTextureColorData(const unsigned char* szData, 
 		unsigned int iType,
 		unsigned int iType,
 		unsigned int* piSkip,
 		unsigned int* piSkip,
 		aiTexture* pcNew);
 		aiTexture* pcNew);
 
 
-	// -------------------------------------------------------------------
-	/** Validate the header data structure of a game studio MDL7 file
-	 * \param pcHeader Input header to be validated
-	 */
-	void ValidateHeader_GameStudioA7(const MDL::Header_MDL7* pcHeader);
-
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Join two materials / skins. Setup UV source ... etc
 	/** Join two materials / skins. Setup UV source ... etc
 	 * \param pcMat1 First input material
 	 * \param pcMat1 First input material
 	 * \param pcMat2 Second input material
 	 * \param pcMat2 Second input material
 	 * \param pcMatOut Output material instance to be filled. Must be empty
 	 * \param pcMatOut Output material instance to be filled. Must be empty
 	 */
 	 */
-	void JoinSkins_GameStudioA7(MaterialHelper* pcMat1,
+	void JoinSkins_3DGS_MDL7(MaterialHelper* pcMat1,
 		MaterialHelper* pcMat2,
 		MaterialHelper* pcMat2,
 		MaterialHelper* pcMatOut);
 		MaterialHelper* pcMatOut);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Generate the final output meshes for a7 models
-	 * \param aiSplit Face-per-material list
-	 * \param pcMats List of all materials
-	 * \param avOutList Output: List of all meshes
-	 * \param pcFaces List of all input faces
-	 * \param vPositions List of all input vectors
-	 * \param vNormals List of all input normal vectors
-	 * \param vTextureCoords1 List of all input UV coords #1
-	 * \param vTextureCoords2 List of all input UV coords #2
+	/** Add a bone transformation key to an animation
+	 * \param iTrafo Index of the transformation (always==frame index?)
+	 * No need to validate this index, it is always valid.
+	 * \param pcBoneTransforms Bone transformation for this index
+	 * \param apcOutBones Output bones array
 	 */
 	 */
-	void GenerateOutputMeshes_GameStudioA7(
-		const std::vector<unsigned int>** aiSplit,
-		const std::vector<MaterialHelper*>& pcMats,
-		std::vector<aiMesh*>& avOutList,
-		const MDL::IntFace_MDL7* pcFaces,
-		const std::vector<aiVector3D>& vPositions,
-		const std::vector<aiVector3D>& vNormals, 
-		const std::vector<aiVector3D>& vTextureCoords1,
-		const std::vector<aiVector3D>& vTextureCoords2);
+	void AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
+		const MDL::BoneTransform_MDL7* pcBoneTransforms,
+		MDL::IntBone_MDL7** apcBonesOut);
 
 
+	// -------------------------------------------------------------------
+	/** Load the bone list of a MDL7 file
+	 * \return If the bones could be loaded successfully, a valid
+	 *   array containing pointers to a temporary bone
+	 *   representation. NULL if the bones could not be loaded.
+	 */
+	MDL::IntBone_MDL7** LoadBones_3DGS_MDL7();
+
+	// -------------------------------------------------------------------
+	/** Load bone transformation keyframes from a file chunk
+	 * \param groupInfo -> doc of data structure
+	 * \param frame -> doc of data structure
+	 * \param shared -> doc of data structure
+	 */
+	void ParseBoneTrafoKeys_3DGS_MDL7(
+		const MDL::IntGroupInfo_MDL7& groupInfo,
+		IntFrameInfo_MDL7& frame,
+		MDL::IntSharedData_MDL7& shared);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Calculate absolute bone animation matrices for each bone
 	/** Calculate absolute bone animation matrices for each bone
 	 * \param pcBones Pointer to the bone section in the file
 	 * \param pcBones Pointer to the bone section in the file
 	 * \param apcOutBones Output bones array
 	 * \param apcOutBones Output bones array
 	 */
 	 */
-	void CalculateAbsBoneAnimMatrices(const MDL::Bone_MDL7* pcBones,
-		aiBone** apcOutBones);
+	void CalcAbsBoneMatrices_3DGS_MDL7(const MDL::Bone_MDL7* pcBones,
+		MDL::IntBone_MDL7** apcOutBones);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Try to load a  palette from the current directory (colormap.lmp)
-	 *  If it is not found the default palette of Quake1 is returned
+	/** Add all bones to the nodegraph (as children of the root node)
+	 * \param apcBonesOut List of bones
+	 * \param pcParent Parent node. New nodes will be added to this node
+	 * \param iParentIndex Index of the parent bone
 	 */
 	 */
-	void SearchPalette(const unsigned char** pszColorMap);
+	void AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut,
+		aiNode* pcParent,uint16_t iParentIndex);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Free a palette created with a previous call to SearchPalette()
+	/** Build output animations
+	 * \param apcBonesOut List of bones
 	 */
 	 */
-	void FreePalette(const unsigned char* pszColorMap);
+	void BuildOutputAnims_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut);
+
+	// -------------------------------------------------------------------
+	/** Handles materials that are just referencing another material
+	 * There is no test file for this feature, but Conitec's doc
+	 * say it is used.
+	 */
+	void HandleMaterialReferences_3DGS_MDL7();
+
+	// -------------------------------------------------------------------
+	/** Copies only the material that are referenced by at least one
+	 * mesh to the final output material list. All other materials
+	 * will be discarded.
+	 * \param shared -> doc of data structure
+	 */
+	void CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared);
+
+	// -------------------------------------------------------------------
+	/** Process the frame section at the end of a group
+	 * \param groupInfo -> doc of data structure
+	 * \param shared -> doc of data structure
+	 * \param szCurrent Pointer to the start of the frame section
+	 * \param szCurrentOut Receives a pointer to the first byte of the
+	 *   next data section.
+	 * \return false to read no further groups (a small workaround for
+	 *   some tiny and unsolved problems ... )
+	 */
+	bool ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+		MDL::IntSharedData_MDL7& shared,
+		const unsigned char* szCurrent,
+		const unsigned char** szCurrentOut);
+
+	// -------------------------------------------------------------------
+	/** Sort all faces by their materials. If the mesh is using
+	 * multiple materials per face (that are blended together) the function
+	 * might create new materials.
+	 * \param groupInfo -> doc of data structure
+	 * \param groupData -> doc of data structure
+	 * \param splittedGroupData -> doc of data structure
+	 */
+	void SortByMaterials_3DGS_MDL7(
+		const MDL::IntGroupInfo_MDL7& groupInfo,
+		MDL::IntGroupData_MDL7& groupData,
+		MDL::IntSplittedGroupData_MDL7& splittedGroupData);
+	
+	// -------------------------------------------------------------------
+	/** Read all faces and vertices from a MDL7 group. The function fills
+	 *  preallocated memory buffers.
+	 * \param groupInfo -> doc of data structure
+	 * \param groupData -> doc of data structure
+	 */
+	void ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+		MDL::IntGroupData_MDL7& groupData);
+
+	// -------------------------------------------------------------------
+	/** Generate the final output meshes for a7 models
+	 * \param groupData -> doc of data structure
+	 * \param splittedGroupData -> doc of data structure
+	 */
+	void GenerateOutputMeshes_3DGS_MDL7(
+		MDL::IntGroupData_MDL7& groupData,
+		MDL::IntSplittedGroupData_MDL7& splittedGroupData);
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Try to determine whether the normals of the model are flipped
 	/** Try to determine whether the normals of the model are flipped
@@ -239,16 +437,13 @@ protected:
 	 */
 	 */
 	void FlipNormals(aiMesh* pcMesh);
 	void FlipNormals(aiMesh* pcMesh);
 
 
-private:
-
-	/** Header of the MDL file */
-	const MDL::Header* m_pcHeader;
+protected:
 
 
 	/** Buffer to hold the loaded file */
 	/** Buffer to hold the loaded file */
 	unsigned char* mBuffer;
 	unsigned char* mBuffer;
 
 
-	/** For GameStudio MDL files: The number in the magic 
-	word, either 3,4 or 5*/
+	/** For GameStudio MDL files: The number in the magic word, either 3,4 or 5 
+	 * (MDL7 doesn't need this, the format has a separate loader) */
 	unsigned int iGSFileVersion;
 	unsigned int iGSFileVersion;
 
 
 	/** Output I/O handler. used to load external lmp files
 	/** Output I/O handler. used to load external lmp files
@@ -258,6 +453,10 @@ private:
 	/** Output scene to be filled
 	/** Output scene to be filled
 	*/
 	*/
 	aiScene* pScene;
 	aiScene* pScene;
+
+	/** Size of the input file in bytes
+	 */
+	unsigned int iFileSize;
 };
 };
 }; // end of namespace Assimp
 }; // end of namespace Assimp
 
 

+ 774 - 0
code/MDLMaterialLoader.cpp

@@ -0,0 +1,774 @@
+/*
+---------------------------------------------------------------------------
+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 material part of the MDL importer class */
+
+// internal headers
+#include "MaterialSystem.h"
+#include "MDLLoader.h"
+#include "MDLDefaultColorMap.h"
+#include "qnan.h"
+
+// 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"
+
+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");
+
+	const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
+	if(pcStream)
+	{
+		if (pcStream->FileSize() >= 768)
+		{
+			szColorMap = new unsigned char[256*3];
+			pcStream->Read(const_cast<unsigned char*>(szColorMap),256*3,1);
+
+			DefaultLogger::get()->info("Found valid colormap.lmp in directory. "
+				"It will be used to decode embedded textures in palletized formats.");
+		}
+		delete pcStream;
+		pcStream = NULL;
+	}
+	*pszColorMap = szColorMap;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::FreePalette(const unsigned char* szColorMap)
+{
+	if (szColorMap != (const unsigned char*)::g_aclrDefaultColorMap)
+	{
+		delete[] szColorMap;
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture* pcTexture)
+{
+	ai_assert(NULL != pcTexture);
+
+	aiColor4D clrOut;
+	clrOut.r = std::numeric_limits<float>::quiet_NaN();
+	if (!pcTexture->mHeight || !pcTexture->mWidth)return clrOut;
+
+	const unsigned int iNumPixels = pcTexture->mHeight*pcTexture->mWidth;
+	const aiTexel* pcTexel = pcTexture->pcData+1;
+	const aiTexel* const pcTexelEnd = &pcTexture->pcData[iNumPixels];
+
+	while (pcTexel != pcTexelEnd)
+	{
+		if (*pcTexel != *(pcTexel-1))
+		{
+			pcTexel = NULL;break;
+		}
+		++pcTexel;
+	}
+	if (pcTexel)
+	{
+		clrOut.r = pcTexture->pcData->r / 255.0f;
+		clrOut.g = pcTexture->pcData->g / 255.0f;
+		clrOut.b = pcTexture->pcData->b / 255.0f;
+		clrOut.a = pcTexture->pcData->a / 255.0f;
+	}
+	return clrOut;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData)
+{
+	const MDL::Header* pcHeader = (const MDL::Header*)this->mBuffer;
+	VALIDATE_FILE_SIZE(szData + pcHeader->skinwidth *
+		pcHeader->skinheight);
+
+	// allocate a new texture object
+	aiTexture* pcNew = new aiTexture();
+	pcNew->mWidth = pcHeader->skinwidth;
+	pcNew->mHeight = pcHeader->skinheight;
+
+	pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+	const unsigned char* szColorMap;
+	this->SearchPalette(&szColorMap);
+
+	// copy texture data
+	for (unsigned int i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+	{
+		const unsigned char val = szData[i];
+		const unsigned char* sz = &szColorMap[val*3];
+
+		pcNew->pcData[i].a = 0xFF;
+		pcNew->pcData[i].r = *sz++;
+		pcNew->pcData[i].g = *sz++;
+		pcNew->pcData[i].b = *sz;
+	}
+
+	this->FreePalette(szColorMap);
+
+	// store the texture
+	aiTexture** pc = this->pScene->mTextures;
+	this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+	for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+		this->pScene->mTextures[i] = pc[i];
+
+	this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+	this->pScene->mNumTextures++;
+	delete[] pc;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::CreateTexture_3DGS_MDL4(const unsigned char* szData, 
+	unsigned int iType,
+	unsigned int* piSkip)
+{
+	const MDL::Header* pcHeader = (const MDL::Header*)this->mBuffer;
+
+	ai_assert(NULL != piSkip);
+
+	if (iType == 1 || iType > 3)
+	{
+		DefaultLogger::get()->error("Unsupported texture file format");
+		return;
+	}
+
+	bool bNoRead = *piSkip == 0xffffffff;
+
+	// allocate a new texture object
+	aiTexture* pcNew = new aiTexture();
+	pcNew->mWidth = pcHeader->skinwidth;
+	pcNew->mHeight = pcHeader->skinheight;
+	
+	if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+	this->ParseTextureColorData(szData,iType,piSkip,pcNew);
+
+	// store the texture
+	if (!bNoRead)
+	{
+		if (!this->pScene->mNumTextures)
+		{
+			this->pScene->mNumTextures = 1;
+			this->pScene->mTextures = new aiTexture*[1];
+			this->pScene->mTextures[0] = pcNew;
+		}
+		else
+		{
+			aiTexture** pc = this->pScene->mTextures;
+			this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+			for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+				this->pScene->mTextures[i] = pc[i];
+			this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+			this->pScene->mNumTextures++;
+			delete[] pc;
+		}
+	}
+	else delete pcNew;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::ParseTextureColorData(const unsigned char* szData, 
+	unsigned int iType,
+	unsigned int* piSkip,
+	aiTexture* pcNew)
+{
+	// allocate storage for the texture image
+	if ((aiTexel*)0xffffffff != pcNew->pcData)
+		pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+	// R5G6B5 format (with or without MIPs)
+	// ****************************************************************
+	if (2 == iType || 10 == iType)
+	{
+		VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*2);
+
+		// copy texture data
+		unsigned int i;
+		if ((aiTexel*)0xffffffff != pcNew->pcData) 
+		{
+			for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+			{
+				MDL::RGB565 val = ((MDL::RGB565*)szData)[i];
+
+				pcNew->pcData[i].a = 0xFF;
+				pcNew->pcData[i].r = (unsigned char)val.b << 3;
+				pcNew->pcData[i].g = (unsigned char)val.g << 2;
+				pcNew->pcData[i].b = (unsigned char)val.r << 3;
+			}
+		} 
+		else i = pcNew->mWidth*pcNew->mHeight;
+		*piSkip = i * 2;
+
+		// apply MIP maps
+		if (10 == iType)
+		{
+			*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+			VALIDATE_FILE_SIZE(szData + *piSkip);
+		}
+	}
+	// ARGB4 format (with or without MIPs)
+	// ****************************************************************
+	else if (3 == iType || 11 == iType)
+	{
+		VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+		// copy texture data
+		unsigned int i;
+		if ((aiTexel*)0xffffffff != pcNew->pcData) 
+		{
+			for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+			{
+				MDL::ARGB4 val = ((MDL::ARGB4*)szData)[i];
+
+				pcNew->pcData[i].a = (unsigned char)val.a << 4;
+				pcNew->pcData[i].r = (unsigned char)val.r << 4;
+				pcNew->pcData[i].g = (unsigned char)val.g << 4;
+				pcNew->pcData[i].b = (unsigned char)val.b << 4;
+			}
+		}
+		else i = pcNew->mWidth*pcNew->mHeight;
+		*piSkip = i * 2;
+
+		// apply MIP maps
+		if (11 == iType)
+		{
+			*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+			VALIDATE_FILE_SIZE(szData + *piSkip);
+		}
+	}
+	// RGB8 format (with or without MIPs)
+	// ****************************************************************
+	else if (4 == iType || 12 == iType)
+	{
+		VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*3);
+
+		// copy texture data
+		unsigned int i;
+		if ((aiTexel*)0xffffffff != pcNew->pcData)
+		{
+			for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+			{
+				const unsigned char* _szData = &szData[i*3];
+
+				pcNew->pcData[i].a = 0xFF;
+				pcNew->pcData[i].b = *_szData++;
+				pcNew->pcData[i].g = *_szData++;
+				pcNew->pcData[i].r = *_szData;
+			}
+		} 
+		else i = pcNew->mWidth*pcNew->mHeight;
+
+
+		// apply MIP maps
+		*piSkip = i * 3;
+		if (12 == iType)
+		{
+			*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) *3;
+			VALIDATE_FILE_SIZE(szData + *piSkip);
+		}
+	}
+	// ARGB8 format (with ir without MIPs)
+	// ****************************************************************
+	else if (5 == iType || 13 == iType)
+	{
+		VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+		// copy texture data
+		unsigned int i;
+		if ((aiTexel*)0xffffffff != pcNew->pcData)
+		{
+			for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+			{
+				const unsigned char* _szData = &szData[i*4];
+
+				pcNew->pcData[i].b = *_szData++;
+				pcNew->pcData[i].g = *_szData++;
+				pcNew->pcData[i].r = *_szData++;
+				pcNew->pcData[i].a = *_szData;
+			}
+		} 
+		else i = pcNew->mWidth*pcNew->mHeight;
+
+		// apply MIP maps
+		*piSkip = i << 2;
+		if (13 == iType)
+		{
+			*piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 2;
+		}
+	}
+	// palletized 8 bit texture. As for Quake 1
+	// ****************************************************************
+	else if (0 == iType)
+	{
+		VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight);
+
+		// copy texture data
+		unsigned int i;
+		if ((aiTexel*)0xffffffff != pcNew->pcData) 
+		{
+
+			const unsigned char* szColorMap;
+			this->SearchPalette(&szColorMap);
+
+			for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+			{
+				const unsigned char val = szData[i];
+				const unsigned char* sz = &szColorMap[val*3];
+
+				pcNew->pcData[i].a = 0xFF;
+				pcNew->pcData[i].r = *sz++;
+				pcNew->pcData[i].g = *sz++;
+				pcNew->pcData[i].b = *sz;
+			}
+			this->FreePalette(szColorMap);
+
+		} 
+		else i = pcNew->mWidth*pcNew->mHeight;
+		*piSkip = i;
+
+		// FIXME: Also support for MIP maps?
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::CreateTexture_3DGS_MDL5(const unsigned char* szData, 
+	unsigned int iType,
+	unsigned int* piSkip)
+{
+	ai_assert(NULL != piSkip);
+
+	bool bNoRead = *piSkip == 0xffffffff;
+
+	// allocate a new texture object
+	aiTexture* pcNew = new aiTexture();
+
+	VALIDATE_FILE_SIZE(szData+8);
+
+	// first read the size of the texture
+	pcNew->mWidth = *((uint32_t*)szData);
+	szData += sizeof(uint32_t);
+
+	pcNew->mHeight = *((uint32_t*)szData);
+	szData += sizeof(uint32_t);
+
+	if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+
+	// this should not occur - at least the docs say it shouldn't
+	// however, you can easily try out what MED does if you have
+	// a model with a DDS texture and export it to MDL5 ...
+	// yes, you're right. It embedds the DDS texture ... :cry:
+	if (6 == iType)
+	{
+		// this is a compressed texture in DDS format
+		*piSkip = pcNew->mWidth;
+		VALIDATE_FILE_SIZE(szData + *piSkip);
+
+		if (!bNoRead)
+		{
+			// place a hint and let the application know that it's
+			// a DDS file
+			pcNew->mHeight = 0;
+			pcNew->achFormatHint[0] = 'd';
+			pcNew->achFormatHint[1] = 'd';
+			pcNew->achFormatHint[2] = 's';
+			pcNew->achFormatHint[3] = '\0';
+
+			pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+			::memcpy(pcNew->pcData,szData,pcNew->mWidth);
+		}
+	}
+	else
+	{
+		// parse the color data of the texture
+		this->ParseTextureColorData(szData,iType,
+			piSkip,pcNew);
+	}
+	*piSkip += sizeof(uint32_t) * 2;
+
+	if (!bNoRead)
+	{
+		// store the texture
+		if (!this->pScene->mNumTextures)
+		{
+			this->pScene->mNumTextures = 1;
+			this->pScene->mTextures = new aiTexture*[1];
+			this->pScene->mTextures[0] = pcNew;
+		}
+		else
+		{
+			aiTexture** pc = this->pScene->mTextures;
+			this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+			for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+				this->pScene->mTextures[i] = pc[i];
+
+			this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+			this->pScene->mNumTextures++;
+			delete[] pc;
+		}
+	}
+	else delete pcNew;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+	const unsigned char* szCurrent,
+	const unsigned char** szCurrentOut,
+	MaterialHelper* pcMatOut,
+	unsigned int iType,
+	unsigned int iWidth,
+	unsigned int iHeight)
+{
+	aiTexture* pcNew = NULL;
+
+	// get the type of the skin
+	unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+	if (0x1 ==  iMasked)
+	{
+		// ***** REFERENCE TO ANOTHER SKIN INDEX *****
+
+		// NOTE: Documentation - if you can call it a documentation, I prefer
+		// the expression "rubbish" - states it is currently unused. However,
+		// I don't know what ideas the terrible developers of Conitec will
+		// have tomorrow, so Im going to implement it.
+		int referrer = (int)iWidth;
+		pcMatOut->AddProperty<int>(&referrer,1,AI_MDL7_REFERRER_MATERIAL);
+	}
+	else if (0x6 == iMasked)
+	{
+		// ***** EMBEDDED DDS FILE *****
+		if (1 != iHeight)
+		{
+			DefaultLogger::get()->warn("Found a reference to an embedded DDS texture, "
+				"but texture height is not equal to 1, which is not supported by MED");
+		}
+
+		pcNew = new aiTexture();
+		pcNew->mHeight = 0;
+		pcNew->mWidth = iWidth;
+		pcNew->achFormatHint[0] = 'd';
+		pcNew->achFormatHint[1] = 'd';
+		pcNew->achFormatHint[2] = 's';
+		pcNew->achFormatHint[3] = '\0';
+
+		pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+		memcpy(pcNew->pcData,szCurrent,pcNew->mWidth);
+		szCurrent += iWidth;
+	}
+	if (0x7 == iMasked)
+	{
+		// ***** REFERENCE TO EXTERNAL FILE *****
+		if (1 != iHeight)
+		{
+			DefaultLogger::get()->warn("Found a reference to an external texture, "
+				"but texture height is not equal to 1, which is not supported by MED");
+		}
+
+		aiString szFile;
+		const size_t iLen = strlen((const char*)szCurrent);
+		size_t iLen2 = iLen+1;
+		iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
+		memcpy(szFile.data,(const char*)szCurrent,iLen2);
+		szFile.length = iLen;
+
+		szCurrent += iLen2;
+
+		// place this as diffuse texture
+		pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+	}
+	else if (iMasked || !iType || (iType && iWidth && iHeight))
+	{
+		// ***** STANDARD COLOR TEXTURE *****
+		pcNew = new aiTexture();
+		if (!iHeight || !iWidth)
+		{
+			DefaultLogger::get()->warn("Found embedded texture, but its width "
+				"an height are both 0. Is this a joke?");
+
+			// generate an empty chess pattern
+			pcNew->mWidth = pcNew->mHeight = 8;
+			pcNew->pcData = new aiTexel[64];
+			for (unsigned int x = 0; x < 8;++x)
+			{
+				for (unsigned int y = 0; y < 8;++y)
+				{
+					bool bSet = false;
+					if (0 == x % 2 && 0 != y % 2 ||
+						0 != x % 2 && 0 == y % 2)bSet = true;
+				
+					aiTexel* pc = &pcNew->pcData[y * 8 + x];
+					if (bSet)pc->r = pc->b = pc->g = 0xFF;
+					else pc->r = pc->b = pc->g = 0;
+					pc->a = 0xFF;
+				}
+			}
+		}
+		else
+		{
+			// it is a standard color texture. Fill in width and height
+			// and call the same function we used for loading MDL5 files
+
+			pcNew->mWidth = iWidth;
+			pcNew->mHeight = iHeight;
+
+			unsigned int iSkip = 0;
+			this->ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew);
+
+			// skip length of texture data
+			szCurrent += iSkip;
+		}
+	}
+
+	// sometimes there are MDL7 files which have a monochrome
+	// texture instead of material colors ... posssible they have
+	// been converted to MDL7 from other formats, such as MDL5
+	aiColor4D clrTexture = this->ReplaceTextureWithColor(pcNew);
+	
+	// check whether a material definition is contained in the skin
+	if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+	{
+		const MDL::Material_MDL7* pcMatIn = (const MDL::Material_MDL7*)szCurrent;
+		szCurrent = (unsigned char*)(pcMatIn+1);
+		VALIDATE_FILE_SIZE(szCurrent);
+
+		aiColor3D clrTemp;
+
+#define COLOR_MULTIPLY_RGB() \
+		if (is_not_qnan(clrTexture.r)) \
+		{ \
+			clrTemp.r *= clrTexture.r; \
+			clrTemp.g *= clrTexture.g; \
+			clrTemp.b *= clrTexture.b; \
+		}
+
+		// read diffuse color
+		clrTemp.r = pcMatIn->Diffuse.r;
+		clrTemp.g = pcMatIn->Diffuse.g;
+		clrTemp.b = pcMatIn->Diffuse.b;
+		COLOR_MULTIPLY_RGB();
+		pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_DIFFUSE);
+
+		// read specular color
+		clrTemp.r = pcMatIn->Specular.r;
+		clrTemp.g = pcMatIn->Specular.g;
+		clrTemp.b = pcMatIn->Specular.b;
+		COLOR_MULTIPLY_RGB();
+		pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_SPECULAR);
+
+		// read ambient color
+		clrTemp.r = pcMatIn->Ambient.r;
+		clrTemp.g = pcMatIn->Ambient.g;
+		clrTemp.b = pcMatIn->Ambient.b;
+		COLOR_MULTIPLY_RGB();
+		pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_AMBIENT);
+
+		// read emissive color
+		clrTemp.r = pcMatIn->Emissive.r;
+		clrTemp.g = pcMatIn->Emissive.g;
+		clrTemp.b = pcMatIn->Emissive.b;
+		pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_EMISSIVE);
+
+		// FIX: Take the opacity from the ambient color
+		// the doc says something else, but it is fact that MED exports the
+		// opacity like this .... ARRRGGHH!
+		clrTemp.r = pcMatIn->Ambient.a;
+		if (is_not_qnan(clrTexture.r))clrTemp.r *= clrTexture.a;
+		pcMatOut->AddProperty<float>(&clrTemp.r,1,AI_MATKEY_OPACITY);
+
+		// read phong power
+		int iShadingMode = (int)aiShadingMode_Gouraud;
+		if (0.0f != pcMatIn->Power)
+		{
+			iShadingMode = (int)aiShadingMode_Phong;
+			pcMatOut->AddProperty<float>(&pcMatIn->Power,1,AI_MATKEY_SHININESS);
+		}
+		pcMatOut->AddProperty<int>(&iShadingMode,1,AI_MATKEY_SHADING_MODEL);
+	}
+	else if (is_not_qnan(clrTexture.r))
+	{
+		pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_DIFFUSE);
+		pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_SPECULAR);
+	}
+	// if the texture could be replaced by a single material color
+	// we don't need the texture anymore
+	if (is_not_qnan(clrTexture.r))
+	{
+		delete pcNew;
+		pcNew = NULL;
+	}
+
+	// if an ASCII effect description (HLSL?) is contained in the file,
+	// we can simply ignore it ...
+	if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+	{
+		VALIDATE_FILE_SIZE(szCurrent);
+		int32_t iMe = *((int32_t*)szCurrent);
+		szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+		VALIDATE_FILE_SIZE(szCurrent);
+	}
+
+	// if an embedded texture has been loaded setup the corresponding
+	// data structures in the aiScene instance
+	if (pcNew && this->pScene->mNumTextures <= 999)
+	{
+		
+			// place this as diffuse texture
+			char szCurrent[5];
+			::sprintf(szCurrent,"*%i",this->pScene->mNumTextures);
+
+			aiString szFile;
+			const size_t iLen = strlen((const char*)szCurrent);
+			size_t iLen2 = iLen+1;
+			iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
+			::memcpy(szFile.data,(const char*)szCurrent,iLen2);
+			szFile.length = iLen;
+
+			pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+			// store the texture
+			aiTexture** pc = this->pScene->mTextures;
+			this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+			for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+				this->pScene->mTextures[i] = pc[i];
+
+			this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+			this->pScene->mNumTextures++;
+			delete[] pc;
+		
+	}
+	VALIDATE_FILE_SIZE(szCurrent);
+	*szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::SkipSkinLump_3DGS_MDL7(
+	const unsigned char* szCurrent,
+	const unsigned char** szCurrentOut,
+	unsigned int iType,
+	unsigned int iWidth,
+	unsigned int iHeight)
+{
+	// get the type of the skin
+	unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+	if (0x6 == iMasked)
+	{
+		szCurrent += iWidth;
+	}
+	if (0x7 == iMasked)
+	{
+		const size_t iLen = ::strlen((const char*)szCurrent);
+		szCurrent += iLen+1;
+	}
+	else if (iMasked || !iType)
+	{
+		if (iMasked || !iType || (iType && iWidth && iHeight))
+		{
+			// ParseTextureColorData(..., aiTexture::pcData == 0xffffffff) will simply
+			// return the size of the color data in bytes in iSkip
+			unsigned int iSkip = 0;
+
+			aiTexture tex;
+			tex.pcData = reinterpret_cast<aiTexel*>(0xffffffff);
+			tex.mHeight = iHeight;
+			tex.mWidth = iWidth;
+			this->ParseTextureColorData(szCurrent,iMasked,&iSkip,&tex);
+
+			// skip length of texture data
+			szCurrent += iSkip;
+		}
+	}
+	
+	// check whether a material definition is contained in the skin
+	if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+	{
+		const MDL::Material_MDL7* pcMatIn = (const MDL::Material_MDL7*)szCurrent;
+		szCurrent = (unsigned char*)(pcMatIn+1);
+	}
+
+	// if an ASCII effect description (HLSL?) is contained in the file,
+	// we can simply ignore it ...
+	if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+	{
+		int32_t iMe = *((int32_t*)szCurrent);
+		szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+	}
+	*szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+	const unsigned char* szCurrent,
+	const unsigned char** szCurrentOut,
+	std::vector<MaterialHelper*>& pcMats)
+{
+	ai_assert(NULL != szCurrent);
+	ai_assert(NULL != szCurrentOut);
+
+	*szCurrentOut = szCurrent;
+	const MDL::Skin_MDL7* pcSkin = (const MDL::Skin_MDL7*)szCurrent;
+	szCurrent += 12;
+
+	// allocate an output material
+	MaterialHelper* pcMatOut = new MaterialHelper();
+	pcMats.push_back(pcMatOut);
+
+	// skip length of file name
+	szCurrent += AI_MDL7_MAX_TEXNAMESIZE;
+
+	this->ParseSkinLump_3DGS_MDL7(szCurrent,szCurrentOut,pcMatOut,
+		pcSkin->typ,pcSkin->width,pcSkin->height);
+	
+	// place the name of the skin in the material
+	const size_t iLen = strlen(pcSkin->texture_name); 
+	if (0 != iLen)
+	{
+		aiString szFile;
+		memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
+		szFile.length = iLen;
+
+		pcMatOut->AddProperty(&szFile,AI_MATKEY_NAME);
+	}
+	return;
+}

+ 1 - 0
code/MaterialSystem.cpp

@@ -465,6 +465,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
 	};
 	};
 
 
 	char szKey[256];
 	char szKey[256];
+	if (iIndex > 100)return AI_FAILURE;
 
 
 	// get the path to the texture
 	// get the path to the texture
 #if _MSC_VER >= 1400
 #if _MSC_VER >= 1400

+ 2 - 1
code/MaterialSystem.h

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp
 namespace Assimp
 {
 {
 
 
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Internal material helper class. Can be used to fill an aiMaterial
 /** Internal material helper class. Can be used to fill an aiMaterial
     structure easily. */
     structure easily. */
@@ -209,4 +210,4 @@ inline aiReturn MaterialHelper::AddProperty<int> (const int* pInput,
 }
 }
 
 
 
 
-#endif //!! AI_MATERIALSYSTEM_H_INC
+#endif //!! AI_MATERIALSYSTEM_H_INC

+ 115 - 0
code/ParsingUtils.h

@@ -0,0 +1,115 @@
+/*
+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 helper functions for text parsing  */
+#ifndef AI_PARSING_UTILS_H_INC
+#define AI_PARSING_UTILS_H_INC
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool IsSpace( const char_t in)
+{
+	return (in == (char_t)' ' || in == (char_t)'\t');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool IsLineEnd( const char_t in)
+{
+	return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool IsSpaceOrNewLine( const char_t in)
+{
+	return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipSpaces( const char_t* in, const char_t** out)
+{
+	while (*in == (char_t)' ' || *in == (char_t)'\t')in++;
+	*out = in;
+	return !IsLineEnd<char_t>(*in);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipSpaces( const char_t** inout)
+{
+	return SkipSpaces<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipLine( const char_t* in, const char_t** out)
+{
+	while (*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0')in++;
+
+	if (*in == (char_t)'\0')
+	{
+		*out = in;
+		return false;
+	}
+	in++;
+	*out = in;
+	return true;
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipLine( const char_t** inout)
+{
+	return SkipLine<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline void SkipSpacesAndLineEnd( const char_t* in, const char_t** out)
+{
+	while (*in == (char_t)' ' || *in == (char_t)'\t' ||
+		*in == (char_t)'\r' || *in == (char_t)'\n')in++;
+	*out = in;
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipSpacesAndLineEnd( const char_t** inout)
+{
+	return SkipSpacesAndLineEnd<char_t>(*inout,inout);
+}
+
+
+#endif // ! AI_PARSING_UTILS_H_INC

+ 7 - 0
code/PlyLoader.cpp

@@ -120,6 +120,7 @@ void PLYImporter::InternReadFile(
 		this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
 		this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
 		this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
 		this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
 		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
 	}
 	}
 	char* szMe = (char*)&this->mBuffer[3];
 	char* szMe = (char*)&this->mBuffer[3];
@@ -136,6 +137,7 @@ void PLYImporter::InternReadFile(
 			SkipLine(szMe,(const char**)&szMe);
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom, (unsigned int)fileSize))
 			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom, (unsigned int)fileSize))
 			{
 			{
+				delete[] this->mBuffer;
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
 			}
 			}
 		}
 		}
@@ -156,16 +158,19 @@ void PLYImporter::InternReadFile(
 			SkipLine(szMe,(const char**)&szMe);
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE, (unsigned int)fileSize))
 			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE, (unsigned int)fileSize))
 			{
 			{
+				delete[] this->mBuffer;
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
 			}
 			}
 		}
 		}
 		else
 		else
 		{
 		{
+			delete[] this->mBuffer;
 			throw new ImportErrorException( "Invalid .ply file: Unknown file format");
 			throw new ImportErrorException( "Invalid .ply file: Unknown file format");
 		}
 		}
 	}
 	}
 	else
 	else
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid .ply file: Missing format specification");
 		throw new ImportErrorException( "Invalid .ply file: Missing format specification");
 	}
 	}
 	this->pcDOM = &sPlyDom;
 	this->pcDOM = &sPlyDom;
@@ -193,6 +198,7 @@ void PLYImporter::InternReadFile(
 	{
 	{
 		if (avPositions.size() < 3)
 		if (avPositions.size() < 3)
 		{
 		{
+			delete[] this->mBuffer;
 			throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
 			throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
 				"a face list. ");
 				"a face list. ");
 		}
 		}
@@ -230,6 +236,7 @@ void PLYImporter::InternReadFile(
 
 
 	if (avMeshes.empty())
 	if (avMeshes.empty())
 	{
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
 		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
 	}
 	}
 
 

+ 2 - 42
code/PlyParser.h

@@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <list>
 #include <list>
 #include <sstream>
 #include <sstream>
 
 
+#include "ParsingUtils.h"
+
 #include "../include/aiTypes.h"
 #include "../include/aiTypes.h"
 #include "../include/aiMesh.h"
 #include "../include/aiMesh.h"
 #include "../include/aiAnim.h"
 #include "../include/aiAnim.h"
@@ -551,48 +553,6 @@ TYPE PLY::PropertyInstance::ConvertTo(
 }
 }
 };
 };
 
 
-// ---------------------------------------------------------------------------------
-inline bool IsSpace( const char in)
-{
-	return (in == ' ' || in == '\t');
-}
-// ---------------------------------------------------------------------------------
-inline bool IsLineEnd( const char in)
-{
-	return (in == '\r' || in == '\n' || in == '\0');
-}
-// ---------------------------------------------------------------------------------
-inline bool IsSpaceOrNewLine( const char in)
-{
-	return IsSpace(in) || IsLineEnd(in);
-}
-// ---------------------------------------------------------------------------------
-inline bool SkipSpaces( const char* in, const char** out)
-{
-	while (*in == ' ' || *in == '\t')in++;
-	*out = in;
-	return !IsLineEnd(*in);
-}
-// ---------------------------------------------------------------------------------
-inline bool SkipLine( const char* in, const char** out)
-{
-	while (*in != '\r' && *in != '\n' && *in != '\0')in++;
-
-	if (*in == '\0')
-	{
-		*out = in;
-		return false;
-	}
-	in++;
-	*out = in;
-	return true;
-}
-// ---------------------------------------------------------------------------------
-inline void SkipSpacesAndLineEnd( const char* in, const char** out)
-{
-	while (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n')in++;
-	*out = in;
-}
 };
 };
 
 
 #endif // !! include guard
 #endif // !! include guard

+ 1070 - 0
code/SMDLoader.cpp

@@ -0,0 +1,1070 @@
+/*
+---------------------------------------------------------------------------
+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 SMD importer class */
+
+#include "MaterialSystem.h"
+#include "SMDLoader.h"
+#include "StringComparison.h"
+#include "fast_atof.h"
+
+#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 <boost/scoped_ptr.hpp>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SMDImporter::SMDImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+SMDImporter::~SMDImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool SMDImporter::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);
+
+	if (extension.length() < 4)return false;
+	if (extension[0] != '.')return false;
+
+	// VTA is not really supported as it contains vertex animations.
+	// However, at least the first keyframe can be loaded
+	if ((extension[1] != 's' && extension[1] != 'S') ||
+	    (extension[2] != 'm' && extension[2] != 'M') ||
+	    (extension[3] != 'd' && extension[3] != 'D'))
+	{
+		if ((extension[1] != 'v' && extension[1] != 'V') ||
+			(extension[2] != 't' && extension[2] != 'T') ||
+			(extension[3] != 'a' && extension[3] != 'A'))
+		{
+			return false;
+		}
+	}
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void SMDImporter::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 SMD/VTA file " + pFile + ".");
+	}
+
+	this->iFileSize = (unsigned int)file->FileSize();
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	this->pScene = pScene;
+	this->mBuffer = new char[this->iFileSize+1];
+	file->Read( (void*)mBuffer, 1, this->iFileSize);
+	this->iSmallestFrame = (1 << 31);
+	this->bHasUVs = true;
+	this->iLineNumber = 1;
+
+	// reserve enough space for ... hm ... 10 textures
+	this->aszTextures.reserve(10);
+
+	// reserve enough space for ... hm ... 1000 triangles
+	this->asTriangles.reserve(1000);
+
+	// reserve enough space for ... hm ... 20 bones
+	this->asBones.reserve(20);
+
+	try
+	{
+		// parse the file ...
+		this->ParseFile();
+
+		// now fix invalid time values and make sure the animation starts at frame 0
+		this->FixTimeValues();
+
+		// compute absolute bone transformation matrices
+		this->ComputeAbsoluteBoneTransformations();
+
+		// create output meshes
+		this->CreateOutputMeshes();
+
+		// build the output animation
+		this->CreateOutputAnimations();
+
+		// build output nodes (bones are added as empty dummy nodes)
+		this->CreateOutputNodes();
+	}
+	catch (ImportErrorException* ex)
+	{
+		delete[] this->mBuffer;
+		throw ex;
+	}
+
+	// delete the file buffer
+	delete[] this->mBuffer;
+}
+// ------------------------------------------------------------------------------------------------
+// Write an error message with line number to the log file
+void SMDImporter::LogErrorNoThrow(const char* msg)
+{
+	char szTemp[1024];
+
+#if _MSC_VER >= 1400
+	sprintf_s(szTemp,"Line %i: %s",this->iLineNumber,msg);
+#else
+	ai_assert(strlen(msg) < 1000);
+	sprintf(szTemp,"Line %i: %s",this->iLineNumber,msg);
+#endif
+
+	DefaultLogger::get()->error(szTemp);
+}
+// ------------------------------------------------------------------------------------------------
+// Write a warning with line number to the log file
+void SMDImporter::LogWarning(const char* msg)
+{
+	char szTemp[1024];
+
+#if _MSC_VER >= 1400
+	sprintf_s(szTemp,"Line %i: %s",this->iLineNumber,msg);
+#else
+	ai_assert(strlen(msg) < 1000);
+	sprintf(szTemp,"Line %i: %s",this->iLineNumber,msg);
+#endif
+	DefaultLogger::get()->warn(szTemp);
+}
+// ------------------------------------------------------------------------------------------------
+// Fix invalid time values in the file
+void SMDImporter::FixTimeValues()
+{
+	double dDelta = (double)this->iSmallestFrame;
+	double dMax = 0.0f;
+	for (std::vector<SMD::Bone>::iterator
+		iBone =  this->asBones.begin();
+		iBone != this->asBones.end();++iBone)
+	{
+		for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator
+			iKey =  (*iBone).sAnim.asKeys.begin();
+			iKey != (*iBone).sAnim.asKeys.end();++iKey)
+		{
+			(*iKey).dTime -= dDelta;
+			dMax = std::max(dMax, (*iKey).dTime);
+		}
+	}
+	this->dLengthOfAnim = dMax;
+}
+// ------------------------------------------------------------------------------------------------
+// create output meshes
+void SMDImporter::CreateOutputMeshes()
+{
+	// we need to sort all faces by their material index
+	// in opposition to other loaders we can be sure that each
+	// material is at least used once.
+	this->pScene->mNumMeshes = (unsigned int) this->aszTextures.size();
+	this->pScene->mMeshes = new aiMesh*[this->pScene->mNumMeshes];
+
+	typedef std::vector<unsigned int> FaceList;
+	FaceList* aaiFaces = new FaceList[this->pScene->mNumMeshes];
+
+	// approximate the space that will be required
+	unsigned int iNum = (unsigned int)this->asTriangles.size() / this->pScene->mNumMeshes;
+	iNum += iNum >> 1;
+	for (unsigned int i = 0; i < this->pScene->mNumMeshes;++i)
+	{
+		aaiFaces[i].reserve(iNum);
+	}
+
+	// collect all faces
+	iNum = 0;
+	for (std::vector<SMD::Face>::const_iterator
+		iFace =  this->asTriangles.begin();
+		iFace != this->asTriangles.end();++iFace,++iNum)
+	{
+		if (0xffffffff == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 );
+		else if ((*iFace).iTexture >= this->aszTextures.size())
+		{
+			DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face");
+			aaiFaces[(*iFace).iTexture].push_back((unsigned int)this->aszTextures.size()-1);
+		}
+		else aaiFaces[(*iFace).iTexture].push_back(iNum);
+	} 
+
+	// now create the output meshes
+	for (unsigned int i = 0; i < this->pScene->mNumMeshes;++i)
+	{
+		aiMesh*& pcMesh = this->pScene->mMeshes[i] = new aiMesh();
+		ai_assert(!aaiFaces[i].empty()); // should not be empty ...
+
+		pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3;
+		pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size();
+		pcMesh->mMaterialIndex = i;
+
+		// storage for bones
+		typedef std::pair<unsigned int,float> TempWeightListEntry;
+		typedef std::vector< TempWeightListEntry > TempBoneWeightList;
+
+		TempBoneWeightList* aaiBones = new TempBoneWeightList[this->asBones.size()]();
+
+		// try to reserve enough memory without wasting too much
+		for (unsigned int iBone = 0; iBone < this->asBones.size();++iBone)
+		{
+			aaiBones[iBone].reserve(pcMesh->mNumVertices/this->asBones.size());
+		}
+
+		// allocate storage
+		pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+		aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+		aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+
+		aiVector3D* pcUVs = NULL;
+		if (this->bHasUVs)
+		{
+			pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+			pcMesh->mNumUVComponents[0] = 2;
+		}
+
+		iNum = 0;
+		for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace)
+		{
+			pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
+			pcMesh->mFaces[iFace].mNumIndices = 3;
+
+			// fill the vertices (hardcode the loop for performance)
+			unsigned int iSrcFace = aaiFaces[i][iFace];
+			SMD::Face& face = this->asTriangles[iSrcFace];
+
+			*pcVerts++ = face.avVertices[0].pos;
+			*pcVerts++ = face.avVertices[1].pos;
+			*pcVerts++ = face.avVertices[2].pos;
+
+			// fill the normals
+			*pcNormals++ = face.avVertices[0].nor;
+			*pcNormals++ = face.avVertices[1].nor;
+			*pcNormals++ = face.avVertices[2].nor;
+
+			// fill the texture coordinates
+			if (pcUVs)
+			{
+				*pcUVs++ = face.avVertices[0].uv;
+				*pcUVs++ = face.avVertices[1].uv;
+				*pcUVs++ = face.avVertices[2].uv;
+			}
+			
+			for (unsigned int iVert = 0; iVert < 3;++iVert)
+			{
+				float fSum = 0.0f;
+				for (unsigned int iBone = 0;iBone < face.avVertices[0].aiBoneLinks.size();++iBone)
+				{
+					TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
+					
+					if (pairval.first >= this->asBones.size())
+					{
+						DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
+							"The bone index will be ignored, the weight will be assigned "
+							"to the vertex' parent node");
+						continue;
+					}
+					aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second));
+
+					fSum += pairval.second;
+				}
+				// if the sum of all vertex weights is not 1.0 we must assign 
+				// the rest to the vertex' parent node. Well, at least the doc says 
+				// we should ...
+				if (fSum <= 0.995f)
+				{
+					aaiBones[face.avVertices[iVert].iParentNode].push_back(
+						TempWeightListEntry(iNum,1.0f-fSum));
+				}
+
+				pcMesh->mFaces[iFace].mIndices[iVert] = iNum++;
+			}
+		}
+
+		// now build all bones of the mesh
+		iNum = 0;
+		for (unsigned int iBone = 0; iBone < this->asBones.size();++iBone)
+		{
+			if (!aaiBones[iBone].empty())++iNum;
+		}
+		pcMesh->mNumBones = iNum;
+		pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
+		iNum = 0;
+		for (unsigned int iBone = 0; iBone < this->asBones.size();++iBone)
+		{
+			if (aaiBones[iBone].empty())continue;
+			aiBone*& bone = pcMesh->mBones[iNum] = new aiBone();
+
+			bone->mNumWeights = (unsigned int)aaiBones[iBone].size();
+			bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+			bone->mOffsetMatrix = this->asBones[iBone].mOffsetMatrix;
+			bone->mName.Set( this->asBones[iBone].mName );
+
+			this->asBones[iBone].bIsUsed = true;
+
+			for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight)
+			{
+				bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first;
+				bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second;
+			}
+			++iNum;
+		}
+
+		delete[] aaiBones;
+	}
+	delete[] aaiFaces;
+}
+// ------------------------------------------------------------------------------------------------
+// add bone child nodes
+void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent)
+{
+	ai_assert(NULL != pcNode && 0 != pcNode->mNumChildren && NULL != pcNode->mChildren);
+
+	// first count ...
+	for (unsigned int i = 0; i < this->asBones.size();++i)
+	{
+		SMD::Bone& bone = this->asBones[i];
+		if (bone.iParent == iParent)++pcNode->mNumChildren;
+	}
+
+	// now allocate the output array
+	pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
+
+	// and fill all subnodes
+	unsigned int qq = 0;
+	for (unsigned int i = 0; i < this->asBones.size();++i)
+	{
+		SMD::Bone& bone = this->asBones[i];
+		if (bone.iParent != iParent)continue;
+
+		aiNode* pc = pcNode->mChildren[qq++] = new aiNode();
+		pc->mName.Set(bone.mName);
+
+		// store the local transformation matrix of the bind pose
+		pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix;
+	}
+}
+// ------------------------------------------------------------------------------------------------
+// create output nodes
+void SMDImporter::CreateOutputNodes()
+{
+	// create one root node that renders all meshes
+	this->pScene->mRootNode = new aiNode();
+	::strcpy(this->pScene->mRootNode->mName.data, "SMD_root");
+	this->pScene->mRootNode->mName.length = 8;
+	this->pScene->mRootNode->mNumMeshes = this->pScene->mNumMeshes;
+	this->pScene->mRootNode->mMeshes = new unsigned int[this->pScene->mNumMeshes];
+	for (unsigned int i = 0; i < this->pScene->mNumMeshes;++i)
+		this->pScene->mRootNode->mMeshes[i] = i;
+
+	// now add all bones as dummy sub nodes to the graph
+	this->AddBoneChildren(this->pScene->mRootNode,(uint32_t)-1);
+}
+// ------------------------------------------------------------------------------------------------
+// create output animations
+void SMDImporter::CreateOutputAnimations()
+{
+	this->pScene->mNumAnimations = 1;
+	this->pScene->mAnimations = new aiAnimation*[1];
+	aiAnimation*& anim = this->pScene->mAnimations[0] = new aiAnimation();
+
+	anim->mDuration = this->dLengthOfAnim;
+	anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
+
+	// this->pScene->mAnimations[0]->mNumBones = 0;
+	for (std::vector<SMD::Bone>::const_iterator
+		i =  this->asBones.begin();
+		i != this->asBones.end();++i)
+	{
+		if ((*i).bIsUsed)++anim->mNumBones;
+	}
+	aiBoneAnim** pp = anim->mBones = new aiBoneAnim*[anim->mNumBones];
+	
+	// now build valid keys
+	unsigned int a = 0;
+	for (std::vector<SMD::Bone>::const_iterator
+		i =  this->asBones.begin();
+		i != this->asBones.end();++i)
+	{
+		if (!(*i).bIsUsed)continue;
+
+		aiBoneAnim* p = pp[a] = new aiBoneAnim();
+
+		// copy the name of the bone
+		p->mBoneName.length = (*i).mName.length();
+		::memcpy(p->mBoneName.data,(*i).mName.c_str(),p->mBoneName.length);
+		p->mBoneName.data[p->mBoneName.length] = '\0';
+
+		p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size();
+		if (p->mNumRotationKeys)
+		{
+			p->mNumPositionKeys = p->mNumRotationKeys;
+			aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys];
+			aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys];
+
+			for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator
+				qq =  (*i).sAnim.asKeys.begin();
+				qq != (*i).sAnim.asKeys.end(); ++qq)
+			{
+				pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime;
+
+				// compute the rotation quaternion from the euler angles
+				pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z );
+				pVecKeys->mValue = (*qq).vPos;
+
+				++pVecKeys; ++pRotKeys;
+			}
+		}
+		++a;
+
+		// there are no scaling keys ...
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void SMDImporter::ComputeAbsoluteBoneTransformations()
+{
+	// for each bone: determine the key with the lowest time value
+	// theoretically the SMD format should have all keyframes
+	// in order. However, I've seen a file where this wasn't true.
+	for (unsigned int i = 0; i < this->asBones.size();++i)
+	{
+		SMD::Bone& bone = this->asBones[i];
+
+		uint32_t iIndex = 0;
+		double dMin = 10e10;
+		for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i)
+		{
+			double d = std::min(bone.sAnim.asKeys[i].dTime,dMin);
+			if (d < dMin)	{
+				dMin = d; iIndex = i;
+			}
+		}
+		bone.sAnim.iFirstTimeKey = iIndex;
+	}
+
+	unsigned int iParent = 0;
+	while (iParent < this->asBones.size())
+	{
+		for (unsigned int iBone = 0; iBone < this->asBones.size();++iBone)
+		{
+			SMD::Bone& bone = this->asBones[iBone];
+	
+			if (iParent == bone.iParent)
+			{
+				SMD::Bone& parentBone = this->asBones[iParent];
+
+			
+				uint32_t iIndex = bone.sAnim.iFirstTimeKey;
+				const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix;
+				aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute;
+
+				// the same for the parent bone ...
+				iIndex = parentBone.sAnim.iFirstTimeKey;
+				const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrix;
+
+				// compute the absolute transformation matrix
+				matOut = mat * mat2;
+			}
+		}
+		++iParent;
+	}
+
+	// store the inverse of the absolute transformation matrix 
+	// of the first key as bone offset matrix
+	for (iParent = 0; iParent < this->asBones.size();++iParent)
+	{
+		SMD::Bone& bone = this->asBones[iParent];
+		aiMatrix4x4& mat = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute;
+		bone.mOffsetMatrix = mat;
+		bone.mOffsetMatrix.Inverse();
+	}
+}
+// ------------------------------------------------------------------------------------------------
+// create output materials
+void SMDImporter::CreateOutputMaterials()
+{
+	this->pScene->mNumMaterials = (unsigned int)this->aszTextures.size();
+	this->pScene->mMaterials = new aiMaterial*[std::min(1u, this->pScene->mNumMaterials)];
+
+	for (unsigned int iMat = 0; iMat < this->pScene->mNumMaterials;++iMat)
+	{
+		MaterialHelper* pcMat = new MaterialHelper();
+		this->pScene->mMaterials[iMat] = pcMat;
+
+		aiString szName;
+#if _MSC_VER >= 1400
+		szName.length = (size_t)::sprintf_s(szName.data,"Texture_%i",iMat);
+#else
+		szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat);
+#endif
+		pcMat->AddProperty(&szName,AI_MATKEY_NAME);
+
+		strcpy(szName.data, this->aszTextures[iMat].c_str() );
+		szName.length = this->aszTextures[iMat].length();
+		pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0));
+	}
+
+	// create a default material if necessary
+	if (0 == this->pScene->mNumMaterials)
+	{
+		this->pScene->mNumMaterials = 1;
+
+		MaterialHelper* pcHelper = new MaterialHelper();
+		this->pScene->mMaterials[0] = pcHelper;
+
+		int iMode = (int)aiShadingMode_Gouraud;
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.7f;
+		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);
+	}
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the file
+void SMDImporter::ParseFile()
+{
+	const char* szCurrent = this->mBuffer;
+
+	// read line per line ...
+	while (true)
+	{
+		if(!SkipSpaces(szCurrent,&szCurrent)) break;
+
+		// "version <n> \n", <n> should be 1 for hl and hl² SMD files
+		if (0 == ASSIMP_strincmp(szCurrent,"version",7) &&
+			IsSpaceOrNewLine(*(szCurrent+7)))
+		{
+			szCurrent += 8;
+			if(!SkipSpaces(szCurrent,&szCurrent)) break;
+			if (1 != strtol10(szCurrent,&szCurrent))
+			{
+				DefaultLogger::get()->warn("SMD.version is not 1. This "
+					"file format is not known. Continuing happily ...");
+			}
+		}
+		// "nodes\n" - Starts the node section
+		else if (0 == ASSIMP_strincmp(szCurrent,"nodes",5) &&
+			IsSpaceOrNewLine(*(szCurrent+5)))
+		{
+			szCurrent += 6;
+			this->ParseNodesSection(szCurrent,&szCurrent);
+		}
+		// "triangles\n" - Starts the triangle section
+		else if (0 == ASSIMP_strincmp(szCurrent,"triangles",9) &&
+			IsSpaceOrNewLine(*(szCurrent+9)))
+		{
+			szCurrent += 10;
+			this->ParseTrianglesSection(szCurrent,&szCurrent);
+		}
+		// "vertexanimation\n" - Starts the vertex animation section
+		else if (0 == ASSIMP_strincmp(szCurrent,"vertexanimation",15) &&
+			IsSpaceOrNewLine(*(szCurrent+15)))
+		{
+			this->bHasUVs = false;
+			szCurrent += 16;
+			this->ParseVASection(szCurrent,&szCurrent);
+		}
+		// "skeleton\n" - Starts the skeleton section
+		else if (0 == ASSIMP_strincmp(szCurrent,"skeleton",8) &&
+			IsSpaceOrNewLine(*(szCurrent+8)))
+		{
+			szCurrent += 9;
+			this->ParseSkeletonSection(szCurrent,&szCurrent);
+		}
+		else SkipLine(szCurrent,&szCurrent);
+	}
+
+	// if there are no triangles, we can't load the model
+	if (this->asTriangles.empty())
+	{
+		throw new ImportErrorException("No triangles have been found in the file");
+	}
+	// check whether all bones have been initialized
+	for (std::vector<SMD::Bone>::const_iterator
+		i =  this->asBones.begin();
+		i != this->asBones.end();++i)
+	{
+		if (!(*i).mName.length())
+		{
+			DefaultLogger::get()->warn("Not all bones have been initialized");
+			break;
+		}
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+unsigned int SMDImporter::GetTextureIndex(const std::string& filename)
+{
+	unsigned int iIndex = 0;
+	for (std::vector<std::string>::const_iterator
+		i =  this->aszTextures.begin();
+		i != this->aszTextures.end();++i,++iIndex)
+	{
+		// case-insensitive ... just for safety
+		if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex;
+	}
+	iIndex = (unsigned int)this->aszTextures.size();
+	this->aszTextures.push_back(filename);
+	return iIndex;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the nodes section of the file
+void SMDImporter::ParseNodesSection(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	while (true)
+	{
+		// "end\n" - Ends the nodes section
+		if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+			IsSpaceOrNewLine(*(szCurrent+3)))
+		{
+			szCurrent += 4;
+			break;
+		}
+		this->ParseNodeInfo(szCurrent,&szCurrent);
+	}
+	*szCurrentOut = szCurrent;
+	SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the triangles section of the file
+void SMDImporter::ParseTrianglesSection(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	// parse a triangle, parse another triangle, parse the next triangle ...
+	// and so on until we reach a token that looks quite similar to "end"
+	while (true)
+	{
+		// "end\n" - Ends the triangles section
+		if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+			IsSpaceOrNewLine(*(szCurrent+3)))
+		{
+			szCurrent += 4;
+			break;
+		}
+		this->ParseTriangle(szCurrent,&szCurrent);
+	}
+	*szCurrentOut = szCurrent;
+	SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the vertex animation section of the file
+void SMDImporter::ParseVASection(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	unsigned int iCurIndex = 0;
+	while (true)
+	{
+		// "end\n" - Ends the "vertexanimation" section
+		if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+			IsSpaceOrNewLine(*(szCurrent+3)))
+		{
+			szCurrent += 4;
+			SkipLine(szCurrent,&szCurrent);
+			break;
+		}
+		// "time <n>\n" 
+		else if (0 == ASSIMP_strincmp(szCurrent,"time",4) &&
+			IsSpaceOrNewLine(*(szCurrent+4)))
+		{
+			szCurrent += 5;
+			// NOTE: The doc says that time values COULD be negative ...
+			int iTime = 0;
+			if(!this->ParseSignedInt(szCurrent,&szCurrent,iTime) || iTime)break;
+			SkipLine(szCurrent,&szCurrent);
+		}
+		else 
+		{
+			this->ParseVertex(szCurrent,&szCurrent,this->asTriangles.back().avVertices[iCurIndex],true);
+			if(3 == ++iCurIndex)
+			{
+				this->asTriangles.push_back(SMD::Face());
+				iCurIndex = 0;
+			}
+		}
+	}
+
+	if (iCurIndex)
+	{
+		// no degenerates, so let this triangle
+		this->aszTextures.pop_back();
+	}
+
+	*szCurrentOut = szCurrent;
+	SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the skeleton section of the file
+void SMDImporter::ParseSkeletonSection(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	int iTime = 0;
+	while (true)
+	{
+		// "end\n" - Ends the skeleton section
+		if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+			IsSpaceOrNewLine(*(szCurrent+3)))
+		{
+			szCurrent += 4;
+			SkipLine(szCurrent,&szCurrent);
+			break;
+		}
+		// "time <n>\n" - Specifies the current animation frame
+		else if (0 == ASSIMP_strincmp(szCurrent,"time",4) &&
+			IsSpaceOrNewLine(*(szCurrent+4)))
+		{
+			szCurrent += 5;
+			// NOTE: The doc says that time values COULD be negative ...
+			if(!this->ParseSignedInt(szCurrent,&szCurrent,iTime))break;
+
+			this->iSmallestFrame = std::min(this->iSmallestFrame,iTime);
+			SkipLine(szCurrent,&szCurrent);
+		}
+		else this->ParseSkeletonElement(szCurrent,&szCurrent,iTime);
+	}
+	*szCurrentOut = szCurrent;	
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a node line
+void SMDImporter::ParseNodeInfo(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	unsigned int iBone  = 0;
+	if(!this->ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index");
+		goto __RETURN; // YEAH!!!
+	}
+	// add our bone to the list
+	if (iBone >= this->asBones.size())this->asBones.resize(iBone+1);
+	SMD::Bone& bone = this->asBones[iBone];
+
+	bool bQuota = true;
+	if ('\"' != *szCurrent)
+	{
+		this->LogWarning("Bone name is expcted to be enclosed in "
+			"double quotation marks. ");
+		bQuota = false;
+	}
+	else ++szCurrent;
+
+	const char* szEnd = szCurrent;
+	while (true)
+	{
+		if (bQuota && '\"' == *szEnd)
+		{
+			iBone = (unsigned int)(szEnd - szCurrent);
+			++szEnd;
+			break;
+		}
+		else if (IsSpaceOrNewLine(*szEnd))
+		{
+			iBone = (unsigned int)(szEnd - szCurrent);
+			break;
+		}
+		else if (!(*szEnd))
+		{
+			this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name");
+			goto __RETURN; // YEAH!!!
+		}
+		++szEnd;
+	}
+	bone.mName = std::string(szCurrent,iBone);
+	// the only negative bone parent index that could occur is -1 AFAIK
+	if(!this->ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1");
+		goto __RETURN; // YEAH!!!
+	}
+
+	// go to the beginning of the next line
+__RETURN:
+	SkipLine(szCurrent,&szCurrent);
+	*szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a skeleton element
+void SMDImporter::ParseSkeletonElement(const char* szCurrent,
+	const char** szCurrentOut,int iTime)
+{
+	aiVector3D vPos;
+	aiVector3D vRot;
+
+	unsigned int iBone  = 0;
+	if(!this->ParseUnsignedInt(szCurrent,&szCurrent,iBone))
+	{
+		DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index");
+		goto __RETURN; // YEAH!!!
+	}
+	if (iBone >= this->asBones.size())
+	{
+		this->LogErrorNoThrow("Bone index in skeleton section is out of range");
+		goto __RETURN; // YEAH!!!
+	}
+	SMD::Bone& bone = this->asBones[iBone];
+
+	bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey());
+	SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back();
+
+	key.dTime = (double)iTime;
+	if(!this->ParseFloat(szCurrent,&szCurrent,vPos.x))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vPos.y))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vPos.z))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vRot.x))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vRot.y))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vRot.z))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z");
+		goto __RETURN; // YEAH!!!
+	}
+	// build the transformation matrix of the key
+	key.matrix.FromEulerAngles(vRot.x,vRot.y,vRot.z);
+	{
+		aiMatrix4x4 mTemp;
+		mTemp.a4 = vPos.x;
+		mTemp.b4 = vPos.y;
+		mTemp.c4 = vPos.z;
+		key.matrix = key.matrix * mTemp;
+	}
+
+	// go to the beginning of the next line
+__RETURN:
+	SkipLine(szCurrent,&szCurrent);
+	*szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a triangle
+void SMDImporter::ParseTriangle(const char* szCurrent,
+	const char** szCurrentOut)
+{
+	this->asTriangles.push_back(SMD::Face());
+	SMD::Face& face = this->asTriangles.back();
+	
+	if(!SkipSpaces(szCurrent,&szCurrent))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle");
+		return;
+	}
+
+	// read the texture file name
+	const char* szEnd = szCurrent;
+	while (!IsSpaceOrNewLine(*szEnd++));
+
+	face.iTexture = this->GetTextureIndex(std::string(szCurrent,
+		(uintptr_t)szEnd-(uintptr_t)szCurrent));
+
+	// load three vertices
+	for (unsigned int iVert = 0; iVert < 3;++iVert)
+	{
+		this->ParseVertex(szCurrent,&szCurrent,
+			face.avVertices[iVert]);
+	}
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a float
+bool SMDImporter::ParseFloat(const char* szCurrent,
+	const char** szCurrentOut, float& out)
+{
+	if(!SkipSpaces(szCurrent,&szCurrent))
+	{
+		return false;
+	}
+	*szCurrentOut = fast_atof_move(szCurrent,out);
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse an unsigned int
+bool SMDImporter::ParseUnsignedInt(const char* szCurrent,
+	const char** szCurrentOut, uint32_t& out)
+{
+	if(!SkipSpaces(szCurrent,&szCurrent))
+	{
+		return false;
+	}
+	out = (uint32_t)strtol10(szCurrent,szCurrentOut);
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a signed int
+bool SMDImporter::ParseSignedInt(const char* szCurrent,
+	const char** szCurrentOut, int32_t& out)
+{
+	if(!SkipSpaces(szCurrent,&szCurrent))
+	{
+		return false;
+	}
+	// handle signs
+	bool bInv = false;
+	if ('-' == *szCurrent)
+	{
+		++szCurrent;
+		bInv = true;
+	}
+	else if ('+' == *szCurrent)++szCurrent;
+
+	// parse the integer
+	out = (int32_t)strtol10(szCurrent,szCurrentOut);
+	if (bInv)out = -out;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a vertex
+void SMDImporter::ParseVertex(const char* szCurrent,
+	const char** szCurrentOut, SMD::Vertex& vertex,
+	bool bVASection /*= false*/)
+{
+	if(!this->ParseSignedInt(szCurrent,&szCurrent,(int32_t&)vertex.iParentNode))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.pos.x))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.pos.y))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.pos.z))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.nor.x))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.nor.y))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.nor.z))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z");
+		goto __RETURN; // YEAH!!!
+	}
+
+	if (bVASection)goto __RETURN;
+
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.uv.x))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x");
+		goto __RETURN; // YEAH!!!
+	}
+	if(!this->ParseFloat(szCurrent,&szCurrent,vertex.uv.y))
+	{
+		this->LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y");
+		goto __RETURN; // YEAH!!!
+	}
+
+	// now read the number of bones affecting this vertex
+	// all elements from now are fully optional, we don't need them
+	unsigned int iSize = 0;
+	if(!this->ParseUnsignedInt(szCurrent,&szCurrent,iSize))goto __RETURN;
+	vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(-1,0.0f));
+
+	for (std::vector<std::pair<unsigned int, float> >::iterator
+		i =  vertex.aiBoneLinks.begin();
+		i != vertex.aiBoneLinks.end();++i)
+	{
+		if(!this->ParseUnsignedInt(szCurrent,&szCurrent,(*i).first))goto __RETURN;
+		if(!this->ParseFloat(szCurrent,&szCurrent,(*i).second))goto __RETURN;
+	}
+
+	// go to the beginning of the next line
+__RETURN:
+	SkipLine(szCurrent,&szCurrent);
+	*szCurrentOut = szCurrent;
+	return;
+}

+ 408 - 0
code/SMDLoader.h

@@ -0,0 +1,408 @@
+/*
+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 Definition of SMD importer class
+//!
+
+#ifndef AI_SMDLOADER_H_INCLUDED
+#define AI_SMDLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "ParsingUtils.h"
+
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiAnim.h"
+#include "../include/aiMaterial.h"
+struct aiNode;
+
+#include <vector>
+
+namespace Assimp	{
+class MaterialHelper;
+
+namespace SMD	{
+
+// ---------------------------------------------------------------------------
+/** Data structure for a vertex in a SMD file
+*/
+struct Vertex
+{
+	Vertex() : iParentNode(0xffffffff)
+	 {}
+
+	//! Vertex position, normal and texture coordinate
+	aiVector3D pos,nor,uv;
+
+	//! Vertex parent node
+	unsigned int iParentNode;
+
+	//! Links to bones: pair.first is the bone index,
+	//! pair.second is the vertex weight.
+	//! WARN: The remaining weight (to reach 1.0f) is assigned
+	//! to the parent node/bone
+	std::vector<std::pair<unsigned int, float> > aiBoneLinks;
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a face in a SMD file
+*/
+struct Face
+{
+	Face() : iTexture(0x0)
+	 {}
+
+	//! Texture index for the face
+	unsigned int iTexture;
+
+	//! The three vertices of the face
+	Vertex avVertices[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a bone in a SMD file
+*/
+struct Bone
+{
+	//! Default constructor
+	Bone() : iParent(0xffffffff), bIsUsed(false)
+	{
+	}
+
+	//! Destructor
+	~Bone()
+	{
+	}
+
+	//! Name of the bone
+	std::string mName;
+
+	//! Parent of the bone
+	uint32_t iParent;
+
+	//! Animation of the bone
+	struct Animation
+	{
+		//! Public default constructor
+		Animation() 
+		{
+			asKeys.reserve(20);
+		}
+
+		//! Data structure for a matrix key
+		struct MatrixKey
+		{
+			//! Matrix at this time
+			aiMatrix4x4 matrix;
+
+			//! Absolute transformation matrix
+			aiMatrix4x4 matrixAbsolute;
+
+			//! Position
+			aiVector3D vPos;
+
+			//! Rotation (euler angles)
+			aiVector3D vRot;
+
+			//! Current time. may be negative, this
+			//! will be fixed later
+			double dTime;
+		};
+
+		//! Index of the key with the smallest time value
+		uint32_t iFirstTimeKey;
+
+		//! Array of matrix keys
+		std::vector<MatrixKey> asKeys;
+
+	} sAnim;
+
+	//! Offset matrix of the bone
+	aiMatrix4x4 mOffsetMatrix;
+
+	//! true if the bone is referenced by at least one mesh
+	bool bIsUsed;
+};
+
+}; //! namespace SMD
+
+// ---------------------------------------------------------------------------
+/** Used to load Half-life 1 and 2 SMD models
+*/
+class SMDImporter : public BaseImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	SMDImporter();
+
+	/** Destructor, private as well */
+	~SMDImporter();
+
+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("*.smd;*.vta");
+	}
+
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. 
+	* See BaseImporter::InternReadFile() for details
+	*/
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+protected:
+
+	// -------------------------------------------------------------------
+	/** Parse the SMD file and create the output scene
+	*/
+	void ParseFile();
+
+	// -------------------------------------------------------------------
+	/** Parse the triangles section of the SMD file
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives a pointer to the heading line of
+	 * the next section (or to EOF)
+	*/
+	void ParseTrianglesSection(const char* szCurrent,
+		const char** szCurrentOut);
+
+	// -------------------------------------------------------------------
+	/** Parse the vertex animation section in VTA files
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives a pointer to the heading line of
+	 * the next section (or to EOF)
+	*/
+	void ParseVASection(const char* szCurrent,
+		const char** szCurrentOut);
+
+	// -------------------------------------------------------------------
+	/** Parse the nodes section of the SMD file
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives a pointer to the heading line of
+	 * the next section (or to EOF)
+	*/
+	void ParseNodesSection(const char* szCurrent,
+		const char** szCurrentOut);
+
+	// -------------------------------------------------------------------
+	/** Parse the skeleton section of the SMD file
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives a pointer to the heading line of
+	 * the next section (or to EOF)
+	*/
+	void ParseSkeletonSection(const char* szCurrent,
+		const char** szCurrentOut);
+
+	// -------------------------------------------------------------------
+	/** Parse a single triangle in the SMD file
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives the output cursor position
+	*/
+	void ParseTriangle(const char* szCurrent,
+		const char** szCurrentOut);
+
+
+	// -------------------------------------------------------------------
+	/** Parse a single vertex in the SMD file
+	 * \param szCurrent Current position in the file. Points to the first
+	 * data line of the section.
+	 * \param szCurrentOut Receives the output cursor position
+	 * \param vertex Vertex to be filled
+	*/
+	void ParseVertex(const char* szCurrent,
+		const char** szCurrentOut, SMD::Vertex& vertex,
+		bool bVASection = false);
+
+	// -------------------------------------------------------------------
+	/** Get  the index of a texture. If the texture was not yet known
+	 *  it will be added to the internal texture list.
+	 * \param filename Name of the texture
+	 * \return Value texture index
+	 */
+	unsigned int GetTextureIndex(const std::string& filename);
+
+	// -------------------------------------------------------------------
+	/** Computes absolute bone transformations
+	 * All output transformations are in worldspace.
+	 */
+	void ComputeAbsoluteBoneTransformations();
+
+
+	// -------------------------------------------------------------------
+	/** Parse a line in the skeleton section
+	 */
+	void ParseSkeletonElement(const char* szCurrent,
+		const char** szCurrentOut,int iTime);
+
+	// -------------------------------------------------------------------
+	/** Parse a line in the nodes section
+	 */
+	void ParseNodeInfo(const char* szCurrent,
+		const char** szCurrentOut);
+
+
+	// -------------------------------------------------------------------
+	/** Parse a floating-point value
+	 */
+	bool ParseFloat(const char* szCurrent,
+		const char** szCurrentOut, float& out);
+
+	// -------------------------------------------------------------------
+	/** Parse an unsigned integer. There may be no sign!
+	 */
+	bool ParseUnsignedInt(const char* szCurrent,
+		const char** szCurrentOut, uint32_t& out);
+
+	// -------------------------------------------------------------------
+	/** Parse a signed integer. Signs (+,-) are handled.
+	 */
+	bool ParseSignedInt(const char* szCurrent,
+		const char** szCurrentOut, int32_t& out);
+
+	// -------------------------------------------------------------------
+	/** Fix invalid time values in the file
+	 */
+	void FixTimeValues();
+
+	// -------------------------------------------------------------------
+	/** Add all children of a bone as subnodes to a node
+	 * \param pcNode Parent node
+	 * \param iParent Parent bone index
+	 */
+	void AddBoneChildren(aiNode* pcNode, uint32_t iParent);
+
+	// -------------------------------------------------------------------
+	/** Build output meshes/materials/nodes/animations
+	 */
+	void CreateOutputMeshes();
+	void CreateOutputNodes();
+	void CreateOutputAnimations();
+	void CreateOutputMaterials();
+
+
+	// -------------------------------------------------------------------
+	/** Print a log message together with the current line number
+	 */
+	void LogErrorNoThrow(const char* msg);
+	void LogWarning(const char* msg);
+
+
+	// -------------------------------------------------------------------
+	inline bool SkipLine( const char* in, const char** out)
+	{
+		::SkipLine(in,out);
+		++iLineNumber;
+		return true;
+	}
+	// -------------------------------------------------------------------
+	inline void SkipSpacesAndLineEnd( const char* in, const char** out)
+	{
+		::SkipSpacesAndLineEnd(in,out);
+		++iLineNumber;
+	}
+
+private:
+
+	/** Buffer to hold the loaded file */
+	const char* mBuffer;
+
+	/** Output scene to be filled
+	*/
+	aiScene* pScene;
+
+	/** Size of the input file in bytes
+	 */
+	unsigned int iFileSize;
+
+	/** Array of textures found in the file
+	 */
+	std::vector<std::string> aszTextures;
+
+	/** Array of triangles found in the file
+	 */
+	std::vector<SMD::Face> asTriangles;
+
+	/** Array of bones found in the file
+	 */
+	std::vector<SMD::Bone> asBones;
+
+	/** Smallest frame index found in the skeleton
+	 */
+	int iSmallestFrame;
+
+	/** Length of the whole animation, in frames
+	 */
+	double dLengthOfAnim;
+
+	/** Do we have texture coordinates?
+	 */
+	bool bHasUVs;
+
+	/** Current line numer
+	 */
+	unsigned int iLineNumber;
+
+};
+}; // end of namespace Assimp
+
+#endif // AI_SMDIMPORTER_H_INC

+ 8 - 4
code/SplitLargeMeshes.h

@@ -54,8 +54,8 @@ namespace Assimp
 class SplitLargeMeshesProcess_Triangle; 
 class SplitLargeMeshesProcess_Triangle; 
 class SplitLargeMeshesProcess_Vertex; 
 class SplitLargeMeshesProcess_Vertex; 
 
 
-// NOTE: If you change these limits, don't forget to also change the
-// corresponding values in the Assimp ports
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
 
 
 // **********************************************************
 // **********************************************************
 // Java: PostProcessStep.java, 
 // Java: PostProcessStep.java, 
@@ -64,10 +64,14 @@ class SplitLargeMeshesProcess_Vertex;
 // **********************************************************
 // **********************************************************
 
 
 // default limit for vertices
 // default limit for vertices
-#define AI_SLM_DEFAULT_MAX_VERTICES		1000000
+#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
+#	define AI_SLM_DEFAULT_MAX_VERTICES		1000000
+#endif
 
 
 // default limit for triangles
 // default limit for triangles
-#define AI_SLM_DEFAULT_MAX_TRIANGLES	1000000
+#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
+#	define AI_SLM_DEFAULT_MAX_TRIANGLES		1000000
+#endif
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Postprocessing filter to split large meshes into submeshes
 /** Postprocessing filter to split large meshes into submeshes

+ 65 - 42
code/TextureTransform.cpp

@@ -55,8 +55,7 @@ namespace Assimp
 void TextureTransform::PreProcessUVTransform(
 void TextureTransform::PreProcessUVTransform(
 	Dot3DS::Texture& rcIn)
 	Dot3DS::Texture& rcIn)
 {
 {
-	std::string s;
-	std::stringstream ss;
+	char szTemp[512];
 	int iField;
 	int iField;
 
 
 	if (rcIn.mOffsetU)
 	if (rcIn.mOffsetU)
@@ -66,10 +65,10 @@ void TextureTransform::PreProcessUVTransform(
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			{
 			{
 				float fNew = rcIn.mOffsetU-(float)iField;
 				float fNew = rcIn.mOffsetU-(float)iField;
-				ss << "[wrap] Found texture coordinate U offset " << rcIn.mOffsetU << ". "
-					"This can be optimized to " << fNew;
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[wrap] Found texture coordinate U offset %f. "
+					"This can be optimized to %f",rcIn.mOffsetU,fNew);
+	
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetU = fNew;
 				rcIn.mOffsetU = fNew;
 			}
 			}
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
@@ -77,18 +76,18 @@ void TextureTransform::PreProcessUVTransform(
 				if (0 != (iField % 2))iField--;
 				if (0 != (iField % 2))iField--;
 				float fNew = rcIn.mOffsetU-(float)iField;
 				float fNew = rcIn.mOffsetU-(float)iField;
 
 
-				ss << "[mirror] Found texture coordinate U offset " << rcIn.mOffsetU << ". "
-					"This can be optimized to " << fNew;
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[mirror] Found texture coordinate U offset %f. "
+					"This can be optimized to %f",rcIn.mOffsetU,fNew);
+
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetU = fNew;
 				rcIn.mOffsetU = fNew;
 			}
 			}
 			else if (aiTextureMapMode_Clamp == rcIn.mMapMode)
 			else if (aiTextureMapMode_Clamp == rcIn.mMapMode)
 			{
 			{
-				ss << "[clamp] Found texture coordinate U offset " << rcIn.mOffsetU << ". "
-					"This can be clamped to 1.0f";
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[clamp] Found texture coordinate U offset %f. "
+					"This can be clamped to 1.0f",rcIn.mOffsetU);
+
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetU = 1.0f;
 				rcIn.mOffsetU = 1.0f;
 			}
 			}
 		}
 		}
@@ -100,10 +99,10 @@ void TextureTransform::PreProcessUVTransform(
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			{
 			{
 				float fNew = rcIn.mOffsetV-(float)iField;
 				float fNew = rcIn.mOffsetV-(float)iField;
-				ss << "[wrap] Found texture coordinate V offset " << rcIn.mOffsetV << ". "
-					"This can be optimized to " << fNew;
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[wrap] Found texture coordinate V offset %f. "
+					"This can be optimized to %f",rcIn.mOffsetV,fNew);
+
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetV = fNew;
 				rcIn.mOffsetV = fNew;
 			}
 			}
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
@@ -111,18 +110,18 @@ void TextureTransform::PreProcessUVTransform(
 				if (0 != (iField % 2))iField--;
 				if (0 != (iField % 2))iField--;
 				float fNew = rcIn.mOffsetV-(float)iField;
 				float fNew = rcIn.mOffsetV-(float)iField;
 
 
-				ss << "[mirror] Found texture coordinate V offset " << rcIn.mOffsetV << ". "
-					"This can be optimized to " << fNew;
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[mirror] Found texture coordinate V offset %f. "
+					"This can be optimized to %f",rcIn.mOffsetV,fNew);
+				
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetV = fNew;
 				rcIn.mOffsetV = fNew;
 			}
 			}
 			else if (aiTextureMapMode_Clamp == rcIn.mMapMode)
 			else if (aiTextureMapMode_Clamp == rcIn.mMapMode)
 			{
 			{
-				ss << "[clamp] Found texture coordinate V offset " << rcIn.mOffsetV << ". "
-					"This can be clamped to 1.0f";
-				ss >> s;
-				DefaultLogger::get()->info(s);
+				sprintf(szTemp,"[clamp] Found texture coordinate U offset %f. "
+					"This can be clamped to 1.0f",rcIn.mOffsetV);
+
+				DefaultLogger::get()->info(szTemp);
 				rcIn.mOffsetV = 1.0f;
 				rcIn.mOffsetV = 1.0f;
 			}
 			}
 		}
 		}
@@ -132,9 +131,11 @@ void TextureTransform::PreProcessUVTransform(
 		if (iField = (int)(rcIn.mRotation / 3.141592654f))
 		if (iField = (int)(rcIn.mRotation / 3.141592654f))
 		{
 		{
 			float fNew = rcIn.mRotation-(float)iField*3.141592654f;
 			float fNew = rcIn.mRotation-(float)iField*3.141592654f;
-			ss << "[wrap] Found texture coordinate rotation " << rcIn.mRotation << ". "
-				"This can be optimized to " << fNew;
-			DefaultLogger::get()->info(s);
+
+			sprintf(szTemp,"[wrap] Found texture coordinate rotation %f. "
+				"This can be optimized to %f",rcIn.mRotation,fNew);
+			DefaultLogger::get()->info(szTemp);
+
 			rcIn.mRotation = fNew;
 			rcIn.mRotation = fNew;
 		}
 		}
 	}
 	}
@@ -300,10 +301,16 @@ void TextureTransform::BakeScaleNOffset(
 	// it is more efficient this way ... 
 	// it is more efficient this way ... 
 
 
 	if (!pcMesh->mTextureCoords[0])return;
 	if (!pcMesh->mTextureCoords[0])return;
-	if (1 == pcSrc->iBakeUVTransform)
+	if (0x1 == pcSrc->iBakeUVTransform)
 	{
 	{
 		char szTemp[512];
 		char szTemp[512];
-		sprintf(szTemp,"Transforming existing UV channel. Source UV: %i" 
+		int iLen;
+#if _MSC_VER >= 1400
+		iLen = ::sprintf_s(szTemp,
+#else
+		iLen = ::sprintf(szTemp,
+#endif
+			"Transforming existing UV channel. Source UV: %i" 
 			" OffsetU: %f" 
 			" OffsetU: %f" 
 			" OffsetV: %f" 
 			" OffsetV: %f" 
 			" ScaleU: %f" 
 			" ScaleU: %f" 
@@ -314,7 +321,9 @@ void TextureTransform::BakeScaleNOffset(
 			pcSrc->pcSingleTexture->mScaleU,
 			pcSrc->pcSingleTexture->mScaleU,
 			pcSrc->pcSingleTexture->mScaleV,
 			pcSrc->pcSingleTexture->mScaleV,
 			pcSrc->pcSingleTexture->mRotation);
 			pcSrc->pcSingleTexture->mRotation);
-		DefaultLogger::get()->info(std::string(szTemp));
+
+		ai_assert(0 < iLen);
+		DefaultLogger::get()->info(std::string(szTemp,iLen));
 
 
 		if (!pcSrc->pcSingleTexture->mRotation)
 		if (!pcSrc->pcSingleTexture->mRotation)
 		{
 		{
@@ -349,7 +358,7 @@ void TextureTransform::BakeScaleNOffset(
 			}
 			}
 		}
 		}
 	}
 	}
-	else if (2 == pcSrc->iBakeUVTransform)
+	else if (0x2 == pcSrc->iBakeUVTransform)
 	{
 	{
 		// first save all texture coordinate sets
 		// first save all texture coordinate sets
 		aiVector3D* apvOriginalSets[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 		aiVector3D* apvOriginalSets[AI_MAX_NUMBER_OF_TEXTURECOORDS];
@@ -367,13 +376,14 @@ void TextureTransform::BakeScaleNOffset(
 		// now we need to find all textures in the material
 		// now we need to find all textures in the material
 		// which require scaling/offset operations
 		// which require scaling/offset operations
 		std::vector<STransformVecInfo> sOps;
 		std::vector<STransformVecInfo> sOps;
-		AddToList(sOps,&pcSrc->sTexDiffuse);
-		AddToList(sOps,&pcSrc->sTexSpecular);
-		AddToList(sOps,&pcSrc->sTexEmissive);
-		AddToList(sOps,&pcSrc->sTexOpacity);
-		AddToList(sOps,&pcSrc->sTexBump);
-		AddToList(sOps,&pcSrc->sTexShininess);
-		AddToList(sOps,&pcSrc->sTexAmbient);
+		sOps.reserve(10);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexDiffuse);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexSpecular);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexEmissive);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexOpacity);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexBump);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexShininess);
+		TextureTransform::AddToList(sOps,&pcSrc->sTexAmbient);
 
 
 		// check the list and find out how many we won't be able
 		// check the list and find out how many we won't be able
 		// to generate.
 		// to generate.
@@ -446,7 +456,13 @@ void TextureTransform::BakeScaleNOffset(
 			pcMesh->mTextureCoords[iNum] = _pvOut;
 			pcMesh->mTextureCoords[iNum] = _pvOut;
 
 
 			char szTemp[512];
 			char szTemp[512];
-			sprintf(szTemp,"Generating additional UV channel. Source UV: %i" 
+			int iLen;
+#if _MSC_VER >= 1400
+			iLen = ::sprintf_s(szTemp,
+#else
+			iLen = ::sprintf(szTemp,
+#endif
+				"Generating additional UV channel. Source UV: %i" 
 				" OffsetU: %f" 
 				" OffsetU: %f" 
 				" OffsetV: %f" 
 				" OffsetV: %f" 
 				" ScaleU: %f" 
 				" ScaleU: %f" 
@@ -457,7 +473,8 @@ void TextureTransform::BakeScaleNOffset(
 				(**i).fScaleU,
 				(**i).fScaleU,
 				(**i).fScaleV,
 				(**i).fScaleV,
 				(**i).fRotation);
 				(**i).fRotation);
-			DefaultLogger::get()->info(std::string(szTemp));
+			ai_assert(0 < iLen);
+			DefaultLogger::get()->info(std::string(szTemp,iLen));
 
 
 			const aiVector3D* pvBase = _pvBase;
 			const aiVector3D* pvBase = _pvBase;
 			aiVector3D* pvOut = _pvOut;
 			aiVector3D* pvOut = _pvOut;
@@ -516,6 +533,12 @@ void TextureTransform::BakeScaleNOffset(
 			if (apvOriginalSets[iNum])delete[] apvOriginalSets[iNum];
 			if (apvOriginalSets[iNum])delete[] apvOriginalSets[iNum];
 		}
 		}
 	}
 	}
+
+	// setup bitflags to indicate which texture coordinate
+	// channels are used (this class works for 2d texture coordinates only)
+
+	unsigned int iIndex = 0;
+	while (pcMesh->HasTextureCoords(iIndex))pcMesh->mNumUVComponents[iIndex++] = 2;
 	return;
 	return;
 }
 }
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 61 - 2
code/ValidateDataStructure.cpp

@@ -342,6 +342,8 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
 void ValidateDSProcess::Validate( const aiMesh* pMesh,
 void ValidateDSProcess::Validate( const aiMesh* pMesh,
 	const aiBone* pBone)
 	const aiBone* pBone)
 {
 {
+	this->Validate(&pBone->mName);
+
 	// check whether all vertices affected by this bone are valid
 	// check whether all vertices affected by this bone are valid
 	for (unsigned int i = 0; i < pBone->mNumWeights;++i)
 	for (unsigned int i = 0; i < pBone->mNumWeights;++i)
 	{
 	{
@@ -359,6 +361,8 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh,
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
 void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
 {
 {
+	this->Validate(&pAnimation->mName);
+
 	// validate all materials
 	// validate all materials
 	if (pAnimation->mNumBones)
 	if (pAnimation->mNumBones)
 	{
 	{
@@ -480,7 +484,7 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
 	// check whether there are material keys that are obviously not legal
 	// check whether there are material keys that are obviously not legal
 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
 	{
 	{
-		aiMaterialProperty* prop = pMaterial->mProperties[i];
+		const aiMaterialProperty* prop = pMaterial->mProperties[i];
 		if (!prop)
 		if (!prop)
 		{
 		{
 			this->ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
 			this->ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
@@ -491,9 +495,39 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
 			this->ReportError("aiMaterial::mProperties[%i].mDataLength or "
 			this->ReportError("aiMaterial::mProperties[%i].mDataLength or "
 				"aiMaterial::mProperties[%i].mData is 0",i,i);
 				"aiMaterial::mProperties[%i].mData is 0",i,i);
 		}
 		}
+		// check all predefined types
+		if (aiPTI_String == prop->mType)
+		{
+			if (prop->mDataLength < sizeof(aiString))
+			{
+				this->ReportError("aiMaterial::mProperties[%i].mDataLength is "
+					"too small to contain a string (%i, needed: %i)",
+					i,prop->mDataLength,sizeof(aiString));
+			}
+			this->Validate((const aiString*)prop->mData);
+		}
+		else if (aiPTI_Float == prop->mType)
+		{
+			if (prop->mDataLength < sizeof(float))
+			{
+				this->ReportError("aiMaterial::mProperties[%i].mDataLength is "
+					"too small to contain a float (%i, needed: %i)",
+					i,prop->mDataLength,sizeof(float));
+			}
+		}
+		else if (aiPTI_Integer == prop->mType)
+		{
+			if (prop->mDataLength < sizeof(int))
+			{
+				this->ReportError("aiMaterial::mProperties[%i].mDataLength is "
+					"too small to contain an integer (%i, needed: %i)",
+					i,prop->mDataLength,sizeof(int));
+			}
+		}
 		// TODO: check whether there is a key with an unknown name ...
 		// TODO: check whether there is a key with an unknown name ...
 	}
 	}
 
 
+	// make some more specific tests 
 	float fTemp;
 	float fTemp;
 	int iShading;
 	int iShading;
 	if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading))
 	if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading))
@@ -518,7 +552,6 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
 		};
 		};
 	}
 	}
 
 
-
 	// check whether there are invalid texture keys
 	// check whether there are invalid texture keys
 	SearchForInvalidTextures(pMaterial,"diffuse");
 	SearchForInvalidTextures(pMaterial,"diffuse");
 	SearchForInvalidTextures(pMaterial,"specular");
 	SearchForInvalidTextures(pMaterial,"specular");
@@ -563,6 +596,8 @@ void ValidateDSProcess::Validate( const aiTexture* pTexture)
 void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 	 const aiBoneAnim* pBoneAnim)
 	 const aiBoneAnim* pBoneAnim)
 {
 {
+	this->Validate(&pBoneAnim->mBoneName);
+
 	// check whether there is a bone with this name ...
 	// check whether there is a bone with this name ...
 	unsigned int i = 0;
 	unsigned int i = 0;
 	for (; i < this->mScene->mNumMeshes;++i)
 	for (; i < this->mScene->mNumMeshes;++i)
@@ -649,6 +684,8 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
 	if (pNode != this->mScene->mRootNode && !pNode->mParent)
 	if (pNode != this->mScene->mRootNode && !pNode->mParent)
 		this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
 		this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
 
 
+	this->Validate(&pNode->mName);
+
 	// validate all meshes
 	// validate all meshes
 	if (pNode->mNumMeshes)
 	if (pNode->mNumMeshes)
 	{
 	{
@@ -687,3 +724,25 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
 		}
 		}
 	}
 	}
 }
 }
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiString* pString)
+{
+	if (pString->length > MAXLEN)
+	{
+		this->ReportError("aiString::length is too large (%i, maximum is %i)",
+			pString->length,MAXLEN);
+	}
+	const char* sz = pString->data;
+	while (true)
+	{
+		if ('\0' == *sz)
+		{
+			if (pString->length != (unsigned int)(sz-pString->data))
+				this->ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+			break;
+		}
+		else if (sz >= &pString->data[MAXLEN])
+			this->ReportError("aiString::data is invalid. There is no terminal character");
+		++sz;
+	}
+}

+ 7 - 0
code/ValidateDataStructure.h

@@ -53,6 +53,7 @@ struct aiBoneAnim;
 struct aiTexture;
 struct aiTexture;
 struct aiMaterial;
 struct aiMaterial;
 struct aiNode;
 struct aiNode;
+struct aiString;
 
 
 namespace Assimp
 namespace Assimp
 {
 {
@@ -158,6 +159,12 @@ protected:
 	 */
 	 */
 	void Validate( const aiNode* pNode);
 	void Validate( const aiNode* pNode);
 
 
+	// -------------------------------------------------------------------
+	/** Validates a string
+	 * @param pString Input string
+	 */
+	void Validate( const aiString* pString);
+
 private:
 private:
 
 
 	aiScene* mScene;
 	aiScene* mScene;

+ 89 - 75
code/XFileImporter.cpp

@@ -579,174 +579,188 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector<XFile::
 
 
 		// texture, if there is one
 		// texture, if there is one
 		if (1 == oldMat.mTextures.size())
 		if (1 == oldMat.mTextures.size())
+		{
+			if (oldMat.mTextures[0].length())
 			{
 			{
-			// if there is only one texture, assume it contains the
-			// diffuse color
-			aiString tex;
-			tex.Set( oldMat.mTextures[0]);
-
-			mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
+				// if there is only one texture assume it contains the diffuse color
+				aiString tex;
+				tex.Set( oldMat.mTextures[0]);
+				mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
 			}
 			}
+		}
 		else
 		else
-			{
+		{
 			// Otherwise ... try to search for typical strings in the
 			// Otherwise ... try to search for typical strings in the
 			// texture's file name like 'bump' or 'diffuse'
 			// texture's file name like 'bump' or 'diffuse'
 			unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
 			unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
 			for( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
 			for( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
-				{
+			{
 				std::string sz = oldMat.mTextures[b];
 				std::string sz = oldMat.mTextures[b];
+				if (!sz.length())continue;
+
 				char key[256];
 				char key[256];
 
 
 				// find the file name
 				// find the file name
 				const size_t iLen = sz.length();
 				const size_t iLen = sz.length();
 				std::string::size_type s = sz.rfind('\\',iLen-1);
 				std::string::size_type s = sz.rfind('\\',iLen-1);
 				if (std::string::npos == s)
 				if (std::string::npos == s)
-					{
+				{
 					s = sz.rfind('/',iLen-1);
 					s = sz.rfind('/',iLen-1);
 					if (std::string::npos == s)s = 0;
 					if (std::string::npos == s)s = 0;
-					}
+				}
 
 
 				// cut off the file extension
 				// cut off the file extension
 				std::string::size_type sExt = sz.rfind('.',iLen-1);
 				std::string::size_type sExt = sz.rfind('.',iLen-1);
 				if (std::string::npos != sExt)
 				if (std::string::npos != sExt)
-					{
+				{
 					sz[sExt] = '\0';
 					sz[sExt] = '\0';
-					}
+				}
 
 
 				// bump map
 				// bump map
 				std::string::size_type s2 = sz.find("bump",s);
 				std::string::size_type s2 = sz.find("bump",s);
 				if (std::string::npos == s2)
 				if (std::string::npos == s2)
+				{
+					if (std::string::npos == (s2 = sz.find("BUMP",s)))
 					{
 					{
-					s2 = sz.find("BUMP",s);
-					if (std::string::npos == s2)
+						if (std::string::npos == (s2 = sz.find("Bump",s)))
 						{
 						{
-						s2 = sz.find("Bump",s);
-						if (std::string::npos == s2)
+							if (std::string::npos == (s2 = sz.find("height",s)))
 							{
 							{
-							s2 = sz.find("height",s);
-							if (std::string::npos == s2)
+								if (std::string::npos == (s2 = sz.find("HEIGHT",s)))
 								{
 								{
-								s2 = sz.find("HEIGHT",s);
-								if (std::string::npos == s2)
-									{
 									s2 = sz.find("Height",s);
 									s2 = sz.find("Height",s);
-									}
 								}
 								}
 							}
 							}
 						}
 						}
 					}
 					}
+				}
 				if (std::string::npos != s2)
 				if (std::string::npos != s2)
-					{
-					sprintf(key,AI_MATKEY_TEXTURE_HEIGHT_ "[%i]",iHM++);
-					}
+				{
+#if _MSC_VER >= 1400
+					::sprintf_s(key,AI_MATKEY_TEXTURE_HEIGHT_ "[%i]",iHM++);
+#else
+					::sprintf(key,AI_MATKEY_TEXTURE_HEIGHT_ "[%i]",iHM++);
+#endif
+				}
 				else
 				else
-					{
+				{
 					// Normal map
 					// Normal map
 					std::string::size_type s2 = sz.find("normal",s);
 					std::string::size_type s2 = sz.find("normal",s);
 					if (std::string::npos == s2)
 					if (std::string::npos == s2)
+					{
+						if (std::string::npos == (s2 = sz.find("NORMAL",s)))
 						{
 						{
-						s2 = sz.find("NORMAL",s);
-						if (std::string::npos == s2)
-							{
-							s2 = sz.find("nm",s); // not really unique
-							if (std::string::npos == s2)
+							if (std::string::npos == (s2 = sz.find("nm",s)))
+							{ 
+								if (std::string::npos == (s2 = sz.find("Normal",s)))
 								{
 								{
-								s2 = sz.find("Normal",s); 
-								if (std::string::npos == s2)
-									{
 									s2 = sz.find("NM",s); 
 									s2 = sz.find("NM",s); 
-									}
 								}
 								}
 							}
 							}
 						}
 						}
+					}
 					if (std::string::npos != s2)
 					if (std::string::npos != s2)
-						{
-						sprintf(key,AI_MATKEY_TEXTURE_NORMALS_ "[%i]",iNM++);
-						}
+					{
+#if _MSC_VER >= 1400
+						::sprintf_s(key,AI_MATKEY_TEXTURE_NORMALS_ "[%i]",iNM++);
+#else
+						::sprintf(key,AI_MATKEY_TEXTURE_NORMALS_ "[%i]",iNM++);
+#endif
+					}
 					else
 					else
-						{
+					{
 
 
 						// specular color texture (not unique, too. Could
 						// specular color texture (not unique, too. Could
 						// also be the material's shininess)
 						// also be the material's shininess)
 						std::string::size_type s2 = sz.find("spec",s);
 						std::string::size_type s2 = sz.find("spec",s);
 						if (std::string::npos == s2)
 						if (std::string::npos == s2)
+						{
+							if (std::string::npos == (s2 = sz.find("Spec",s)))
 							{
 							{
-							s2 = sz.find("Spec",s);
-							if (std::string::npos == s2)
+								if (std::string::npos == (sz.find("SPEC",s)))
 								{
 								{
-								s2 = sz.find("SPEC",s);
-								if (std::string::npos == s2)
+									if (std::string::npos == (s2 = sz.find("Glanz",s)))
 									{
 									{
-									s2 = sz.find("Glanz",s);
-									if (std::string::npos == s2)
-										{
 										s2 = sz.find("glanz",s);
 										s2 = sz.find("glanz",s);
-										}
 									}
 									}
 								}
 								}
 							}
 							}
+						}
 						if (std::string::npos != s2)
 						if (std::string::npos != s2)
-							{
-							sprintf(key,AI_MATKEY_TEXTURE_SPECULAR_ "[%i]",iSM++);
-							}
+						{
+#if _MSC_VER >= 1400
+							::sprintf_s(key,AI_MATKEY_TEXTURE_SPECULAR_ "[%i]",iSM++);
+#else
+							::sprintf(key,AI_MATKEY_TEXTURE_SPECULAR_ "[%i]",iSM++);
+#endif
+						}
 						else
 						else
-							{
+						{
 							// ambient color texture
 							// ambient color texture
 							std::string::size_type s2 = sz.find("ambi",s);
 							std::string::size_type s2 = sz.find("ambi",s);
 							if (std::string::npos == s2)
 							if (std::string::npos == s2)
+							{
+								if (std::string::npos == (s2 = sz.find("AMBI",s)))
 								{
 								{
-								s2 = sz.find("AMBI",s);
-								if (std::string::npos == s2)
+									if (std::string::npos == (s2 = sz.find("env",s)))
 									{
 									{
-									s2 = sz.find("umgebungsfarbe",s);
-									if (std::string::npos == s2)
-										{
 										s2 = sz.find("Ambi",s);
 										s2 = sz.find("Ambi",s);
-										}
 									}
 									}
 								}
 								}
+							}
 							if (std::string::npos != s2)
 							if (std::string::npos != s2)
-								{
-								sprintf(key,AI_MATKEY_TEXTURE_AMBIENT_ "[%i]",iAM++);
-								}
+							{
+#if _MSC_VER >= 1400
+								::sprintf_s(key,AI_MATKEY_TEXTURE_AMBIENT_ "[%i]",iAM++);
+#else
+								::sprintf(key,AI_MATKEY_TEXTURE_AMBIENT_ "[%i]",iAM++);
+#endif
+							}
 							else
 							else
-								{
+							{
 								// emissive color texture
 								// emissive color texture
 								std::string::size_type s2 = sz.find("emissive",s);
 								std::string::size_type s2 = sz.find("emissive",s);
 								if (std::string::npos == s2)
 								if (std::string::npos == s2)
-									{
+								{
 									s2 = sz.find("EMISSIVE",s);
 									s2 = sz.find("EMISSIVE",s);
 									if (std::string::npos == s2)
 									if (std::string::npos == s2)
-										{
+									{
 										// self illumination
 										// self illumination
-										s2 = sz.find("self",s);
-										if (std::string::npos == s2)
-											{
+										if (std::string::npos == (s2 = sz.find("self",s)))
+										{
 											s2 = sz.find("Emissive",s);
 											s2 = sz.find("Emissive",s);
-											}
 										}
 										}
 									}
 									}
+								}
 								if (std::string::npos != s2)
 								if (std::string::npos != s2)
-									{
-									sprintf(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++);
-									}
+								{
+#if _MSC_VER >= 1400
+									::sprintf_s(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++);
+#else
+									::sprintf(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++);
+#endif
+								}
 								else
 								else
-									{
+								{
 									// assume it is a diffuse texture
 									// assume it is a diffuse texture
-									sprintf(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++);
-									}
+#if _MSC_VER >= 1400
+									::sprintf_s(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++);
+#else
+									::sprintf(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++);
+#endif
 								}
 								}
 							}
 							}
 						}
 						}
 					}
 					}
+				}
 
 
 				aiString tex;
 				aiString tex;
 				tex.Set( oldMat.mTextures[b] );
 				tex.Set( oldMat.mTextures[b] );
 
 
 				mat->AddProperty( &tex, key);
 				mat->AddProperty( &tex, key);
-				}
-
 			}
 			}
+
+		}
 		pScene->mMaterials[pScene->mNumMaterials] = mat;
 		pScene->mMaterials[pScene->mNumMaterials] = mat;
 		mImportedMats[oldMat.mName] = pScene->mNumMaterials;
 		mImportedMats[oldMat.mName] = pScene->mNumMaterials;
 		pScene->mNumMaterials++;
 		pScene->mNumMaterials++;

+ 15 - 8
code/XFileParser.cpp

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "XFileHelper.h"
 #include "XFileHelper.h"
 #include "BaseImporter.h"
 #include "BaseImporter.h"
 #include "fast_atof.h"
 #include "fast_atof.h"
+#include "../include/DefaultLogger.h"
 
 
 #include <boost/format.hpp>
 #include <boost/format.hpp>
 
 
@@ -150,11 +151,11 @@ void XFileParser::ParseFile()
 		if( objectName == "}")
 		if( objectName == "}")
 		{
 		{
 			// whatever?
 			// whatever?
-			// os::Printer::log("} found in dataObject", ELL_WARNING);
+			DefaultLogger::get()->warn("} found in dataObject");
 		} else
 		} else
 		{
 		{
 			// unknown format
 			// unknown format
-			//os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in animation of .x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -248,7 +249,7 @@ void XFileParser::ParseDataObjectFrame( Node* pParent)
 			ParseDataObjectMesh( mesh);
 			ParseDataObjectMesh( mesh);
 		} else
 		} else
 		{
 		{
-			// os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in frame in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -338,7 +339,7 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
 			ParseDataObjectSkinWeights( pMesh);
 			ParseDataObjectSkinWeights( pMesh);
 		else
 		else
 		{
 		{
-			//os::Printer::log("Unknown data object in mesh in x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in mesh in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -533,7 +534,7 @@ void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
 			// ignore
 			// ignore
 		} else
 		} else
 		{
 		{
-			// os::Printer::log("Unknown data object in material list in x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in material list in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -571,7 +572,7 @@ void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
 			pMaterial->mTextures.push_back( texname);
 			pMaterial->mTextures.push_back( texname);
 		} else
 		} else
 		{
 		{
-			// os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in material in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -608,7 +609,7 @@ void XFileParser::ParseDataObjectAnimationSet()
 			ParseDataObjectAnimation( anim);
 			ParseDataObjectAnimation( anim);
 		else
 		else
 		{
 		{
-			// os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in animation set in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -644,7 +645,7 @@ void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
 			CheckForClosingBrace();
 			CheckForClosingBrace();
 		} else
 		} else
 		{
 		{
-			//os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING);
+			DefaultLogger::get()->warn("Unknown data object in animation in x file");
 			ParseUnknownDataObject();
 			ParseUnknownDataObject();
 		}
 		}
 	}
 	}
@@ -748,6 +749,12 @@ void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
 	readHeadOfDataObject();
 	readHeadOfDataObject();
 	GetNextTokenAsString( pName);
 	GetNextTokenAsString( pName);
 	CheckForClosingBrace();
 	CheckForClosingBrace();
+
+	// FIX: some files (e.g. AnimationTest.x) have "" as texture file name
+	if (!pName.length())
+	{
+		DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture.");
+	}
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 4 - 5
code/extra/MakeVerboseFormat.cpp

@@ -49,17 +49,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-
+// ------------------------------------------------------------------------------------------------
 MakeVerboseFormatProcess::MakeVerboseFormatProcess()
 MakeVerboseFormatProcess::MakeVerboseFormatProcess()
 {
 {
 	// nothing to do here
 	// nothing to do here
 }
 }
-
+// ------------------------------------------------------------------------------------------------
 MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
 MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
 {
 {
 	// nothing to do here
 	// nothing to do here
 }
 }
-// -------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 // Executes the post processing step on the given imported data.
 void MakeVerboseFormatProcess::Execute( aiScene* pScene)
 void MakeVerboseFormatProcess::Execute( aiScene* pScene)
 {
 {
@@ -76,8 +76,7 @@ void MakeVerboseFormatProcess::Execute( aiScene* pScene)
 	else DefaultLogger::get()->debug("MakeVerboseFormatProcess. There was nothing to do.");
 	else DefaultLogger::get()->debug("MakeVerboseFormatProcess. There was nothing to do.");
 
 
 }
 }
-
-// -------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 // Executes the post processing step on the given imported data.
 bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
 bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
 {
 {

+ 42 - 0
code/qnan.h

@@ -0,0 +1,42 @@
+
+
+#if (!defined AI_QNAN_H_INCLUDED)
+#define AI_QNAN_H_INCLUDED
+
+#if (!defined ASSIMP_BUILD_CPP_09)
+#	include <boost/static_assert.hpp>
+#endif
+
+inline bool is_qnan(const float in)
+{
+	// _isnan() takes a double as argument and would
+	// require a cast. Therefore we must do it on our own ...
+	// Another method would be to check whether in != in.
+	// This should also wor since nan compares to inequal, 
+	// even when compared with itself. However, this could
+	// case problems with other special floats like snan or inf
+	union _tagFPUNION
+	{
+		float f;
+		int32_t i;
+	} FPUNION1,FPUNION2;
+
+	// use a compile-time asertion if possible
+#if (defined ASSIMP_BUILD_CPP_09)
+	static_assert(sizeof(float)==sizeof(int32_t),
+		"A float seems not to be 4 bytes on this platform");
+#else
+	BOOST_STATIC_ASSERT(sizeof(float)==sizeof(int32_t));
+#endif
+
+	FPUNION1.f = in;
+	FPUNION2.f = std::numeric_limits<float>::quiet_NaN();
+	return FPUNION1.i == FPUNION2.i;
+}
+
+inline bool is_not_qnan(const float in)
+{
+	return !is_qnan(in);
+}
+
+#endif // !! AI_QNAN_H_INCLUDED

+ 34 - 16
doc/dox.h

@@ -528,9 +528,9 @@ void ConvertMaterial( aiMaterial* matIn, D3DMATERIAL9* matOut )
 
 
 Textures:
 Textures:
 
 
-Textures can have various types and purposes. Sometimes ASSIMP is not able to
-determine the exact purpose of a texture. Normally it will assume diffuse as default
-purpose. Possible purposes for a texture:
+Textures can have various types and intended purposes. Sometimes ASSIMP is not able to
+determine the exact designated use of a texture. Normally it will assume a texture to be
+a diffuse color map by default. Texture types:
 
 
 <b>1. Diffuse textures.</b> Diffuse textures are combined with the result of the diffuse lighting term.
 <b>1. Diffuse textures.</b> Diffuse textures are combined with the result of the diffuse lighting term.
 <br>
 <br>
@@ -549,10 +549,10 @@ normally grayscale images, black stands for fully transparent, white for fully o
 <b>6. Height maps.</b> Height maps specify the relative height of a point on a triangle on a
 <b>6. Height maps.</b> Height maps specify the relative height of a point on a triangle on a
 per-texel base. Normally height maps (sometimes called "Bump maps") are converted to normal
 per-texel base. Normally height maps (sometimes called "Bump maps") are converted to normal
 maps before rendering. Height maps are normally grayscale textures. Height maps could also
 maps before rendering. Height maps are normally grayscale textures. Height maps could also
-be used as displacement maps on a highly tesselated surface.
+be used as displacement maps on highly tesselated surfaces.
 <br>
 <br>
 <b>7. Normal maps.</b> Normal maps contain normal vectors for a single texel, in tangent space.
 <b>7. Normal maps.</b> Normal maps contain normal vectors for a single texel, in tangent space.
-They are not bound to an object. However, all lighting omputations must be done in tangent space. 
+They are not bound to an object. However, all lighting computations must be done in tangent space. 
 There are many resources on Normal Mapping on the internet.
 There are many resources on Normal Mapping on the internet.
 <br>
 <br>
 <b>8. Shininess maps</b> Shininess maps (sometimes called "Gloss" or "SpecularMap") specify
 <b>8. Shininess maps</b> Shininess maps (sometimes called "Gloss" or "SpecularMap") specify
@@ -560,7 +560,7 @@ the shininess of a texel mapped on a surface. They are normally used together wi
 to make flat surfaces look as if they were real 3d objects.
 to make flat surfaces look as if they were real 3d objects.
 <br>
 <br>
 
 
-Textures are generally defined by a set of parameters, including
+Textures are generally defined by a set of parameters including
 <br>
 <br>
 <b>1. The path to the texture.</b>  This property is always set. If it is not set, a texture 
 <b>1. The path to the texture.</b>  This property is always set. If it is not set, a texture 
 is not existing. This can either be a valid path (beware, sometimes
 is not existing. This can either be a valid path (beware, sometimes
@@ -585,26 +585,31 @@ else // your loading code to load from a path ...
 <b>2. An UV coordinate index.</b> This is an index into the UV coordinate set list of the
 <b>2. An UV coordinate index.</b> This is an index into the UV coordinate set list of the
 corresponding mesh. Note: Some formats don't define this, so beware, it could be that
 corresponding mesh. Note: Some formats don't define this, so beware, it could be that
 a second diffuse texture in a mesh was originally intended to use a second UV channel although
 a second diffuse texture in a mesh was originally intended to use a second UV channel although
-ASSIMP states it uses the first one. UV coordinate source indices are defined by the
+ASSIMP says it uses the first one. UV coordinate source indices are defined by the
 <i>AI_MATKEY_UVWSRC_<textype>(<texindex>)</i> material property. Assume 0 as default value if
 <i>AI_MATKEY_UVWSRC_<textype>(<texindex>)</i> material property. Assume 0 as default value if
 this property is not set.
 this property is not set.
 <br>
 <br>
 <b>3. A blend factor.</b> This is used if multiple textures are assigned to a slot, e.g. two
 <b>3. A blend factor.</b> This is used if multiple textures are assigned to a slot, e.g. two
 or more textures on the diffuse channel. A texture's color value is multiplied with its
 or more textures on the diffuse channel. A texture's color value is multiplied with its
-blend factor before it is combined with the previous color value (from the last texture) using
-a specific blend operation (see 4.). Blend factor are defined by the
+blend factor before it is combined with the previous color value (from the last texture or the 
+diffuse/specular/ambient/emissive base color) using
+a blend operation (see 4.). Blend factor are defined by the
 <i>AI_MATKEY_TEXBLEND_<textype>(<texindex>)</i> material property. Assume 1.0f as default value 
 <i>AI_MATKEY_TEXBLEND_<textype>(<texindex>)</i> material property. Assume 1.0f as default value 
 if this property is not set.
 if this property is not set.
 <br>
 <br>
 <b>4. A blend operation.</b> This is used if multiple textures are assigned to a slot, e.g. two
 <b>4. A blend operation.</b> This is used if multiple textures are assigned to a slot, e.g. two
 or more textures on the diffuse channel. After a texture's color value has been multiplied
 or more textures on the diffuse channel. After a texture's color value has been multiplied
-with its blend factor, the blend operation is used to combine it with the previous color value.
+with its blend factor, the blend operation is used to combine it with the previous color value 
+(from the last texture or the diffuse/specular/ambient/emissive base color).
 Blend operations are stored as integer property, however their type is aiTextureOp.
 Blend operations are stored as integer property, however their type is aiTextureOp.
 Blend factor are defined by the <i>AI_TEXOP_BLEND_<textype>(<texindex>)</i> material property. Assume
 Blend factor are defined by the <i>AI_TEXOP_BLEND_<textype>(<texindex>)</i> material property. Assume
-aiTextureOp_Multiply as default value if this property is not set. The blend operation for
-the first texture in a texture slot (e.g. diffuse 0) specifies how the diffuse base color/
-vertex color have to be combined with the texture color value.
+aiTextureOp_Multiply as default value if this property is not set.
 <br>
 <br>
+<b>5. Mapping modes for all axes </b> The mapping mode for an axis specifies how the rendering
+system should deal with UV coordinates beyond the 0-1 range. Mapping modes are
+defined by the <i>AI_MATKEY_MAPPINGMODE_<axis>_<textype>(<texindex>)</i> material property.
+<axis> is either U,V or W. The data type is int, however the real type is aiTextureMapMode.
+The default value is aiTextureMapMode_Wrap.
 
 
 You can use the aiGetMaterialTexture() function to read all texture parameters at once (maybe
 You can use the aiGetMaterialTexture() function to read all texture parameters at once (maybe
 if you're too lazy to read 4 or 5 values manually if there's a smart helper function 
 if you're too lazy to read 4 or 5 values manually if there's a smart helper function 
@@ -612,13 +617,15 @@ doing all the work for you ...).
 
 
 @code
 @code
 if (AI_SUCCESS != aiGetMaterialTexture(
 if (AI_SUCCESS != aiGetMaterialTexture(
-   pcMat,               // Material object
-   0,                   // first texture in the diffuse slot
-   AI_TEXTYPE_DIFFUSE,  // purpose of texture is diffuse
+   pcMat,               // aiMaterial structure
+   0,                   // we want the first diffuse texture
+   AI_TEXTYPE_DIFFUSE,  // we want the first diffuse texture
    &path,               // receives the path of the texture
    &path,               // receives the path of the texture
    &uv,                 // receives the UV index of the texture
    &uv,                 // receives the UV index of the texture
    &blend,              // receives the blend factor of the texture
    &blend,              // receives the blend factor of the texture
    &op,                 // receives the blend operation of the texture
    &op,                 // receives the blend operation of the texture
+   &mmodes,				// receives an array of three aiMappingMode's, each specifying
+	                    // the mapping mode for a particular axis. Order: UV(W)
    // (you may also specify 0 for a parameter if you don't need it)
    // (you may also specify 0 for a parameter if you don't need it)
    )) 
    )) 
 {
 {
@@ -626,6 +633,17 @@ if (AI_SUCCESS != aiGetMaterialTexture(
 }
 }
 @endcode
 @endcode
 
 
+<br>
+As you can see, there's much undefined and subject to speculations. When implementing
+ASSIMP's material system the most important point was to keep it as flexible as possible.
+The first step you should do when you implement ASSIMP materials into your application is
+to make a list of all material properties your rendering engine supports, too. Then I suggest
+you to take a look at the remaining material properties: many of them can be simplified and replaced
+with other properties, e.g. a diffuse texture blend factor can often be premultiplied 
+with the diffuse base color! At last a few properties you do not support will remain. Forget them.
+Most models won't look worse if only small details of its material cannot be rendered as it was intended
+by the artist.
+
 @section bones Bones
 @section bones Bones
 
 
 A mesh may have a set of bones in the form of aiBone structures.. Bones are a means to deform a mesh 
 A mesh may have a set of bones in the form of aiBone structures.. Bones are a means to deform a mesh 

+ 1 - 1
include/IOStream.h

@@ -27,7 +27,7 @@ namespace Assimp
 * implementation for IOSystem that creates instances of your custom IO class.
 * implementation for IOSystem that creates instances of your custom IO class.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-class IOStream 
+class ASSIMP_API IOStream 
 {
 {
 protected:
 protected:
 	/** Constructor protected, use IOSystem::Open() to create an instance. */
 	/** Constructor protected, use IOSystem::Open() to create an instance. */

+ 3 - 1
include/IOSystem.h

@@ -11,6 +11,8 @@
 
 
 #include <string>
 #include <string>
 
 
+#include "aiDefines.h"
+
 namespace Assimp
 namespace Assimp
 {
 {
 
 
@@ -23,7 +25,7 @@ class IOStream;
 * to the importer library. If you implement this interface, you also want to
 * to the importer library. If you implement this interface, you also want to
 * supply a custom implementation for IOStream.
 * supply a custom implementation for IOStream.
 */
 */
-class IOSystem
+class ASSIMP_API IOSystem
 {
 {
 public:
 public:
 	/** Constructor. Create an instance of your derived class and assign it to 
 	/** Constructor. Create an instance of your derived class and assign it to 

+ 1 - 1
include/LogStream.h

@@ -9,7 +9,7 @@ namespace Assimp
 /**	@class	LogStream
 /**	@class	LogStream
 	 *	@brief	Abstract interface for log stream implementations.
 	 *	@brief	Abstract interface for log stream implementations.
  */
  */
-class LogStream
+class ASSIMP_API LogStream
 {
 {
 protected:
 protected:
 	/**	@brief	Default constructor	*/
 	/**	@brief	Default constructor	*/

+ 42 - 1
include/Logger.h

@@ -1,7 +1,48 @@
+/*
+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_LOGGER_H_INC
 #ifndef AI_LOGGER_H_INC
 #define AI_LOGGER_H_INC
 #define AI_LOGGER_H_INC
 
 
 #include <string>
 #include <string>
+#include "aiDefines.h"
 
 
 namespace Assimp
 namespace Assimp
 {
 {
@@ -12,7 +53,7 @@ class LogStream;
 /**	@class	Logger
 /**	@class	Logger
  *	@brief	Abstract interface for logger implementations.
  *	@brief	Abstract interface for logger implementations.
  */
  */
-class Logger
+class ASSIMP_API Logger
 {
 {
 public:
 public:
 	/**	@enum	LogSeverity
 	/**	@enum	LogSeverity

+ 97 - 0
include/aiDefines.h

@@ -0,0 +1,97 @@
+/*
+---------------------------------------------------------------------------
+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_DEFINES_H_INC
+#define AI_DEFINES_H_INC
+
+// compiler specific includes and definitions
+#if (defined _MSC_VER)
+
+	// include stdint.h from the C98 standard
+#	include "Compiler/VisualStudio/stdint.h"
+
+#	undef ASSIMP_API
+
+	// ************************************************************
+	// Define ASSIMP_BUILD_DLL_EXPORT to build a DLL of the library
+	// ************************************************************
+#	if (defined ASSIMP_BUILD_DLL_EXPORT)
+#		define ASSIMP_API __declspec(dllexport)
+
+	// ************************************************************
+	// Define ASSIMP_DLL before including Assimp to use ASSIMP in
+	// an external DLL (otherwise a static library is used)
+	// ************************************************************
+#	elif (defined ASSIMP_DLL)
+#		define ASSIMP_API __declspec(dllimport)
+#	else
+#		define ASSIMP_API 
+#	endif
+
+#endif // (defined _MSC_VER)
+
+#ifdef __cplusplus
+#	define C_STRUCT
+#else
+	// ************************************************************
+	// To build the documentation, make sure ASSIMP_DOXYGEN_BUILD
+	// is defined by Doxygen's preprocessor. The corresponding
+	// entries in the DoxyFile look like this:
+#if 0
+	ENABLE_PREPROCESSING   = YES
+	MACRO_EXPANSION        = YES
+	EXPAND_ONLY_PREDEF     = YES
+	SEARCH_INCLUDES        = YES
+	INCLUDE_PATH           = 
+	INCLUDE_FILE_PATTERNS  = 
+	PREDEFINED             = ASSIMP_DOXYGEN_BUILD=1
+	EXPAND_AS_DEFINED      = C_STRUCT
+	SKIP_FUNCTION_MACROS   = YES
+#endif
+	// ************************************************************
+#	if (defined ASSIMP_DOXYGEN_BUILD)
+#		define C_STRUCT
+#	else
+#		define C_STRUCT struct
+#	endif
+#endif
+
+#endif // !! AI_DEFINES_H_INC

+ 104 - 26
include/aiMaterial.h

@@ -52,6 +52,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+// Default material name
+#define AI_DEFAULT_MATERIAL_NAME "aiDefaultMat"
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Defines type identifiers for use within the material system.
 /** Defines type identifiers for use within the material system.
 *
 *
@@ -235,6 +238,18 @@ struct aiMaterialProperty
     char* mData;
     char* mData;
 };
 };
 
 
+#ifdef __cplusplus
+} // need to end extern C block to allow template member functions
+#endif
+
+#define AI_TEXTYPE_OPACITY		0x0
+#define AI_TEXTYPE_SPECULAR		0x1
+#define AI_TEXTYPE_AMBIENT		0x2
+#define AI_TEXTYPE_EMISSIVE		0x3
+#define AI_TEXTYPE_HEIGHT		0x4
+#define AI_TEXTYPE_NORMALS		0x5
+#define AI_TEXTYPE_SHININESS	0x6
+#define AI_TEXTYPE_DIFFUSE		0x7
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Data structure for a material
 /** Data structure for a material
@@ -245,13 +260,67 @@ struct aiMaterialProperty
 *  enough for nearly all purposes. 
 *  enough for nearly all purposes. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-struct aiMaterial
+struct ASSIMP_API aiMaterial
 {
 {
 #ifdef __cplusplus
 #ifdef __cplusplus
 protected:
 protected:
     aiMaterial() {}
     aiMaterial() {}
 public:
 public:
-#endif // __cplusplus
+
+	// -------------------------------------------------------------------
+    /** Retrieve an array of Type values with a specific key 
+     *  from the material
+     *
+     * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+     * @param pOut Pointer to a buffer to receive the result. 
+     * @param pMax Specifies the size of the given buffer, in Type's.
+     * Receives the number of values (not bytes!) read. 
+     * NULL is a valid value for this parameter.
+     */
+    template <typename Type>
+    inline aiReturn Get(const char* pKey,Type* pOut,
+        unsigned int* pMax);
+
+    // -------------------------------------------------------------------
+    /** Retrieve a Type value with a specific key 
+     *  from the material
+	 *
+	 * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+	 * @param pOut Reference to receive the output value
+	 */
+	template <typename Type>
+	inline aiReturn Get(const char* pKey,Type& pOut);
+
+	// -------------------------------------------------------------------
+	/** Helper function to get a texture from a material
+	*
+	*  This function is provided just for convinience. 
+	*  @param iIndex Index of the texture to retrieve. If the index is too 
+	*		large the function fails.
+	*  @param iTexType One of the AI_TEXTYPE constants. Specifies the type of
+	*		the texture to retrieve (e.g. diffuse, specular, height map ...)
+	*  @param szPath Receives the output path
+	*		NULL is no allowed as value
+	*  @param piUVIndex Receives the UV index of the texture. 
+	*		NULL is allowed as value.
+	*  @param pfBlendFactor Receives the blend factor for the texture
+	*		NULL is allowed as value.
+	*  @param peTextureOp Receives the texture operation to perform between
+	*		this texture and the previous texture. NULL is allowed as value.
+	*  @param peMapMode Receives the mapping modes to be used for the texture.
+	*      The parameter may be NULL but if it is a valid pointer it MUST
+	*      point to an array of 3 aiTextureMapMode variables (one for each
+	*      axis: UVW order (=XYZ)). 
+	*/
+	// -------------------------------------------------------------------
+	inline aiReturn GetTexture(unsigned int iIndex,
+		unsigned int iTexType,
+		C_STRUCT aiString* szPath,
+		unsigned int* piUVIndex		= NULL,
+		float* pfBlendFactor		= NULL,
+		aiTextureOp* peTextureOp	= NULL,
+		aiTextureMapMode* peMapMode = NULL); 
+#endif
 
 
     /** List of all material properties loaded.
     /** List of all material properties loaded.
     */
     */
@@ -263,6 +332,11 @@ public:
     unsigned int mNumAllocated;
     unsigned int mNumAllocated;
 };
 };
 
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @def AI_BUILD_KEY
 /** @def AI_BUILD_KEY
  * Builds a material texture key with a dynamic index.
  * Builds a material texture key with a dynamic index.
@@ -300,13 +374,27 @@ public:
  * @param out Array of chars to receive the output value. It must be 
  * @param out Array of chars to receive the output value. It must be 
  *  sufficiently large. This will be checked via a static assertion for
  *  sufficiently large. This will be checked via a static assertion for
  *  C++0x. For MSVC8 and later versions the security enhanced version of
  *  C++0x. For MSVC8 and later versions the security enhanced version of
- *  sprintf() will be used. However, if your buffer is at least 512 bytes
+ *  sprintf() will be used. However, if your buffer is at least 256 bytes
  *  long you'll never overrun.
  *  long you'll never overrun.
 */
 */
+// ---------------------------------------------------------------------------
 #if _MSC_VER >= 1400
 #if _MSC_VER >= 1400
+
+	// MSVC 8+. Use the sprintf_s function with security enhancements
 #	define AI_BUILD_KEY(base,index,out) \
 #	define AI_BUILD_KEY(base,index,out) \
 	::sprintf_s(out,"%s[%i]",base,index);
 	::sprintf_s(out,"%s[%i]",base,index);
+
+#elif (defined ASSIMP_BUILD_CPP_09)
+
+	// C++09 compiler. Use a static assertion to validate the size
+	// of the output buffer
+#	define AI_BUILD_KEY(base,index,out) \
+	static_assert(sizeof(out) >= 180,"Output buffer is too small");  \
+	::sprintf(out,"%s[%i]",base,index); 
+
 #else
 #else
+
+	// otherwise ... simply hope the buffer is large enough :-)
 #	define AI_BUILD_KEY(base,index,out) \
 #	define AI_BUILD_KEY(base,index,out) \
 	::sprintf(out,"%s[%i]",base,index);
 	::sprintf(out,"%s[%i]",base,index);
 #endif
 #endif
@@ -416,7 +504,7 @@ public:
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @def AI_MATKEY_TEXTURE_DIFFUSE
 /** @def AI_MATKEY_TEXTURE_DIFFUSE
-*  Defines a specified diffuse texture channel of the material
+*  Defines a specific diffuse texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -429,7 +517,7 @@ public:
 #define AI_MATKEY_TEXTURE_DIFFUSE_  "$tex.file.diffuse"
 #define AI_MATKEY_TEXTURE_DIFFUSE_  "$tex.file.diffuse"
 
 
 /** @def AI_MATKEY_TEXTURE_AMBIENT
 /** @def AI_MATKEY_TEXTURE_AMBIENT
- *  Defines a specified ambient texture channel of the material
+ *  Defines a specific ambient texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -442,7 +530,7 @@ public:
 #define AI_MATKEY_TEXTURE_AMBIENT_   "$tex.file.ambient"
 #define AI_MATKEY_TEXTURE_AMBIENT_   "$tex.file.ambient"
 
 
 /** @def AI_MATKEY_TEXTURE_SPECULAR
 /** @def AI_MATKEY_TEXTURE_SPECULAR
- *  Defines a specified specular texture channel of the material
+ *  Defines a specific specular texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -455,7 +543,7 @@ public:
 #define AI_MATKEY_TEXTURE_SPECULAR_   "$tex.file.specular"
 #define AI_MATKEY_TEXTURE_SPECULAR_   "$tex.file.specular"
 
 
 /** @def AI_MATKEY_TEXTURE_EMISSIVE
 /** @def AI_MATKEY_TEXTURE_EMISSIVE
- *  Defines a specified emissive texture channel of the material
+ *  Defines a specific emissive texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -468,7 +556,7 @@ public:
 #define AI_MATKEY_TEXTURE_EMISSIVE_   "$tex.file.emissive"
 #define AI_MATKEY_TEXTURE_EMISSIVE_   "$tex.file.emissive"
 
 
 /** @def AI_MATKEY_TEXTURE_NORMALS
 /** @def AI_MATKEY_TEXTURE_NORMALS
- *  Defines a specified normal texture channel of the material
+ *  Defines a specific normal texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -498,7 +586,7 @@ public:
 #define AI_MATKEY_TEXTURE_HEIGHT_   "$tex.file.height"
 #define AI_MATKEY_TEXTURE_HEIGHT_   "$tex.file.height"
 
 
 /** @def AI_MATKEY_TEXTURE_SHININESS
 /** @def AI_MATKEY_TEXTURE_SHININESS
- *  Defines a specified shininess texture channel of the material
+ *  Defines a specific shininess texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -511,7 +599,7 @@ public:
 #define AI_MATKEY_TEXTURE_SHININESS_   "$tex.file.shininess"
 #define AI_MATKEY_TEXTURE_SHININESS_   "$tex.file.shininess"
 
 
 /** @def AI_MATKEY_TEXTURE_OPACITY
 /** @def AI_MATKEY_TEXTURE_OPACITY
- *  Defines a specified opacity texture channel of the material
+ *  Defines a specific opacity texture channel of the material
  *  <br>
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
  * <b>Default value:</b> none <br>
@@ -772,7 +860,7 @@ public:
 *         structure or NULL if the key has not been found. 
 *         structure or NULL if the key has not been found. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialProperty(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialProperty(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     const char* pKey,
     const C_STRUCT aiMaterialProperty** pPropOut);
     const C_STRUCT aiMaterialProperty** pPropOut);
 
 
@@ -788,7 +876,7 @@ aiReturn aiGetMaterialProperty(const C_STRUCT aiMaterial* pMat,
 *        Receives the number of values (not bytes!) read. 
 *        Receives the number of values (not bytes!) read. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialFloatArray(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialFloatArray(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     const char* pKey,
     float* pOut,
     float* pOut,
     unsigned int* pMax);
     unsigned int* pMax);
@@ -817,7 +905,7 @@ inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial* pMat,
 *        Receives the number of values (not bytes!) read. 
 *        Receives the number of values (not bytes!) read. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     const char* pKey,
     int* pOut,
     int* pOut,
     unsigned int* pMax);
     unsigned int* pMax);
@@ -844,7 +932,7 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat,
 *	@param pOut Pointer to a buffer to receive the result. 
 *	@param pOut Pointer to a buffer to receive the result. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     const char* pKey,
     aiColor4D* pOut);
     aiColor4D* pOut);
 
 
@@ -857,20 +945,11 @@ aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat,
 *	@param pOut Pointer to a buffer to receive the result. 
 *	@param pOut Pointer to a buffer to receive the result. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     const char* pKey,
     aiString* pOut);
     aiString* pOut);
 
 
 
 
-#define AI_TEXTYPE_OPACITY		0x0
-#define AI_TEXTYPE_SPECULAR		0x1
-#define AI_TEXTYPE_AMBIENT		0x2
-#define AI_TEXTYPE_EMISSIVE		0x3
-#define AI_TEXTYPE_HEIGHT		0x4
-#define AI_TEXTYPE_NORMALS		0x5
-#define AI_TEXTYPE_SHININESS	0x6
-#define AI_TEXTYPE_DIFFUSE		0x7
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper function to get a texture from a material
 /** Helper function to get a texture from a material
  *
  *
@@ -895,7 +974,7 @@ aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat,
  */
  */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 #ifdef __cplusplus
 #ifdef __cplusplus
-aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
+ASSIMP_API aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
     unsigned int iIndex,
     unsigned int iIndex,
     unsigned int iTexType,
     unsigned int iTexType,
     C_STRUCT aiString* szPath,
     C_STRUCT aiString* szPath,
@@ -920,5 +999,4 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
 #include "aiMaterial.inl"
 #include "aiMaterial.inl"
 
 
 #endif //!__cplusplus
 #endif //!__cplusplus
-
 #endif //!!AI_MATERIAL_H_INC
 #endif //!!AI_MATERIAL_H_INC

+ 18 - 43
include/aiMaterial.inl

@@ -46,46 +46,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_MATERIAL_INL_INC
 #ifndef AI_MATERIAL_INL_INC
 #define AI_MATERIAL_INL_INC
 #define AI_MATERIAL_INL_INC
 
 
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** @brief A class that provides easy access to the property list of a
- *  material (aiMaterial) via template methods. You can cast an
- *  aiMaterial* to aiMaterialCPP*
- *  @note This extra class is necessary since template methods
- *  are not allowed within C-linkage blocks (extern "C")
- */
-class aiMaterialCPP : public aiMaterial
+inline aiReturn aiMaterial::GetTexture(unsigned int iIndex,
+	unsigned int iTexType,
+	aiString* szPath,
+	unsigned int* piUVIndex		,
+	float* pfBlendFactor		,
+	aiTextureOp* peTextureOp	,
+	aiTextureMapMode* peMapMode )
 {
 {
-public:
-
-	// -------------------------------------------------------------------
-    /** Retrieve an array of Type values with a specific key 
-     *  from the material
-     *
-     * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
-     * @param pOut Pointer to a buffer to receive the result. 
-     * @param pMax Specifies the size of the given buffer, in Type's.
-     * Receives the number of values (not bytes!) read. 
-     * NULL is a valid value for this parameter.
-     */
-    template <typename Type>
-    inline aiReturn Get(const char* pKey,Type* pOut,
-        unsigned int* pMax);
-
-    // -------------------------------------------------------------------
-    /** Retrieve a Type value with a specific key 
-     *  from the material
-     *
-     * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
-     * @param pOut Reference to receive the output value
-     */
-    template <typename Type>
-	inline aiReturn Get(const char* pKey,Type& pOut);
-};
-
+	return aiGetMaterialTexture(this,iIndex,iTexType,szPath,
+		piUVIndex,pfBlendFactor,peTextureOp,peMapMode);
+}
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <typename Type>
 template <typename Type>
-inline aiReturn aiMaterialCPP::Get(const char* pKey,Type* pOut,
+inline aiReturn aiMaterial::Get(const char* pKey,Type* pOut,
 	unsigned int* pMax)
 	unsigned int* pMax)
 {
 {
 	unsigned int iNum = pMax ? *pMax : 1;
 	unsigned int iNum = pMax ? *pMax : 1;
@@ -105,7 +80,7 @@ inline aiReturn aiMaterialCPP::Get(const char* pKey,Type* pOut,
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <typename Type>
 template <typename Type>
-inline aiReturn aiMaterialCPP::Get(const char* pKey,Type& pOut)
+inline aiReturn aiMaterial::Get(const char* pKey,Type& pOut)
 {
 {
 	aiMaterialProperty* prop;
 	aiMaterialProperty* prop;
 	aiReturn ret = aiGetMaterialProperty(this,pKey,&prop);
 	aiReturn ret = aiGetMaterialProperty(this,pKey,&prop);
@@ -120,39 +95,39 @@ inline aiReturn aiMaterialCPP::Get(const char* pKey,Type& pOut)
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<float>(const char* pKey,float* pOut,
+inline aiReturn aiMaterial::Get<float>(const char* pKey,float* pOut,
 	unsigned int* pMax)
 	unsigned int* pMax)
 {
 {
 	return aiGetMaterialFloatArray(this,pKey,pOut,pMax);
 	return aiGetMaterialFloatArray(this,pKey,pOut,pMax);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<int>(const char* pKey,int* pOut,
+inline aiReturn aiMaterial::Get<int>(const char* pKey,int* pOut,
 	unsigned int* pMax)
 	unsigned int* pMax)
 {
 {
 	return aiGetMaterialIntegerArray(this,pKey,pOut,pMax);
 	return aiGetMaterialIntegerArray(this,pKey,pOut,pMax);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<float>(const char* pKey,float& pOut)
+inline aiReturn aiMaterial::Get<float>(const char* pKey,float& pOut)
 {
 {
 	return aiGetMaterialFloat(this,pKey,&pOut);
 	return aiGetMaterialFloat(this,pKey,&pOut);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<int>(const char* pKey,int& pOut)
+inline aiReturn aiMaterial::Get<int>(const char* pKey,int& pOut)
 {
 {
 	return aiGetMaterialInteger(this,pKey,&pOut);
 	return aiGetMaterialInteger(this,pKey,&pOut);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<aiColor4D>(const char* pKey,aiColor4D& pOut)
+inline aiReturn aiMaterial::Get<aiColor4D>(const char* pKey,aiColor4D& pOut)
 {
 {
 	return aiGetMaterialColor(this,pKey,&pOut);
 	return aiGetMaterialColor(this,pKey,&pOut);
 }
 }
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 template <>
 template <>
-inline aiReturn aiMaterialCPP::Get<aiString>(const char* pKey,aiString& pOut)
+inline aiReturn aiMaterial::Get<aiString>(const char* pKey,aiString& pOut)
 {
 {
 	return aiGetMaterialString(this,pKey,&pOut);
 	return aiGetMaterialString(this,pKey,&pOut);
 }
 }

+ 3 - 2
include/aiMatrix3x3.h

@@ -9,7 +9,7 @@ extern "C" {
 struct aiMatrix4x4;
 struct aiMatrix4x4;
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** Represents a column-major 3x3 matrix 
+/** Represents a row-major 3x3 matrix 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 struct aiMatrix3x3
 struct aiMatrix3x3
@@ -28,7 +28,8 @@ struct aiMatrix3x3
 		c1(_c1), c2(_c2), c3(_c3)
 		c1(_c1), c2(_c2), c3(_c3)
 	{}
 	{}
 
 
-	/** Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. */
+	/** Construction from a 4x4 matrix. The remaining parts of the 
+	    matrix are ignored. */
 	explicit aiMatrix3x3( const aiMatrix4x4& pMatrix);
 	explicit aiMatrix3x3( const aiMatrix4x4& pMatrix);
 
 
 	aiMatrix3x3& operator *= (const aiMatrix3x3& m);
 	aiMatrix3x3& operator *= (const aiMatrix3x3& m);

+ 1 - 0
include/aiMatrix3x3.inl

@@ -50,5 +50,6 @@ inline aiMatrix3x3& aiMatrix3x3::Transpose()
 }
 }
 
 
 
 
+
 #endif // __cplusplus
 #endif // __cplusplus
 #endif // AI_MATRIX3x3_INL_INC
 #endif // AI_MATRIX3x3_INL_INC

+ 31 - 1
include/aiMatrix4x4.h

@@ -7,6 +7,7 @@ extern "C" {
 #endif
 #endif
 
 
 struct aiMatrix3x3;
 struct aiMatrix3x3;
+struct aiQuaternion;
 
 
 // Set packing to 4
 // Set packing to 4
 #if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
 #if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
@@ -19,7 +20,7 @@ struct aiMatrix3x3;
 #endif
 #endif
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-/** Represents a column-major 4x4 matrix, 
+/** Represents a row-major 4x4 matrix, 
 *  use this for homogenious coordinates 
 *  use this for homogenious coordinates 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -57,6 +58,35 @@ struct aiMatrix4x4
 	inline bool operator== (const aiMatrix4x4 m) const;
 	inline bool operator== (const aiMatrix4x4 m) const;
 	inline bool operator!= (const aiMatrix4x4 m) const;
 	inline bool operator!= (const aiMatrix4x4 m) const;
 
 
+
+	/** \brief Decompose a trafo matrix into its original components
+	 * \param scaling Receives the output scaling for the x,y,z axes
+	 * \param rotation Receives the output rotation as a hamilton
+	 *   quaternion 
+	 * \param position Receives the output position for the x,y,z axes
+	 */
+	inline void Decompose (aiVector3D& scaling, aiQuaternion& rotation,
+		aiVector3D& position) const;
+
+
+	/** \brief Decompose a trafo matrix with no scaling into its 
+	 *  original components
+	 *  \param rotation Receives the output rotation as a hamilton
+	 *  quaternion 
+	 *  \param position Receives the output position for the x,y,z axes
+	 */
+	inline void DecomposeNoScaling (aiQuaternion& rotation,
+		aiVector3D& position) const;
+
+
+	/** \brief Creates a trafo matrix from a set of euler angles
+	 *  \param x Rotation angle for the x-axis, in radians
+	 *  \param y Rotation angle for the y-axis, in radians
+	 *  \param z Rotation angle for the z-axis, in radians
+	 */
+	inline void FromEulerAngles(float x, float y, float z);
+
+
 #endif // __cplusplus
 #endif // __cplusplus
 
 
 	float a1, a2, a3, a4;
 	float a1, a2, a3, a4;

+ 93 - 0
include/aiMatrix4x4.inl

@@ -11,6 +11,9 @@
 #include <limits>
 #include <limits>
 #include <math.h>
 #include <math.h>
 
 
+#include "aiAssert.h"
+#include "aiQuaternion.h"
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
 inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
 {
 {
@@ -144,6 +147,96 @@ inline bool aiMatrix4x4::operator!= (const aiMatrix4x4 m) const
 {
 {
 	return !(*this == m);
 	return !(*this == m);
 }
 }
+// ---------------------------------------------------------------------------
+inline void aiMatrix4x4::Decompose (aiVector3D& scaling, aiQuaternion& rotation,
+	aiVector3D& position) const
+{
+	const aiMatrix4x4& _this = *this;
+
+	// extract translation
+	position.x = _this[0][3];
+	position.y = _this[1][3];
+	position.z = _this[2][3];
+
+	// extract the rows of the matrix
+	aiVector3D vRows[3] = {
+		aiVector3D(_this[0][0],_this[1][1],_this[2][0]),
+		aiVector3D(_this[0][1],_this[1][1],_this[2][1]),
+		aiVector3D(_this[0][2],_this[1][2],_this[2][2])
+	};
+
+	// extract the scaling factors
+	scaling.x = vRows[0].Length();
+	scaling.y = vRows[1].Length();
+	scaling.z = vRows[2].Length();
+
+	// and remove all scaling from the matrix
+	if(scaling.x)
+	{
+		vRows[0].x /= scaling.x;
+		vRows[0].y /= scaling.x;
+		vRows[0].z /= scaling.x;
+	}
+	if(scaling.y)
+	{
+		vRows[1].x /= scaling.y;
+		vRows[1].y /= scaling.y;
+		vRows[1].z /= scaling.y;
+	}
+	if(scaling.z)
+	{
+		vRows[2].x /= scaling.z;
+		vRows[2].y /= scaling.z;
+		vRows[2].z /= scaling.z;
+	}
+
+	// build a 3x3 rotation matrix
+	aiMatrix3x3 m(vRows[0].x,vRows[0].y,vRows[0].z,
+		vRows[1].x,vRows[1].y,vRows[1].z,
+		vRows[2].x,vRows[2].y,vRows[2].z);
+
+	// and generate the rotation quaternion from it
+	rotation = aiQuaternion(m);
+}
+// ---------------------------------------------------------------------------
+inline void aiMatrix4x4::DecomposeNoScaling (aiQuaternion& rotation,
+	aiVector3D& position) const
+{
+	const aiMatrix4x4& _this = *this;
+
+	// extract translation
+	position.x = _this[0][3];
+	position.y = _this[1][3];
+	position.z = _this[2][3];
+
+	// extract rotation
+	rotation = aiQuaternion((aiMatrix3x3)_this);
+}
+// ---------------------------------------------------------------------------
+inline void aiMatrix4x4::FromEulerAngles(float x, float y, float z)
+{
+	aiMatrix4x4& _this = *this;
+
+	const float A       = ::cosf(x);
+    const float B       = ::sinf(x);
+    const float C       = ::cosf(y);
+    const float D       = ::sinf(y);
+    const float E       = ::cosf(z);
+    const float F       = ::sinf(z);
+    const float AD      =   A * D;
+    const float BD      =   B * D;
+    _this.a1  =   C * E;
+    _this.a2  =  -C * F;
+    _this.a3  =   D;
+    _this.b1  =  BD * E + A * F;
+    _this.b2  = -BD * F + A * E;
+    _this.b3  =  -B * C;
+    _this.c1  = -AD * E + B * F;
+    _this.c2  =  AD * F + B * E;
+    _this.c3 =   A * C;
+    _this.a4 = _this.b4 = _this.c4 = _this.d1 = _this.d2 = _this.d3 =  0.0f;
+    _this.d4 = 1.0f;
+}
 
 
 #endif // __cplusplus
 #endif // __cplusplus
 #endif // AI_MATRIX4x4_INL_INC
 #endif // AI_MATRIX4x4_INL_INC

+ 16 - 5
include/aiPostProcess.h

@@ -130,8 +130,10 @@ enum aiPostProcessSteps
 	* to a maximum value. If any vertex is affected by more than that number
 	* to a maximum value. If any vertex is affected by more than that number
 	* of bones, the least important vertex weights are removed and the remaining
 	* of bones, the least important vertex weights are removed and the remaining
 	* vertex weights are renormalized so that the weights still sum up to 1.
 	* vertex weights are renormalized so that the weights still sum up to 1.
-	* At the moment the maximum bone count is hardcoded to 4.
-	*
+	* The default bone weight limit is 4 (defined as AI_LMW_MAX_WEIGHTS in
+	* LimitBoneWeightsProcess.h), but you can use the aiSetBoneWeightLimit
+	* function to supply your own limit to the post processing step.
+	* 
 	* If you intend to perform the skinning in hardware, this post processing step
 	* If you intend to perform the skinning in hardware, this post processing step
 	* might be of interest for you.
 	* might be of interest for you.
 	*/
 	*/
@@ -156,8 +158,7 @@ enum aiPostProcessSteps
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  *       the internal header file SplitLargeMeshes.h
  *       the internal header file SplitLargeMeshes.h
  */
  */
-aiReturn aiSetVertexSplitLimit(unsigned int pLimit);
-
+ASSIMP_API aiReturn aiSetVertexSplitLimit(unsigned int pLimit);
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** \brief Set the maximum number of triangles in a mesh.
 /** \brief Set the maximum number of triangles in a mesh.
@@ -168,7 +169,17 @@ aiReturn aiSetVertexSplitLimit(unsigned int pLimit);
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  *       the internal header file SplitLargeMeshes.h
  *       the internal header file SplitLargeMeshes.h
  */
  */
-aiReturn aiSetTriangleSplitLimit(unsigned int pLimit);
+ASSIMP_API aiReturn aiSetTriangleSplitLimit(unsigned int pLimit);
+
+// ---------------------------------------------------------------------------
+/** \brief Set the maximum number of bones affecting a single vertex
+ *
+ * This is used by the aiProcess_LimitBoneWeights PostProcess-Step.
+ * \param pLimit Bone limit
+ * \note The default value is AI_LMW_MAX_WEIGHTS, defined in
+ *       the internal header file LimitBoneWeightsProcess.h
+ */
+ASSIMP_API aiReturn aiSetBoneWeightLimit(unsigned int pLimit);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // end of extern "C"
 } // end of extern "C"

+ 24 - 0
include/aiQuaternion.h

@@ -16,9 +16,13 @@ struct aiQuaternion
 #ifdef __cplusplus
 #ifdef __cplusplus
 	aiQuaternion() : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {}
 	aiQuaternion() : w(0.0f), x(0.0f), y(0.0f), z(0.0f) {}
 	aiQuaternion(float _w, float _x, float _y, float _z) : w(_w), x(_x), y(_y), z(_z) {}
 	aiQuaternion(float _w, float _x, float _y, float _z) : w(_w), x(_x), y(_y), z(_z) {}
+
 	/** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
 	/** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
 	aiQuaternion( const aiMatrix3x3& pRotMatrix);
 	aiQuaternion( const aiMatrix3x3& pRotMatrix);
 
 
+	/** Construct from euler angles */
+	aiQuaternion( float rotx, float roty, float rotz);
+
 	/** Returns a matrix representation of the quaternion */
 	/** Returns a matrix representation of the quaternion */
 	aiMatrix3x3 GetMatrix() const;
 	aiMatrix3x3 GetMatrix() const;
 #endif // __cplusplus
 #endif // __cplusplus
@@ -73,6 +77,24 @@ inline aiQuaternion::aiQuaternion( const aiMatrix3x3 &pRotMatrix)
 	}
 	}
 }
 }
 
 
+// ---------------------------------------------------------------------------
+// Construction from euler angles
+inline aiQuaternion::aiQuaternion( float fPitch, float fYaw, float fRoll )
+{
+	const float fSinPitch(sin(fPitch*0.5F));
+	const float fCosPitch(cos(fPitch*0.5F));
+	const float fSinYaw(sin(fYaw*0.5F));
+	const float fCosYaw(cos(fYaw*0.5F));
+	const float fSinRoll(sin(fRoll*0.5F));
+	const float fCosRoll(cos(fRoll*0.5F));
+	const float fCosPitchCosYaw(fCosPitch*fCosYaw);
+	const float fSinPitchSinYaw(fSinPitch*fSinYaw);
+	x = fSinRoll * fCosPitchCosYaw     - fCosRoll * fSinPitchSinYaw;
+	y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw;
+	z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw;
+	w = fCosRoll * fCosPitchCosYaw     + fSinRoll * fSinPitchSinYaw;
+}
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // Returns a matrix representation of the quaternion
 // Returns a matrix representation of the quaternion
 inline aiMatrix3x3 aiQuaternion::GetMatrix() const
 inline aiMatrix3x3 aiQuaternion::GetMatrix() const
@@ -91,6 +113,8 @@ inline aiMatrix3x3 aiQuaternion::GetMatrix() const
 	return resMatrix;
 	return resMatrix;
 }
 }
 
 
+
+
 } // end extern "C"
 } // end extern "C"
 #endif // __cplusplus
 #endif // __cplusplus
 
 

+ 16 - 1
include/aiScene.h

@@ -94,6 +94,7 @@ struct aiNode
 	/** Constructor */
 	/** Constructor */
 	aiNode() 
 	aiNode() 
 	{ 
 	{ 
+		// set all members to zero by default
 		mParent = NULL; 
 		mParent = NULL; 
 		mNumChildren = 0; mChildren = NULL;
 		mNumChildren = 0; mChildren = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
@@ -102,6 +103,7 @@ struct aiNode
 	/** Destructor */
 	/** Destructor */
 	~aiNode()
 	~aiNode()
 	{
 	{
+		// delete al children recursively
 		for( unsigned int a = 0; a < mNumChildren; a++)
 		for( unsigned int a = 0; a < mNumChildren; a++)
 			delete mChildren[a];
 			delete mChildren[a];
 		delete [] mChildren;
 		delete [] mChildren;
@@ -127,6 +129,8 @@ struct aiScene
 	*/
 	*/
 	C_STRUCT aiNode* mRootNode;
 	C_STRUCT aiNode* mRootNode;
 
 
+
+
 	/** The number of meshes in the scene. */
 	/** The number of meshes in the scene. */
 	unsigned int mNumMeshes;
 	unsigned int mNumMeshes;
 
 
@@ -137,6 +141,8 @@ struct aiScene
 	*/
 	*/
 	C_STRUCT aiMesh** mMeshes;
 	C_STRUCT aiMesh** mMeshes;
 
 
+
+
 	/** The number of materials in the scene. */
 	/** The number of materials in the scene. */
 	unsigned int mNumMaterials;
 	unsigned int mNumMaterials;
 
 
@@ -147,6 +153,8 @@ struct aiScene
 	*/
 	*/
 	C_STRUCT aiMaterial** mMaterials;
 	C_STRUCT aiMaterial** mMaterials;
 
 
+
+
 	/** The number of animations in the scene. */
 	/** The number of animations in the scene. */
 	unsigned int mNumAnimations; 
 	unsigned int mNumAnimations; 
 
 
@@ -157,20 +165,25 @@ struct aiScene
 	*/
 	*/
 	C_STRUCT aiAnimation** mAnimations;
 	C_STRUCT aiAnimation** mAnimations;
 
 
+
+
 	/** The number of textures embedded into the file */
 	/** The number of textures embedded into the file */
 	unsigned int mNumTextures;
 	unsigned int mNumTextures;
 
 
 	/** The array of embedded textures.
 	/** The array of embedded textures.
 	* 
 	* 
 	* Not many file formats embedd their textures into the file.
 	* Not many file formats embedd their textures into the file.
-	* Examples include Quake's MDL format (which is also used by
+	* An example is Quake's MDL format (which is also used by
 	* some GameStudio™ versions)
 	* some GameStudio™ versions)
 	*/
 	*/
 	C_STRUCT aiTexture** mTextures;
 	C_STRUCT aiTexture** mTextures;
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
+
+	//! Default constructor
 	aiScene()
 	aiScene()
 	{
 	{
+		// set all members to zero by default
 		mRootNode = NULL;
 		mRootNode = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
 		mNumMaterials = 0; mMaterials = NULL;
 		mNumMaterials = 0; mMaterials = NULL;
@@ -178,8 +191,10 @@ struct aiScene
 		mNumTextures = 0; mTextures = NULL;
 		mNumTextures = 0; mTextures = NULL;
 	}
 	}
 
 
+	//! Destructor
 	~aiScene()
 	~aiScene()
 	{
 	{
+		// delete all subobjects recursively
 		delete mRootNode;
 		delete mRootNode;
 		for( unsigned int a = 0; a < mNumMeshes; a++)
 		for( unsigned int a = 0; a < mNumMeshes; a++)
 			delete mMeshes[a];
 			delete mMeshes[a];

+ 15 - 0
include/aiTexture.h

@@ -68,6 +68,21 @@ struct aiTexel
 	unsigned char g;
 	unsigned char g;
 	unsigned char r;
 	unsigned char r;
 	unsigned char a;
 	unsigned char a;
+
+	//! Comparison operator
+	bool operator== (const aiTexel& other) const
+	{
+		return b == other.b && r == other.r &&
+			g == other.g && a == other.a;
+	}
+
+	//! Negative comparison operator
+	bool operator!= (const aiTexel& other) const
+	{
+		return b != other.b || r != other.r ||
+			g != other.g || a != other.a;
+	}
+
 } PACK_STRUCT;
 } PACK_STRUCT;
 
 
 // reset packing to the original value
 // reset packing to the original value

+ 8 - 17
include/aiTypes.h

@@ -45,10 +45,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sys/types.h>
 #include <sys/types.h>
 #include <memory.h>
 #include <memory.h>
 
 
-#if (defined _MSC_VER)
-#	include "Compiler/VisualStudio/stdint.h"
-#endif // (defined _MSC_VER)
+#include "aiDefines.h"
 
 
+// include math helper classes and their implementations
 #include "aiVector3D.h"
 #include "aiVector3D.h"
 #include "aiMatrix3x3.h"
 #include "aiMatrix3x3.h"
 #include "aiMatrix4x4.h"
 #include "aiMatrix4x4.h"
@@ -59,13 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifdef __cplusplus
 #ifdef __cplusplus
 #	include <string>
 #	include <string>
 extern "C" {
 extern "C" {
-#	define C_STRUCT
-#else
-#	if (defined ASSIMP_DOXYGEN_BUILD)
-#		define C_STRUCT
-#	else
-#		define C_STRUCT struct
-#	endif
 #endif
 #endif
 
 
 /** Maximum dimension for strings, ASSIMP strings are zero terminated */
 /** Maximum dimension for strings, ASSIMP strings are zero terminated */
@@ -145,14 +137,14 @@ struct aiString
 	inline aiString() :
 	inline aiString() :
 		length(0) 
 		length(0) 
 	{
 	{
-		// empty
+		data[0] = '\0';
 	}
 	}
 
 
 	//! construction from a given std::string
 	//! construction from a given std::string
 	inline aiString(const aiString& rOther) : 
 	inline aiString(const aiString& rOther) : 
 		length(rOther.length) 
 		length(rOther.length) 
 	{
 	{
-		memcpy( data, rOther.data, rOther.length);
+		::memcpy( data, rOther.data, rOther.length);
 		this->data[this->length] = '\0';
 		this->data[this->length] = '\0';
 	}
 	}
 
 
@@ -162,7 +154,7 @@ struct aiString
 		if( pString.length() > MAXLEN - 1)
 		if( pString.length() > MAXLEN - 1)
 			return;
 			return;
 		length = pString.length();
 		length = pString.length();
-		memcpy( data, pString.c_str(), length);
+		::memcpy( data, pString.c_str(), length);
 		data[length] = 0;
 		data[length] = 0;
 	}
 	}
 
 
@@ -177,7 +169,7 @@ struct aiString
 	bool operator!=(const aiString& other) const
 	bool operator!=(const aiString& other) const
 	{
 	{
 		return  (this->length != other.length ||
 		return  (this->length != other.length ||
-				 0 != strcmp(this->data,other.data));
+				 0 != ::strcmp(this->data,other.data));
 	}
 	}
 
 
 
 
@@ -194,11 +186,10 @@ struct aiString
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /**	Standard return type for all library functions.
 /**	Standard return type for all library functions.
 *
 *
-* To check whether a function failed or not check against
-* AI_SUCCESS.
+* To check whether or not a function failed check against
+* AI_SUCCESS. The error codes are mainly used by the C-API.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-
 enum aiReturn
 enum aiReturn
 {
 {
 	//! Indicates that a function was successful
 	//! Indicates that a function was successful

+ 8 - 7
include/assimp.h

@@ -71,7 +71,8 @@ struct aiString;
 * @return Pointer to the imported data or NULL if the import failed. 
 * @return Pointer to the imported data or NULL if the import failed. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-const C_STRUCT aiScene* aiImportFile( const char* pFile, unsigned int pFlags);
+ASSIMP_API const C_STRUCT aiScene* aiImportFile( const char* pFile, 
+	unsigned int pFlags);
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -94,8 +95,8 @@ const C_STRUCT aiScene* aiImportFile( const char* pFile, unsigned int pFlags);
 * to this function. Therefore the C-API is thread-safe. 
 * to this function. Therefore the C-API is thread-safe. 
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-const C_STRUCT aiScene* aiImportFileEx( const C_STRUCT aiFileIO* pFile);
-
+ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( 
+	const C_STRUCT aiFileIO* pFile);
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -105,7 +106,7 @@ const C_STRUCT aiScene* aiImportFileEx( const C_STRUCT aiFileIO* pFile);
 * @param pScene The imported data to release. NULL is a valid value.
 * @param pScene The imported data to release. NULL is a valid value.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-void aiReleaseImport( const C_STRUCT aiScene* pScene);
+ASSIMP_API void aiReleaseImport( const C_STRUCT aiScene* pScene);
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -115,7 +116,7 @@ void aiReleaseImport( const C_STRUCT aiScene* pScene);
 * import process. NULL if there was no error.
 * import process. NULL if there was no error.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-const char* aiGetErrorString();
+ASSIMP_API const char* aiGetErrorString();
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -126,7 +127,7 @@ const char* aiGetErrorString();
 * @return 1 if the extension is supported, 0 otherwise
 * @return 1 if the extension is supported, 0 otherwise
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-int aiIsExtensionSupported(const char* szExtension);
+ASSIMP_API int aiIsExtensionSupported(const char* szExtension);
 
 
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
@@ -138,7 +139,7 @@ int aiIsExtensionSupported(const char* szExtension);
  * Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter.
  * Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter.
 */
 */
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-void aiGetExtensionList(C_STRUCT aiString* szOut);
+ASSIMP_API void aiGetExtensionList(C_STRUCT aiString* szOut);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 37 - 9
include/assimp.hpp

@@ -50,6 +50,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
+#include "aiDefines.h"
+
 struct aiScene;
 struct aiScene;
 
 
 namespace Assimp
 namespace Assimp
@@ -81,7 +83,7 @@ class IOSystem;
 * @note One Importer instance is not thread-safe. If you use multiple
 * @note One Importer instance is not thread-safe. If you use multiple
 * threads for loading each thread should manage its own Importer instance.
 * threads for loading each thread should manage its own Importer instance.
 */
 */
-class Importer
+class ASSIMP_API Importer
 {
 {
 	// used internally
 	// used internally
 	friend class BaseProcess;
 	friend class BaseProcess;
@@ -101,6 +103,7 @@ public:
 	 */
 	 */
 	~Importer();
 	~Importer();
 
 
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Supplies a custom IO handler to the importer to use to open and
 	/** Supplies a custom IO handler to the importer to use to open and
 	 * access files. If you need the importer to use custion IO logic to 
 	 * access files. If you need the importer to use custion IO logic to 
@@ -112,10 +115,31 @@ public:
 	 * afterwards. The previously assigned handler will be deleted.
 	 * afterwards. The previously assigned handler will be deleted.
 	 *
 	 *
 	 * @param pIOHandler The IO handler to be used in all file accesses 
 	 * @param pIOHandler The IO handler to be used in all file accesses 
-	 * of the Importer. NULL resets it to the default handler.
+	 *   of the Importer. NULL resets it to the default handler.
 	 */
 	 */
 	void SetIOHandler( IOSystem* pIOHandler);
 	void SetIOHandler( IOSystem* pIOHandler);
 
 
+
+	// -------------------------------------------------------------------
+	/** Retrieves the IO handler that is currently set.
+	 * You can use IsDefaultIOHandler() to check whether the returned
+	 * interface is the default IO handler provided by ASSIMP. The default
+	 * handler is active as long the application doesn't supply its own
+	 * custom IO handler via SetIOHandler().
+	 * @return A valid IOSystem interface
+	 */
+	IOSystem* GetIOHandler();
+
+
+	// -------------------------------------------------------------------
+	/** Checks whether a default IO handler is active 
+	 * A default handler is active as long the application doesn't 
+	 * supply its own custom IO handler via SetIOHandler().
+	 * @return true by default
+	 */
+	bool IsDefaultIOHandler();
+
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Reads the given file and returns its contents if successful. 
 	/** Reads the given file and returns its contents if successful. 
 	* 
 	* 
@@ -127,18 +151,19 @@ public:
 	* GetErrorString().
 	* GetErrorString().
 	* @param pFile Path and filename to the file to be imported.
 	* @param pFile Path and filename to the file to be imported.
 	* @param pFlags Optional post processing steps to be executed after 
 	* @param pFlags Optional post processing steps to be executed after 
-	*   a successful import. Provide a bitwise combination of the #aiPostProcessSteps
-	*   flags.
+	*   a successful import. Provide a bitwise combination of the 
+	*   #aiPostProcessSteps flags.
 	* @return A pointer to the imported data, NULL if the import failed.
 	* @return A pointer to the imported data, NULL if the import failed.
 	*/
 	*/
 	const aiScene* ReadFile( const std::string& pFile, unsigned int pFlags);
 	const aiScene* ReadFile( const std::string& pFile, unsigned int pFlags);
 
 
+
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Returns an error description of an error that occured in ReadFile(). 
 	/** Returns an error description of an error that occured in ReadFile(). 
 	*
 	*
 	* Returns an empty string if no error occured.
 	* Returns an empty string if no error occured.
 	* @return A description of the last error, an empty string if no 
 	* @return A description of the last error, an empty string if no 
-	*         error occured.
+	*   error occured.
 	*/
 	*/
 	inline const std::string& GetErrorString() const 
 	inline const std::string& GetErrorString() const 
 		{ return mErrorString; }
 		{ return mErrorString; }
@@ -147,20 +172,20 @@ public:
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
 	/** Returns whether a given file extension is supported by ASSIMP
 	/** Returns whether a given file extension is supported by ASSIMP
 	*
 	*
-	* @param szExtension Extension for which the function queries support.
-	* Must include a leading dot '.'. Example: ".3ds", ".md3"
+	* @param szExtension Extension to be checked.
+	*   Must include a leading dot '.'. Example: ".3ds", ".md3"
 	* @return true if the extension is supported, false otherwise
 	* @return true if the extension is supported, false otherwise
 	*/
 	*/
 	bool IsExtensionSupported(const std::string& szExtension);
 	bool IsExtensionSupported(const std::string& szExtension);
 
 
 
 
 	// -------------------------------------------------------------------
 	// -------------------------------------------------------------------
-	/** Get a full list of all file extensions generally supported by ASSIMP.
+	/** Get a full list of all file extensions supported by ASSIMP.
 	*
 	*
 	* If a file extension is contained in the list this does, of course, not
 	* If a file extension is contained in the list this does, of course, not
 	* mean that ASSIMP is able to load all files with this extension.
 	* mean that ASSIMP is able to load all files with this extension.
 	* @param szOut String to receive the extension list.
 	* @param szOut String to receive the extension list.
-	* Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter.
+	*   Format of the list: "*.3ds;*.obj;*.dae". 
 	*/
 	*/
 	void GetExtensionList(std::string& szOut);
 	void GetExtensionList(std::string& szOut);
 
 
@@ -174,12 +199,15 @@ public:
 		{return this->mScene;}
 		{return this->mScene;}
 
 
 private:
 private:
+
 	/** Empty copy constructor. */
 	/** Empty copy constructor. */
 	Importer(const Importer &other);
 	Importer(const Importer &other);
 
 
 protected:
 protected:
+
 	/** IO handler to use for all file accesses. */
 	/** IO handler to use for all file accesses. */
 	IOSystem* mIOHandler;
 	IOSystem* mIOHandler;
+	bool mIsDefaultHandler;
 
 
 	/** Format-specific importer worker objects - 
 	/** Format-specific importer worker objects - 
 	 * one for each format we can read. */
 	 * one for each format we can read. */

+ 1 - 1
port/jAssimp/assimp.iml

@@ -7,7 +7,7 @@
         <attribute name="URI" value="/" />
         <attribute name="URI" value="/" />
       </containerElement>
       </containerElement>
     </containerInfo>
     </containerInfo>
-    <setting name="jarPath" value="J:\Programmieren\ASSIMP\assimp2\port\jAssimp\assimp.jar" />
+    <setting name="jarPath" value="J:\Programmieren\ASSIMP\assimp\port\jAssimp\assimp.jar" />
     <setting name="buildJar" value="true" />
     <setting name="buildJar" value="true" />
     <setting name="mainClass" value="" />
     <setting name="mainClass" value="" />
   </component>
   </component>

+ 0 - 0
code/jAssimp/BuildHeader.bat → port/jAssimp/jni_bridge/BuildHeader.bat


+ 0 - 0
code/jAssimp/JNICalls.cpp → port/jAssimp/jni_bridge/JNICalls.cpp


+ 0 - 0
code/jAssimp/JNIEnvironment.cpp → port/jAssimp/jni_bridge/JNIEnvironment.cpp


+ 0 - 0
code/jAssimp/JNIEnvironment.h → port/jAssimp/jni_bridge/JNIEnvironment.h


+ 0 - 0
code/jAssimp/JNILogger.cpp → port/jAssimp/jni_bridge/JNILogger.cpp


+ 0 - 0
code/jAssimp/JNILogger.h → port/jAssimp/jni_bridge/JNILogger.h


+ 0 - 0
code/jAssimp/assimp_Animation.h → port/jAssimp/jni_bridge/assimp_Animation.h


+ 0 - 0
code/jAssimp/assimp_Importer.h → port/jAssimp/jni_bridge/assimp_Importer.h


+ 0 - 0
code/jAssimp/assimp_Material.h → port/jAssimp/jni_bridge/assimp_Material.h


+ 0 - 0
code/jAssimp/assimp_Mesh.h → port/jAssimp/jni_bridge/assimp_Mesh.h


+ 0 - 0
code/jAssimp/assimp_Node.h → port/jAssimp/jni_bridge/assimp_Node.h


+ 0 - 0
code/jAssimp/assimp_PostProcessStep.h → port/jAssimp/jni_bridge/assimp_PostProcessStep.h


+ 0 - 0
code/jAssimp/assimp_Scene.h → port/jAssimp/jni_bridge/assimp_Scene.h


+ 0 - 0
code/jAssimp/assimp_Texture.h → port/jAssimp/jni_bridge/assimp_Texture.h


+ 11 - 0
port/jAssimp/src/assimp/Bone.java

@@ -0,0 +1,11 @@
+package assimp;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Alex
+ * Date: 08.06.2008
+ * Time: 15:31:01
+ * To change this template use File | Settings | File Templates.
+ */
+public class Bone {
+}

+ 14 - 0
port/jAssimp/src/assimp/IOStream.java

@@ -0,0 +1,14 @@
+package assimp;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Alex
+ * Date: 15.06.2008
+ * Time: 19:51:45
+ * To change this template use File | Settings | File Templates.
+ */
+public interface IOStream {
+
+   
+
+}

+ 73 - 0
port/jAssimp/src/assimp/IOSystem.java

@@ -0,0 +1,73 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+
+package assimp;
+
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+
+
+/**
+ *
+ */
+public interface IOSystem {
+
+    /**
+     * Called to check whether a file is existing
+     *
+     * @param file Filename
+     * @return true if the file is existing and accessible
+     */
+    boolean Exists(String file);
+
+
+    /**
+     * Open a file and return an <code> IOStream </code> interface
+     * to access it.
+     *
+     * @param file File name of the file to be opened
+     * @return A valid IOStream interface
+     * @throws FileNotFoundException if the file can't be accessed
+     */
+    IOStream Open(String file) throws FileNotFoundException;
+
+}

+ 109 - 7
port/jAssimp/src/assimp/Importer.java

@@ -43,6 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package assimp;
 package assimp;
 
 
 import java.util.Vector;
 import java.util.Vector;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.File;
+import java.lang.ref.Reference;
 
 
 /**
 /**
  * Main class of jAssimp. The class is a simple wrapper for the native
  * Main class of jAssimp. The class is a simple wrapper for the native
@@ -59,6 +64,65 @@ import java.util.Vector;
  */
  */
 public class Importer {
 public class Importer {
 
 
+    /**
+     * Default implementation of <code>IOStream</code>.
+     * <br>
+     * This might become a performance bottleneck: The application
+     * needs to map the data read by this interface into a C-style
+     * array. For every single read operation! Therefore it is a good
+     * optimization to use the default C IOStream handler if no custom
+     * java handler was specified. Assuming that the Java Runtime is using
+     * the fXXX-family of functions internally too, the result should be
+     * the same. The problem is: we can't be sure we'll be able to open
+     * the file for reading from both Java and native code. Therefore we
+     * need to close our Java <code>FileReader</code> handle before
+     * control is given to native code.
+     */
+    private class DefaultIOStream implements IOStream {
+
+        private FileReader reader = null;
+
+        /**
+         * Construction with a given path
+         * @param file Path to the file to be opened
+         * @throws FileNotFoundException If the file isn't accessible at all
+         */
+        public DefaultIOStream(final String file) throws FileNotFoundException {
+            reader = new FileReader(file);
+        }
+
+    }
+
+    /**
+     * Default implementation of <code>IOSystem</code>.
+     */
+    private class DefaultIOSystem implements IOSystem {
+
+        /**
+         * Called to check whether a file is existing
+         *
+         * @param file Filename
+         * @return true if the file is existing and accessible
+         */
+        public boolean Exists(String file) {
+            File f = new File(file);
+            return f.exists();
+        }
+
+        /**
+         * Open a file and return an <code> IOStream </code> interface
+         * to access it.
+         *
+         * @param file File name of the file to be opened
+         * @return A valid IOStream interface
+         * @throws FileNotFoundException if the file can't be accessed
+         */
+        public IOStream Open(String file) throws FileNotFoundException {
+            return new DefaultIOStream(file);
+        }
+    }
+
+
     /**
     /**
      * List of all postprocess steps to apply to the model
      * List of all postprocess steps to apply to the model
      * Empty by default.
      * Empty by default.
@@ -83,17 +147,27 @@ public class Importer {
      */
      */
     private String path = null;
     private String path = null;
 
 
+    /**
+     * I/O system to be used
+     */
+    private IOSystem ioSystem = null;
+
     /**
     /**
      * Public constructor. Initialises the JNI bridge to the native
      * Public constructor. Initialises the JNI bridge to the native
      * ASSIMP library. A native Assimp::Importer object is constructed and
      * ASSIMP library. A native Assimp::Importer object is constructed and
      * initialized. The flag list is set to zero, a default I/O handler
      * initialized. The flag list is set to zero, a default I/O handler
-     * is constructed.
+     * is initialized.
      *
      *
+     * @param iVersion Version of the JNI interface to be used.
      * @throws NativeError Thrown if the jassimp library could not be loaded
      * @throws NativeError Thrown if the jassimp library could not be loaded
      *                     or if the entry point to the module wasn't found. if this exception
      *                     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.
      *                     is not thrown, you can assume that jAssimp is fully available.
      */
      */
-    public Importer() throws NativeError {
+    public Importer(int iVersion) throws NativeError {
+
+        // allocate a default I/O system
+        ioSystem = new DefaultIOSystem();
+
         /** try to load the jassimp library. First try to load the
         /** try to load the jassimp library. First try to load the
          * x64 version, in case of failure the x86 version
          * x64 version, in case of failure the x86 version
          */
          */
@@ -111,7 +185,7 @@ public class Importer {
         // now create the native Importer class and setup our internal
         // now create the native Importer class and setup our internal
         // data structures outside the VM.
         // data structures outside the VM.
         try {
         try {
-            if (0xffffffffffffffffl == (this.m_iNativeHandle = _NativeInitContext())) {
+            if (0xffffffffffffffffl == (this.m_iNativeHandle = _NativeInitContext(iVersion))) {
                 throw new NativeError(
                 throw new NativeError(
                         "Unable to initialize the native library context." +
                         "Unable to initialize the native library context." +
                                 "The initialization routine has failed");
                                 "The initialization routine has failed");
@@ -125,6 +199,32 @@ public class Importer {
         return;
         return;
     }
     }
 
 
+    public Importer() throws NativeError {
+        this(0);
+    }
+
+    /**
+     * Get the I/O system (<code>IOSystem</code>) to be used for loading
+     * assets. If no custom implementation was provided via <code>setIoSystem()</code>
+     * a default implementation will be used. Use <code>isDefaultIoSystem()</code>
+     * to check this.
+     * @return Always a valid <code>IOSystem</code> object, never null.
+     */
+    public IOSystem getIoSystem() {
+        return ioSystem;
+    }
+
+    
+    /**
+     * Checks whether a default IO system is currently being used to load
+     * assets. Using the default IO system has many performance benefits,
+     * but it is possible to provide a custom IO system (<code>setIoSystem()</code>).
+     * This allows applications to add support for archives like ZIP.
+     * @return true if a default <code>IOSystem</code> is active,
+     */
+    public boolean isDefaultIoSystem() {
+        return ioSystem instanceof DefaultIOSystem;
+    }
 
 
     /**
     /**
      * Add a postprocess step to the list of steps to be executed on
      * Add a postprocess step to the list of steps to be executed on
@@ -213,6 +313,8 @@ public class Importer {
             else if (step.equals(PostProcessStep.GenSmoothNormals)) flags |= 0x40;
             else if (step.equals(PostProcessStep.GenSmoothNormals)) flags |= 0x40;
             else if (step.equals(PostProcessStep.SplitLargeMeshes)) flags |= 0x80;
             else if (step.equals(PostProcessStep.SplitLargeMeshes)) flags |= 0x80;
             else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100;
             else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100;
+            else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200;
+            else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400;
         }
         }
 
 
         // now load the mesh
         // now load the mesh
@@ -227,6 +329,7 @@ public class Importer {
         }
         }
         catch (NativeError exc) {
         catch (NativeError exc) {
 
 
+            // delete everything ...
             this.scene = null;
             this.scene = null;
             this.path = null;
             this.path = null;
             throw exc;
             throw exc;
@@ -247,9 +350,8 @@ public class Importer {
 
 
         final Importer importer = (Importer) o;
         final Importer importer = (Importer) o;
 
 
-        if (m_iNativeHandle != importer.m_iNativeHandle) return false;
+        return m_iNativeHandle == importer.m_iNativeHandle;
 
 
-        return true;
     }
     }
 
 
     /**
     /**
@@ -268,7 +370,6 @@ public class Importer {
         if (0xffffffff == _NativeFreeContext(this.m_iNativeHandle)) {
         if (0xffffffff == _NativeFreeContext(this.m_iNativeHandle)) {
             throw new NativeError("Unable to destroy the native library context");
             throw new NativeError("Unable to destroy the native library context");
         }
         }
-        return;
     }
     }
 
 
     /**
     /**
@@ -306,9 +407,10 @@ public class Importer {
      * library functions are available, too. If they are not, an <code>
      * library functions are available, too. If they are not, an <code>
      * UnsatisfiedLinkError</code> will be thrown during model loading.
      * UnsatisfiedLinkError</code> will be thrown during model loading.
      *
      *
+     * @param version Version of the JNI bridge requested
      * @return Unique handle for the class or 0xffffffff if an error occured
      * @return Unique handle for the class or 0xffffffff if an error occured
      */
      */
-    private native int _NativeInitContext();
+    private native int _NativeInitContext(int version);
 
 
     /**
     /**
      * JNI bridge call. For internal use only
      * JNI bridge call. For internal use only

+ 69 - 0
port/jAssimp/src/assimp/Material.java

@@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package assimp;
 package assimp;
 
 
 
 
+
 /**
 /**
  * Class to wrap materials. Materials are represented in ASSIMP as a list of
  * Class to wrap materials. Materials are represented in ASSIMP as a list of
  * key/value pairs, the key being a <code>String</code> and the value being
  * key/value pairs, the key being a <code>String</code> and the value being
@@ -52,6 +53,74 @@ package assimp;
  * @version 1.0
  * @version 1.0
  */
  */
 public class Material extends Mappable {
 public class Material extends Mappable {
+
+
+    public static final String MATKEY_NAME = "$mat.name";
+
+
+    /**
+     * Specifies the blend operation to be used to combine the Nth
+     * diffuse texture with the (N-1)th diffuse texture (or the diffuse
+     * base color for the first diffuse texture)
+     * <br>
+     * <b>Type:</b> int (TextureOp)<br>
+     * <b>Default value:</b> 0<br>
+     * <b>Requires:</b> MATKEY_TEXTURE_DIFFUSE(0)<br>
+     */
+    public static String MATKEY_TEXOP_DIFFUSE(int N) {
+        return "$tex.op.diffuse[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_SPECULAR(int N) {
+        return "$tex.op.specular[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_AMBIENT(int N) {
+        return "$tex.op.ambient[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_EMISSIVE(int N) {
+        return "$tex.op.emissive[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_NORMALS(int N) {
+        return "$tex.op.normals[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_HEIGHT(int N) {
+        return "$tex.op.height[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_SHININESS(int N) {
+        return "$tex.op.shininess[" + N + "]";
+    }
+
+    /**
+     * @see MATKEY_TEXOP_DIFFUSE()
+     */
+    public static String MATKEY_TEXOP_OPACITY(int N) {
+        return "$tex.op.opacity[" + N + "]";
+    }
+
+
     /**
     /**
      * Construction from a given parent object and array index
      * Construction from a given parent object and array index
      *
      *

+ 5 - 31
port/jAssimp/src/assimp/Mesh.java

@@ -183,29 +183,9 @@ public class Mesh extends Mappable {
         assert (parent instanceof Scene);
         assert (parent instanceof Scene);
 
 
         Scene sc = (Scene) parent;
         Scene sc = (Scene) parent;
-        if (0xffffffff == (this.m_iPresentFlags = this._NativeGetPresenceFlags(
-                sc.getImporter().getContext(), this.getArrayIndex()))) {
-            throw new NativeError("Unable to obtain a list of vertex presence flags");
-        }
-        if (0xffffffff == (this.m_iNumVertices = this._NativeGetNumVertices(
-                sc.getImporter().getContext(), this.getArrayIndex()))) {
-            throw new NativeError("Unable to obtain the number of vertices in the mesh");
-        }
-        if (0xffffffff == (this.m_iNumFaces = this._NativeGetNumFaces(
-                sc.getImporter().getContext(), this.getArrayIndex()))) {
-            throw new NativeError("Unable to obtain the number of faces in the mesh");
-        }
-        if (0xffffffff == (this.m_iNumBones = this._NativeGetNumBones(
-                sc.getImporter().getContext(), this.getArrayIndex()))) {
-            throw new NativeError("Unable to obtain the number of bones in the mesh");
-        }
-        if (0xffffffff == (this.m_iMaterialIndex = this._NativeGetMaterialIndex(
-                sc.getImporter().getContext(), this.getArrayIndex()))) {
-            throw new NativeError("Unable to obtain the material index of the mesh");
-        }
-        if (0xffffffff == this._NativeGetNumUVComponents(
-                sc.getImporter().getContext(), this.getArrayIndex(), this.m_aiNumUVComponents)) {
-            throw new NativeError("Unable to obtain the number of UV components");
+        if (0xffffffff == this._NativeInitMembers(
+                sc.getImporter().getContext(), this.getArrayIndex())) {
+            throw new NativeError("Unable to intiailise class members via JNI");
         }
         }
     }
     }
 
 
@@ -821,18 +801,12 @@ public class Mesh extends Mappable {
 
 
     /**
     /**
      * JNI bridge function - for internal use only
      * JNI bridge function - for internal use only
-     * Retrieve the number of vertices in the mesh
+     * Initialise class members
      *
      *
      * @param context Current importer context (imp.hashCode)
      * @param context Current importer context (imp.hashCode)
      * @return Number of vertices in the mesh
      * @return Number of vertices in the mesh
      */
      */
-    private native int _NativeGetNumVertices(long context, long index);
-
-    private native int _NativeGetNumFaces(long context, long index);
-
-    private native int _NativeGetNumBones(long context, long index);
-
-    private native int _NativeGetMaterialIndex(long context, long index);
+    private native int _NativeInitMembers(long context, long index);
 
 
     /**
     /**
      * JNI bridge function - for internal use only
      * JNI bridge function - for internal use only

+ 217 - 5
port/jAssimp/src/assimp/Node.java

@@ -1,11 +1,223 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+
 package assimp;
 package assimp;
 
 
+import java.util.Vector;
+
+
 /**
 /**
- * Created by IntelliJ IDEA.
- * User: Alex
- * Date: 22.05.2008
- * Time: 13:05:12
- * To change this template use File | Settings | File Templates.
+ * A node in the imported hierarchy.
+ * <p/>
+ * Each node has name, a parent node (except for the root node),
+ * a transformation relative to its parent and possibly several child nodes.
+ * Simple file formats don't support hierarchical structures, for these formats
+ * the imported scene does consist of only a single root node with no childs.
+ *
+ * @author Aramis (Alexander Gessler)
+ * @version 1.0
  */
  */
 public class Node {
 public class Node {
+
+
+    /**
+     * List of all meshes of this node.
+     * The array contains indices into the Scene's mesh list
+     */
+    private int[] meshIndices = null;
+
+
+    /**
+     * Local transformation matrix of the node
+     * Stored in row-major order.
+     */
+    private float[] nodeTransform = null;
+
+
+    /**
+     * Name of the node
+     * The name might be empty (length of zero) but all nodes which
+     * need to be accessed afterwards by bones or anims are usually named.
+     */
+    private String name = "";
+
+
+    /**
+     * List of all child nodes
+     * May be empty
+     */
+    private Vector<Node> children = null;
+    private int numChildren = 0; // temporary
+
+    /**
+     * Parent scene
+     */
+    private Scene parentScene = null;
+
+
+    /**
+     * Parent node or null if we're the root node of the scene
+     */
+    private Node parent = null;
+
+    /**
+     * Constructs a new node and initializes it
+     * @param parentScene Parent scene object
+     * @param parentNode Parent node or null for root nodes
+     * @param index Unique index of the node
+     */
+    public Node(Scene parentScene, Node parentNode, int index) {
+
+        this.parentScene = parentScene;
+        this.parent = parentNode;
+
+        // Initialize JNI class members, including numChildren
+        this._NativeInitMembers(parentScene.getImporter().getContext(),index);
+
+        // get all children of the node
+        for (int i = 0; i < numChildren;++i) {
+            this.children.add(new Node(parentScene, this,  ++index));
+        }
+    }
+
+
+    /**
+     * Get a list of all meshes of this node
+     *
+     * @return Array containing indices into the Scene's mesh list
+     */
+    int[] getMeshes() {
+        return meshIndices;
+    }
+
+
+    /**
+     * Get the local transformation matrix of the node in row-major
+     * order:
+     * <code>
+     * a1 a2 a3 a4 (the translational part of the matrix is stored
+     * b1 b2 b3 b4  in (a4|b4|c4))
+     * c1 c2 c3 c4
+     * d1 d2 d3 d4
+     * </code>
+     *
+     * @return Row-major transformation matrix
+     */
+    float[] getTransformRowMajor() {
+        return nodeTransform;
+    }
+
+
+    /**
+     * Get the local transformation matrix of the node in column-major
+     * order:
+     * <code>
+     * a1 b1 c1 d1 (the translational part of the matrix is stored
+     * a2 b2 c2 d2  in (a4|b4|c4))
+     * a3 b3 c3 d3
+     * a4 b4 c4 d4
+     * </code>
+     *
+     * @return Column-major transformation matrix
+     */
+    float[] getTransformColumnMajor() {
+
+        float[] transform = new float[16];
+        transform[0] = nodeTransform[0];
+        transform[1] = nodeTransform[4];
+        transform[2] = nodeTransform[8];
+        transform[3] = nodeTransform[12];
+        transform[4] = nodeTransform[1];
+        transform[5] = nodeTransform[5];
+        transform[6] = nodeTransform[9];
+        transform[7] = nodeTransform[13];
+        transform[8] = nodeTransform[2];
+        transform[9] = nodeTransform[6];
+        transform[10] = nodeTransform[10];
+        transform[11] = nodeTransform[14];
+        transform[12] = nodeTransform[3];
+        transform[13] = nodeTransform[7];
+        transform[15] = nodeTransform[11];
+        transform[16] = nodeTransform[15];
+        return transform;
+    }
+
+
+    private native int _NativeInitMembers(long context, int nodeIndex);
+
+
+    /**
+     * Get the name of the node.
+     * The name might be empty (length of zero) but all nodes which
+     * need to be accessed afterwards by bones or anims are usually named.
+     *
+     * @return Node name
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * Get the list of all child nodes of *this* node
+     * @return List of children. May be empty.
+     */
+    public Vector<Node> getChildren() {
+        return children;
+    }
+
+    /**
+     * Get the parent node of the node
+     * @return Parent node
+     */
+    public Node getParent() {
+        return parent;
+    }
+
+    /**
+     * Get the parent scene of the node
+     * @return Never null
+     */
+    public Scene getParentScene() {
+        return parentScene;
+    }
 }
 }

+ 56 - 1
port/jAssimp/src/assimp/PostProcessStep.java

@@ -45,7 +45,7 @@ package assimp;
 /**
 /**
  * Enumeration class that defines postprocess steps that can be executed on a model
  * Enumeration class that defines postprocess steps that can be executed on a model
  * after it has been loaded. All PPSteps are implemented in C++, so their performance
  * after it has been loaded. All PPSteps are implemented in C++, so their performance
- * is awesome. Most steps are O(n * log(n)).
+ * is awesome. Most steps are O(n * log(n)). ;-)
  *
  *
  * @author Aramis (Alexander Gessler)
  * @author Aramis (Alexander Gessler)
  * @version 1.0
  * @version 1.0
@@ -62,8 +62,15 @@ public class PostProcessStep {
      */
      */
     public static final int DEFAULT_TRIANGLE_SPLIT_LIMIT = 1000000;
     public static final int DEFAULT_TRIANGLE_SPLIT_LIMIT = 1000000;
 
 
+    /**
+     * Default bone weight limit for the LimitBoneWeight process
+     */
+    public static final int DEFAULT_BONE_WEIGHT_LIMIT = 4;
+
+
     private static int s_iVertexSplitLimit = DEFAULT_VERTEX_SPLIT_LIMIT;
     private static int s_iVertexSplitLimit = DEFAULT_VERTEX_SPLIT_LIMIT;
     private static int s_iTriangleSplitLimit = DEFAULT_TRIANGLE_SPLIT_LIMIT;
     private static int s_iTriangleSplitLimit = DEFAULT_TRIANGLE_SPLIT_LIMIT;
+    private static int s_iBoneWeightLimit = DEFAULT_BONE_WEIGHT_LIMIT;
 
 
     /**
     /**
      * Identifies and joins identical vertex data sets within all imported
      * Identifies and joins identical vertex data sets within all imported
@@ -154,6 +161,31 @@ public class PostProcessStep {
     public static final PostProcessStep PreTransformVertices =
     public static final PostProcessStep PreTransformVertices =
             new PostProcessStep("PreTransformVertices");
             new PostProcessStep("PreTransformVertices");
 
 
+    /**
+     * Limits the number of bones simultaneously affecting a single vertex
+     * to a maximum value. If any vertex is affected by more than that number
+     * of bones, the least important vertex weights are removed and the remaining
+     * vertex weights are renormalized so that the weights still sum up to 1.
+     * The default bone weight limit is 4 (DEFAULT_BONE_WEIGHT_LIMIT).
+     * However, you can use setBoneWeightLimit() to supply your own limit.
+     * If you intend to perform the skinning in hardware, this post processing step
+     * might be of interest for you.
+     */
+    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
+     * bones are linked correctly, all material are correct and so on ...
+     * This is primarily intended for our internal debugging stuff,
+     * however, it could be of interest for applications like editors
+     * where stability is more important than loading performance.
+     */
+    public static final PostProcessStep ValidateDataStructure =
+            new PostProcessStep("ValidateDataStructure");
+
 
 
     /**
     /**
      * Set the vertex split limit for the "SplitLargeMeshes" process
      * Set the vertex split limit for the "SplitLargeMeshes" process
@@ -191,6 +223,22 @@ public class PostProcessStep {
         return limit;
         return limit;
     }
     }
 
 
+    /**
+     * Set the bone weight limit for the "LimitBoneWeights" process
+     * If a mesh exceeds this limit it will be splitted
+     *
+     * @param limit new bone weight limit. Pass 0xffffffff to disable it.
+     * @return Old bone weight limit
+     */
+    public static synchronized int setSetBoneWeightLimit(int limit) {
+        if (s_iBoneWeightLimit != limit) {
+            // send to the JNI bridge ...
+            s_iBoneWeightLimit = limit;
+            _NativeSetBoneWeightLimit(limit);
+        }
+        return limit;
+    }
+
     /**
     /**
      * JNI bridge call. For internal use only
      * JNI bridge call. For internal use only
      *
      *
@@ -205,6 +253,13 @@ public class PostProcessStep {
      */
      */
     private native static void _NativeSetTriangleSplitLimit(int limit);
     private native static void _NativeSetTriangleSplitLimit(int limit);
 
 
+    /**
+     * JNI bridge call. For internal use only
+     *
+     * @param limit New bone weight limit
+     */
+    private native static void _NativeSetBoneWeightLimit(int limit);
+
 
 
     private final String myName; // for debug only
     private final String myName; // for debug only
 
 

+ 116 - 0
port/jAssimp/src/assimp/ShadingMode.java

@@ -0,0 +1,116 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+package assimp;
+
+/**
+ * Defines all shading models supported by the library
+ * <p/>
+ * NOTE: The list of shading modes has been taken from Blender3D.
+ * See Blender3D documentation for more information. The API does
+ * not distinguish between "specular" and "diffuse" shaders (thus the
+ * specular term for diffuse shading models like Oren-Nayar remains
+ * undefined)
+ */
+public class ShadingMode {
+
+    /**
+     * Flat shading. Shading is done on per-face base,
+     * diffuse only.
+     */
+    int Flat = 0x1;
+
+    /**
+     * Diffuse gouraud shading. Shading on per-vertex base
+     */
+    int Gouraud = 0x2;
+
+    /**
+     * Diffuse/Specular Phong-Shading
+     * <p/>
+     * Shading is applied on per-pixel base. This is the
+     * slowest algorithm, but generates the best results.
+     */
+    int Phong = 0x3;
+
+    /**
+     * Diffuse/Specular Phong-Blinn-Shading
+     * <p/>
+     * Shading is applied on per-pixel base. This is a little
+     * bit faster than phong and in some cases even
+     * more realistic
+     */
+    int Blinn = 0x4;
+
+    /**
+     * Toon-Shading per pixel
+     * <p/>
+     * Shading is applied on per-pixel base. The output looks
+     * like a comic. Often combined with edge detection.
+     */
+    int Toon = 0x5;
+
+    /**
+     * OrenNayar-Shading per pixel
+     * <p/>
+     * Extension to standard lambertian shading, taking the
+     * roughness of the material into account
+     */
+    int OrenNayar = 0x6;
+
+    /**
+     * Minnaert-Shading per pixel
+     * <p/>
+     * Extension to standard lambertian shading, taking the
+     * "darkness" of the material into account
+     */
+    int Minnaert = 0x7;
+
+    /**
+     * CookTorrance-Shading per pixel
+     */
+    int CookTorrance = 0x8;
+
+    /**
+     * No shading at all
+     */
+    int NoShading = 0x8;
+}

+ 52 - 0
port/jAssimp/src/assimp/TextureMapMode.java

@@ -0,0 +1,52 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+package assimp;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Alex
+ * Date: 08.06.2008
+ * Time: 17:27:11
+ * To change this template use File | Settings | File Templates.
+ */
+public class TextureMapMode {
+}

+ 71 - 0
port/jAssimp/src/assimp/TextureOp.java

@@ -0,0 +1,71 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+package assimp;
+
+public class TextureOp {
+
+    private TextureOp() {}
+
+    /** T = T1 * T2
+     */
+    public static int Add = 0x0;
+
+    /** T = T1 * T2
+     */
+    public static int Multiply = 0x1;
+
+    /** T = T1 - T2
+     */
+    public static int Subtract = 0x2;
+
+    /** T = T1 / T2
+     */
+    public static int Divide = 0x3;
+
+    /** T = (T1 + T2) - (T1 * T2)
+     */
+    public static int SmoothAdd = 0x4;
+
+    /** T = T1 + (T2-0.5)
+     */
+    public static int SignedAdd = 0x5;
+}

BIN
test/HMP/planar.hmp


BIN
test/HMP/terrain.hmp


BIN
test/HMP/terrain_withtexture.hmp


+ 15 - 0
test/SMD/triangle.smd

@@ -0,0 +1,15 @@
+version 1
+
+nodes
+  0 "TriangleTest0"  -1
+end
+skeleton
+time 0
+  0 0.0 0.0 0.0 0.0 0.0 0.0
+end
+triangles
+none.quak
+0  0.0 0.0 0.0 1.0 1.0 1.0 0.0 0.0
+0  1.0 0.0 0.0 1.0 1.0 1.0 1.0 0.0
+0  1.0 1.0 0.0 1.0 1.0 1.0 1.0 1.0
+end

+ 6 - 4
tools/assimp_view/Material.cpp

@@ -137,7 +137,6 @@ int CMaterialManager::SetDefaultTexture(IDirect3DTexture9** p_ppiOut)
 bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString)
 bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString)
 {
 {
 	char szTempB[MAX_PATH];
 	char szTempB[MAX_PATH];
-
 	strcpy(szTempB,szTemp);
 	strcpy(szTempB,szTemp);
 
 
 	// go to the beginning of the file name
 	// go to the beginning of the file name
@@ -146,7 +145,9 @@ bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString)
 
 
 	char* szFile2 = szTemp + (szFile - szTempB)+1;
 	char* szFile2 = szTemp + (szFile - szTempB)+1;
 	szFile++;
 	szFile++;
-	char* szExt = strrchr(szFile,'.')+1;
+	char* szExt = strrchr(szFile,'.');
+	if (!szExt)return false;
+	szExt++;
 	*szFile = 0;
 	*szFile = 0;
 
 
 	strcat(szTempB,"*.*");
 	strcat(szTempB,"*.*");
@@ -163,9 +164,10 @@ bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString)
 		{
 		{
 			if (!(strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0))
 			if (!(strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0))
 			{
 			{
-				char* szExtFound = strrchr(info.cFileName, '.')+1;
-				if ((char*)0x1 != szExtFound)
+				char* szExtFound = strrchr(info.cFileName, '.');
+				if (szExtFound)
 				{
 				{
+					++szExtFound;
 					if (0 == ASSIMP_stricmp(szExtFound,szExt))
 					if (0 == ASSIMP_stricmp(szExtFound,szExt))
 					{
 					{
 						const unsigned int iSizeFound = (const unsigned int) ( 
 						const unsigned int iSizeFound = (const unsigned int) ( 

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

@@ -49,7 +49,7 @@
     <option name="MAXIMUM_HEAP_SIZE" value="128" />
     <option name="MAXIMUM_HEAP_SIZE" value="128" />
   </component>
   </component>
   <component name="JavadocGenerationManager">
   <component name="JavadocGenerationManager">
-    <option name="OUTPUT_DIRECTORY" value="J:/Programmieren/ASSIMP/assimp2/doc/javadoc" />
+    <option name="OUTPUT_DIRECTORY" value="J:/Programmieren/ASSIMP/assimp/doc/javadoc" />
     <option name="OPTION_SCOPE" value="package" />
     <option name="OPTION_SCOPE" value="package" />
     <option name="OPTION_HIERARCHY" value="true" />
     <option name="OPTION_HIERARCHY" value="true" />
     <option name="OPTION_NAVIGATOR" value="true" />
     <option name="OPTION_NAVIGATOR" value="true" />
@@ -178,7 +178,7 @@
   </component>
   </component>
   <component name="ProjectModuleManager">
   <component name="ProjectModuleManager">
     <modules>
     <modules>
-      <module fileurl="file://J:/Programmieren/ASSIMP/assimp2/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/assimp2/port/jAssimp/assimp.iml" />
+      <module fileurl="file://J:/Programmieren/ASSIMP/ASSIMP/port/jAssimp/assimp.iml" filepath="J:/Programmieren/ASSIMP/ASSIMP/port/jAssimp/assimp.iml" />
     </modules>
     </modules>
   </component>
   </component>
   <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" />
   <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" />

+ 74 - 21
workspaces/vc8/assimp.vcproj

@@ -48,7 +48,7 @@
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 				DebugInformationFormat="4"
 			/>
 			/>
 			<Tool
 			<Tool
@@ -174,6 +174,7 @@
 				BufferSecurityCheck="false"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			/>
 			<Tool
 			<Tool
 				Name="VCManagedResourceCompilerTool"
 				Name="VCManagedResourceCompilerTool"
@@ -294,11 +295,12 @@
 				EnableIntrinsicFunctions="true"
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				FavorSizeOrSpeed="1"
 				AdditionalIncludeDirectories=""
 				AdditionalIncludeDirectories=""
-				PreprocessorDefinitions="NDEBUG, _SCL_SECURE_NO_WARNINGS, _CRT_SECURE_NO_WARNINGS,WIN32"
+				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_BUILD_DLL_EXPORT"
 				StringPooling="true"
 				StringPooling="true"
 				BufferSecurityCheck="false"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			/>
 			<Tool
 			<Tool
 				Name="VCManagedResourceCompilerTool"
 				Name="VCManagedResourceCompilerTool"
@@ -367,7 +369,7 @@
 				EnableIntrinsicFunctions="true"
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				FavorSizeOrSpeed="1"
 				AdditionalIncludeDirectories=""
 				AdditionalIncludeDirectories=""
-				PreprocessorDefinitions="NDEBUG, _SCL_SECURE_NO_WARNINGS, _CRT_SECURE_NO_WARNINGS,WIN32"
+				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_BUILD_DLL_EXPORT"
 				StringPooling="true"
 				StringPooling="true"
 				BufferSecurityCheck="false"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				EnableEnhancedInstructionSet="2"
@@ -437,12 +439,13 @@
 				Name="VCCLCompilerTool"
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				Optimization="0"
 				AdditionalIncludeDirectories=""
 				AdditionalIncludeDirectories=""
-				PreprocessorDefinitions="DEBUG, _SCL_SECURE_NO_WARNINGS, _CRT_SECURE_NO_WARNINGS,WIN32"
+				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_BUILD_DLL_EXPORT"
 				BasicRuntimeChecks="3"
 				BasicRuntimeChecks="3"
 				SmallerTypeCheck="true"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 				DebugInformationFormat="4"
 			/>
 			/>
 			<Tool
 			<Tool
@@ -509,7 +512,7 @@
 				Name="VCCLCompilerTool"
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				Optimization="0"
 				AdditionalIncludeDirectories=""
 				AdditionalIncludeDirectories=""
-				PreprocessorDefinitions="DEBUG, _SCL_SECURE_NO_WARNINGS, _CRT_SECURE_NO_WARNINGS,WIN32"
+				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_BUILD_DLL_EXPORT"
 				BasicRuntimeChecks="3"
 				BasicRuntimeChecks="3"
 				SmallerTypeCheck="true"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
@@ -584,11 +587,12 @@
 				EnableIntrinsicFunctions="true"
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				FavorSizeOrSpeed="1"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
-				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT"
+				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT;ASSIMP_BUILD_DLL_EXPORT"
 				StringPooling="true"
 				StringPooling="true"
 				BufferSecurityCheck="false"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			/>
 			<Tool
 			<Tool
 				Name="VCManagedResourceCompilerTool"
 				Name="VCManagedResourceCompilerTool"
@@ -658,7 +662,7 @@
 				EnableIntrinsicFunctions="true"
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				FavorSizeOrSpeed="1"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
-				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT"
+				PreprocessorDefinitions="NDEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT;ASSIMP_BUILD_DLL_EXPORT"
 				StringPooling="true"
 				StringPooling="true"
 				BufferSecurityCheck="false"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				EnableEnhancedInstructionSet="2"
@@ -729,12 +733,13 @@
 				Name="VCCLCompilerTool"
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				Optimization="0"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
-				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT"
+				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT;ASSIMP_BUILD_DLL_EXPORT"
 				BasicRuntimeChecks="3"
 				BasicRuntimeChecks="3"
 				SmallerTypeCheck="true"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 				DebugInformationFormat="4"
 			/>
 			/>
 			<Tool
 			<Tool
@@ -802,7 +807,7 @@
 				Name="VCCLCompilerTool"
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				Optimization="0"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
 				AdditionalIncludeDirectories="&quot;$(JAVA_HOME)\include&quot;;&quot;$(JAVA_HOME)\include\win32&quot;"
-				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT"
+				PreprocessorDefinitions="DEBUG;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;ASSIMP_JNI_EXPORT;ASSIMP_BUILD_DLL_EXPORT"
 				BasicRuntimeChecks="3"
 				BasicRuntimeChecks="3"
 				SmallerTypeCheck="true"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
@@ -865,6 +870,10 @@
 				RelativePath="..\..\include\aiAssert.h"
 				RelativePath="..\..\include\aiAssert.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\include\aiDefines.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\include\aiFileIO.h"
 				RelativePath="..\..\include\aiFileIO.h"
 				>
 				>
@@ -1021,10 +1030,18 @@
 				RelativePath="..\..\code\MaterialSystem.h"
 				RelativePath="..\..\code\MaterialSystem.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\ParsingUtils.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\PretransformVertices.h"
 				RelativePath="..\..\code\PretransformVertices.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\qnan.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\SpatialSort.h"
 				RelativePath="..\..\code\SpatialSort.h"
 				>
 				>
@@ -1217,39 +1234,39 @@
 				Name="jAssimp"
 				Name="jAssimp"
 				>
 				>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Importer.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Material.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Material.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Mesh.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\jAssimp\assimp_Mesh.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Node.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\jAssimp\assimp_Node.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_PostProcessStep.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_PostProcessStep.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Scene.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Scene.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Texture.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Texture.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\JNIEnvironment.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\JNILogger.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
 					>
 					>
 				</File>
 				</File>
 			</Filter>
 			</Filter>
@@ -1261,6 +1278,22 @@
 					>
 					>
 				</File>
 				</File>
 			</Filter>
 			</Filter>
+			<Filter
+				Name="HMPLoader"
+				>
+				<File
+					RelativePath="..\..\code\HMPLoader.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="SMDLoader"
+				>
+				<File
+					RelativePath="..\..\code\SMDLoader.h"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		</Filter>
 		<Filter
 		<Filter
 			Name="sources"
 			Name="sources"
@@ -1436,6 +1469,10 @@
 					RelativePath="..\..\code\MDLLoader.cpp"
 					RelativePath="..\..\code\MDLLoader.cpp"
 					>
 					>
 				</File>
 				</File>
+				<File
+					RelativePath="..\..\code\MDLMaterialLoader.cpp"
+					>
+				</File>
 			</Filter>
 			</Filter>
 			<Filter
 			<Filter
 				Name="ASELoader"
 				Name="ASELoader"
@@ -1461,15 +1498,15 @@
 				Name="jAssimp"
 				Name="jAssimp"
 				>
 				>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\JNICalls.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNICalls.cpp"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\JNIEnvironment.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.cpp"
 					>
 					>
 				</File>
 				</File>
 				<File
 				<File
-					RelativePath="..\..\code\jAssimp\JNILogger.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.cpp"
 					>
 					>
 				</File>
 				</File>
 			</Filter>
 			</Filter>
@@ -1481,6 +1518,22 @@
 					>
 					>
 				</File>
 				</File>
 			</Filter>
 			</Filter>
+			<Filter
+				Name="HMPLoader"
+				>
+				<File
+					RelativePath="..\..\code\HMPLoader.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="SMDLoader"
+				>
+				<File
+					RelativePath="..\..\code\SMDLoader.cpp"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		</Filter>
 		<Filter
 		<Filter
 			Name="doc"
 			Name="doc"

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