ソースを参照

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 年 前
コミット
b422d4e303
90 ファイル変更6258 行追加1299 行削除
  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 */
+
+// internal headers
 #include "3DSLoader.h"
 #include "MaterialSystem.h"
 #include "TextureTransform.h"
 #include "StringComparison.h"
+#include "qnan.h"
 
+// public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/IOStream.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/aiAssert.h"
 
-#include <boost/scoped_ptr.hpp>
 
 using namespace Assimp;
 
@@ -427,10 +430,12 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 			a != (*i).mFaceMaterials.end();++a,++iNum)
 		{
 		// 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);
 		}
@@ -450,15 +455,15 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 				p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName);
 				avOutMeshes.push_back(p_pcOut);
 
-
+// (code for keyframe animation. however, this is currently not supported by Assimp)
+#if 0
 				if (bFirst)
 				{
 					p_pcOut->mColors[1] = (aiColor4D*)new aiMatrix4x4();
-
 					*((aiMatrix4x4*)p_pcOut->mColors[1]) = (*i).mMat;
 					bFirst = false;
 				}
-
+#endif
 
 				// convert vertices
 				p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
@@ -519,16 +524,6 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
 					// apply texture coordinate scalings
 					TextureTransform::BakeScaleNOffset ( p_pcOut, &this->mScene->mMaterials[
 						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];
 
+// (code for keyframe animation. however, this is currently not supported by Assimp)
+#if 0
 		if (NULL != pcSOut->mMeshes[iIndex]->mColors[1])
 		{
 			pcOut->mTransformation = *((aiMatrix4x4*)
@@ -587,7 +584,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node*
 			delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1];
 			pcSOut->mMeshes[iIndex]->mColors[1] = NULL;
 		}
-
+#endif
 		pcOut->mMeshes[i] = iIndex;
 	}
 
@@ -681,11 +678,17 @@ void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
 			pcNode->mMeshes[0] = i;
 			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
 			pcOut->mRootNode->mChildren[i] = pcNode;

+ 26 - 30
code/3DSHelper.h

@@ -93,10 +93,22 @@ public:
 	//! From AutoDesk 3ds SDK
 	typedef enum
 	{
+		// translated to gouraud shading with wireframe active
 		Wire = 0,
+
+		// if this material is set, no vertex normals will
+		// be calculated for the model. Face normals + gouraud
 		Flat = 1,
+
+		// standard gouraud shading
 		Gouraud = 2,
+
+		// phong shading
 		Phong = 3,
+
+		// cooktorrance or anistropic phong shading ...
+		// the exact meaning is unknown, if you know it
+		// feel free to tell me ;-)
 		Metal = 4,
 
 		// required by the ASE loader
@@ -127,7 +139,8 @@ public:
 		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,
 
 		// Unknown. Possibly a reference to an external .mli file?
@@ -387,9 +400,10 @@ struct Material
 	mTwoSided			(false)
 	{
 		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
@@ -442,9 +456,10 @@ struct Mesh
 	Mesh()
 	{
 		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
@@ -481,9 +496,10 @@ struct Node
 
 	{
 		static int iCnt = 0;
-		std::stringstream ss;
-		ss << "$$_UNNAMED_" << iCnt++ << "_$$"; 
-		ss >> mName;
+		
+		char szTemp[128];
+		sprintf(szTemp,"$$_UNNAMED_%i_$$",iCnt++);
+		mName = szTemp;
 
 		mHierarchyPos = 0;
 		mHierarchyIndex = 0;
@@ -538,26 +554,6 @@ struct Scene
 	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 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 */
+
+// internal headers
 #include "3DSLoader.h"
 #include "MaterialSystem.h"
 #include "TextureTransform.h"
 #include "StringComparison.h"
+#include "qnan.h"
 
+// public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/IOStream.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/aiAssert.h"
 
+// boost headers
 #include <boost/scoped_ptr.hpp>
 
 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
@@ -92,22 +99,6 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co
 	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. 
 void Dot3DSImporter::InternReadFile( 
 	const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
@@ -125,7 +116,7 @@ void Dot3DSImporter::InternReadFile(
 	size_t fileSize = file->FileSize();
 	if( fileSize < 16)
 	{
-		throw new ImportErrorException( ".3ds File is too small.");
+		throw new ImportErrorException( "3DS File is too small.");
 	}
 
 	this->mScene = new Dot3DS::Scene();
@@ -780,8 +771,9 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 	case Dot3DSFile::CHUNK_FACEMAT:
 
 		// 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;
 		}
 
@@ -800,7 +792,7 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 			break;
 			}
 		}
-		if (iIndex == 0xFFFFFFFF)
+		if (0xFFFFFFFF == iIndex)
 		{
 			// this material is not known. Ignore this. We will later
 			// assign the default material to all faces using *this*
@@ -818,13 +810,14 @@ void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
 
 			// check range
 			if (iTemp >= mMesh.mFaceMaterials.size())
-				{
+			{
+				DefaultLogger::get()->error("Invalid face index in face material list");
 				mMesh.mFaceMaterials[mMesh.mFaceMaterials.size()-1] = iIndex;
-				}
+			}
 			else
-				{
+			{
 				mMesh.mFaceMaterials[iTemp] = iIndex;
-				}
+			}
 			this->mCurrent += sizeof(uint16_t);
 		}
 
@@ -959,8 +952,7 @@ void Dot3DSImporter::ParseMeshChunk(int* piRemaining)
 		}
 		break;
 
-#if (defined _DEBUG)
-
+#if 0
 	case Dot3DSFile::CHUNK_TXTINFO:
 
 		// 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 */
+
+// internal headers
 #include "ASELoader.h"
 #include "3DSSpatialSort.h"
 #include "MaterialSystem.h"
 #include "StringComparison.h"
 #include "TextureTransform.h"
+
+// utilities
 #include "fast_atof.h"
+#include "qnan.h"
 
+// ASSIMP public headers
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"

+ 10 - 2
code/BaseImporter.h

@@ -111,7 +111,7 @@ public:
 	// -------------------------------------------------------------------
 	/** Imports the given file and returns the imported data.
 	* 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
 	* beforehand.
 	*
@@ -154,7 +154,15 @@ protected:
 	 * an error. If it terminates normally, the data in aiScene is 
 	 * expected to be correct. Override this function to implement the 
 	 * 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 pScene The scene object to hold the imported data.
 	 * NULL is not a valid parameter.

+ 10 - 6
code/DefaultIOStream.cpp

@@ -53,7 +53,7 @@ DefaultIOStream::~DefaultIOStream()
 {
 	if (this->mFile)
 	{
-		fclose(this->mFile);
+		::fclose(this->mFile);
 	}	
 }
 // ---------------------------------------------------------------------------
@@ -61,20 +61,24 @@ size_t DefaultIOStream::Read(void* pvBuffer,
 								size_t pSize, 
 								size_t pCount)
 {
+	ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+
 	if (!this->mFile)
 		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 pSize,
 								 size_t pCount)
 {
+	ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+
 	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,
@@ -82,7 +86,7 @@ aiReturn DefaultIOStream::Seek(size_t pOffset,
 {
 	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_END == pOrigin ? SEEK_END : SEEK_SET))) 
 		? AI_SUCCESS : AI_FAILURE);
@@ -92,7 +96,7 @@ size_t DefaultIOStream::Tell() const
 {
 	if (!this->mFile)return 0;
 
-	return ftell(this->mFile);
+	return ::ftell(this->mFile);
 }
 // ---------------------------------------------------------------------------
 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.
 bool DefaultIOSystem::Exists( const std::string& pFile) const
 {
-	FILE* file = fopen( pFile.c_str(), "rb");
+	FILE* file = ::fopen( pFile.c_str(), "rb");
 	if( !file)
 		return false;
 
-	fclose( file);
+	::fclose( file);
 	return true;
 }
 
@@ -77,7 +77,7 @@ bool DefaultIOSystem::Exists( const std::string& pFile) const
 // Open a new file with a given path.
 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) 
 		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 "DefaultIOSystem.h"
 
+// Importers
 #if (!defined AI_BUILD_NO_X_IMPORTER)
 #	include "XFileImporter.h"
 #endif
@@ -81,18 +82,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #if (!defined AI_BUILD_NO_OBJ_IMPORTER)
 #	include "ObjFileImporter.h"
 #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;
 
@@ -105,6 +135,7 @@ Importer::Importer() :
 {
 	// allocate a default IO handler
 	mIOHandler = new DefaultIOSystem;
+	mIsDefaultHandler = true;
 
 	// add an instance of each worker class here
 #if (!defined AI_BUILD_NO_X_IMPORTER)
@@ -134,20 +165,51 @@ Importer::Importer() :
 #if (!defined AI_BUILD_NO_ASE_IMPORTER)
 	mImporter.push_back( new ASEImporter());
 #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());
+#endif
+#if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
 	mPostProcessingSteps.push_back( new TriangulateProcess());
+#endif
+#if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
 	mPostProcessingSteps.push_back( new PretransformVertices());
+#endif
+#if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS)
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
+#endif
+#if (!defined AI_BUILD_NO_KILLNORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new KillNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_GENFACENORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_GENVERTEXNORMALS_PROCESS)
 	mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
+#endif
+#if (!defined AI_BUILD_NO_CALCTANGENTS_PROCESS)
 	mPostProcessingSteps.push_back( new CalcTangentsProcess());
+#endif
+#if (!defined AI_BUILD_NO_JOINVERTICES_PROCESS)
 	mPostProcessingSteps.push_back( new JoinVerticesProcess());
+#endif
+#if (!defined AI_BUILD_NO_SPLITLARGEMESHES_PROCESS)
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
+#endif
+#if (!defined AI_BUILD_NO_CONVERTTOLH_PROCESS)
 	mPostProcessingSteps.push_back( new ConvertToLHProcess());
+#endif
+#if (!defined AI_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
 	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.
 void Importer::SetIOHandler( IOSystem* pIOHandler)
 {
-	if (NULL == pIOHandler)
+	if (!pIOHandler)
 	{
 		delete mIOHandler;
 		mIOHandler = new DefaultIOSystem();
+		mIsDefaultHandler = true;
 	}
 	else if (mIOHandler != pIOHandler)
 	{
 		delete mIOHandler;
 		mIOHandler = pIOHandler;
+		mIsDefaultHandler = false;
 	}
 	return;
 }
 // ------------------------------------------------------------------------------------------------
+IOSystem* Importer::GetIOHandler()
+{
+	return mIOHandler;
+}
+// ------------------------------------------------------------------------------------------------
+bool Importer::IsDefaultIOHandler()
+{
+	return mIsDefaultHandler;
+}
+// ------------------------------------------------------------------------------------------------
 // Validate post process step flags 
 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 */
 
 #include <vector>
 #include <assert.h>
+
 #include "LimitBoneWeightsProcess.h"
+
 #include "../include/aiPostProcess.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
+#include "../include/DefaultLogger.h"
 
 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
 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. */
 #ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
 #define AI_LIMITBONEWEIGHTSPROCESS_H_INC
@@ -9,6 +49,18 @@ struct aiMesh;
 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
 * 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
 		Weight() { }
 		Weight( unsigned int pBone, float pWeight) { mBone = pBone; mWeight = pWeight; }
+
 		/** Comparision operator to sort bone weights by descending weight */
 		bool operator < (const Weight& pWeight) const { return mWeight > pWeight.mWeight; }
 	};
 
+public:
 	/** Maximum number of bones influencing any single vertex. */
-	unsigned int mMaxWeights;
+	static unsigned int mMaxWeights;
 };
 
 } // end of namespace Assimp

+ 26 - 24
code/MD2FileData.h

@@ -80,34 +80,33 @@ namespace MD2
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for the MD2 main 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;
 
 
+
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 OpenGl draw command
  */
-// ---------------------------------------------------------------------------
 struct GLCommand
 {
    float s, t;
@@ -117,7 +116,6 @@ struct GLCommand
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 triangle
  */
-// ---------------------------------------------------------------------------
 struct Triangle
 {
 	uint16_t vertexIndices[3];
@@ -127,7 +125,6 @@ struct Triangle
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 vertex
  */
-// ---------------------------------------------------------------------------
 struct Vertex
 {
 	uint8_t vertex[3];
@@ -137,7 +134,6 @@ struct Vertex
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 frame
  */
-// ---------------------------------------------------------------------------
 struct Frame
 {
 	float scale[3];
@@ -149,7 +145,6 @@ struct Frame
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 texture coordinate
  */
-// ---------------------------------------------------------------------------
 struct TexCoord
 {
 	int16_t s;
@@ -159,7 +154,6 @@ struct TexCoord
 // ---------------------------------------------------------------------------
 /**	\brief Data structure for a MD2 skin
  */
-// ---------------------------------------------------------------------------
 struct Skin
 {
 	char name[AI_MD2_MAXQPATH];              /* texture file name */
@@ -171,6 +165,14 @@ struct Skin
 #endif
 #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 */
 #include "MD2Loader.h"
 #include "MaterialSystem.h"
-
-#include "MD2NormalTable.h"
+#include "MD2NormalTable.h" // shouldn't be included by other units
 
 #include "../include/IOStream.h"
 #include "../include/IOSystem.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 #include <boost/scoped_ptr.hpp>
 
 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
 MD2Importer::MD2Importer()
@@ -99,12 +101,43 @@ bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 		return false;
 	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. 
@@ -121,10 +154,10 @@ void MD2Importer::InternReadFile(
 
 	// check whether the md3 file is large enough to contain
 	// at least the file header
-	size_t fileSize = file->FileSize();
+	fileSize = (unsigned int)file->FileSize();
 	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
@@ -137,22 +170,26 @@ void MD2Importer::InternReadFile(
 	if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
 		this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
 	}
 
 	// check file format version
 	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
 	if (0 == this->m_pcHeader->numFrames)
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
 	}
 	if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md2 file: File is too small");
 	}
 
@@ -166,39 +203,35 @@ void MD2Importer::InternReadFile(
 	pScene->mMaterials[0] = new MaterialHelper();
 	pScene->mNumMeshes = 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
-	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
-	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
-	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
 	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
-	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
 		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;
 		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
 	{
@@ -237,13 +278,42 @@ void MD2Importer::InternReadFile(
 
 		clr.b = clr.g = clr.r = 0.05f;
 		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+		aiString szName;
+		szName.Set(AI_DEFAULT_MATERIAL_NAME);
+		pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
 	}
 
 
 	// now read all triangles of the first frame, apply scaling and translation
 	unsigned int iCurrent = 0;
-	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)
 		{
@@ -259,13 +329,16 @@ void MD2Importer::InternReadFile(
 			{
 				// validate vertex indices
 				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
+				{
+					DefaultLogger::get()->error("Vertex index is outside the allowed range");
 					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+				}
 
 				// copy face indices
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 
 				// 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 += pcFrame->translate[0];
@@ -278,23 +351,28 @@ void MD2Importer::InternReadFile(
 				vec.y += pcFrame->translate[2];
 
 				// 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
 				if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
+				{
+					DefaultLogger::get()->error("UV index is outside the allowed range");
 					pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
+				}
 
-				aiVector3D* pcOut = &vTexCoords[iCurrent];
+				aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
 				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[1] = iTemp+1;
 			pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
@@ -316,13 +394,16 @@ void MD2Importer::InternReadFile(
 			{
 				// validate vertex indices
 				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
+				{
+					DefaultLogger::get()->error("Vertex index is outside the allowed range");
 					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+				}
 
 				// copy face indices
 				unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
 
 				// 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 += pcFrame->translate[0];
@@ -335,39 +416,17 @@ void MD2Importer::InternReadFile(
 				vec.y += pcFrame->translate[2];
 
 				// 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[1] = iTemp+1;
 			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;
 }

+ 9 - 0
code/MD2Loader.h

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

+ 23 - 23
code/MD3FileData.h

@@ -89,10 +89,10 @@ namespace MD3
 struct Header
 {
 	//! magic number
-	int32_t IDENT;
+	uint32_t IDENT;
 
 	//! file format version
-	int32_t VERSION;
+	uint32_t VERSION;
 
 	//! original name in .pak archive
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
@@ -101,28 +101,28 @@ struct Header
 	int32_t FLAGS;
 
 	//! number of frames in the file
-	int32_t NUM_FRAMES;
+	uint32_t NUM_FRAMES;
 
 	//! number of tags in the file
-	int32_t NUM_TAGS;
+	uint32_t NUM_TAGS;
 
 	//! number of surfaces in the file
-	int32_t NUM_SURFACES;
+	uint32_t NUM_SURFACES;
 
 	//! number of skins in the file
-	int32_t NUM_SKINS;
+	uint32_t NUM_SKINS;
 
 	//! offset of the first frame
-	int32_t OFS_FRAMES;
+	uint32_t OFS_FRAMES;
 
 	//! offset of the first tag
-	int32_t OFS_TAGS;
+	uint32_t OFS_TAGS;
 
 	//! offset of the first surface
-	int32_t OFS_SURFACES;
+	uint32_t OFS_SURFACES;
 
 	//! end of file
-	int32_t OFS_EOF;
+	uint32_t OFS_EOF;
 } PACK_STRUCT;
 
 
@@ -162,29 +162,29 @@ struct Surface
 	int32_t FLAGS;
 
 	//! number of frames in the surface
-	int32_t NUM_FRAMES;
+	uint32_t NUM_FRAMES;
 
 	//! number of shaders in the surface
-	int32_t NUM_SHADER;
+	uint32_t NUM_SHADER;
 
 	//! number of vertices in the surface
-	int32_t NUM_VERTICES;
+	uint32_t NUM_VERTICES;
 
 	//! number of triangles in the surface
-	int32_t NUM_TRIANGLES;
+	uint32_t NUM_TRIANGLES;
 
 
 	//! offset to the triangle data 
-	int32_t OFS_TRIANGLES;
+	uint32_t OFS_TRIANGLES;
 
 	//! offset to the shader data
-	int32_t OFS_SHADERS;
+	uint32_t OFS_SHADERS;
 
 	//! offset to the texture coordinate data
-	int32_t OFS_ST;
+	uint32_t OFS_ST;
 
 	//! offset to the vertex/normal data
-	int32_t OFS_XYZNORMAL;
+	uint32_t OFS_XYZNORMAL;
 
 	//! offset to the end of the Surface object
 	int32_t OFS_END;
@@ -200,7 +200,7 @@ struct Shader
 	unsigned char NAME[ AI_MD3_MAXQPATH ];
 
 	//! index of the shader
-	int32_t SHADER_INDEX;
+	uint32_t SHADER_INDEX;
 } PACK_STRUCT;
 
 
@@ -211,7 +211,7 @@ struct Shader
 struct Triangle
 {
 	//! triangle indices
-	int32_t INDEXES[3];
+	uint32_t INDEXES[3];
 } PACK_STRUCT;
 
 
@@ -236,7 +236,7 @@ struct Vertex
 	int16_t X,Y,Z;
 
 	//! encoded normal vector
-	int16_t  NORMAL;
+	uint16_t  NORMAL;
 } PACK_STRUCT;
 
 // reset packing to the original value
@@ -254,9 +254,9 @@ struct Vertex
  *	\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 ));
 	lat *= 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/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 #include <boost/scoped_ptr.hpp>
 
@@ -78,12 +79,48 @@ bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 		return false;
 	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. 
@@ -100,7 +137,7 @@ void MD3Importer::InternReadFile(
 
 	// check whether the md3 file is large enough to contain
 	// at least the file header
-	size_t fileSize = file->FileSize();
+	fileSize = (unsigned int)file->FileSize();
 	if( fileSize < sizeof(MD3::Header))
 	{
 		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 &&
 		this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
 	}
 
 	// check file format version
 	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
 	if (0 == this->m_pcHeader->NUM_FRAMES)
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
 	}
 	if (0 == this->m_pcHeader->NUM_SURFACES)
 	{
+		delete[] this->mBuffer;
 		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
 	const MD3::Surface* pcSurfaces = (const MD3::Surface*)
@@ -150,11 +187,19 @@ void MD3Importer::InternReadFile(
 	pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
 	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 iNumMaterials = 0;
 	unsigned int iDefaultMatIndex = 0xFFFFFFFF;
 	while (iNum-- > 0)
 	{
+		// validate the surface
+		this->ValidateSurfaceHeaderOffsets(pcSurfaces);
+
 		// navigate to the vertex list of the surface
 		const MD3::Vertex* pcVertices = (const MD3::Vertex*)
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
@@ -171,7 +216,6 @@ void MD3Importer::InternReadFile(
 		const MD3::Shader* pcShaders = (const MD3::Shader*)
 			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
 
-
 		// if the submesh is empty ignore it
 		if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
 		{
@@ -185,14 +229,14 @@ void MD3Importer::InternReadFile(
 		aiMesh* pcMesh = pScene->mMeshes[iNum];
 
 		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->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
 		pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
 		pcMesh->mVertices = 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;
 
 		// 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].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[1] = iTemp+1;
 			pcMesh->mFaces[i].mIndices[2] = iTemp+0;
@@ -233,11 +278,11 @@ void MD3Importer::InternReadFile(
 			// make a relative path.
 			// if the MD3's internal path itself and the given path are using
 			// 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)
 			{
@@ -276,7 +321,7 @@ void MD3Importer::InternReadFile(
 
 				aiString szOut;
 				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))
 					{
@@ -294,25 +339,35 @@ void MD3Importer::InternReadFile(
 
 				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;
 				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
 
+				// add a small ambient color value - Quake 3 seems to have one
 				aiColor3D clr;
-				clr.b = clr.g = clr.r = 1.0f;
-				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
-				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
-
 				clr.b = clr.g = clr.r = 0.05f;
 				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
 
+				aiString szName;
+				szName.Set(AI_DEFAULT_MATERIAL_NAME);
+				pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
 				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
 				pcMesh->mMaterialIndex = iNumMaterials++;
 			}
@@ -343,31 +398,27 @@ void MD3Importer::InternReadFile(
 				pcMesh->mMaterialIndex = iNumMaterials++;
 			}
 		}
+		// go to the next surface
 		pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
 	}
 
 	if (0 == pScene->mNumMeshes)
 	{
 		// cleanup before returning
-		delete pScene;
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
 	}
 	pScene->mNumMaterials = iNumMaterials;
 
 	// now we need to generate an empty node graph
 	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)
-	{
-		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;
 	return;
 }

+ 10 - 0
code/MD3Loader.h

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

+ 1 - 1
code/MD4Loader.cpp

@@ -109,7 +109,7 @@ void MD4Importer::InternReadFile(
 	size_t fileSize = file->FileSize();
 	if( fileSize < sizeof(MD4::Header))
 	{
-		throw new ImportErrorException( ".mdd File is too small.");
+		throw new ImportErrorException( ".md4 File is too small.");
 	}
 	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. 
 //
 // 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
 #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.
 #endif
 
-namespace Assimp
-{
-namespace MDL
-{
+namespace Assimp	{
+namespace MDL	{
 
 // magic bytes used in Quake 1 MDL meshes
 #define AI_MDL_MAGIC_NUMBER_BE	'IDPO'
 #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
 #define AI_MDL_MAGIC_NUMBER_BE_GS4	'MDL3'
 #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,
-// but models should not exceed these limits.
+// (however it warns) but models should not exceed these limits.
 #if (!defined AI_MDL_VERSION)
 #	define AI_MDL_VERSION				6
 #endif
@@ -113,11 +116,25 @@ namespace MDL
 #	define AI_MDL_MAX_TRIANGLES			2048	
 #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
  *  \brief Data structure for the MDL main header
  */
-// ---------------------------------------------------------------------------
 struct Header
 {
 	//! magic number: "IDPO"
@@ -157,12 +174,13 @@ struct Header
 	int32_t num_frames;      
 
 	//! 0 = synchron, 1 = random . Ignored
+	//! (MDLn formats: number of texture coordinates)
 	int32_t synctype;         
 
 	//! State flag
 	int32_t flags;     
 
-	//! ???
+	//! Could be the total size of the file (and not a float)
 	float size;
 } PACK_STRUCT;
 
@@ -171,7 +189,6 @@ struct Header
 /** \struct Header_MDL7
  *  \brief Data structure for the MDL 7 main header
  */
-// ---------------------------------------------------------------------------
 struct Header_MDL7
 {
 	//! magic number: "MDL7"
@@ -181,13 +198,13 @@ struct Header_MDL7
 	int32_t	version;		
 
 	//! Number of bones in file
-	int32_t	bones_num;
+	uint32_t	bones_num;
 
 	//! Number of groups in file
-	int32_t	groups_num;
+	uint32_t	groups_num;
 
 	//! Size of data in the file
-	int32_t	data_size;	
+	uint32_t	data_size;	
 
 	//! Ignored. Used to store entity specific information
 	int32_t	entlump_size;	
@@ -195,45 +212,77 @@ struct Header_MDL7
 	//! Ignored. Used to store MED related data
 	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;
+
+	//! Size of the Skin_MDL 7 data structure used in the file
 	uint16_t skin_stc_size;
+
+	//! Size of a single color (e.g. in a material)
 	uint16_t colorvalue_stc_size;
+
+	//! Size of the Material_MDL7 data structure used in the file
 	uint16_t material_stc_size;
+
+	//! Size of a texture coordinate set in the file
 	uint16_t skinpoint_stc_size;
+
+	//! Size of a triangle in the file
 	uint16_t triangle_stc_size;
+
+	//! Size of a normal vertex in the file
 	uint16_t mainvertex_stc_size;
+
+	//! Size of a per-frame animated vertex in the file
+	//! (this is not supported)
 	uint16_t framevertex_stc_size;
+
+	//! Size of a bone animation matrix
 	uint16_t bonetrans_stc_size;
+
+	//! Size of the Frame_MDL7 data structure used in the file
 	uint16_t frame_stc_size;
 } PACK_STRUCT;
 
-
-#define AI_MDL7_MAX_BONENAMESIZE	20 
-
 // ---------------------------------------------------------------------------
 /** \struct Bone_MDL7
- *  \brief Bone in a MDL7 file
+ *  \brief Data structure for a bone in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 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;
-	uint8_t _unused_[2]; // 
+	uint8_t _unused_[2]; 
+
+	//! Relative position of the bone (relative to the
+	//! parent bone)
 	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
  *  \brief Group in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 struct Group_MDL7
 {
 	//! = '1' -> triangle based Mesh
@@ -268,14 +317,14 @@ struct Group_MDL7
 #define	AI_MDL7_SKINTYPE_MATERIAL_ASCDEF		0x20
 #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
  *  \brief Deformer in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 struct Deformer_MDL7
 {
 	int8_t	deformer_version;		// 0
@@ -291,7 +340,6 @@ struct Deformer_MDL7
 /** \struct DeformerElement_MDL7
  *  \brief Deformer element in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 struct DeformerElement_MDL7
 {
 	//! bei deformer_typ==0 (==bones) element_index == bone index
@@ -305,7 +353,6 @@ struct DeformerElement_MDL7
 /** \struct DeformerWeight_MDL7
  *  \brief Deformer weight in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 struct DeformerWeight_MDL7
 {
 	//! for deformer_typ==0 (==bones) index == vertex index
@@ -313,27 +360,14 @@ struct DeformerWeight_MDL7
 	float	weight;
 } PACK_STRUCT;
 
-// maximum length of texture file name
-#define AI_MDL7_MAX_TEXNAMESIZE		0x10
 
 // don't know why this was in the original headers ...
-// to be removed in future versions
 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
 {
 	float r,g,b,a;
@@ -341,9 +375,8 @@ struct ColorValue_MDL7
 
 // ---------------------------------------------------------------------------
 /** \struct Material_MDL7
- *  \brief Material in a MDL7 file
+ *  \brief Data structure for a Material in a MDL7 file
  */
-// ---------------------------------------------------------------------------
 struct Material_MDL7
 {
 	//! Diffuse base color of the material
@@ -365,9 +398,8 @@ struct Material_MDL7
 
 // ---------------------------------------------------------------------------
 /** \struct Skin
- *  \brief Skin data structure #1
+ *  \brief Skin data structure #1 - used by Quake1, MDL2, MDL3 and MDL4
  */
-// ---------------------------------------------------------------------------
 struct Skin
 {
 	//! 0 = single (Skin), 1 = group (GroupSkin)
@@ -387,12 +419,40 @@ struct Skin
 	uint8_t *data;  
 } PACK_STRUCT;
 
+
+// ---------------------------------------------------------------------------
+/** \struct Skin
+ *  \brief Skin data structure #2 - used by MDL5, MDL6 and MDL7
+ *  \see Skin
+ */
 struct Skin_MDL5
 {
 	int32_t size, width, height;      
 	uint8_t *data;  
 } 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
 {
 	uint16_t r : 5;
@@ -400,6 +460,10 @@ struct RGB565
 	uint16_t b : 5;
 } PACK_STRUCT;
 
+// ---------------------------------------------------------------------------
+/** \struct ARGB4
+ *  \brief Data structure for a ARGB4444 pixel in a texture
+ */
 struct ARGB4
 {
 	uint16_t a : 4;
@@ -412,7 +476,6 @@ struct ARGB4
 /** \struct GroupSkin
  *  \brief Skin data structure #2 (group of pictures)
  */
-// ---------------------------------------------------------------------------
 struct GroupSkin
 {
 	//! 0 = single (Skin), 1 = group (GroupSkin)
@@ -430,9 +493,8 @@ struct GroupSkin
 
 // ---------------------------------------------------------------------------
 /** \struct TexCoord
- *  \brief Texture coordinate data structure
+ *  \brief Texture coordinate data structure used by the Quake1 MDL format
  */
-// ---------------------------------------------------------------------------
 struct TexCoord
 {
 	//! Is the vertex on the noundary between front and back piece?
@@ -445,7 +507,10 @@ struct TexCoord
 	int32_t t;
 } PACK_STRUCT;
 
-
+// ---------------------------------------------------------------------------
+/** \struct TexCoord_MDL3
+ *  \brief Data structure for an UV coordinate in the 3DGS MDL3 format
+ */
 struct TexCoord_MDL3
 {
 	//! position, horizontally in range 0..skinwidth-1
@@ -455,6 +520,10 @@ struct TexCoord_MDL3
 	int16_t v; 
 } PACK_STRUCT;
 
+// ---------------------------------------------------------------------------
+/** \struct TexCoord_MDL7
+ *  \brief Data structure for an UV coordinate in the 3DGS MDL7 format
+ */
 struct TexCoord_MDL7
 {
 	//! position, horizontally in range 0..1
@@ -464,12 +533,26 @@ struct TexCoord_MDL7
 	float v; 
 } 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
- *  \brief Triangle data structure
+ *  \brief Triangle data structure for the Quake1 MDL format
  */
-// ---------------------------------------------------------------------------
 struct Triangle
 {
 	//! 0 = backface, 1 = frontface
@@ -479,7 +562,10 @@ struct Triangle
 	int32_t vertex[3];   
 } PACK_STRUCT;
 
-
+// ---------------------------------------------------------------------------
+/** \struct Triangle_MDL3
+ *  \brief Triangle data structure for the 3DGS MDL3 format
+ */
 struct Triangle_MDL3
 {
 	//!  Index of 3 3D vertices in range 0..numverts
@@ -489,17 +575,10 @@ struct Triangle_MDL3
 	uint16_t index_uv[3]; 
 } 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
 {
 	//! Vertex indices
@@ -509,6 +588,15 @@ struct Triangle_MDL7
 	SkinSet_MDL7  skinsets[2];
 } 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
 #if (!defined AI_MDL_BACKFACE)
@@ -522,7 +610,6 @@ struct Triangle_MDL7
 /** \struct Vertex
  *  \brief Vertex data structure
  */
-// ---------------------------------------------------------------------------
 struct Vertex
 {
 	uint8_t v[3];
@@ -530,6 +617,7 @@ struct Vertex
 } PACK_STRUCT;
 
 
+// ---------------------------------------------------------------------------
 struct Vertex_MDL4
 {
 	uint16_t v[3];
@@ -544,13 +632,12 @@ struct Vertex_MDL4
 /** \struct Vertex_MDL7
  *  \brief Vertex data structure used in MDL7 files
  */
-// ---------------------------------------------------------------------------
 struct Vertex_MDL7
 {
 	float	x,y,z;
 	uint16_t vertindex;	// = bone index
 	union {
-		uint16_t norm162index;
+		uint8_t norm162index;
 		float norm[3];
 	};
 } PACK_STRUCT;
@@ -560,7 +647,6 @@ struct Vertex_MDL7
 /** \struct BoneTransform_MDL7
  *  \brief bone transformation matrix structure used in MDL7 files
  */
-// ---------------------------------------------------------------------------
 struct BoneTransform_MDL7
 {
 	//! 4*3
@@ -582,7 +668,6 @@ struct BoneTransform_MDL7
 /** \struct Frame_MDL7
  *  \brief Frame data structure used by MDL7 files
  */
-// ---------------------------------------------------------------------------
 struct Frame_MDL7
 {
 	char	frame_name[AI_MDL7_MAX_FRAMENAMESIZE];
@@ -595,7 +680,6 @@ struct Frame_MDL7
 /** \struct SimpleFrame
  *  \brief Data structure for a simple frame
  */
-// ---------------------------------------------------------------------------
 struct SimpleFrame
 {
 	//! Minimum vertex of the bounding box
@@ -615,7 +699,6 @@ struct SimpleFrame
 /** \struct Frame
  *  \brief Model frame data structure
  */
-// ---------------------------------------------------------------------------
 struct Frame
 {
 	//! 0 = simple frame, !0 = group frame
@@ -626,6 +709,7 @@ struct Frame
 } PACK_STRUCT;
 
 
+// ---------------------------------------------------------------------------
 struct SimpleFrame_MDLn_SP
 {
 	//! Minimum vertex of the bounding box
@@ -645,7 +729,6 @@ struct SimpleFrame_MDLn_SP
 /** \struct GroupFrame
  *  \brief Data structure for a group of frames
  */
-// ---------------------------------------------------------------------------
 struct GroupFrame
 {
 	//! 0 = simple frame, !0 = group frame
@@ -675,7 +758,6 @@ struct GroupFrame
 /** \struct IntFace_MDL7
  *  \brief Internal data structure to temporarily represent a face
  */
-// ---------------------------------------------------------------------------
 struct IntFace_MDL7
 {
 	// 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
  *  original material indices.
  */
-// ---------------------------------------------------------------------------
 struct IntMaterial_MDL7
 {
 	// provide a constructor for our own convenience
@@ -717,6 +798,188 @@ struct IntMaterial_MDL7
 	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
 
 #endif // !! AI_MDLFILEHELPER_H_INC

ファイルの差分が大きいため隠しています
+ 280 - 499
code/MDLLoader.cpp


+ 252 - 53
code/MDLLoader.h

@@ -61,6 +61,11 @@ class MaterialHelper;
 
 using namespace MDL;
 
+
+#if (!defined VALIDATE_FILE_SIZE)
+#	define VALIDATE_FILE_SIZE(msg) this->SizeCheck(msg,__FILE__,__LINE__)
+#endif
+
 // ---------------------------------------------------------------------------
 /** Used to load MDL files
 */
@@ -104,38 +109,82 @@ protected:
 protected:
 
 	// -------------------------------------------------------------------
-	/** Import a quake 1 MDL file
+	/** Import a quake 1 MDL file (IDPO)
 	*/
 	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( );
 
+
+	// *******************************************************************
+	// 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
 	 * \param szData Input data
 	 * \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* piSkip);
 
@@ -143,92 +192,241 @@ protected:
 	/** Used to load textures from MDL5
 	 * \param szData Input data
 	 * \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* 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 szCurrentOut Output data pointer
 	 * \param pcMats Material list for this group. To be filled ...
 	 */
-	void ParseSkinLump_GameStudioA7(
+	void ParseSkinLump_3DGS_MDL7(
 		const unsigned char* szCurrent,
 		const unsigned char** szCurrentOut,
 		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
 	 * \param szData Current data pointer
 	 * \param iType type of the texture data. No DDS or external
 	 * \param piSkip Receive the number of bytes to skip
 	 * \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, 
 		unsigned int iType,
 		unsigned int* piSkip,
 		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
 	 * \param pcMat1 First input material
 	 * \param pcMat2 Second input material
 	 * \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* 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
 	 * \param pcBones Pointer to the bone section in the file
 	 * \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
@@ -239,16 +437,13 @@ protected:
 	 */
 	void FlipNormals(aiMesh* pcMesh);
 
-private:
-
-	/** Header of the MDL file */
-	const MDL::Header* m_pcHeader;
+protected:
 
 	/** Buffer to hold the loaded file */
 	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;
 
 	/** Output I/O handler. used to load external lmp files
@@ -258,6 +453,10 @@ private:
 	/** Output scene to be filled
 	*/
 	aiScene* pScene;
+
+	/** Size of the input file in bytes
+	 */
+	unsigned int iFileSize;
 };
 }; // 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];
+	if (iIndex > 100)return AI_FAILURE;
 
 	// get the path to the texture
 #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
 {
 
+
 // ---------------------------------------------------------------------------
 /** Internal material helper class. Can be used to fill an aiMaterial
     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[2] != 'Y' && this->mBuffer[2] != 'y')
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
 	}
 	char* szMe = (char*)&this->mBuffer[3];
@@ -136,6 +137,7 @@ void PLYImporter::InternReadFile(
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom, (unsigned int)fileSize))
 			{
+				delete[] this->mBuffer;
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
 			}
 		}
@@ -156,16 +158,19 @@ void PLYImporter::InternReadFile(
 			SkipLine(szMe,(const char**)&szMe);
 			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE, (unsigned int)fileSize))
 			{
+				delete[] this->mBuffer;
 				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
 			}
 		}
 		else
 		{
+			delete[] this->mBuffer;
 			throw new ImportErrorException( "Invalid .ply file: Unknown file format");
 		}
 	}
 	else
 	{
+		delete[] this->mBuffer;
 		throw new ImportErrorException( "Invalid .ply file: Missing format specification");
 	}
 	this->pcDOM = &sPlyDom;
@@ -193,6 +198,7 @@ void PLYImporter::InternReadFile(
 	{
 		if (avPositions.size() < 3)
 		{
+			delete[] this->mBuffer;
 			throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
 				"a face list. ");
 		}
@@ -230,6 +236,7 @@ void PLYImporter::InternReadFile(
 
 	if (avMeshes.empty())
 	{
+		delete[] this->mBuffer;
 		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 <sstream>
 
+#include "ParsingUtils.h"
+
 #include "../include/aiTypes.h"
 #include "../include/aiMesh.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

+ 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_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, 
@@ -64,10 +64,14 @@ class SplitLargeMeshesProcess_Vertex;
 // **********************************************************
 
 // 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
-#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

+ 65 - 42
code/TextureTransform.cpp

@@ -55,8 +55,7 @@ namespace Assimp
 void TextureTransform::PreProcessUVTransform(
 	Dot3DS::Texture& rcIn)
 {
-	std::string s;
-	std::stringstream ss;
+	char szTemp[512];
 	int iField;
 
 	if (rcIn.mOffsetU)
@@ -66,10 +65,10 @@ void TextureTransform::PreProcessUVTransform(
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			{
 				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;
 			}
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
@@ -77,18 +76,18 @@ void TextureTransform::PreProcessUVTransform(
 				if (0 != (iField % 2))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;
 			}
 			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;
 			}
 		}
@@ -100,10 +99,10 @@ void TextureTransform::PreProcessUVTransform(
 			if (aiTextureMapMode_Wrap == rcIn.mMapMode)
 			{
 				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;
 			}
 			else if (aiTextureMapMode_Mirror == rcIn.mMapMode)
@@ -111,18 +110,18 @@ void TextureTransform::PreProcessUVTransform(
 				if (0 != (iField % 2))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;
 			}
 			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;
 			}
 		}
@@ -132,9 +131,11 @@ void TextureTransform::PreProcessUVTransform(
 		if (iField = (int)(rcIn.mRotation / 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;
 		}
 	}
@@ -300,10 +301,16 @@ void TextureTransform::BakeScaleNOffset(
 	// it is more efficient this way ... 
 
 	if (!pcMesh->mTextureCoords[0])return;
-	if (1 == pcSrc->iBakeUVTransform)
+	if (0x1 == pcSrc->iBakeUVTransform)
 	{
 		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" 
 			" OffsetV: %f" 
 			" ScaleU: %f" 
@@ -314,7 +321,9 @@ void TextureTransform::BakeScaleNOffset(
 			pcSrc->pcSingleTexture->mScaleU,
 			pcSrc->pcSingleTexture->mScaleV,
 			pcSrc->pcSingleTexture->mRotation);
-		DefaultLogger::get()->info(std::string(szTemp));
+
+		ai_assert(0 < iLen);
+		DefaultLogger::get()->info(std::string(szTemp,iLen));
 
 		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
 		aiVector3D* apvOriginalSets[AI_MAX_NUMBER_OF_TEXTURECOORDS];
@@ -367,13 +376,14 @@ void TextureTransform::BakeScaleNOffset(
 		// now we need to find all textures in the material
 		// which require scaling/offset operations
 		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
 		// to generate.
@@ -446,7 +456,13 @@ void TextureTransform::BakeScaleNOffset(
 			pcMesh->mTextureCoords[iNum] = _pvOut;
 
 			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" 
 				" OffsetV: %f" 
 				" ScaleU: %f" 
@@ -457,7 +473,8 @@ void TextureTransform::BakeScaleNOffset(
 				(**i).fScaleU,
 				(**i).fScaleV,
 				(**i).fRotation);
-			DefaultLogger::get()->info(std::string(szTemp));
+			ai_assert(0 < iLen);
+			DefaultLogger::get()->info(std::string(szTemp,iLen));
 
 			const aiVector3D* pvBase = _pvBase;
 			aiVector3D* pvOut = _pvOut;
@@ -516,6 +533,12 @@ void TextureTransform::BakeScaleNOffset(
 			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;
 }
 // ------------------------------------------------------------------------------------------------

+ 61 - 2
code/ValidateDataStructure.cpp

@@ -342,6 +342,8 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
 void ValidateDSProcess::Validate( const aiMesh* pMesh,
 	const aiBone* pBone)
 {
+	this->Validate(&pBone->mName);
+
 	// check whether all vertices affected by this bone are valid
 	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)
 {
+	this->Validate(&pAnimation->mName);
+
 	// validate all materials
 	if (pAnimation->mNumBones)
 	{
@@ -480,7 +484,7 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
 	// check whether there are material keys that are obviously not legal
 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
 	{
-		aiMaterialProperty* prop = pMaterial->mProperties[i];
+		const aiMaterialProperty* prop = pMaterial->mProperties[i];
 		if (!prop)
 		{
 			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 "
 				"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 ...
 	}
 
+	// make some more specific tests 
 	float fTemp;
 	int 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
 	SearchForInvalidTextures(pMaterial,"diffuse");
 	SearchForInvalidTextures(pMaterial,"specular");
@@ -563,6 +596,8 @@ void ValidateDSProcess::Validate( const aiTexture* pTexture)
 void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 	 const aiBoneAnim* pBoneAnim)
 {
+	this->Validate(&pBoneAnim->mBoneName);
+
 	// check whether there is a bone with this name ...
 	unsigned int i = 0;
 	for (; i < this->mScene->mNumMeshes;++i)
@@ -649,6 +684,8 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
 	if (pNode != this->mScene->mRootNode && !pNode->mParent)
 		this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
 
+	this->Validate(&pNode->mName);
+
 	// validate all meshes
 	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 aiMaterial;
 struct aiNode;
+struct aiString;
 
 namespace Assimp
 {
@@ -158,6 +159,12 @@ protected:
 	 */
 	void Validate( const aiNode* pNode);
 
+	// -------------------------------------------------------------------
+	/** Validates a string
+	 * @param pString Input string
+	 */
+	void Validate( const aiString* pString);
+
 private:
 
 	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
 		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
-			{
+		{
 			// Otherwise ... try to search for typical strings in the
 			// texture's file name like 'bump' or 'diffuse'
 			unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
 			for( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
-				{
+			{
 				std::string sz = oldMat.mTextures[b];
+				if (!sz.length())continue;
+
 				char key[256];
 
 				// find the file name
 				const size_t iLen = sz.length();
 				std::string::size_type s = sz.rfind('\\',iLen-1);
 				if (std::string::npos == s)
-					{
+				{
 					s = sz.rfind('/',iLen-1);
 					if (std::string::npos == s)s = 0;
-					}
+				}
 
 				// cut off the file extension
 				std::string::size_type sExt = sz.rfind('.',iLen-1);
 				if (std::string::npos != sExt)
-					{
+				{
 					sz[sExt] = '\0';
-					}
+				}
 
 				// bump map
 				std::string::size_type 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("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);
-									}
 								}
 							}
 						}
 					}
+				}
 				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
-					{
+				{
 					// Normal map
 					std::string::size_type s2 = sz.find("normal",s);
 					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); 
-									}
 								}
 							}
 						}
+					}
 					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
-						{
+					{
 
 						// specular color texture (not unique, too. Could
 						// also be the material's shininess)
 						std::string::size_type s2 = sz.find("spec",s);
 						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);
-										}
 									}
 								}
 							}
+						}
 						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
-							{
+						{
 							// ambient color texture
 							std::string::size_type s2 = sz.find("ambi",s);
 							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);
-										}
 									}
 								}
+							}
 							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
-								{
+							{
 								// emissive color texture
 								std::string::size_type s2 = sz.find("emissive",s);
 								if (std::string::npos == s2)
-									{
+								{
 									s2 = sz.find("EMISSIVE",s);
 									if (std::string::npos == s2)
-										{
+									{
 										// 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);
-											}
 										}
 									}
+								}
 								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
-									{
+								{
 									// 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;
 				tex.Set( oldMat.mTextures[b] );
 
 				mat->AddProperty( &tex, key);
-				}
-
 			}
+
+		}
 		pScene->mMaterials[pScene->mNumMaterials] = mat;
 		mImportedMats[oldMat.mName] = 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 "BaseImporter.h"
 #include "fast_atof.h"
+#include "../include/DefaultLogger.h"
 
 #include <boost/format.hpp>
 
@@ -150,11 +151,11 @@ void XFileParser::ParseFile()
 		if( objectName == "}")
 		{
 			// whatever?
-			// os::Printer::log("} found in dataObject", ELL_WARNING);
+			DefaultLogger::get()->warn("} found in dataObject");
 		} else
 		{
 			// 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();
 		}
 	}
@@ -248,7 +249,7 @@ void XFileParser::ParseDataObjectFrame( Node* pParent)
 			ParseDataObjectMesh( mesh);
 		} 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();
 		}
 	}
@@ -338,7 +339,7 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
 			ParseDataObjectSkinWeights( pMesh);
 		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();
 		}
 	}
@@ -533,7 +534,7 @@ void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
 			// ignore
 		} 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();
 		}
 	}
@@ -571,7 +572,7 @@ void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
 			pMaterial->mTextures.push_back( texname);
 		} 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();
 		}
 	}
@@ -608,7 +609,7 @@ void XFileParser::ParseDataObjectAnimationSet()
 			ParseDataObjectAnimation( anim);
 		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();
 		}
 	}
@@ -644,7 +645,7 @@ void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
 			CheckForClosingBrace();
 		} 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();
 		}
 	}
@@ -748,6 +749,12 @@ void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
 	readHeadOfDataObject();
 	GetNextTokenAsString( pName);
 	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;
 
-
+// ------------------------------------------------------------------------------------------------
 MakeVerboseFormatProcess::MakeVerboseFormatProcess()
 {
 	// nothing to do here
 }
-
+// ------------------------------------------------------------------------------------------------
 MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
 {
 	// nothing to do here
 }
-// -------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 void MakeVerboseFormatProcess::Execute( aiScene* pScene)
 {
@@ -76,8 +76,7 @@ void MakeVerboseFormatProcess::Execute( aiScene* pScene)
 	else DefaultLogger::get()->debug("MakeVerboseFormatProcess. There was nothing to do.");
 
 }
-
-// -------------------------------------------------------------------
+// ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 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 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.
 <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
 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
-be used as displacement maps on a highly tesselated surface.
+be used as displacement maps on highly tesselated surfaces.
 <br>
 <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.
 <br>
 <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.
 <br>
 
-Textures are generally defined by a set of parameters, including
+Textures are generally defined by a set of parameters including
 <br>
 <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
@@ -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
 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
-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
 this property is not set.
 <br>
 <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
-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 
 if this property is not set.
 <br>
 <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
-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 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>
+<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
 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
 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
    &uv,                 // receives the UV index of the texture
    &blend,              // receives the blend factor 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)
    )) 
 {
@@ -626,6 +633,17 @@ if (AI_SUCCESS != aiGetMaterialTexture(
 }
 @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
 
 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.
 */
 // ---------------------------------------------------------------------------
-class IOStream 
+class ASSIMP_API IOStream 
 {
 protected:
 	/** Constructor protected, use IOSystem::Open() to create an instance. */

+ 3 - 1
include/IOSystem.h

@@ -11,6 +11,8 @@
 
 #include <string>
 
+#include "aiDefines.h"
+
 namespace Assimp
 {
 
@@ -23,7 +25,7 @@ class IOStream;
 * to the importer library. If you implement this interface, you also want to
 * supply a custom implementation for IOStream.
 */
-class IOSystem
+class ASSIMP_API IOSystem
 {
 public:
 	/** 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
 	 *	@brief	Abstract interface for log stream implementations.
  */
-class LogStream
+class ASSIMP_API LogStream
 {
 protected:
 	/**	@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
 #define AI_LOGGER_H_INC
 
 #include <string>
+#include "aiDefines.h"
 
 namespace Assimp
 {
@@ -12,7 +53,7 @@ class LogStream;
 /**	@class	Logger
  *	@brief	Abstract interface for logger implementations.
  */
-class Logger
+class ASSIMP_API Logger
 {
 public:
 	/**	@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" {
 #endif
 
+// Default material name
+#define AI_DEFAULT_MATERIAL_NAME "aiDefaultMat"
+
 // ---------------------------------------------------------------------------
 /** Defines type identifiers for use within the material system.
 *
@@ -235,6 +238,18 @@ struct aiMaterialProperty
     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
@@ -245,13 +260,67 @@ struct aiMaterialProperty
 *  enough for nearly all purposes. 
 */
 // ---------------------------------------------------------------------------
-struct aiMaterial
+struct ASSIMP_API aiMaterial
 {
 #ifdef __cplusplus
 protected:
     aiMaterial() {}
 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.
     */
@@ -263,6 +332,11 @@ public:
     unsigned int mNumAllocated;
 };
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
 // ---------------------------------------------------------------------------
 /** @def AI_BUILD_KEY
  * 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 
  *  sufficiently large. This will be checked via a static assertion for
  *  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.
 */
+// ---------------------------------------------------------------------------
 #if _MSC_VER >= 1400
+
+	// MSVC 8+. Use the sprintf_s function with security enhancements
 #	define AI_BUILD_KEY(base,index,out) \
 	::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
+
+	// otherwise ... simply hope the buffer is large enough :-)
 #	define AI_BUILD_KEY(base,index,out) \
 	::sprintf(out,"%s[%i]",base,index);
 #endif
@@ -416,7 +504,7 @@ public:
 
 // ---------------------------------------------------------------------------
 /** @def AI_MATKEY_TEXTURE_DIFFUSE
-*  Defines a specified diffuse texture channel of the material
+*  Defines a specific diffuse texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -429,7 +517,7 @@ public:
 #define AI_MATKEY_TEXTURE_DIFFUSE_  "$tex.file.diffuse"
 
 /** @def AI_MATKEY_TEXTURE_AMBIENT
- *  Defines a specified ambient texture channel of the material
+ *  Defines a specific ambient texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -442,7 +530,7 @@ public:
 #define AI_MATKEY_TEXTURE_AMBIENT_   "$tex.file.ambient"
 
 /** @def AI_MATKEY_TEXTURE_SPECULAR
- *  Defines a specified specular texture channel of the material
+ *  Defines a specific specular texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -455,7 +543,7 @@ public:
 #define AI_MATKEY_TEXTURE_SPECULAR_   "$tex.file.specular"
 
 /** @def AI_MATKEY_TEXTURE_EMISSIVE
- *  Defines a specified emissive texture channel of the material
+ *  Defines a specific emissive texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -468,7 +556,7 @@ public:
 #define AI_MATKEY_TEXTURE_EMISSIVE_   "$tex.file.emissive"
 
 /** @def AI_MATKEY_TEXTURE_NORMALS
- *  Defines a specified normal texture channel of the material
+ *  Defines a specific normal texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -498,7 +586,7 @@ public:
 #define AI_MATKEY_TEXTURE_HEIGHT_   "$tex.file.height"
 
 /** @def AI_MATKEY_TEXTURE_SHININESS
- *  Defines a specified shininess texture channel of the material
+ *  Defines a specific shininess texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -511,7 +599,7 @@ public:
 #define AI_MATKEY_TEXTURE_SHININESS_   "$tex.file.shininess"
 
 /** @def AI_MATKEY_TEXTURE_OPACITY
- *  Defines a specified opacity texture channel of the material
+ *  Defines a specific opacity texture channel of the material
  *  <br>
  * <b>Type:</b> string (aiString)<br>
  * <b>Default value:</b> none <br>
@@ -772,7 +860,7 @@ public:
 *         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 C_STRUCT aiMaterialProperty** pPropOut);
 
@@ -788,7 +876,7 @@ aiReturn aiGetMaterialProperty(const C_STRUCT aiMaterial* pMat,
 *        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,
     float* pOut,
     unsigned int* pMax);
@@ -817,7 +905,7 @@ inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial* pMat,
 *        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,
     int* pOut,
     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. 
 */
 // ---------------------------------------------------------------------------
-aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, 
+ASSIMP_API aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat, 
     const char* pKey,
     aiColor4D* pOut);
 
@@ -857,20 +945,11 @@ aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat,
 *	@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,
     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
  *
@@ -895,7 +974,7 @@ aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat,
  */
 // ---------------------------------------------------------------------------
 #ifdef __cplusplus
-aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
+ASSIMP_API aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
     unsigned int iIndex,
     unsigned int iTexType,
     C_STRUCT aiString* szPath,
@@ -920,5 +999,4 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* pMat,
 #include "aiMaterial.inl"
 
 #endif //!__cplusplus
-
 #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
 #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>
-inline aiReturn aiMaterialCPP::Get(const char* pKey,Type* pOut,
+inline aiReturn aiMaterial::Get(const char* pKey,Type* pOut,
 	unsigned int* pMax)
 {
 	unsigned int iNum = pMax ? *pMax : 1;
@@ -105,7 +80,7 @@ inline aiReturn aiMaterialCPP::Get(const char* pKey,Type* pOut,
 }
 // ---------------------------------------------------------------------------
 template <typename Type>
-inline aiReturn aiMaterialCPP::Get(const char* pKey,Type& pOut)
+inline aiReturn aiMaterial::Get(const char* pKey,Type& pOut)
 {
 	aiMaterialProperty* prop;
 	aiReturn ret = aiGetMaterialProperty(this,pKey,&prop);
@@ -120,39 +95,39 @@ inline aiReturn aiMaterialCPP::Get(const char* pKey,Type& pOut)
 }
 // ---------------------------------------------------------------------------
 template <>
-inline aiReturn aiMaterialCPP::Get<float>(const char* pKey,float* pOut,
+inline aiReturn aiMaterial::Get<float>(const char* pKey,float* pOut,
 	unsigned int* pMax)
 {
 	return aiGetMaterialFloatArray(this,pKey,pOut,pMax);
 }
 // ---------------------------------------------------------------------------
 template <>
-inline aiReturn aiMaterialCPP::Get<int>(const char* pKey,int* pOut,
+inline aiReturn aiMaterial::Get<int>(const char* pKey,int* pOut,
 	unsigned int* pMax)
 {
 	return aiGetMaterialIntegerArray(this,pKey,pOut,pMax);
 }
 // ---------------------------------------------------------------------------
 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);
 }
 // ---------------------------------------------------------------------------
 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);
 }
 // ---------------------------------------------------------------------------
 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);
 }
 // ---------------------------------------------------------------------------
 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);
 }

+ 3 - 2
include/aiMatrix3x3.h

@@ -9,7 +9,7 @@ extern "C" {
 struct aiMatrix4x4;
 
 // ---------------------------------------------------------------------------
-/** Represents a column-major 3x3 matrix 
+/** Represents a row-major 3x3 matrix 
 */
 // ---------------------------------------------------------------------------
 struct aiMatrix3x3
@@ -28,7 +28,8 @@ struct aiMatrix3x3
 		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);
 
 	aiMatrix3x3& operator *= (const aiMatrix3x3& m);

+ 1 - 0
include/aiMatrix3x3.inl

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

+ 31 - 1
include/aiMatrix4x4.h

@@ -7,6 +7,7 @@ extern "C" {
 #endif
 
 struct aiMatrix3x3;
+struct aiQuaternion;
 
 // Set packing to 4
 #if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
@@ -19,7 +20,7 @@ struct aiMatrix3x3;
 #endif
 
 // ---------------------------------------------------------------------------
-/** Represents a column-major 4x4 matrix, 
+/** Represents a row-major 4x4 matrix, 
 *  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;
 
+
+	/** \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
 
 	float a1, a2, a3, a4;

+ 93 - 0
include/aiMatrix4x4.inl

@@ -11,6 +11,9 @@
 #include <limits>
 #include <math.h>
 
+#include "aiAssert.h"
+#include "aiQuaternion.h"
+
 // ---------------------------------------------------------------------------
 inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
 {
@@ -144,6 +147,96 @@ inline bool aiMatrix4x4::operator!= (const aiMatrix4x4 m) const
 {
 	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 // 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
 	* 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.
-	* 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
 	* might be of interest for you.
 	*/
@@ -156,8 +158,7 @@ enum aiPostProcessSteps
  * \note The default value is AI_SLM_DEFAULT_MAX_VERTICES, defined in
  *       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.
@@ -168,7 +169,17 @@ aiReturn aiSetVertexSplitLimit(unsigned int pLimit);
  * \note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES, defined in
  *       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
 } // end of extern "C"

+ 24 - 0
include/aiQuaternion.h

@@ -16,9 +16,13 @@ struct aiQuaternion
 #ifdef __cplusplus
 	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) {}
+
 	/** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
 	aiQuaternion( const aiMatrix3x3& pRotMatrix);
 
+	/** Construct from euler angles */
+	aiQuaternion( float rotx, float roty, float rotz);
+
 	/** Returns a matrix representation of the quaternion */
 	aiMatrix3x3 GetMatrix() const;
 #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
 inline aiMatrix3x3 aiQuaternion::GetMatrix() const
@@ -91,6 +113,8 @@ inline aiMatrix3x3 aiQuaternion::GetMatrix() const
 	return resMatrix;
 }
 
+
+
 } // end extern "C"
 #endif // __cplusplus
 

+ 16 - 1
include/aiScene.h

@@ -94,6 +94,7 @@ struct aiNode
 	/** Constructor */
 	aiNode() 
 	{ 
+		// set all members to zero by default
 		mParent = NULL; 
 		mNumChildren = 0; mChildren = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
@@ -102,6 +103,7 @@ struct aiNode
 	/** Destructor */
 	~aiNode()
 	{
+		// delete al children recursively
 		for( unsigned int a = 0; a < mNumChildren; a++)
 			delete mChildren[a];
 		delete [] mChildren;
@@ -127,6 +129,8 @@ struct aiScene
 	*/
 	C_STRUCT aiNode* mRootNode;
 
+
+
 	/** The number of meshes in the scene. */
 	unsigned int mNumMeshes;
 
@@ -137,6 +141,8 @@ struct aiScene
 	*/
 	C_STRUCT aiMesh** mMeshes;
 
+
+
 	/** The number of materials in the scene. */
 	unsigned int mNumMaterials;
 
@@ -147,6 +153,8 @@ struct aiScene
 	*/
 	C_STRUCT aiMaterial** mMaterials;
 
+
+
 	/** The number of animations in the scene. */
 	unsigned int mNumAnimations; 
 
@@ -157,20 +165,25 @@ struct aiScene
 	*/
 	C_STRUCT aiAnimation** mAnimations;
 
+
+
 	/** The number of textures embedded into the file */
 	unsigned int mNumTextures;
 
 	/** The array of embedded textures.
 	* 
 	* 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)
 	*/
 	C_STRUCT aiTexture** mTextures;
 
 #ifdef __cplusplus
+
+	//! Default constructor
 	aiScene()
 	{
+		// set all members to zero by default
 		mRootNode = NULL;
 		mNumMeshes = 0; mMeshes = NULL;
 		mNumMaterials = 0; mMaterials = NULL;
@@ -178,8 +191,10 @@ struct aiScene
 		mNumTextures = 0; mTextures = NULL;
 	}
 
+	//! Destructor
 	~aiScene()
 	{
+		// delete all subobjects recursively
 		delete mRootNode;
 		for( unsigned int a = 0; a < mNumMeshes; a++)
 			delete mMeshes[a];

+ 15 - 0
include/aiTexture.h

@@ -68,6 +68,21 @@ struct aiTexel
 	unsigned char g;
 	unsigned char r;
 	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;
 
 // 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 <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 "aiMatrix3x3.h"
 #include "aiMatrix4x4.h"
@@ -59,13 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifdef __cplusplus
 #	include <string>
 extern "C" {
-#	define C_STRUCT
-#else
-#	if (defined ASSIMP_DOXYGEN_BUILD)
-#		define C_STRUCT
-#	else
-#		define C_STRUCT struct
-#	endif
 #endif
 
 /** Maximum dimension for strings, ASSIMP strings are zero terminated */
@@ -145,14 +137,14 @@ struct aiString
 	inline aiString() :
 		length(0) 
 	{
-		// empty
+		data[0] = '\0';
 	}
 
 	//! construction from a given std::string
 	inline aiString(const aiString& rOther) : 
 		length(rOther.length) 
 	{
-		memcpy( data, rOther.data, rOther.length);
+		::memcpy( data, rOther.data, rOther.length);
 		this->data[this->length] = '\0';
 	}
 
@@ -162,7 +154,7 @@ struct aiString
 		if( pString.length() > MAXLEN - 1)
 			return;
 		length = pString.length();
-		memcpy( data, pString.c_str(), length);
+		::memcpy( data, pString.c_str(), length);
 		data[length] = 0;
 	}
 
@@ -177,7 +169,7 @@ struct aiString
 	bool operator!=(const aiString& other) const
 	{
 		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.
 *
-* 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
 {
 	//! 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. 
 */
 // ---------------------------------------------------------------------------
-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. 
 */
 // ---------------------------------------------------------------------------
-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.
 */
 // ---------------------------------------------------------------------------
-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.
 */
 // ---------------------------------------------------------------------------
-const char* aiGetErrorString();
+ASSIMP_API const char* aiGetErrorString();
 
 
 // ---------------------------------------------------------------------------
@@ -126,7 +127,7 @@ const char* aiGetErrorString();
 * @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.
 */
 // ---------------------------------------------------------------------------
-void aiGetExtensionList(C_STRUCT aiString* szOut);
+ASSIMP_API void aiGetExtensionList(C_STRUCT aiString* szOut);
 
 #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 <vector>
 
+#include "aiDefines.h"
+
 struct aiScene;
 
 namespace Assimp
@@ -81,7 +83,7 @@ class IOSystem;
 * @note One Importer instance is not thread-safe. If you use multiple
 * threads for loading each thread should manage its own Importer instance.
 */
-class Importer
+class ASSIMP_API Importer
 {
 	// used internally
 	friend class BaseProcess;
@@ -101,6 +103,7 @@ public:
 	 */
 	~Importer();
 
+
 	// -------------------------------------------------------------------
 	/** 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 
@@ -112,10 +115,31 @@ public:
 	 * afterwards. The previously assigned handler will be deleted.
 	 *
 	 * @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);
 
+
+	// -------------------------------------------------------------------
+	/** 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. 
 	* 
@@ -127,18 +151,19 @@ public:
 	* GetErrorString().
 	* @param pFile Path and filename to the file to be imported.
 	* @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.
 	*/
 	const aiScene* ReadFile( const std::string& pFile, unsigned int pFlags);
 
+
 	// -------------------------------------------------------------------
 	/** Returns an error description of an error that occured in ReadFile(). 
 	*
 	* Returns an empty string if no error occured.
 	* @return A description of the last error, an empty string if no 
-	*         error occured.
+	*   error occured.
 	*/
 	inline const std::string& GetErrorString() const 
 		{ return mErrorString; }
@@ -147,20 +172,20 @@ public:
 	// -------------------------------------------------------------------
 	/** 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
 	*/
 	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
 	* mean that ASSIMP is able to load all files with this extension.
 	* @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);
 
@@ -174,12 +199,15 @@ public:
 		{return this->mScene;}
 
 private:
+
 	/** Empty copy constructor. */
 	Importer(const Importer &other);
 
 protected:
+
 	/** IO handler to use for all file accesses. */
 	IOSystem* mIOHandler;
+	bool mIsDefaultHandler;
 
 	/** Format-specific importer worker objects - 
 	 * one for each format we can read. */

+ 1 - 1
port/jAssimp/assimp.iml

@@ -7,7 +7,7 @@
         <attribute name="URI" value="/" />
       </containerElement>
     </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="mainClass" value="" />
   </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;
 
 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
@@ -59,6 +64,65 @@ import java.util.Vector;
  */
 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
      * Empty by default.
@@ -83,17 +147,27 @@ public class Importer {
      */
     private String path = null;
 
+    /**
+     * I/O system to be used
+     */
+    private IOSystem ioSystem = null;
+
     /**
      * Public constructor. Initialises the JNI bridge to the native
      * ASSIMP library. A native Assimp::Importer object is constructed and
      * 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
      *                     or if the entry point to the module wasn't found. if this exception
      *                     is not thrown, you can assume that jAssimp is fully available.
      */
-    public Importer() 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
          * 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
         // data structures outside the VM.
         try {
-            if (0xffffffffffffffffl == (this.m_iNativeHandle = _NativeInitContext())) {
+            if (0xffffffffffffffffl == (this.m_iNativeHandle = _NativeInitContext(iVersion))) {
                 throw new NativeError(
                         "Unable to initialize the native library context." +
                                 "The initialization routine has failed");
@@ -125,6 +199,32 @@ public class Importer {
         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
@@ -213,6 +313,8 @@ public class Importer {
             else if (step.equals(PostProcessStep.GenSmoothNormals)) flags |= 0x40;
             else if (step.equals(PostProcessStep.SplitLargeMeshes)) flags |= 0x80;
             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
@@ -227,6 +329,7 @@ public class Importer {
         }
         catch (NativeError exc) {
 
+            // delete everything ...
             this.scene = null;
             this.path = null;
             throw exc;
@@ -247,9 +350,8 @@ public class Importer {
 
         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)) {
             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>
      * 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
      */
-    private native int _NativeInitContext();
+    private native int _NativeInitContext(int version);
 
     /**
      * 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;
 
 
+
 /**
  * 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
@@ -52,6 +53,74 @@ package assimp;
  * @version 1.0
  */
 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
      *

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

@@ -183,29 +183,9 @@ public class Mesh extends Mappable {
         assert (parent instanceof Scene);
 
         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
-     * Retrieve the number of vertices in the mesh
+     * Initialise class members
      *
      * @param context Current importer context (imp.hashCode)
      * @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

+ 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;
 
+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 {
+
+
+    /**
+     * 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
  * 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)
  * @version 1.0
@@ -62,8 +62,15 @@ public class PostProcessStep {
      */
     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_iTriangleSplitLimit = DEFAULT_TRIANGLE_SPLIT_LIMIT;
+    private static int s_iBoneWeightLimit = DEFAULT_BONE_WEIGHT_LIMIT;
 
     /**
      * Identifies and joins identical vertex data sets within all imported
@@ -154,6 +161,31 @@ public class PostProcessStep {
     public static final 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
@@ -191,6 +223,22 @@ public class PostProcessStep {
         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
      *
@@ -205,6 +253,13 @@ public class PostProcessStep {
      */
     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
 

+ 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)
 {
 	char szTempB[MAX_PATH];
-
 	strcpy(szTempB,szTemp);
 
 	// 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;
 	szFile++;
-	char* szExt = strrchr(szFile,'.')+1;
+	char* szExt = strrchr(szFile,'.');
+	if (!szExt)return false;
+	szExt++;
 	*szFile = 0;
 
 	strcat(szTempB,"*.*");
@@ -163,9 +164,10 @@ bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString)
 		{
 			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))
 					{
 						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" />
   </component>
   <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_HIERARCHY" value="true" />
     <option name="OPTION_NAVIGATOR" value="true" />
@@ -178,7 +178,7 @@
   </component>
   <component name="ProjectModuleManager">
     <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>
   </component>
   <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"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="true"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 			/>
 			<Tool
@@ -174,6 +174,7 @@
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -294,11 +295,12 @@
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				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"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -367,7 +369,7 @@
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				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"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
@@ -437,12 +439,13 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				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"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 			/>
 			<Tool
@@ -509,7 +512,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				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"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
@@ -584,11 +587,12 @@
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				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"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 			/>
 			<Tool
 				Name="VCManagedResourceCompilerTool"
@@ -658,7 +662,7 @@
 				EnableIntrinsicFunctions="true"
 				FavorSizeOrSpeed="1"
 				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"
 				BufferSecurityCheck="false"
 				EnableEnhancedInstructionSet="2"
@@ -729,12 +733,13 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				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"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="4"
 			/>
 			<Tool
@@ -802,7 +807,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				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"
 				SmallerTypeCheck="true"
 				RuntimeLibrary="1"
@@ -865,6 +870,10 @@
 				RelativePath="..\..\include\aiAssert.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\include\aiDefines.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\include\aiFileIO.h"
 				>
@@ -1021,10 +1030,18 @@
 				RelativePath="..\..\code\MaterialSystem.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\code\ParsingUtils.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\code\PretransformVertices.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\code\qnan.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\code\SpatialSort.h"
 				>
@@ -1217,39 +1234,39 @@
 				Name="jAssimp"
 				>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Importer.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Importer.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Material.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Material.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Mesh.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\jAssimp\assimp_Mesh.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Node.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\jAssimp\assimp_Node.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_PostProcessStep.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_PostProcessStep.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Scene.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Scene.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\assimp_Texture.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\assimp_Texture.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\JNIEnvironment.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.h"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\JNILogger.h"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h"
 					>
 				</File>
 			</Filter>
@@ -1261,6 +1278,22 @@
 					>
 				</File>
 			</Filter>
+			<Filter
+				Name="HMPLoader"
+				>
+				<File
+					RelativePath="..\..\code\HMPLoader.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="SMDLoader"
+				>
+				<File
+					RelativePath="..\..\code\SMDLoader.h"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		<Filter
 			Name="sources"
@@ -1436,6 +1469,10 @@
 					RelativePath="..\..\code\MDLLoader.cpp"
 					>
 				</File>
+				<File
+					RelativePath="..\..\code\MDLMaterialLoader.cpp"
+					>
+				</File>
 			</Filter>
 			<Filter
 				Name="ASELoader"
@@ -1461,15 +1498,15 @@
 				Name="jAssimp"
 				>
 				<File
-					RelativePath="..\..\code\jAssimp\JNICalls.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNICalls.cpp"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\JNIEnvironment.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNIEnvironment.cpp"
 					>
 				</File>
 				<File
-					RelativePath="..\..\code\jAssimp\JNILogger.cpp"
+					RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.cpp"
 					>
 				</File>
 			</Filter>
@@ -1481,6 +1518,22 @@
 					>
 				</File>
 			</Filter>
+			<Filter
+				Name="HMPLoader"
+				>
+				<File
+					RelativePath="..\..\code\HMPLoader.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="SMDLoader"
+				>
+				<File
+					RelativePath="..\..\code\SMDLoader.cpp"
+					>
+				</File>
+			</Filter>
 		</Filter>
 		<Filter
 			Name="doc"

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません