Browse Source

Initiual commit: AssetImporter source moved from ZFXCE repository to its own repository. PLease do not use the ZFXCE repo any more.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
kimmi 17 years ago
commit
b76f999cb7
100 changed files with 16507 additions and 0 deletions
  1. 0 0
      CREDITS
  2. 0 0
      INSTALL
  3. 5 0
      README
  4. 827 0
      code/3DSConverter.cpp
  5. 134 0
      code/3DSGenNormals.cpp
  6. 472 0
      code/3DSHelper.h
  7. 1310 0
      code/3DSLoader.cpp
  8. 235 0
      code/3DSLoader.h
  9. 148 0
      code/3DSSpatialSort.cpp
  10. 83 0
      code/3DSSpatialSort.h
  11. 61 0
      code/Assimp.cpp
  12. 45 0
      code/BaseImporter.cpp
  13. 105 0
      code/BaseImporter.h
  14. 59 0
      code/BaseProcess.h
  15. 186 0
      code/CalcTangentsProcess.cpp
  16. 55 0
      code/CalcTangentsProcess.h
  17. 155 0
      code/ConvertToLHProcess.cpp
  18. 88 0
      code/ConvertToLHProcess.h
  19. 92 0
      code/DefaultIOStream.cpp
  20. 82 0
      code/DefaultIOStream.h
  21. 64 0
      code/DefaultIOSystem.cpp
  22. 40 0
      code/DefaultIOSystem.h
  23. 67 0
      code/GenFaceNormalsProcess.cpp
  24. 48 0
      code/GenFaceNormalsProcess.h
  25. 104 0
      code/GenVertexNormalsProcess.cpp
  26. 47 0
      code/GenVertexNormalsProcess.h
  27. 136 0
      code/Importer.cpp
  28. 262 0
      code/JoinVerticesProcess.cpp
  29. 65 0
      code/JoinVerticesProcess.h
  30. 43 0
      code/KillNormalsProcess.cpp
  31. 47 0
      code/KillNormalsProcess.h
  32. 137 0
      code/MD2FileData.h
  33. 310 0
      code/MD2Loader.cpp
  34. 57 0
      code/MD2Loader.h
  35. 171 0
      code/MD2NormalTable.h
  36. 273 0
      code/MD3FileData.h
  37. 328 0
      code/MD3Loader.cpp
  38. 60 0
      code/MD3Loader.h
  39. 0 0
      code/MD4FileData.h
  40. 57 0
      code/MD4Loader.h
  41. 57 0
      code/MD5Loader.h
  42. 285 0
      code/MaterialSystem.cpp
  43. 187 0
      code/MaterialSystem.h
  44. 170 0
      code/ObjFileData.h
  45. 354 0
      code/ObjFileImporter.cpp
  46. 78 0
      code/ObjFileImporter.h
  47. 56 0
      code/ObjFileMtlImporter.cpp
  48. 36 0
      code/ObjFileMtlImporter.h
  49. 551 0
      code/ObjFileParser.cpp
  50. 56 0
      code/ObjTools.h
  51. 990 0
      code/PlyLoader.cpp
  52. 99 0
      code/PlyLoader.h
  53. 874 0
      code/PlyParser.cpp
  54. 483 0
      code/PlyParser.h
  55. 93 0
      code/SpatialSort.cpp
  56. 72 0
      code/SpatialSort.h
  57. 227 0
      code/SplitLargeMeshes.cpp
  58. 60 0
      code/SplitLargeMeshes.h
  59. 83 0
      code/TriangulateProcess.cpp
  60. 54 0
      code/TriangulateProcess.h
  61. 147 0
      code/XFileHelper.h
  62. 703 0
      code/XFileImporter.cpp
  63. 97 0
      code/XFileImporter.h
  64. 1182 0
      code/XFileParser.cpp
  65. 119 0
      code/XFileParser.h
  66. 26 0
      code/aiAssert.cpp
  67. 114 0
      code/fast_atof.h
  68. BIN
      doc/ImporterNotes.rtf
  69. 83 0
      doc/datastructure.xml
  70. 222 0
      include/Compiler/VisualStudio/stdint.h
  71. 111 0
      include/IOStream.h
  72. 90 0
      include/IOSystem.h
  73. 68 0
      include/ObjFileParser.h
  74. 118 0
      include/aiAnim.h
  75. 28 0
      include/aiAssert.h
  76. 23 0
      include/aiDefs.h
  77. 63 0
      include/aiFileIO.h
  78. 476 0
      include/aiMaterial.h
  79. 50 0
      include/aiMatrix3x3.h
  80. 54 0
      include/aiMatrix3x3.inl
  81. 77 0
      include/aiMatrix4x4.h
  82. 136 0
      include/aiMatrix4x4.inl
  83. 268 0
      include/aiMesh.h
  84. 78 0
      include/aiPostProcess.h
  85. 96 0
      include/aiQuaternion.h
  86. 147 0
      include/aiScene.h
  87. 140 0
      include/aiTypes.h
  88. 99 0
      include/aiVector3D.h
  89. 33 0
      include/aiVector3D.inl
  90. 77 0
      include/assimp.h
  91. 123 0
      include/assimp.hpp
  92. BIN
      test/3DSFiles/CWALL02.jpg
  93. BIN
      test/3DSFiles/IMAGE1.bmp
  94. BIN
      test/3DSFiles/IMAGE2.jpg
  95. BIN
      test/3DSFiles/cube_with_diffuse_texture.3DS
  96. BIN
      test/3DSFiles/cube_with_specular_texture.3DS
  97. BIN
      test/3DSFiles/test.png
  98. BIN
      test/3DSFiles/test1.3ds
  99. 1 0
      test/3DSFiles/textures.txt
  100. 135 0
      tools/assimp_view/AssetHelper.h

+ 0 - 0
CREDITS


+ 0 - 0
INSTALL


+ 5 - 0
README

@@ -0,0 +1,5 @@
+		 AssetImporter
+		---------------
+To use the asset importer lbrary just compile it with visual-c++ 8.0 (at this 
+moment no other build enviroments are supported).
+	

+ 827 - 0
code/3DSConverter.cpp

@@ -0,0 +1,827 @@
+/** @file Implementation of the 3ds importer class */
+#include "3DSLoader.h"
+#include "MaterialSystem.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;
+
+
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ReplaceDefaultMaterial()
+{
+	// try to find an existing material that matches the
+	// typical default material setting:
+	// - no textures
+	// - diffuse color (in grey!)
+	// NOTE: This is here to workaround the fact that some
+	// exporters are writing a default material, too.
+	unsigned int iIndex = 0xcdcdcdcd;
+	for (unsigned int i = 0; i < this->mScene->mMaterials.size();++i)
+	{
+	if (std::string::npos == this->mScene->mMaterials[i].mName.find("default") &&
+		std::string::npos == this->mScene->mMaterials[i].mName.find("DEFAULT"))continue;
+
+	if (this->mScene->mMaterials[i].mDiffuse.r !=
+		this->mScene->mMaterials[i].mDiffuse.g ||
+		this->mScene->mMaterials[i].mDiffuse.r !=
+		this->mScene->mMaterials[i].mDiffuse.b)continue;
+
+	if (this->mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0	||
+		this->mScene->mMaterials[i].sTexBump.mMapName.length()!= 0		|| 
+		this->mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0	||
+		this->mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0	||
+		this->mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0	||
+		this->mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )continue;
+
+	iIndex = i;
+	}
+	if (0xcdcdcdcd == iIndex)iIndex = this->mScene->mMaterials.size();
+
+	// now iterate through all meshes and through all faces and
+	// find all faces that are using the default material
+	unsigned int iCnt = 0;
+	for (std::vector<Dot3DS::Mesh>::iterator
+		i =  this->mScene->mMeshes.begin();
+		i != this->mScene->mMeshes.end();++i)
+	{
+		for (std::vector<unsigned int>::iterator
+			a =  (*i).mFaceMaterials.begin();
+			a != (*i).mFaceMaterials.end();++a)
+		{
+			// NOTE: The additional check seems to be necessary,
+			// some exporters seem to generate invalid data here
+			if (0xcdcdcdcd == (*a) || (*a) >= this->mScene->mMaterials.size())
+			{
+				(*a) = iIndex;
+				++iCnt;
+			}
+		}
+	}
+	if (0 != iCnt && iIndex == this->mScene->mMaterials.size())
+	{
+		// we need to create our own default material
+		Dot3DS::Material sMat;
+		sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
+		sMat.mName = "%%%DEFAULT";
+		this->mScene->mMaterials.push_back(sMat);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh)
+{
+	for (std::vector< Dot3DS::Face >::iterator
+		 i =  sMesh->mFaces.begin();
+		 i != sMesh->mFaces.end();++i)
+	{
+		// check whether all indices are in range
+		if ((*i).i1 >= sMesh->mPositions.size())
+		{
+			(*i).i1 = sMesh->mPositions.size()-1;
+		}
+		if ((*i).i2 >= sMesh->mPositions.size())
+		{
+			(*i).i2 = sMesh->mPositions.size()-1;
+		}
+		if ((*i).i3 >= sMesh->mPositions.size())
+		{
+			(*i).i3 = sMesh->mPositions.size()-1;
+		}
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh)
+{
+	std::vector<aiVector3D> vNew;
+	vNew.resize(sMesh->mFaces.size() * 3);
+
+	std::vector<aiVector2D> vNew2;
+
+	// TODO: Remove this step. By maintaining a small LUT it
+	// would be possible to do this directly in the parsing step
+	unsigned int iBase = 0;
+
+	if (0 != sMesh->mTexCoords.size())
+	{
+		vNew2.resize(sMesh->mFaces.size() * 3);
+		for (unsigned int i = 0; i < sMesh->mFaces.size();++i)
+		{
+			// position and texture coordinates
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i1];
+			vNew2[iBase]   = sMesh->mTexCoords[sMesh->mFaces[i].i1];
+			sMesh->mFaces[i].i1 = iBase++;
+
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i2];
+			vNew2[iBase]   = sMesh->mTexCoords[sMesh->mFaces[i].i2];
+			sMesh->mFaces[i].i2 = iBase++;
+
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i3];
+			vNew2[iBase]   = sMesh->mTexCoords[sMesh->mFaces[i].i3];
+			sMesh->mFaces[i].i3 = iBase++;
+		}
+	}
+	else
+	{
+		for (unsigned int i = 0; i < sMesh->mFaces.size();++i)
+		{
+			// position only
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i1];
+			sMesh->mFaces[i].i1 = iBase++;
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i2];
+			sMesh->mFaces[i].i2 = iBase++;
+			vNew[iBase]   = sMesh->mPositions[sMesh->mFaces[i].i3];
+			sMesh->mFaces[i].i3 = iBase++;
+		}
+	}
+	sMesh->mPositions = vNew;
+	sMesh->mTexCoords = vNew2;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat,
+	MaterialHelper& mat)
+{
+	// NOTE: Pass the background image to the viewer by bypassing the
+	// material system. This is an evil hack, never do it  again!
+	if (0 != this->mBackgroundImage.length() && this->bHasBG)
+		{
+		aiString tex;
+		tex.Set( this->mBackgroundImage);
+		mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
+
+		// be sure this is only done for the first material
+		this->mBackgroundImage = std::string("");
+		}
+
+	// At first add the base ambient color of the
+	// scene to	the material
+	oldMat.mAmbient.r += this->mClrAmbient.r;
+	oldMat.mAmbient.g += this->mClrAmbient.g;
+	oldMat.mAmbient.b += this->mClrAmbient.b;
+
+	aiString name;
+	name.Set( oldMat.mName);
+	mat.AddProperty( &name, AI_MATKEY_NAME);
+
+	// material colors
+	mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+	mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+	mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+	mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+	mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+
+	// opacity
+	mat.AddProperty<float>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
+
+	// bump height scaling
+	mat.AddProperty<float>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
+
+	// shading mode
+	aiShadingMode eShading = aiShadingMode_NoShading;
+	switch (oldMat.mShading)
+	{
+		case Dot3DS::Dot3DSFile::Flat:
+			eShading = aiShadingMode_Flat; break;
+		case Dot3DS::Dot3DSFile::Phong :
+			eShading = aiShadingMode_Phong; break;
+
+		// I don't know what "Wire" shading should be,
+		// assume it is simple lambertian diffuse (L dot N) shading
+		case Dot3DS::Dot3DSFile::Wire:
+		case Dot3DS::Dot3DSFile::Gouraud:
+			eShading = aiShadingMode_Gouraud; break;
+
+		// assume cook-torrance shading for metals.
+		// NOTE: I assume the real shader inside 3ds max is an anisotropic
+		// Phong-Blinn shader, but this is a good approximation too
+		case Dot3DS::Dot3DSFile::Metal :
+			eShading = aiShadingMode_CookTorrance; break;
+	}
+	mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+
+	// texture, if there is one
+	if( oldMat.sTexDiffuse.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexDiffuse.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
+		mat.AddProperty<float>( &oldMat.sTexDiffuse.mTextureBlend, 1, AI_MATKEY_TEXBLEND_DIFFUSE(0));
+	}
+	if( oldMat.sTexSpecular.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexSpecular.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(0));
+		mat.AddProperty<float>( &oldMat.sTexSpecular.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SPECULAR(0));
+	}
+	if( oldMat.sTexOpacity.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexOpacity.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_OPACITY(0));
+		mat.AddProperty<float>( &oldMat.sTexOpacity.mTextureBlend, 1,AI_MATKEY_TEXBLEND_OPACITY(0));
+	}
+	if( oldMat.sTexEmissive.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexEmissive.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(0));
+		mat.AddProperty<float>( &oldMat.sTexEmissive.mTextureBlend, 1, AI_MATKEY_TEXBLEND_EMISSIVE(0));
+	}
+	if( oldMat.sTexBump.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexBump.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_BUMP(0));
+		mat.AddProperty<float>( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_BUMP(0));
+	}
+	if( oldMat.sTexShininess.mMapName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.sTexShininess.mMapName);
+		mat.AddProperty( &tex, AI_MATKEY_TEXTURE_SHININESS(0));
+		mat.AddProperty<float>( &oldMat.sTexBump.mTextureBlend, 1, AI_MATKEY_TEXBLEND_SHININESS(0));
+	}
+
+	// store the name of the material itself, too
+	if( oldMat.mName.length() > 0)
+	{
+		aiString tex;
+		tex.Set( oldMat.mName);
+		mat.AddProperty( &tex, AI_MATKEY_NAME);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void SetupMatUVSrc (aiMaterial* pcMat, const Dot3DS::Material* pcMatIn)
+	{
+	MaterialHelper* pcHelper = (MaterialHelper*)pcMat;
+	pcHelper->AddProperty<int>(&pcMatIn->sTexDiffuse.iUVSrc,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
+	pcHelper->AddProperty<int>(&pcMatIn->sTexSpecular.iUVSrc,1,AI_MATKEY_UVWSRC_SPECULAR(0));
+	pcHelper->AddProperty<int>(&pcMatIn->sTexEmissive.iUVSrc,1,AI_MATKEY_UVWSRC_EMISSIVE(0));
+	pcHelper->AddProperty<int>(&pcMatIn->sTexBump.iUVSrc,1,AI_MATKEY_UVWSRC_BUMP(0));
+	pcHelper->AddProperty<int>(&pcMatIn->sTexShininess.iUVSrc,1,AI_MATKEY_UVWSRC_SHININESS(0));
+	pcHelper->AddProperty<int>(&pcMatIn->sTexOpacity.iUVSrc,1,AI_MATKEY_UVWSRC_OPACITY(0));
+	}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ConvertMeshes(aiScene* pcOut)
+{
+	std::vector<aiMesh*> avOutMeshes;
+	avOutMeshes.reserve(this->mScene->mMeshes.size() * 2);
+
+	unsigned int iFaceCnt = 0;
+
+	// we need to split all meshes by their materials
+	for (std::vector<Dot3DS::Mesh>::iterator
+		i =  this->mScene->mMeshes.begin();
+		i != this->mScene->mMeshes.end();++i)
+	{
+		std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[
+			this->mScene->mMaterials.size()];
+
+		unsigned int iNum = 0;
+		for (std::vector<unsigned int>::const_iterator
+			a =  (*i).mFaceMaterials.begin();
+			a != (*i).mFaceMaterials.end();++a,++iNum)
+		{
+		// check range
+		if ((*a) >= this->mScene->mMaterials.size())
+			{
+			// use the last material instead
+			aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum);
+			}
+		else aiSplit[*a].push_back(iNum);
+		}
+		// now generate submeshes
+#if 0
+		bool bFirst = true;
+#endif
+		for (unsigned int p = 0; p < this->mScene->mMaterials.size();++p)
+		{
+			if (aiSplit[p].size() != 0)
+			{
+				aiMesh* p_pcOut = new aiMesh();
+
+				// be sure to setup the correct material index
+				p_pcOut->mMaterialIndex = p;
+
+				// use the color data as temporary storage
+				p_pcOut->mColors[0] = (aiColor4D*)new std::string((*i).mName);
+				avOutMeshes.push_back(p_pcOut);
+
+#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 = aiSplit[p].size()*3;
+				p_pcOut->mNumFaces = aiSplit[p].size();
+
+				iFaceCnt += p_pcOut->mNumFaces;
+				if (p_pcOut->mNumVertices != 0)
+				{
+					p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices];
+					p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices];
+					unsigned int iBase = 0;
+
+					p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
+					for (unsigned int q = 0; q < aiSplit[p].size();++q)
+					{
+						unsigned int iIndex = aiSplit[p][q];
+
+						p_pcOut->mFaces[q].mIndices = new unsigned int[3];
+						p_pcOut->mFaces[q].mNumIndices = 3;
+
+						p_pcOut->mFaces[q].mIndices[0] = iBase;
+						p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i1];
+						p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i1];
+
+						p_pcOut->mFaces[q].mIndices[1] = iBase;
+						p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i2];
+						p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i2];
+
+						p_pcOut->mFaces[q].mIndices[2] = iBase;
+						p_pcOut->mVertices[iBase] = (*i).mPositions[(*i).mFaces[iIndex].i3];
+						p_pcOut->mNormals[iBase++] = (*i).mNormals[(*i).mFaces[iIndex].i3];
+					}
+				}
+				// convert texture coordinates
+				if ((*i).mTexCoords.size() != 0)
+				{
+					p_pcOut->mTextureCoords[0] = new aiVector3D[p_pcOut->mNumVertices];
+
+					unsigned int iBase = 0;
+					for (unsigned int q = 0; q < aiSplit[p].size();++q)
+					{
+						unsigned int iIndex2 = aiSplit[p][q];
+
+						unsigned int iIndex = (*i).mFaces[iIndex2].i1;
+						aiVector2D& pc = (*i).mTexCoords[iIndex];
+						p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
+
+						iIndex = (*i).mFaces[iIndex2].i2;
+						pc = (*i).mTexCoords[iIndex];
+						p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
+
+						iIndex = (*i).mFaces[iIndex2].i3;
+						pc = (*i).mTexCoords[iIndex];
+						p_pcOut->mTextureCoords[0][iBase++] = aiVector3D(pc.x,pc.y,0.0f);
+					}
+					// apply texture coordinate scalings
+					this->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;
+				}
+			}
+		}
+		delete[] aiSplit;
+	}
+	pcOut->mNumMeshes = avOutMeshes.size();
+	pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
+	for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
+	{
+		pcOut->mMeshes[a] = avOutMeshes[a];
+	}
+
+	if (0 == iFaceCnt)
+	{
+		throw new ImportErrorException("No faces loaded. The mesh is empty");
+	}
+
+	// for each material in the scene we need to setup the UV source
+	// set for each texture
+	for (unsigned int a = 0; a < pcOut->mNumMaterials;++a)
+	{
+		SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] );
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn)
+{
+	// find the corresponding mesh indices
+	std::vector<unsigned int> iArray;
+
+	if (pcIn->mName != "$$$DUMMY")
+	{		
+		for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
+		{
+			if (0 == ASSIMP_stricmp(pcIn->mName.c_str(),
+				((std::string*)pcSOut->mMeshes[a]->mColors[0])->c_str()))
+			{
+				iArray.push_back(a);
+			}
+		}
+	}
+	pcOut->mName.Set(pcIn->mName);
+	pcOut->mNumMeshes = iArray.size();
+	pcOut->mMeshes = new unsigned int[iArray.size()];
+	
+	for (unsigned int i = 0;i < iArray.size();++i)
+	{
+		const unsigned int iIndex = iArray[i];
+#if 0
+		if (NULL != pcSOut->mMeshes[iIndex]->mColors[1])
+		{
+			pcOut->mTransformation = *((aiMatrix4x4*)
+				(pcSOut->mMeshes[iIndex]->mColors[1]));
+
+			delete (aiMatrix4x4*)pcSOut->mMeshes[iIndex]->mColors[1];
+			pcSOut->mMeshes[iIndex]->mColors[1] = NULL;
+		}
+#endif
+		pcOut->mMeshes[i] = iIndex;
+	}
+
+	// NOTE: Not necessary. We can use the given transformation matrix.
+	// However, we'd need it if we wanted to implement keyframe animation
+
+#if 0
+	// build the scaling matrix. Toggle y and z axis
+	aiMatrix4x4 mS;
+	mS.a1 = pcIn->vScaling.x;
+	mS.b2 = pcIn->vScaling.z;
+	mS.c3 = pcIn->vScaling.y;
+
+	// build the translation matrix. Toggle y and z axis
+	aiMatrix4x4 mT;
+	mT.a4 = pcIn->vPosition.x;
+	mT.b4 = pcIn->vPosition.z;
+	mT.c4 = pcIn->vPosition.y;
+
+	// build the pivot matrix. Toggle y and z axis
+	aiMatrix4x4 mP;
+	mP.a4 = pcIn->vPivot.x;
+	mP.b4 = pcIn->vPivot.z;
+	mP.c4 = pcIn->vPivot.y;
+#endif
+	pcOut->mTransformation = aiMatrix4x4(); //  mT * pcIn->mRotation * mS * mP * pcOut->mTransformation.Inverse(); 
+
+	pcOut->mNumChildren = pcIn->mChildren.size();
+	pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
+	for (unsigned int i = 0; i < pcIn->mChildren.size();++i)
+	{
+		pcOut->mChildren[i] = new aiNode();
+		pcOut->mChildren[i]->mParent = pcOut;
+		AddNodeToGraph(pcSOut,pcOut->mChildren[i],
+			pcIn->mChildren[i]);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+inline bool HasUVTransform(const Dot3DS::Texture& rcIn)
+	{
+	return (0.0f != rcIn.mOffsetU ||
+			0.0f != rcIn.mOffsetV ||
+			1.0f != rcIn.mScaleU  ||
+			1.0f != rcIn.mScaleV  ||
+			0.0f != rcIn.mRotation);
+	}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ApplyScaleNOffset()
+	{
+	unsigned int iNum = 0;
+	for (std::vector<Dot3DS::Material>::iterator
+		i =  this->mScene->mMaterials.begin();
+		i != this->mScene->mMaterials.end();++i,++iNum)
+		{
+		unsigned int iCnt = 0;
+		Dot3DS::Texture* pcTexture = NULL;
+		if (HasUVTransform((*i).sTexDiffuse))
+			{
+			(*i).sTexDiffuse.bPrivate = true;
+			pcTexture = &(*i).sTexDiffuse;
+			++iCnt;
+			}
+		if (HasUVTransform((*i).sTexSpecular))
+			{
+			(*i).sTexSpecular.bPrivate = true;
+			pcTexture = &(*i).sTexSpecular;
+			++iCnt;
+			}
+		if (HasUVTransform((*i).sTexOpacity))
+			{
+			(*i).sTexOpacity.bPrivate = true;
+			pcTexture = &(*i).sTexOpacity;
+			++iCnt;
+			}
+		if (HasUVTransform((*i).sTexEmissive))
+			{
+			(*i).sTexEmissive.bPrivate = true;
+			pcTexture = &(*i).sTexEmissive;
+			++iCnt;
+			}
+		if (HasUVTransform((*i).sTexBump))
+			{
+			(*i).sTexBump.bPrivate = true;
+			pcTexture = &(*i).sTexBump;
+			++iCnt;
+			}
+		if (HasUVTransform((*i).sTexShininess))
+			{
+			(*i).sTexShininess.bPrivate = true;
+			pcTexture = &(*i).sTexShininess;
+			++iCnt;
+			}
+		if (0 != iCnt)
+			{
+			// if only one texture needs scaling/offset operations
+			// we can apply them directly to the first texture
+			// coordinate sets of all meshes referencing *this* material
+			// However, we can't do it  now. We need to wait until
+			// everything is sorted by materials.
+			if (1 == iCnt)
+				{
+				(*i).iBakeUVTransform = 1;
+				(*i).pcSingleTexture = pcTexture;
+				}
+			// we will need to generate a separate new texture channel
+			// for each texture. 
+			// However, we can't do it  now. We need to wait until
+			// everything is sorted by materials.
+			else (*i).iBakeUVTransform = 2;
+			}
+		}
+	}
+// ------------------------------------------------------------------------------------------------
+struct STransformVecInfo 
+	{
+	float fScaleU;
+	float fScaleV;
+	float fOffsetU;
+	float fOffsetV;
+	float fRotation;
+
+	std::vector<Dot3DS::Texture*> pcTextures; 
+	};
+// ------------------------------------------------------------------------------------------------
+void AddToList(std::vector<STransformVecInfo>& rasVec,Dot3DS::Texture* pcTex)
+	{
+	if (0 == pcTex->mMapName.length())return;
+
+	for (std::vector<STransformVecInfo>::iterator
+		i =  rasVec.begin();
+		i != rasVec.end();++i)
+		{
+		if ((*i).fOffsetU == pcTex->mOffsetU &&
+			(*i).fOffsetV == pcTex->mOffsetV && 
+			(*i).fScaleU  == pcTex->mScaleU  &&
+			(*i).fScaleV  == pcTex->mScaleV  &&
+			(*i).fRotation == pcTex->mRotation)
+			{
+			(*i).pcTextures.push_back(pcTex);
+			return;
+			}
+		}
+	STransformVecInfo sInfo;
+	sInfo.fScaleU = pcTex->mScaleU;
+	sInfo.fScaleV = pcTex->mScaleV;
+	sInfo.fOffsetU = pcTex->mOffsetU;
+	sInfo.fOffsetV = pcTex->mOffsetV;
+	sInfo.fRotation = pcTex->mRotation;
+	sInfo.pcTextures.push_back(pcTex);
+
+	rasVec.push_back(sInfo);
+	}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::BakeScaleNOffset(
+	aiMesh* pcMesh, Dot3DS::Material* pcSrc)
+	{
+	if (!pcMesh->mTextureCoords[0])return;
+	if (1 == pcSrc->iBakeUVTransform)
+		{
+		if (0.0f == pcSrc->pcSingleTexture->mRotation)
+			{
+			for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+				{
+				pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU;
+				pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV;
+
+				pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU;
+				pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV;
+				}
+			}
+		else
+			{
+			const float fSin = sinf(pcSrc->pcSingleTexture->mRotation);
+			const float fCos = cosf(pcSrc->pcSingleTexture->mRotation);
+			for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+				{
+				pcMesh->mTextureCoords[0][i].x *= pcSrc->pcSingleTexture->mScaleU;
+				pcMesh->mTextureCoords[0][i].y *= pcSrc->pcSingleTexture->mScaleV;
+
+				pcMesh->mTextureCoords[0][i].x *= fCos;
+				pcMesh->mTextureCoords[0][i].y *= fSin;
+
+				pcMesh->mTextureCoords[0][i].x += pcSrc->pcSingleTexture->mOffsetU;
+				pcMesh->mTextureCoords[0][i].y += pcSrc->pcSingleTexture->mOffsetV;
+				}
+			}
+		}
+	else if (2 == pcSrc->iBakeUVTransform)
+		{
+		// 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);
+
+		const aiVector3D* _pvBase;
+		if (0.0f == sOps[0].fOffsetU && 0.0f == sOps[0].fOffsetV &&
+			1.0f == sOps[0].fScaleU  && 1.0f == sOps[0].fScaleV &&
+			0.0f == sOps[0].fRotation)
+			{
+			// we'll have an unmodified set, so we can use *this* one
+			_pvBase = pcMesh->mTextureCoords[0];
+			}
+		else
+			{
+			_pvBase = new aiVector3D[pcMesh->mNumVertices];
+			memcpy(const_cast<aiVector3D*>(_pvBase),pcMesh->mTextureCoords[0],
+				pcMesh->mNumVertices * sizeof(aiVector3D));
+			}
+
+		unsigned int iCnt = 0;
+		for (std::vector<STransformVecInfo>::iterator
+			i =  sOps.begin();
+			i != sOps.end();++i,++iCnt)
+			{
+			if (!pcMesh->mTextureCoords[iCnt])
+				{
+				pcMesh->mTextureCoords[iCnt] = new aiVector3D[pcMesh->mNumVertices];
+				}
+			// more than 4 UV texture channels are not available
+			if (iCnt > 3)
+				{
+				for (std::vector<Dot3DS::Texture*>::iterator
+					a =  (*i).pcTextures.begin();
+					a != (*i).pcTextures.end();++a)
+					{
+					(*a)->iUVSrc = 0;
+					}
+				continue;
+				}
+			const aiVector3D* pvBase = _pvBase;
+
+			if (0.0f == (*i).fRotation)
+				{
+				for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
+					{
+					pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU;
+					pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV;
+
+					pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU;
+					pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV;
+
+					pvBase++;
+					}
+				}
+			else
+				{
+				const float fSin = sinf((*i).fRotation);
+				const float fCos = cosf((*i).fRotation);
+				for (unsigned int n = 0; n < pcMesh->mNumVertices;++n)
+					{
+					pcMesh->mTextureCoords[iCnt][n].x = pvBase->x * (*i).fScaleU;
+					pcMesh->mTextureCoords[iCnt][n].y = pvBase->y * (*i).fScaleV;
+
+					pcMesh->mTextureCoords[iCnt][n].x *= fCos;
+					pcMesh->mTextureCoords[iCnt][n].y *= fSin;
+
+					pcMesh->mTextureCoords[iCnt][n].x += (*i).fOffsetU;
+					pcMesh->mTextureCoords[iCnt][n].y += (*i).fOffsetV;
+
+					pvBase++;
+					}
+				}
+			// setup UV source
+			for (std::vector<Dot3DS::Texture*>::iterator
+				a =  (*i).pcTextures.begin();
+				a != (*i).pcTextures.end();++a)
+				{
+				(*a)->iUVSrc = iCnt;
+				}
+			}
+
+		// release temporary storage
+		if (_pvBase != pcMesh->mTextureCoords[0])
+			delete[] _pvBase;
+		}
+	}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut)
+{
+	pcOut->mRootNode = new aiNode();
+
+	if (0 == this->mRootNode->mChildren.size())
+		{
+		// seems the file has not even a hierarchy.
+		// generate a flat hiearachy which looks like this:
+		//
+		//                ROOT_NODE
+		//                   |
+		//   ----------------------------------------
+		//   |       |       |            |
+		// MESH_0  MESH_1  MESH_2  ...  MESH_N
+		//
+		unsigned int iCnt = 0;
+
+		pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes;
+		pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mNumMeshes ];
+
+		for (unsigned int i = 0; i < pcOut->mNumMeshes;++i)
+			{
+			aiNode* pcNode = new aiNode();
+			pcNode->mParent = pcOut->mRootNode;
+			pcNode->mNumChildren = 0;
+			pcNode->mChildren = 0;
+			pcNode->mMeshes = new unsigned int[1];
+			pcNode->mMeshes[0] = i;
+			pcNode->mNumMeshes = 1;
+			pcNode->mName.Set("UNNAMED");
+
+			// add the new child to the parent node
+			pcOut->mRootNode->mChildren[i] = pcNode;
+			}
+		}
+	else this->AddNodeToGraph(pcOut,  pcOut->mRootNode, this->mRootNode);
+
+	for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
+	{
+		delete (std::string*)pcOut->mMeshes[a]->mColors[0];
+		pcOut->mMeshes[a]->mColors[0] = NULL;
+
+		// may be NULL
+		delete (aiMatrix4x4*)pcOut->mMeshes[a]->mColors[1];
+		pcOut->mMeshes[a]->mColors[1] = NULL;
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ConvertScene(aiScene* pcOut)
+{
+	pcOut->mNumMaterials = this->mScene->mMaterials.size();
+	pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
+
+	for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
+	{
+		MaterialHelper* pcNew = new MaterialHelper();
+		this->ConvertMaterial(this->mScene->mMaterials[i],*pcNew);
+		pcOut->mMaterials[i] = pcNew;
+	}
+	this->ConvertMeshes(pcOut);
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::GenTexCoord (Dot3DS::Texture* pcTexture,
+	const std::vector<aiVector2D>& p_vIn,
+	std::vector<aiVector2D>& p_vOut)
+{
+	p_vOut.resize(p_vIn.size());
+
+	std::vector<aiVector2D>::const_iterator i =  p_vIn.begin();
+	std::vector<aiVector2D>::iterator		a =  p_vOut.begin();
+	for(;i != p_vOut.end();++i,++a)
+	{
+		// TODO: Find out in which order 3ds max is performing
+		// scaling and translation. However it seems reasonable to
+		// scale first.
+		//
+		// TODO: http://www.jalix.org/ressources/graphics/3DS/_specifications/3ds-0.1.htm
+		// says it is not u and v scale but 1/u and 1/v scale. Other sources
+		// tell different things. Believe this one, the author seems to be funny
+		// or drunken or both ;-)
+		(*a) = (*i);
+		(*a).x /= pcTexture->mScaleU;
+		(*a).y /= pcTexture->mScaleV;
+		(*a).x += pcTexture->mOffsetU;
+		(*a).y += pcTexture->mOffsetV;
+	}
+	return;
+}

+ 134 - 0
code/3DSGenNormals.cpp

@@ -0,0 +1,134 @@
+/** @file Implementation of the 3ds importer class */
+#include "3DSLoader.h"
+#include "MaterialSystem.h"
+#include <algorithm>
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+#include "../include/aiAssert.h"
+#include "3DSSpatialSort.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh)
+{
+	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	// First generate face normals
+	sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D());
+	for( unsigned int a = 0; a < sMesh->mFaces.size(); a++)
+	{
+		const Dot3DS::Face& face = sMesh->mFaces[a];
+
+		// assume it is a triangle
+		aiVector3D* pV1 = &sMesh->mPositions[face.i1];
+		aiVector3D* pV2 = &sMesh->mPositions[face.i2];
+		aiVector3D* pV3 = &sMesh->mPositions[face.i3];
+
+		aiVector3D pDelta1 = *pV2 - *pV1;
+		aiVector3D pDelta2 = *pV3 - *pV1;
+		aiVector3D vNor = pDelta1 ^ pDelta2;
+		
+		//float fLength = vNor.Length();
+		//if (0.0f != fLength)vNor /= fLength;
+
+
+		sMesh->mNormals[face.i1] = vNor;
+		sMesh->mNormals[face.i2] = vNor;
+		sMesh->mNormals[face.i3] = vNor;
+	}
+
+	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	// calculate the position bounds so we have a reliable epsilon to 
+	// check position differences against 
+	// @Schrompf: This is the 6th time this snippet is repeated!
+	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+	for( unsigned int a = 0; a < sMesh->mPositions.size(); a++)
+	{
+		minVec.x = std::min( minVec.x, sMesh->mPositions[a].x);
+		minVec.y = std::min( minVec.y, sMesh->mPositions[a].y);
+		minVec.z = std::min( minVec.z, sMesh->mPositions[a].z);
+		maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x);
+		maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y);
+		maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z);
+	}
+	const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
+
+
+	std::vector<aiVector3D> avNormals;
+	avNormals.resize(sMesh->mNormals.size());
+	
+	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	// now generate the spatial sort tree
+	D3DSSpatialSorter sSort(sMesh);
+
+	for( std::vector<Dot3DS::Face>::iterator
+		i =  sMesh->mFaces.begin();
+		i != sMesh->mFaces.end();++i)
+		{
+		std::vector<unsigned int> poResult;
+		// need to repeat the code for all three vertices of the triangle
+
+		// vertex 1
+		sSort.FindPositions(sMesh->mPositions[(*i).i1],(*i).iSmoothGroup,
+			posEpsilon,poResult);
+
+		aiVector3D vNormals;
+		float fDiv = 0.0f;
+		for (std::vector<unsigned int>::const_iterator
+			a =  poResult.begin();
+			a != poResult.end();++a)
+			{
+			vNormals += sMesh->mNormals[(*a)];
+			fDiv += 1.0f;
+			}
+		vNormals.x /= fDiv;
+		vNormals.y /= fDiv;
+		vNormals.z /= fDiv;
+		vNormals.Normalize();
+		avNormals[(*i).i1] = vNormals;
+		poResult.clear();
+
+		// vertex 2
+		sSort.FindPositions(sMesh->mPositions[(*i).i2],(*i).iSmoothGroup,
+			posEpsilon,poResult);
+
+		vNormals = aiVector3D();
+		fDiv = 0.0f;
+		for (std::vector<unsigned int>::const_iterator
+			a =  poResult.begin();
+			a != poResult.end();++a)
+			{
+			vNormals += sMesh->mNormals[(*a)];
+			fDiv += 1.0f;
+			}
+		vNormals.x /= fDiv;
+		vNormals.y /= fDiv;
+		vNormals.z /= fDiv;
+		vNormals.Normalize();
+		avNormals[(*i).i2] = vNormals;
+		poResult.clear();
+
+		// vertex 3
+		sSort.FindPositions(sMesh->mPositions[(*i).i3],(*i).iSmoothGroup,
+			posEpsilon ,poResult);
+
+		vNormals = aiVector3D();
+		fDiv = 0.0f;
+		for (std::vector<unsigned int>::const_iterator
+			a =  poResult.begin();
+			a != poResult.end();++a)
+			{
+			vNormals += sMesh->mNormals[(*a)];
+			fDiv += 1.0f;
+			}
+		vNormals.x /= fDiv;
+		vNormals.y /= fDiv;
+		vNormals.z /= fDiv;
+		vNormals.Normalize();
+		avNormals[(*i).i3] = vNormals;
+		}
+	sMesh->mNormals = avNormals;
+	return;
+}

+ 472 - 0
code/3DSHelper.h

@@ -0,0 +1,472 @@
+/** @file Defines the helper data structures for importing XFiles */
+#ifndef AI_3DSFILEHELPER_H_INC
+#define AI_3DSFILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiQuaternion.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+#include "SpatialSort.h"
+
+namespace Assimp
+{
+	namespace Dot3DS
+	{
+
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
+#	pragma pack(push,2)
+#	define PACK_STRUCT
+#elif defined( __GNUC__ )
+#	define PACK_STRUCT	__attribute__((packed))
+#else
+#	error Compiler not supported
+#endif
+
+// ---------------------------------------------------------------------------
+/** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks
+*  and data structures.
+*/
+class Dot3DSFile
+{
+public:
+	inline Dot3DSFile() {}
+
+	// data structure for a single chunk in a .3ds file
+	struct Chunk
+	{
+		unsigned short	Flag;
+		long			Size;
+	} PACK_STRUCT;
+
+	// source for this used own structures,
+	// replaced it with out standard math helpers
+	typedef aiMatrix3x3 MatTransform;
+	typedef aiVector3D MatTranslate;
+
+	// Used for shading field in material3ds structure
+	// From AutoDesk 3ds SDK
+	typedef enum
+	{
+		Wire = 0,
+		Flat = 1,
+		Gouraud = 2,
+		Phong = 3,
+		Metal = 4
+	} shadetype3ds;
+
+	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	// enum for all chunks in 3ds files. Unused
+	// ones are commented, list is not complete since
+	// there are many undocumented chunks.
+	//
+	// Links: http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt
+	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	enum 
+	{
+
+		// **************************************************************
+		// Base chunks which can be found everywhere in the file
+		CHUNK_VERSION	= 0x0002,
+		CHUNK_RGBF      = 0x0010,		// float4 R; float4 G; float4 B
+		CHUNK_RGBB      = 0x0011,		// int1 R; int1 G; int B
+
+		// Linear color values (gamma = 2.2?)
+		CHUNK_LINRGBF      = 0x0013,	// float4 R; float4 G; float4 B
+		CHUNK_LINRGBB      = 0x0012,	// int1 R; int1 G; int B
+
+		CHUNK_PERCENTW	= 0x0030,		// int2   percentage
+		CHUNK_PERCENTF	= 0x0031,		// float4  percentage
+		// **************************************************************
+
+		// Unknown and ignored
+		CHUNK_PRJ       = 0xC23D,
+
+		// Unknown. Possibly a reference to an external .mli file?
+		CHUNK_MLI       = 0x3DAA,
+
+		// Primary main chunk of the .3ds file
+		CHUNK_MAIN      = 0x4D4D,
+
+		// Mesh main chunk
+		CHUNK_OBJMESH   = 0x3D3D,
+
+		// Specifies the background color of the .3ds file
+		// This is passed through the material system for
+		// viewing purposes.
+		CHUNK_BKGCOLOR  = 0x1200,
+
+		// Specifies the ambient base color of the scene.
+		// This is added to all materials in the file
+		CHUNK_AMBCOLOR  = 0x2100,
+
+		// Specifies the background image for the whole scene
+		// This value is passed through the material system
+		// to the viewer 
+		CHUNK_BIT_MAP   = 0x1100,
+		CHUNK_BIT_MAP_EXISTS  = 0x1101,
+
+		// **************************************************************
+		// Viewport related stuff. Ignored
+		CHUNK_DEFAULT_VIEW = 0x3000,
+		CHUNK_VIEW_TOP = 0x3010,
+		CHUNK_VIEW_BOTTOM = 0x3020,
+		CHUNK_VIEW_LEFT = 0x3030,
+		CHUNK_VIEW_RIGHT = 0x3040,
+		CHUNK_VIEW_FRONT = 0x3050,
+		CHUNK_VIEW_BACK = 0x3060,
+		CHUNK_VIEW_USER = 0x3070,
+		CHUNK_VIEW_CAMERA = 0x3080,
+		// **************************************************************
+
+		// Mesh chunks
+		CHUNK_OBJBLOCK  = 0x4000,
+		CHUNK_TRIMESH   = 0x4100,
+		CHUNK_VERTLIST  = 0x4110,
+		CHUNK_VERTFLAGS = 0x4111,
+		CHUNK_FACELIST  = 0x4120,
+		CHUNK_FACEMAT   = 0x4130,
+		CHUNK_MAPLIST   = 0x4140,
+		CHUNK_SMOOLIST  = 0x4150,
+		CHUNK_TRMATRIX  = 0x4160,
+		CHUNK_MESHCOLOR = 0x4165,
+		CHUNK_TXTINFO   = 0x4170,
+		CHUNK_LIGHT     = 0x4600,
+		CHUNK_SPOTLIGHT = 0x4610,
+		CHUNK_CAMERA    = 0x4700,
+		CHUNK_HIERARCHY = 0x4F00,
+
+		// Specifies the global scaling factor. This is applied
+		// to the root node's transformation matrix
+		CHUNK_MASTER_SCALE    = 0x0100,
+
+		// **************************************************************
+		// Material chunks
+		CHUNK_MAT_MATERIAL  = 0xAFFF,
+
+			// asciiz containing the name of the material
+			CHUNK_MAT_MATNAME   = 0xA000, 
+			CHUNK_MAT_AMBIENT   = 0xA010, // followed by color chunk
+			CHUNK_MAT_DIFFUSE   = 0xA020, // followed by color chunk
+			CHUNK_MAT_SPECULAR  = 0xA030, // followed by color chunk
+
+			// Specifies the shininess of the material
+			// followed by percentage chunk
+			CHUNK_MAT_SHININESS  = 0xA040, 
+
+			// Specifies the shading mode to be used
+			// followed by a short
+			CHUNK_MAT_SHADING  = 0xA100, 
+
+			// NOTE: Emissive color (self illumination) seems not
+			// to be a color but a single value, type is unknown.
+			// Make the parser accept both of them.
+			// followed by percentage chunk (?)
+			CHUNK_MAT_SELF_ILLUM = 0xA080,  
+
+			// Always followed by percentage chunk	(?)
+			CHUNK_MAT_SELF_ILPCT = 0xA084,  
+
+			// Always followed by percentage chunk
+			CHUNK_MAT_TRANSPARENCY = 0xA050, 
+
+			// Diffuse texture channel 0 
+			CHUNK_MAT_TEXTURE   = 0xA200,
+
+			// Contains opacity information for each texel
+			CHUNK_MAT_OPACMAP = 0xA210,
+
+			// Contains a reflection map to be used to reflect
+			// the environment. This is partially supported.
+			CHUNK_MAT_REFLMAP = 0xA220,
+
+			// Self Illumination map (emissive colors)
+			CHUNK_MAT_SELFIMAP = 0xA33d,	
+
+			// Bumpmap. Not specified whether it is a heightmap
+			// or a normal map. Assme it is a heightmap since
+			// artist normally prefer this format.
+			CHUNK_MAT_BUMPMAP = 0xA230,
+
+			// Specular map. Seems to influence the specular color
+			CHUNK_MAT_SPECMAP = 0xA204,
+
+			// Holds shininess data. I assume the specular exponent is
+			// calculated like this:
+			//
+			// s[x,y] =  stex[x,y] * base_shininess;
+			// I also assume the data in the texture must be renormalized
+			// (normally by dividing / 255) after loading.
+			CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+
+			// Scaling in U/V direction.
+			// (need to gen separate UV coordinate set 
+			// and do this by hand)
+			CHUNK_MAT_MAP_USCALE 	  = 0xA354,
+			CHUNK_MAT_MAP_VSCALE 	  = 0xA356,
+
+			// Translation in U/V direction.
+			// (need to gen separate UV coordinate set 
+			// and do this by hand)
+			CHUNK_MAT_MAP_UOFFSET 	  = 0xA358,
+			CHUNK_MAT_MAP_VOFFSET 	  = 0xA35a,
+
+			// UV-coordinates rotation around the z-axis
+			// Assumed to be in radians.
+			CHUNK_MAT_MAP_ANG = 0xA35C,
+
+			// Specifies the file name of a texture
+			CHUNK_MAPFILE   = 0xA300,
+		// **************************************************************
+
+		// Main keyframer chunk. Contains translation/rotation/scaling data
+		CHUNK_KEYFRAMER		= 0xB000,
+
+		// Supported sub chunks
+		CHUNK_TRACKINFO		= 0xB002,
+		CHUNK_TRACKOBJNAME  = 0xB010,
+		CHUNK_TRACKPIVOT    = 0xB013,
+		CHUNK_TRACKPOS      = 0xB020,
+		CHUNK_TRACKROTATE   = 0xB021,
+		CHUNK_TRACKSCALE    = 0xB022,
+
+		// **************************************************************
+		// Keyframes for various other stuff in the file
+		// Ignored
+		CHUNK_AMBIENTKEY    = 0xB001,
+		CHUNK_TRACKMORPH    = 0xB026,
+		CHUNK_TRACKHIDE     = 0xB029,
+		CHUNK_OBJNUMBER     = 0xB030,
+		CHUNK_TRACKCAMERA	= 0xB003,
+		CHUNK_TRACKFOV		= 0xB023,
+		CHUNK_TRACKROLL		= 0xB024,
+		CHUNK_TRACKCAMTGT	= 0xB004,
+		CHUNK_TRACKLIGHT	= 0xB005,
+		CHUNK_TRACKLIGTGT	= 0xB006,
+		CHUNK_TRACKSPOTL	= 0xB007,
+		CHUNK_FRAMES		= 0xB008
+		// **************************************************************
+	};
+};
+
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+#	pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds mesh face */
+struct Face
+{
+	Face() : iSmoothGroup(0), bDirection(true)
+	{
+		// let the rest uninitialized for performance
+	}
+
+	
+	// Indices. .3ds is using uint16. However, after
+	// an unique vrtex set has been geneerated it might
+	// be an index becomes > 2^16
+	uint32_t i1;
+	uint32_t i2;
+	uint32_t i3;
+
+	// specifies to which smoothing group the face belongs to
+	uint32_t iSmoothGroup;
+
+	// Direction the normal vector of the face 
+	// will be pointing to
+	bool bDirection;
+};
+// ---------------------------------------------------------------------------
+/** Helper structure representing a texture */
+struct Texture
+{
+	Texture()
+		: 
+		mScaleU(1.0f),
+		mScaleV(1.0f),
+		mOffsetU(0.0f),
+		mOffsetV(0.0f),
+		mRotation(0.0f),
+		iUVSrc(0)
+	{
+		mTextureBlend = std::numeric_limits<float>::quiet_NaN();
+	}
+	// Specifies the blending factor for the texture
+	float mTextureBlend;
+
+	// Specifies the filename of the texture
+	std::string mMapName;
+
+	// Specifies texture coordinate offsets/scaling/rotations
+	float mScaleU;
+	float mScaleV;
+	float mOffsetU;
+	float mOffsetV;
+	float mRotation;
+
+	// Used internally
+	bool bPrivate;
+	int iUVSrc;
+};
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds material */
+struct Material
+{
+	Material()
+		: 
+	mSpecularExponent	(0.0f),
+	mShading(Dot3DSFile::Gouraud),
+	mTransparency		(1.0f),
+	mBumpHeight			(1.0f),
+	iBakeUVTransform	(0),
+	pcSingleTexture		(NULL)
+	{
+		static int iCnt = 0;
+		std::stringstream ss(mName);
+		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
+	}
+
+	// Name of the material
+	std::string mName;
+	// Diffuse color of the material
+	aiColor3D mDiffuse;
+	// Specular exponent
+	float mSpecularExponent;
+	// Specular color of the material
+	aiColor3D mSpecular;
+	// Ambient color of the material
+	aiColor3D mAmbient;
+	// Shading type to be used
+	Dot3DSFile::shadetype3ds mShading;
+	// Opacity of the material
+	float mTransparency;
+
+	// Different texture channels
+	Texture sTexDiffuse;
+	Texture sTexOpacity;
+	Texture sTexSpecular;
+	Texture sTexBump;
+	Texture sTexEmissive;
+	Texture sTexShininess;
+	
+	/*
+	float mReflectionTextureBlend;
+	std::string mReflectionTexture;
+	*/
+	float mBumpHeight;
+
+	// Emissive color
+	aiColor3D mEmissive;
+
+	// Used internally
+	unsigned int iBakeUVTransform;
+	Texture* pcSingleTexture;
+};
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file mesh */
+struct Mesh
+{
+	Mesh()
+	{
+		static int iCnt = 0;
+		std::stringstream ss(mName);
+		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
+#if 0
+		for (unsigned int i = 0; i < 32;++i)
+			bSmoothGroupRequired[i] = false;
+#endif
+	}
+	std::string mName;
+
+	std::vector<aiVector3D> mPositions;
+	std::vector<Face> mFaces;
+	std::vector<aiVector2D> mTexCoords;
+	std::vector<unsigned int> mFaceMaterials;
+	std::vector<aiVector3D> mNormals;
+
+#if 0
+	bool bSmoothGroupRequired[32];
+#endif
+
+	aiMatrix4x4 mMat;
+};
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file node */
+struct Node
+{
+	Node()
+#if 0
+		: vScaling(1.0f,1.0f,1.0f)
+#endif
+	{
+		static int iCnt = 0;
+		std::stringstream ss(mName);
+		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
+
+		mHierarchyPos = 0;
+		mHierarchyIndex = 0;
+	}
+
+	Node* mParent;
+	std::vector<Node*> mChildren;
+	std::string mName;
+	int16_t mHierarchyPos;
+	int16_t mHierarchyIndex;
+
+#if 0
+	aiVector3D vPivot;
+	aiVector3D vScaling;
+	aiMatrix4x4 mRotation;
+	aiVector3D vPosition;
+#endif
+
+	inline Node& push_back(Node* pc)
+	{
+		mChildren.push_back(pc);
+		pc->mParent = this;
+		pc->mHierarchyPos = this->mHierarchyPos+1;
+		return *this;
+	}
+};
+// ---------------------------------------------------------------------------
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+
+	// NOTE: 3ds references materials globally
+	std::vector<Material> mMaterials;
+	std::vector<Mesh> mMeshes;
+
+	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
+
+#endif // AI_XFILEHELPER_H_INC

+ 1310 - 0
code/3DSLoader.cpp

@@ -0,0 +1,1310 @@
+/** @file Implementation of the 3ds importer class */
+#include "3DSLoader.h"
+#include "MaterialSystem.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;
+
+#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."	\
+
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Dot3DSImporter::Dot3DSImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+Dot3DSImporter::~Dot3DSImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool Dot3DSImporter::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);
+
+	// not brilliant but working ;-)
+	if( extension == ".3ds" || extension == ".3DS" || 
+		extension == ".3Ds" || extension == ".3dS")
+		return true;
+
+	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)
+{
+	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 file " + pFile + ".");
+	}
+
+	// check whether the .3ds file is large enough to contain
+	// at least one chunk.
+	size_t fileSize = file->FileSize();
+	if( fileSize < 16)
+	{
+		throw new ImportErrorException( ".3ds File is too small.");
+	}
+
+	this->mScene = new Dot3DS::Scene();
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	this->mBuffer = new unsigned char[fileSize];
+	file->Read( mBuffer, 1, fileSize);
+	this->mCurrent = this->mBuffer;
+	this->mLast = this->mBuffer+fileSize;
+
+	// initialize members
+	this->mLastNodeIndex = -1;
+	this->mCurrentNode = new Dot3DS::Node();
+	this->mRootNode = this->mCurrentNode;
+	this->mRootNode->mHierarchyPos = -1;
+	this->mRootNode->mHierarchyIndex = -1;
+	this->mRootNode->mParent = NULL;
+	this->mMasterScale = 1.0f;
+	this->mBackgroundImage = "";
+	this->bHasBG = false;
+
+	int iRemaining = (unsigned int)fileSize;
+	this->ParseMainChunk(&iRemaining);
+
+	// Generate an unique set of vertices/indices for
+	// all meshes contained in the file
+	for (std::vector<Dot3DS::Mesh>::iterator
+		i =  this->mScene->mMeshes.begin();
+		i != this->mScene->mMeshes.end();++i)
+	{
+		// TODO: see function body
+		this->CheckIndices(&(*i));
+		this->MakeUnique(&(*i));
+
+		// first generate normals for the mesh
+		this->GenNormals(&(*i));
+	}
+
+	// Apply scaling and offsets to all texture coordinates
+	this->ApplyScaleNOffset();
+
+	// Replace all occurences of the default material with a valid material.
+	// Generate it if no material containing DEFAULT in its name has been
+	// found in the file
+	this->ReplaceDefaultMaterial();
+
+	try 
+		{
+		// Convert the scene from our internal representation to an aiScene object
+		this->ConvertScene(pScene);
+		}
+	catch (ImportErrorException ex)
+		{
+		// delete the scene itself
+		if (pScene->mMeshes)
+			{
+			for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+				delete pScene->mMeshes[i];
+			delete[] pScene->mMeshes;
+			}
+		if (pScene->mMaterials)
+			{
+			for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+				delete pScene->mMaterials[i];
+			delete[] pScene->mMaterials;
+			}
+		// there are no animations
+		if (pScene->mRootNode)DeleteNodeRecursively(pScene->mRootNode);
+		throw ex;
+		}
+
+	// Generate the node graph for the scene. This is a little bit
+	// tricky since we'll need to split some meshes into submeshes
+	this->GenerateNodeGraph(pScene);
+
+	// Now apply a master scaling factor to the scene
+	this->ApplyMasterScale(pScene);
+
+	delete[] this->mBuffer;
+	delete this->mScene;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ApplyMasterScale(aiScene* pScene)
+{
+	// NOTE: Some invalid files have masterscale set to 0.0
+	if (0.0f == this->mMasterScale)
+		{
+		this->mMasterScale = 1.0f;
+		}
+	else this->mMasterScale = 1.0f / this->mMasterScale;
+
+	// construct an uniform scaling matrix and multiply with it
+	pScene->mRootNode->mTransformation *= aiMatrix4x4( 
+		this->mMasterScale,0.0f, 0.0f, 0.0f,
+		0.0f, this->mMasterScale,0.0f, 0.0f,
+		0.0f, 0.0f, this->mMasterScale,0.0f,
+		0.0f, 0.0f, 0.0f, 1.0f);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut)
+{
+	ai_assert(p_ppcOut != NULL);
+
+	// read chunk
+	if ((unsigned int)this->mCurrent >= (unsigned int)this->mLast)
+	{
+		*p_ppcOut = NULL;
+		return;
+	}
+	const unsigned int iDiff = (unsigned int)this->mLast - (unsigned int)this->mCurrent;
+	if (iDiff < sizeof(Dot3DSFile::Chunk)) 
+	{
+		*p_ppcOut = NULL;
+		return;
+	}
+
+	*p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent;
+	this->mCurrent += sizeof(Dot3DSFile::Chunk);
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseMainChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_MAIN:
+	//case 0x444d: // bugfix
+
+		this->ParseEditorChunk(&iRemaining);
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseMainChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseEditorChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_OBJMESH:
+
+		this->ParseObjectChunk(&iRemaining);
+		break;
+
+	// NOTE: In several documentations in the internet this
+	// chunk appears at different locations
+	case Dot3DSFile::CHUNK_KEYFRAMER:
+
+		this->ParseKeyframeChunk(&iRemaining);
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseEditorChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseObjectChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	const unsigned char* sz = this->mCurrent;
+	unsigned int iCnt = 0;
+
+	// get chunk type
+	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_OBJBLOCK:
+
+		this->mScene->mMeshes.push_back(Dot3DS::Mesh());
+
+		// at first we need to parse the name of the
+		// geometry object
+
+		while (*sz++ != '\0')
+		{
+			if (sz > pcCurNext-1)break;
+			++iCnt;
+		}
+		this->mScene->mMeshes.back().mName = std::string(
+			(const char*)this->mCurrent,iCnt);
+		++iCnt;
+
+		this->mCurrent += iCnt;
+		iRemaining -= iCnt;
+		this->ParseChunk(&iRemaining);
+		break;
+
+	case Dot3DSFile::CHUNK_MAT_MATERIAL:
+
+		this->mScene->mMaterials.push_back(Dot3DS::Material());
+		this->ParseMaterialChunk(&iRemaining);
+		break;
+
+	case Dot3DSFile::CHUNK_AMBCOLOR:
+
+		// This is the ambient base color of the scene.
+		// We add it to the ambient color of all materials
+		this->ParseColorChunk(&this->mClrAmbient,true);
+		if (is_qnan(this->mClrAmbient.r))
+			{
+			this->mClrAmbient.r = 0.0f;
+			this->mClrAmbient.g = 0.0f;
+			this->mClrAmbient.b = 0.0f;
+			}
+		break;
+
+
+	case Dot3DSFile::CHUNK_BIT_MAP:
+
+		this->mBackgroundImage = std::string((const char*)this->mCurrent);
+		break;
+
+
+	case Dot3DSFile::CHUNK_BIT_MAP_EXISTS:
+		bHasBG = true;
+		break;
+
+
+	case Dot3DSFile::CHUNK_MASTER_SCALE:
+
+		this->mMasterScale = *((float*)this->mCurrent);
+		this->mCurrent += sizeof(float);
+		break;
+
+	// NOTE: In several documentations in the internet this
+	// chunk appears at different locations
+	case Dot3DSFile::CHUNK_KEYFRAMER:
+
+		this->ParseKeyframeChunk(&iRemaining);
+		break;
+
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseObjectChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::SkipChunk()
+{
+	const Dot3DSFile::Chunk* psChunk;
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+	this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk);
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_TRIMESH:
+		// this starts a new mesh
+		this->ParseMeshChunk(&iRemaining);
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseKeyframeChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_TRACKINFO:
+		// this starts a new mesh
+		this->ParseHierarchyChunk(&iRemaining);
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseKeyframeChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurrent)
+{
+	if (NULL == pcCurrent)
+	{
+		this->mRootNode->push_back(pcNode);
+		return;
+	}
+	if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos)
+	{
+		if(NULL != pcCurrent->mParent)
+			pcCurrent->mParent->push_back(pcNode);
+		else pcCurrent->push_back(pcNode);
+		return;
+	}
+	return this->InverseNodeSearch(pcNode,pcCurrent->mParent);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseHierarchyChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	const unsigned char* sz = (unsigned char*)this->mCurrent;
+	unsigned int iCnt = 0;
+	uint16_t iHierarchy;
+	//uint16_t iTemp;
+	Dot3DS::Node* pcNode;
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_TRACKOBJNAME:
+		
+		// get object name
+		while (*sz++ != '\0')
+		{
+			if (sz > pcCurNext-1)break;
+			++iCnt;
+		}
+		pcNode = new Dot3DS::Node();
+		pcNode->mName = std::string((const char*)this->mCurrent,iCnt);
+
+		iCnt++;
+		// there are two unknown values which we can safely ignore
+		this->mCurrent += iCnt + sizeof(uint16_t)*2;
+		iHierarchy = *((uint16_t*)this->mCurrent);
+		iHierarchy++;
+		pcNode->mHierarchyPos = iHierarchy;
+		pcNode->mHierarchyIndex = this->mLastNodeIndex;
+		if (iHierarchy > this->mLastNodeIndex)
+		{
+			// place it at the current position in the hierarchy
+			this->mCurrentNode->push_back(pcNode);
+		}
+		else
+		{
+			// need to go back to the specified position in the hierarchy.
+			this->InverseNodeSearch(pcNode,this->mCurrentNode);
+		}
+		this->mLastNodeIndex++;
+		this->mCurrentNode = pcNode;
+		break;
+
+#if 0
+
+	case Dot3DSFile::CHUNK_TRACKPIVOT:
+
+		this->mCurrentNode->vPivot = *((aiVector3D*)this->mCurrent);
+		this->mCurrent += sizeof(aiVector3D);
+		break;
+
+
+	case Dot3DSFile::CHUNK_TRACKPOS:
+
+		/*
+		+2 short flags; 
+		+8 short unknown[4];
+		+2 short keys;
+		+2 short unknown;
+		struct {
+		+2 short framenum;
+		+4 long unknown;
+		float pos_x, pos_y, pos_z; 
+		}  pos[keys]; 	
+		*/
+		this->mCurrent += 10;
+		iTemp = *((uint16_t*)mCurrent);
+
+		this->mCurrent += sizeof(uint16_t) * 2;
+
+		if (0 != iTemp)
+			{
+			for (unsigned int i = 0; i < (unsigned int)iTemp;++i)
+				{
+				uint16_t sNum = *((uint16_t*)mCurrent);
+				this->mCurrent += sizeof(uint16_t);
+
+				if (0 == sNum)
+					{
+					this->mCurrent += sizeof(uint32_t);
+					this->mCurrentNode->vPosition = *((aiVector3D*)this->mCurrent);
+					this->mCurrent += sizeof(aiVector3D);
+					}
+				else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D);
+				}
+			}
+		break;
+
+	case Dot3DSFile::CHUNK_TRACKROTATE:
+
+		/*
+		+2 short flags; 
+		+8 short unknown[4];
+		+2 short keys;
+		+2 short unknown;
+		struct {
+		+2 short framenum;
+		+4 long unknown;
+		float rad , pos_x, pos_y, pos_z; 
+		}  pos[keys]; 	
+		*/
+		this->mCurrent += 10;
+		iTemp = *((uint16_t*)mCurrent);
+
+		this->mCurrent += sizeof(uint16_t) * 2;
+		if (0 != iTemp)
+			{
+			bool neg = false;
+			unsigned int iNum0 = 0;
+			for (unsigned int i = 0; i < (unsigned int)iTemp;++i)
+				{
+				uint16_t sNum = *((uint16_t*)mCurrent);
+				this->mCurrent += sizeof(uint16_t);
+
+				if (0 == sNum)
+					{
+					this->mCurrent		+= sizeof(uint32_t);
+					float fRadians		= *((float*)this->mCurrent);
+					this->mCurrent		+= sizeof(float);
+					aiVector3D vAxis	= *((aiVector3D*)this->mCurrent);
+					this->mCurrent		+= sizeof(aiVector3D);
+
+					// some idiotic files have rotations with fRadians = 0 ...
+					if (0.0f != fRadians)
+						{
+
+						// if the radians go beyond PI then the rotations 
+						// thereafter must be inversed
+#if 0
+						if (neg)fRadians *= -1.0f;
+						if ((fRadians >= 3.1415926f || fRadians <= -3.1415926f))
+							{
+							neg = !neg;
+							}
+#endif 
+
+
+						// get the rotation matrix around the axis
+						const float fSin = sinf(-fRadians);
+						const float fCos = cosf(-fRadians);
+						const float fOneMinusCos = 1.0f - fCos;
+
+						std::swap(vAxis.z,vAxis.y);
+						vAxis.Normalize();
+
+						aiMatrix4x4 mRot = aiMatrix4x4(
+							(vAxis.x * vAxis.x) * fOneMinusCos + fCos,
+							(vAxis.x * vAxis.y) * fOneMinusCos - (vAxis.z * fSin),
+							(vAxis.x * vAxis.z) * fOneMinusCos + (vAxis.y * fSin),
+							0.0f,
+							(vAxis.y * vAxis.x) * fOneMinusCos + (vAxis.z * fSin),
+							(vAxis.y * vAxis.y) * fOneMinusCos + fCos,
+							(vAxis.y * vAxis.z) * fOneMinusCos - (vAxis.x * fSin),
+							0.0f,
+							(vAxis.z * vAxis.x) * fOneMinusCos - (vAxis.y * fSin),
+							(vAxis.z * vAxis.y) * fOneMinusCos + (vAxis.x * fSin),
+							(vAxis.z * vAxis.z) * fOneMinusCos + fCos,
+							0.0f,0.0f,0.0f,0.0f,1.0f);
+						//mRot.Transpose();
+
+						// build a chain of concatenated rotation matrix'
+						// if there are multiple track chunks for the same frame
+						if (0 != iNum0)
+							{
+							this->mCurrentNode->mRotation = this->mCurrentNode->mRotation * mRot;
+							}
+						else
+							{
+							// for the first time simply set the rotation matrix
+							this->mCurrentNode->mRotation = mRot;
+							}
+						iNum0++;
+						}
+					}
+				else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D) + sizeof(float);
+				}
+			}
+		break;
+
+	case Dot3DSFile::CHUNK_TRACKSCALE:
+
+		/*
+		+2 short flags; 
+		+8 short unknown[4];
+		+2 short keys;
+		+2 short unknown;
+		struct {
+		+2 short framenum;
+		+4 long unknown;
+		float pos_x, pos_y, pos_z; 
+		}  pos[keys]; 	
+		*/
+		this->mCurrent += 10;
+		iTemp = *((uint16_t*)mCurrent);
+
+		this->mCurrent += sizeof(uint16_t) * 2;
+
+		if (0 != iTemp)
+			{
+			for (unsigned int i = 0; i < (unsigned int)iTemp;++i)
+				{
+				uint16_t sNum = *((uint16_t*)mCurrent);
+				this->mCurrent += sizeof(uint16_t);
+				if (0 == sNum)
+					{
+					this->mCurrent += sizeof(uint32_t);
+					aiVector3D vMe = *((aiVector3D*)this->mCurrent);
+					// ignore zero scalings
+					if (0.0f != vMe.x && 0.0f != vMe.y && 0.0f != vMe.z)
+						{
+						this->mCurrentNode->vScaling.x *= vMe.x;
+						this->mCurrentNode->vScaling.y *= vMe.y;
+						this->mCurrentNode->vScaling.z *= vMe.z;
+						}
+					this->mCurrent += sizeof(aiVector3D);
+					}
+				else this->mCurrent += sizeof(uint32_t) + sizeof(aiVector3D);
+				}
+			}
+		break;
+
+#endif // 0
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next top-level chunk
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return this->ParseHierarchyChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseFaceChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	const unsigned char* sz = this->mCurrent;
+	uint32_t iCnt = 0,iTemp;
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_SMOOLIST:
+
+		// one int32 for each face
+		for (std::vector<Dot3DS::Face>::iterator
+			i =  mMesh.mFaces.begin();
+			i != mMesh.mFaces.end();++i)
+		{
+			// nth bit is set for nth smoothing group
+			(*i).iSmoothGroup = *((uint32_t*)this->mCurrent);
+#if 0
+			for (unsigned int x = 0, a = 1; x < 32;++x,a <<= 1)
+			{
+				if ((*i).iSmoothGroup & a)
+					mMesh.bSmoothGroupRequired[x] = true;
+			}
+#endif
+			this->mCurrent += sizeof(uint32_t);
+		}
+		break;
+
+	case Dot3DSFile::CHUNK_FACEMAT:
+
+		// at fist an asciiz with the material name
+		while (*sz++ != '\0')
+		{
+			if (sz > pcCurNext-1)break;
+		}
+
+		// find the index of the material
+		unsigned int iIndex = 0xFFFFFFFF;
+		iCnt = 0;
+		for (std::vector<Dot3DS::Material>::const_iterator
+			i =  this->mScene->mMaterials.begin();
+			i != this->mScene->mMaterials.end();++i,++iCnt)
+		{
+			// compare case-independent to be sure it works
+			if (0 == ASSIMP_stricmp((const char*)this->mCurrent,
+				(const char*)((*i).mName.c_str())))
+			{
+			iIndex = iCnt;
+			break;
+			}
+		}
+		if (iIndex == 0xFFFFFFFF)
+		{
+			// this material is not known. Ignore this. We will later
+			// assign the default material to all faces using *this*
+			// material. Use 0xcdcdcdcd as special value to indicate
+			// this.
+			iIndex = 0xcdcdcdcd;
+		}
+		this->mCurrent = sz;
+		iCnt = (int)(*((uint16_t*)this->mCurrent));
+		this->mCurrent += sizeof(uint16_t);
+
+		for (unsigned int i = 0; i < iCnt;++i)
+		{
+			iTemp = (uint16_t)*((uint16_t*)this->mCurrent);
+
+			// check range
+			if (iTemp >= mMesh.mFaceMaterials.size())
+				{
+				mMesh.mFaceMaterials[mMesh.mFaceMaterials.size()-1] = iIndex;
+				}
+			else
+				{
+				mMesh.mFaceMaterials[iTemp] = iIndex;
+				}
+			this->mCurrent += sizeof(uint16_t);
+		}
+
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next chunk on this level
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return ParseFaceChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseMeshChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back();
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	const unsigned char* sz = this->mCurrent;
+	unsigned int iCnt = 0;
+	int iRemaining;
+	uint16_t iNum = 0;
+	float* pf;
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_VERTLIST:
+
+		iNum = *((short*)this->mCurrent);
+		this->mCurrent += sizeof(short);
+		while (iNum-- > 0)
+		{
+			mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent));
+			mMesh.mPositions.back().z *= -1.0f;
+			this->mCurrent += sizeof(aiVector3D);
+		}
+		break;
+
+	case Dot3DSFile::CHUNK_TRMATRIX:
+		{
+		// http://www.gamedev.net/community/forums/topic.asp?topic_id=263063
+		// http://www.gamedev.net/community/forums/topic.asp?topic_id=392310
+		pf = (float*)this->mCurrent;
+		this->mCurrent += 12 * sizeof(float);
+
+		mMesh.mMat.a1 = pf[0];
+		mMesh.mMat.a2 = pf[1];
+		mMesh.mMat.a3 = pf[2];
+		mMesh.mMat.b1 = pf[3];
+		mMesh.mMat.b2 = pf[4];
+		mMesh.mMat.b3 = pf[5];
+		mMesh.mMat.c1 = pf[6];
+		mMesh.mMat.c2 = pf[7];
+		mMesh.mMat.c3 = pf[8];
+		mMesh.mMat.d1 = pf[9];
+		mMesh.mMat.d2 = pf[10];
+		mMesh.mMat.d3 = pf[11];
+
+		std::swap((float&)mMesh.mMat.d2, (float&)mMesh.mMat.d3);
+		std::swap((float&)mMesh.mMat.a2, (float&)mMesh.mMat.a3);
+		std::swap((float&)mMesh.mMat.b1, (float&)mMesh.mMat.c1);
+		std::swap((float&)mMesh.mMat.c2, (float&)mMesh.mMat.b3);
+		std::swap((float&)mMesh.mMat.b2, (float&)mMesh.mMat.c3);
+
+		mMesh.mMat.Transpose();
+
+		//aiMatrix4x4 mInv = mMesh.mMat;
+		//mInv.Inverse();
+
+		//// invert the matrix and transform all vertices with it
+		//// (the origin of all vertices is 0|0|0 now)
+		//for (register unsigned int i = 0; i < mMesh.mPositions.size();++i)
+		//	{
+		//	aiVector3D a,c;
+		//	a = mMesh.mPositions[i];
+		//	c[0]= mInv[0][0]*a[0] + mInv[1][0]*a[1] + mInv[2][0]*a[2] + mInv[3][0];
+		//	c[1]= mInv[0][1]*a[0] + mInv[1][1]*a[1] + mInv[2][1]*a[2] + mInv[3][1];
+		//	c[2]= mInv[0][2]*a[0] + mInv[1][2]*a[1] + mInv[2][2]*a[2] + mInv[3][2];
+		//	mMesh.mPositions[i] = c;
+		//	}
+
+		// now check whether the matrix has got a negative determinant
+		// If yes, we need to flip all vertices x axis ....
+		// From lib3ds, mesh.c
+		if (mMesh.mMat.Determinant() < 0.0f)
+			{
+			aiMatrix4x4 mInv = mMesh.mMat;
+			mInv.Inverse();
+
+			aiMatrix4x4 mMe = mMesh.mMat;
+			mMe.a1 *= -1.0f;
+			mMe.a2 *= -1.0f;
+			mMe.a3 *= -1.0f;
+			mMe.a4 *= -1.0f;
+			mInv = mMe * mInv;
+			for (register unsigned int i = 0; i < mMesh.mPositions.size();++i)
+				{
+				aiVector3D a,c;
+				a = mMesh.mPositions[i];
+				c[0]= mInv[0][0]*a[0] + mInv[1][0]*a[1] + mInv[2][0]*a[2] + mInv[3][0];
+				c[1]= mInv[0][1]*a[0] + mInv[1][1]*a[1] + mInv[2][1]*a[2] + mInv[3][1];
+				c[2]= mInv[0][2]*a[0] + mInv[1][2]*a[1] + mInv[2][2]*a[2] + mInv[3][2];
+				mMesh.mPositions[i] = c;
+				}
+			}
+		}
+		break;
+
+	case Dot3DSFile::CHUNK_MAPLIST:
+
+		iNum = *((uint16_t*)this->mCurrent);
+		this->mCurrent += sizeof(uint16_t);
+		while (iNum-- > 0)
+		{
+			mMesh.mTexCoords.push_back(*((aiVector2D*)this->mCurrent));
+			this->mCurrent += sizeof(aiVector2D);
+		}
+		break;
+
+#if (defined _DEBUG)
+
+	case Dot3DSFile::CHUNK_TXTINFO:
+
+		// for debugging purposes. Read two bytes to determine the mapping type
+		iNum = *((uint16_t*)this->mCurrent);
+		this->mCurrent += sizeof(uint16_t);
+	break;
+#endif
+
+	case Dot3DSFile::CHUNK_FACELIST:
+
+		iNum = *((uint16_t*)this->mCurrent);
+		this->mCurrent += sizeof(uint16_t);
+		while (iNum-- > 0)
+		{
+			Dot3DS::Face sFace;
+			sFace.i1 = *((uint16_t*)this->mCurrent);
+			this->mCurrent += sizeof(uint16_t);
+			sFace.i2 = *((uint16_t*)this->mCurrent);
+			this->mCurrent += sizeof(uint16_t);
+			sFace.i3 = *((uint16_t*)this->mCurrent);
+			this->mCurrent += 2*sizeof(uint16_t);
+			mMesh.mFaces.push_back(sFace);
+
+			//if (sFace.i1 < sFace.i2)sFace.bDirection = false;
+		}
+
+		// resize the material array (0xcdcdcdcd marks the
+		// default material; so if a face is not referenced
+		// by a material $$DEFAULT will be assigned to it)
+		mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd);
+
+		iRemaining = (int)pcCurNext - (int)this->mCurrent;
+		if (iRemaining > 0)this->ParseFaceChunk(&iRemaining);
+		break;
+
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next chunk on this level
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return ParseMeshChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseMaterialChunk(int* piRemaining)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	const unsigned char* sz = this->mCurrent;
+	unsigned int iCnt = 0;
+	int iRemaining;
+	aiColor3D* pc;
+	float* pcf;
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_MAT_MATNAME:
+
+		// string in file is zero-terminated, 
+		// this should be no problem. However, validate whether
+		// it overlaps the end of the chunk, if yes we should
+		// truncate it.
+		while (*sz++ != '\0')
+		{
+			if (sz > pcCurNext-1)break;
+			++iCnt;
+		}
+		this->mScene->mMaterials.back().mName = std::string(
+			(const char*)this->mCurrent,iCnt);
+		break;
+	case Dot3DSFile::CHUNK_MAT_DIFFUSE:
+		pc = &this->mScene->mMaterials.back().mDiffuse;
+		this->ParseColorChunk(pc);
+		if (is_qnan(pc->r))
+		{
+			// color chunk is invalid. Simply ignore it
+			pc->r = pc->g = pc->b = 1.0f;
+		}
+		break;
+	case Dot3DSFile::CHUNK_MAT_SPECULAR:
+		pc = &this->mScene->mMaterials.back().mSpecular;
+		this->ParseColorChunk(pc);
+		if (is_qnan(pc->r))
+		{
+			// color chunk is invalid. Simply ignore it
+			pc->r = pc->g = pc->b = 1.0f;
+		}
+		break;
+	case Dot3DSFile::CHUNK_MAT_AMBIENT:
+		pc = &this->mScene->mMaterials.back().mAmbient;
+		this->ParseColorChunk(pc);
+		if (is_qnan(pc->r))
+		{
+			// color chunk is invalid. Simply ignore it
+			pc->r = pc->g = pc->b = 1.0f;
+		}
+		break;
+	case Dot3DSFile::CHUNK_MAT_SELF_ILLUM:
+		pc = &this->mScene->mMaterials.back().mEmissive;
+		this->ParseColorChunk(pc);
+		if (is_qnan(pc->r))
+		{
+			// color chunk is invalid. Simply ignore it
+			// EMISSSIVE TO 0|0|0
+			pc->r = pc->g = pc->b = 0.0f;
+		}
+		break;
+	case Dot3DSFile::CHUNK_MAT_TRANSPARENCY:
+		pcf = &this->mScene->mMaterials.back().mTransparency;
+		*pcf = this->ParsePercentageChunk();
+		// NOTE: transparency, not opacity
+		*pcf = 1.0f - *pcf;
+		if (is_qnan(*pcf))
+			*pcf = 0.0f;
+		break;
+
+	case Dot3DSFile::CHUNK_MAT_SHADING:
+		
+		this->mScene->mMaterials.back().mShading =
+			(Dot3DS::Dot3DSFile::shadetype3ds)*((uint16_t*)this->mCurrent);
+
+		this->mCurrent += sizeof(uint16_t);
+		break;
+
+	case Dot3DSFile::CHUNK_MAT_SHININESS:
+		pcf = &this->mScene->mMaterials.back().mSpecularExponent;
+		*pcf = this->ParsePercentageChunk();
+		if (is_qnan(*pcf))
+			*pcf = 0.0f;
+		else *pcf *= (float)0xFFFF;
+		break;
+
+	case Dot3DSFile::CHUNK_MAT_SELF_ILPCT:
+		pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend;
+		*pcf = this->ParsePercentageChunk();
+		if (is_qnan(*pcf))
+			*pcf = 1.0f;
+		break;
+
+	// parse texture chunks
+	case Dot3DSFile::CHUNK_MAT_TEXTURE:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexDiffuse);
+		break;
+	case Dot3DSFile::CHUNK_MAT_BUMPMAP:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexBump);
+		break;
+	case Dot3DSFile::CHUNK_MAT_OPACMAP:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexOpacity);
+		break;
+	case Dot3DSFile::CHUNK_MAT_MAT_SHINMAP:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexShininess);
+		break;
+	case Dot3DSFile::CHUNK_MAT_SPECMAP:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexSpecular);
+		break;
+	case Dot3DSFile::CHUNK_MAT_SELFIMAP:
+		iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk));
+		this->ParseTextureChunk(&iRemaining,&this->mScene->mMaterials.back().sTexEmissive);
+		break;
+	};
+	if ((unsigned int)pcCurNext < (unsigned int)this->mCurrent)
+	{
+		// place an error message. If we crash the programmer
+		// will be able to find it
+		this->mErrorText = ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG;
+		pcCurNext = this->mCurrent;
+	}
+	// Go to the starting position of the next chunk on this level
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return ParseMaterialChunk(piRemaining);
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseTextureChunk(int* piRemaining,Dot3DS::Texture* pcOut)
+{
+	const Dot3DSFile::Chunk* psChunk;
+
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return;
+
+	const unsigned char* pcCur = this->mCurrent;
+	const unsigned char* pcCurNext = pcCur + (psChunk->Size 
+		- sizeof(Dot3DSFile::Chunk));
+
+	// get chunk type
+	const unsigned char* sz = this->mCurrent;
+	unsigned int iCnt = 0;
+	switch (psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_MAPFILE:
+		// string in file is zero-terminated, 
+		// this should be no problem. However, validate whether
+		// it overlaps the end of the chunk, if yes we should
+		// truncate it.
+		while (*sz++ != '\0')
+		{
+			if (sz > pcCurNext-1)break;
+			++iCnt;
+		}
+		pcOut->mMapName = std::string((const char*)this->mCurrent,iCnt);
+		break;
+	// manually parse the blend factor
+	case Dot3DSFile::CHUNK_PERCENTF:
+		pcOut->mTextureBlend = *((float*)this->mCurrent);
+		break;
+	// manually parse the blend factor
+	case Dot3DSFile::CHUNK_PERCENTW:
+		pcOut->mTextureBlend = (float)(*((short*)this->mCurrent)) / (float)0xFFFF;
+		break;
+
+	case Dot3DSFile::CHUNK_MAT_MAP_USCALE:
+		pcOut->mScaleU = *((float*)this->mCurrent);
+		break;
+	case Dot3DSFile::CHUNK_MAT_MAP_VSCALE:
+		pcOut->mScaleV = *((float*)this->mCurrent);
+		break;
+	case Dot3DSFile::CHUNK_MAT_MAP_UOFFSET:
+		pcOut->mOffsetU = *((float*)this->mCurrent);
+		break;
+	case Dot3DSFile::CHUNK_MAT_MAP_VOFFSET:
+		pcOut->mOffsetV = *((float*)this->mCurrent);
+		break;
+	case Dot3DSFile::CHUNK_MAT_MAP_ANG:
+		pcOut->mRotation = *((float*)this->mCurrent);
+		break;
+	};
+
+	// Go to the starting position of the next chunk on this level
+	this->mCurrent = pcCurNext;
+
+	*piRemaining -= psChunk->Size;
+	if (0 >= *piRemaining)return;
+	return ParseTextureChunk(piRemaining,pcOut);
+}
+// ------------------------------------------------------------------------------------------------
+float Dot3DSImporter::ParsePercentageChunk()
+{
+	const Dot3DSFile::Chunk* psChunk;
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)return std::numeric_limits<float>::quiet_NaN();
+
+	if (Dot3DSFile::CHUNK_PERCENTF == psChunk->Flag)
+	{
+		if (sizeof(float) > psChunk->Size)
+			return std::numeric_limits<float>::quiet_NaN();
+		return *((float*)this->mCurrent);
+	}
+	else if (Dot3DSFile::CHUNK_PERCENTW == psChunk->Flag)
+	{
+		if (2 > psChunk->Size)
+			return std::numeric_limits<float>::quiet_NaN();
+		return (float)(*((short*)this->mCurrent)) / (float)0xFFFF;
+	}
+	this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk);
+	return std::numeric_limits<float>::quiet_NaN();
+}
+// ------------------------------------------------------------------------------------------------
+void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut,
+	bool p_bAcceptPercent)
+{
+	ai_assert(p_pcOut != NULL);
+
+	// error return value
+	static const aiColor3D clrError = aiColor3D(std::numeric_limits<float>::quiet_NaN(),
+		std::numeric_limits<float>::quiet_NaN(),
+		std::numeric_limits<float>::quiet_NaN());
+
+	const Dot3DSFile::Chunk* psChunk;
+	this->ReadChunk(&psChunk);
+	if (NULL == psChunk)
+	{
+		*p_pcOut = clrError;
+		return;
+	}
+	const unsigned char* pcCur = this->mCurrent;
+	this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk);
+	bool bGamma = false;
+	switch(psChunk->Flag)
+	{
+	case Dot3DSFile::CHUNK_LINRGBF:
+		bGamma = true;
+	case Dot3DSFile::CHUNK_RGBF:
+		if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		{
+			*p_pcOut = clrError;
+			return;
+		}
+		p_pcOut->r = ((float*)pcCur)[0];
+		p_pcOut->g = ((float*)pcCur)[1];
+		p_pcOut->b = ((float*)pcCur)[2];
+		break;
+
+	case Dot3DSFile::CHUNK_LINRGBB:
+		bGamma = true;
+	case Dot3DSFile::CHUNK_RGBB:
+		if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		{
+			*p_pcOut = clrError;
+			return;
+		}
+		p_pcOut->r = (float)pcCur[0] / 255.0f;
+		p_pcOut->g = (float)pcCur[1] / 255.0f;
+		p_pcOut->b = (float)pcCur[2] / 255.0f;
+		break;
+
+	// percentage chunks: accepted to be compatible with various
+	// .3ds files with very curious content
+	case Dot3DSFile::CHUNK_PERCENTF:
+		if (p_bAcceptPercent && 4 <= psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		{
+			p_pcOut->r = *((float*)pcCur);
+			p_pcOut->g = *((float*)pcCur);
+			p_pcOut->b = *((float*)pcCur);
+			break;
+		}
+		*p_pcOut = clrError;
+		return;
+	case Dot3DSFile::CHUNK_PERCENTW:
+		if (p_bAcceptPercent && 1 <= psChunk->Size - sizeof(Dot3DSFile::Chunk))
+		{
+			p_pcOut->r = (float)pcCur[0] / 255.0f;
+			p_pcOut->g = (float)pcCur[0] / 255.0f;
+			p_pcOut->b = (float)pcCur[0] / 255.0f;
+			break;
+		}
+		*p_pcOut = clrError;
+		return;
+
+	default:
+		// skip unknown chunks, hope this won't cause any problems.
+		return this->ParseColorChunk(p_pcOut,p_bAcceptPercent);
+	};
+	// assume input gamma = 1.0, output gamma = 2.2 
+	// Not sure whether this is correct, too tired to 
+	// think about it ;-)
+	if (bGamma)
+	{
+		p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f);
+		p_pcOut->g = powf(p_pcOut->g, 1.0f / 2.2f);
+		p_pcOut->b = powf(p_pcOut->b, 1.0f / 2.2f);
+	}
+	return;
+}

+ 235 - 0
code/3DSLoader.h

@@ -0,0 +1,235 @@
+/** @file Definition of the .3ds importer class. */
+#ifndef AI_3DSIMPORTER_H_INC
+#define AI_3DSIMPORTER_H_INC
+
+#include <map>
+
+#include "BaseImporter.h"
+
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+#include "3DSHelper.h"
+namespace Assimp
+{
+	class MaterialHelper;
+
+	using namespace Dot3DS;
+
+	// ---------------------------------------------------------------------------
+	/** The Dot3DSImporter is a worker class capable of importing a scene from a
+	* 3ds Max 4/5 File (.3ds)
+	*/
+	class Dot3DSImporter : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		Dot3DSImporter();
+
+		/** Destructor, private as well */
+		~Dot3DSImporter();
+
+	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:
+		// -------------------------------------------------------------------
+		/** Imports the given file into the given scene structure. 
+		* See BaseImporter::InternReadFile() for details
+		*/
+		void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+
+		// -------------------------------------------------------------------
+		/** Converts a temporary material to the outer representation 
+		*/
+		void ConvertMaterial(Dot3DS::Material& p_cMat,
+			MaterialHelper& p_pcOut);
+
+
+		// -------------------------------------------------------------------
+		/** Read a chunk, get a pointer to it
+		*  The mCurrent pointer will be increased by sizeof(Dot3DSFile::Chunk),
+		* thus pointing directly to the data of the chunk
+		*/
+		void ReadChunk(const Dot3DSFile::Chunk** p_ppcOut);
+
+		// -------------------------------------------------------------------
+		/** Parse a percentage chunk. mCurrent will point to the next
+		* chunk behind afterwards. If no percentage chunk is found
+		* QNAN is returned.
+		*/
+		float ParsePercentageChunk();
+
+		// -------------------------------------------------------------------
+		/** Parse a color chunk. mCurrent will point to the next
+		* chunk behind afterwards. If no color chunk is found
+		* QNAN is returned in all members.
+		*/
+		void ParseColorChunk(aiColor3D* p_pcOut,
+			bool p_bAcceptPercent = true);
+
+
+		// -------------------------------------------------------------------
+		/** Skip a chunk in the file
+		*/
+		void SkipChunk();
+
+		// -------------------------------------------------------------------
+		/** Generate the nodegraph
+		*/
+		void GenerateNodeGraph(aiScene* pcOut);
+
+		// -------------------------------------------------------------------
+		/** Parse a main top-level chunk in the file
+		*/
+		void ParseMainChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a top-level chunk in the file
+		*/
+		void ParseChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a top-level editor chunk in the file
+		*/
+		void ParseEditorChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a top-level object chunk in the file
+		*/
+		void ParseObjectChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a material chunk in the file
+		*/
+		void ParseMaterialChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Apply texture coordinate offsets
+		*/
+		void ApplyScaleNOffset();
+		void BakeScaleNOffset(aiMesh* pcMesh, Dot3DS::Material* pcSrc);
+
+		// -------------------------------------------------------------------
+		/** Parse a mesh chunk in the file
+		*/
+		void ParseMeshChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a face list chunk in the file
+		*/
+		void ParseFaceChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a keyframe chunk in the file
+		*/
+		void ParseKeyframeChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a hierarchy chunk in the file
+		*/
+		void ParseHierarchyChunk(int* piRemaining);
+
+		// -------------------------------------------------------------------
+		/** Parse a texture chunk in the file
+		*/
+		void ParseTextureChunk(int* piRemaining,Dot3DS::Texture* pcOut);
+
+		// -------------------------------------------------------------------
+		/** Convert the meshes in the file
+		*/
+		void ConvertMeshes(aiScene* pcOut);
+
+		// -------------------------------------------------------------------
+		/** Replace the default material in the scene
+		*/
+		void ReplaceDefaultMaterial();
+
+		// -------------------------------------------------------------------
+		/** Convert the whole scene
+		*/
+		void ConvertScene(aiScene* pcOut);
+
+		// -------------------------------------------------------------------
+		/** U/V Scaling/Offset handling
+		*/
+		void GenTexCoord (Dot3DS::Texture* pcTexture,
+			const std::vector<aiVector2D>& p_vIn,
+			std::vector<aiVector2D>& p_vOut);
+
+		// -------------------------------------------------------------------
+		/** generate normal vectors for a given mesh
+		*/
+		void GenNormals(Dot3DS::Mesh* sMesh);
+
+
+		// -------------------------------------------------------------------
+		/** generate unique vertices for a mesh
+		*/
+		void MakeUnique(Dot3DS::Mesh* sMesh);
+
+
+		// -------------------------------------------------------------------
+		/** Add a node to the node graph
+		*/
+		void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn);
+
+		// -------------------------------------------------------------------
+		/** Search for a node in the graph.
+		* Called recursively
+		*/
+		void InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurrent);
+
+
+		// -------------------------------------------------------------------
+		/** Apply the master scaling factor to the mesh
+		*/
+		void ApplyMasterScale(aiScene* pScene);
+
+
+		// -------------------------------------------------------------------
+		/** Clamp all indices in the file to a valid range
+		*/
+		void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh);
+
+
+	protected:
+		/** Buffer to hold the loaded file */
+		unsigned char* mBuffer;
+
+		/** Pointer to the current read position */
+		const unsigned char* mCurrent;
+
+		/** Used to store old chunk addresses to jump back in the file*/
+		const unsigned char* mLast;
+
+		/** Last touched node index */
+		short mLastNodeIndex;
+
+		/** Current node, root node */
+		Dot3DS::Node* mCurrentNode, *mRootNode;
+
+		/** Scene under construction */
+		Dot3DS::Scene* mScene;
+
+		/** Ambient base color of the scene */
+		aiColor3D mClrAmbient;
+
+		/** Master scaling factor of the scene */
+		float mMasterScale;
+
+		/** Path to the background image of the scene */
+		std::string mBackgroundImage;
+		bool bHasBG;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 148 - 0
code/3DSSpatialSort.cpp

@@ -0,0 +1,148 @@
+/** @file Implementation of the helper class to quickly find vertices close to a given position */
+#include <algorithm>
+#include "3DSSpatialSort.h"
+
+using namespace Assimp;
+using namespace Assimp::Dot3DS;
+
+// ------------------------------------------------------------------------------------------------
+// Constructs a spatially sorted representation from the given position array.
+D3DSSpatialSorter::D3DSSpatialSorter( const aiVector3D* pPositions,
+									 unsigned int pNumPositions, unsigned int pElementOffset)
+	{
+	// define the reference plane. We choose some arbitrary vector away from all basic axises 
+	// in the hope that no model spreads all its vertices along this plane.
+	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
+	mPlaneNormal.Normalize();
+
+	// store references to all given positions along with their distance to the reference plane
+	mPositions.reserve( pNumPositions);
+	for( unsigned int a = 0; a < pNumPositions; a++)
+		{
+		const char* tempPointer = reinterpret_cast<const char*> (pPositions);
+		const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
+
+		// store position by index and distance
+		float distance = *vec * mPlaneNormal;
+		mPositions.push_back( Entry( a, *vec, distance,0));
+		}
+
+	// now sort the array ascending by distance.
+	std::sort( mPositions.begin(), mPositions.end());
+	}
+// ------------------------------------------------------------------------------------------------
+D3DSSpatialSorter::D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh)
+	{
+	// define the reference plane. We choose some arbitrary vector away from all basic axises 
+	// in the hope that no model spreads all its vertices along this plane.
+	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
+	mPlaneNormal.Normalize();
+
+	// store references to all given positions along with their distance to the reference plane
+	mPositions.reserve( p_pcMesh->mPositions.size());
+	for( std::vector<Dot3DS::Face>::const_iterator
+		i =  p_pcMesh->mFaces.begin();
+		i != p_pcMesh->mFaces.end();++i)
+		{
+		// store position by index and distance
+		float distance = p_pcMesh->mPositions[(*i).i1] * mPlaneNormal;
+		mPositions.push_back( Entry( (*i).i1, p_pcMesh->mPositions[(*i).i1], 
+			distance, (*i).iSmoothGroup));
+
+		// triangle vertex 2
+		distance = p_pcMesh->mPositions[(*i).i2] * mPlaneNormal;
+		mPositions.push_back( Entry( (*i).i2, p_pcMesh->mPositions[(*i).i2], 
+			distance, (*i).iSmoothGroup));
+
+		// triangle vertex 3
+		distance = p_pcMesh->mPositions[(*i).i3] * mPlaneNormal;
+		mPositions.push_back( Entry( (*i).i3, p_pcMesh->mPositions[(*i).i3], 
+			distance, (*i).iSmoothGroup));
+		}
+
+	// now sort the array ascending by distance.
+	std::sort( this->mPositions.begin(), this->mPositions.end());
+	}
+// ------------------------------------------------------------------------------------------------
+// Destructor
+D3DSSpatialSorter::~D3DSSpatialSorter()
+	{
+	// nothing to do here, everything destructs automatically
+	}
+
+// ------------------------------------------------------------------------------------------------
+// Returns an iterator for all positions close to the given position.
+void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, 
+									  uint32_t pSG,float pRadius, std::vector<unsigned int>& poResults) const
+	{
+	float dist = pPosition * mPlaneNormal;
+	float minDist = dist - pRadius, maxDist = dist + pRadius;
+
+	// clear the array in this strange fashion because a simple clear() would also deallocate
+	// the array which we want to avoid
+	poResults.erase( poResults.begin(), poResults.end());
+
+	// quick check for positions outside the range
+	if( mPositions.size() == 0)
+		return;
+	if( maxDist < mPositions.front().mDistance)
+		return;
+	if( minDist > mPositions.back().mDistance)
+		return;
+
+	// do a binary search for the minimal distance to start the iteration there
+	unsigned int index = mPositions.size() / 2;
+	unsigned int binaryStepSize = mPositions.size() / 4;
+	while( binaryStepSize > 1)
+		{
+		if( mPositions[index].mDistance < minDist)
+			index += binaryStepSize;
+		else
+			index -= binaryStepSize;
+
+		binaryStepSize /= 2;
+		}
+
+	// depending on the direction of the last step we need to single step a bit back or forth
+	// to find the actual beginning element of the range
+	while( index > 0 && mPositions[index].mDistance > minDist)
+		index--;
+	while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
+		index++;
+
+	// Mow start iterating from there until the first position lays outside of the distance range.
+	// Add all positions inside the distance range within the given radius to the result aray
+
+	if (0 == pSG)
+		{
+		std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+		float squareEpsilon = pRadius * pRadius;
+		while( it->mDistance < maxDist)
+			{
+			if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
+				{
+				poResults.push_back( it->mIndex);
+				}
+			++it;
+			if( it == mPositions.end())
+				break;
+			}
+		}
+	else
+		{
+		std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+		float squareEpsilon = pRadius * pRadius;
+		while( it->mDistance < maxDist)
+			{
+			if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
+				(it->mSmoothGroups & pSG || 0 == it->mSmoothGroups))
+				{
+				poResults.push_back( it->mIndex);
+				}
+			++it;
+			if( it == mPositions.end())
+				break;
+			}
+		}
+	}
+

+ 83 - 0
code/3DSSpatialSort.h

@@ -0,0 +1,83 @@
+/** Small helper classes to optimise finding vertizes close to a given location */
+#ifndef AI_D3DSSPATIALSORT_H_INC
+#define AI_D3DSSPATIALSORT_H_INC
+
+#include <vector>
+#include "../include/aiVector3D.h"
+#include "3DSHelper.h"
+
+namespace Assimp
+{
+
+using namespace Dot3DS;
+
+// ------------------------------------------------------------------------------------------------
+/** Specialized version of SpatialSort to support smoothing groups
+ *  This is used in the .3ds loader
+ */
+class D3DSSpatialSorter
+{
+public:
+
+	D3DSSpatialSorter() {/* This is unintialized. This is evil. This is OK. */}
+
+	/** Constructs a spatially sorted representation from the given position array.
+	 * Supply the positions in its layout in memory, the class will only refer to them
+	 * by index.
+	 * @param pPositions Pointer to the first position vector of the array.
+	 * @param pNumPositions Number of vectors to expect in that array.
+	 * @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector.
+	 * @note Smoothing groups are ignored
+	 */
+	D3DSSpatialSorter( const aiVector3D* pPositions,
+		unsigned int pNumPositions, unsigned int pElementOffset);
+
+	/** Construction from a given face array, handling smoothing groups properly
+	 * @param p_pcMesh Input mesh.
+	 */
+	D3DSSpatialSorter( const Dot3DS::Mesh* p_pcMesh);
+
+
+	/** Destructor */
+	~D3DSSpatialSorter();
+
+	/** Returns an iterator for all positions close to the given position.
+	 * @param pPosition The position to look for vertices.
+	 * @param pSG Only included vertices with at least one shared smooth group
+	 * @param pRadius Maximal distance from the position a vertex may have to be counted in.
+	 * @param poResults The container to store the indices of the found positions. Will be emptied
+	 *   by the call so it may contain anything.
+	 * @return An iterator to iterate over all vertices in the given area.
+	 */
+	void FindPositions( const aiVector3D& pPosition, uint32_t pSG,
+		float pRadius, std::vector<unsigned int>& poResults) const;
+
+protected:
+	/** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+	aiVector3D mPlaneNormal;
+
+	/** An entry in a spatially sorted position array. Consists of a vertex index,
+	 * its position and its precalculated distance from the reference plane */
+	struct Entry
+	{
+		unsigned int mIndex; ///< The vertex referred by this entry
+		aiVector3D mPosition; ///< Position
+		uint32_t mSmoothGroups;
+		float mDistance; ///< Distance of this vertex to the sorting plane
+
+		Entry() { /** intentionally not initialized.*/ }
+		Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) 
+			: 
+			mPosition( pPosition), mIndex( pIndex), mDistance( pDistance),mSmoothGroups (pSG)
+			{ 	}
+
+		bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
+	};
+
+	// all positions, sorted by distance to the sorting plane
+	std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC

+ 61 - 0
code/Assimp.cpp

@@ -0,0 +1,61 @@
+/** @file Implementation of the Plain-C API */
+#include <map>
+
+#include "../include/assimp.h"
+#include "../include/assimp.hpp"
+
+/** Stores the importer objects for all active import processes */
+typedef std::map< const aiScene*, Assimp::Importer* > ImporterMap;
+/** Local storage of all active import processes */
+static ImporterMap gActiveImports;
+
+/** Error message of the last failed import process */
+static std::string gLastErrorString;
+
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its content. 
+const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
+{
+	// create an Importer for this file
+	Assimp::Importer* imp = new Assimp::Importer;
+	// and have it read the file
+	const aiScene* scene = imp->ReadFile( pFile, pFlags);
+
+	// if succeeded, place it in the collection of active processes
+	if( scene)
+	{
+		gActiveImports[scene] = imp;
+	} 
+	else
+	{
+		// if failed, extract error code and destroy the import
+		gLastErrorString = imp->GetErrorString();
+		delete imp;
+	}
+
+	// return imported data. If the import failed the pointer is NULL anyways
+	return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Releases all resources associated with the given import process. 
+void aiReleaseImport( const aiScene* pScene)
+{
+	// find the importer associated with this data
+	ImporterMap::iterator it = gActiveImports.find( pScene);
+	// it should be there... else the user is playing fools with us
+	if( it == gActiveImports.end())
+		return;
+
+	// kill the importer, the data dies with it
+	delete it->second;
+	gActiveImports.erase( it);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process. 
+const char* aiGetErrorString()
+{
+	return gLastErrorString.c_str();
+}

+ 45 - 0
code/BaseImporter.cpp

@@ -0,0 +1,45 @@
+/** @file Implementation of the few default functions of the base importer class */
+#include "BaseImporter.h"
+#include "../include/aiScene.h"
+#include "aiAssert.h"
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BaseImporter::BaseImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BaseImporter::~BaseImporter()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file and returns the imported data.
+aiScene* BaseImporter::ReadFile( const std::string& pFile, IOSystem* pIOHandler)
+{
+	// create a scene object to hold the data
+	aiScene* scene = new aiScene;
+
+	// dispatch importing
+	try
+	{
+		InternReadFile( pFile, scene, pIOHandler);
+	} catch( ImportErrorException* exception)
+	{
+		// extract error description
+		mErrorText = exception->GetErrorText();
+		delete exception;
+
+		// and kill the partially imported data
+		delete scene;
+		scene = NULL;
+	}
+
+	// return what we gathered from the import. 
+	return scene;
+}

+ 105 - 0
code/BaseImporter.h

@@ -0,0 +1,105 @@
+/** @file Definition of the base class for all importer worker classes. */
+#ifndef AI_BASEIMPORTER_H_INC
+#define AI_BASEIMPORTER_H_INC
+
+#include <string>
+
+struct aiScene;
+
+namespace Assimp
+{
+
+class IOSystem;
+
+// ---------------------------------------------------------------------------
+/** Simple exception class to be thrown if an error occurs while importing. */
+class ImportErrorException 
+{
+public:
+	/** Constructor with arguments */
+	ImportErrorException( const std::string& pErrorText)
+	{
+		mErrorText = pErrorText;
+	}
+
+	/** Returns the error text provided when throwing the exception */
+	const std::string& GetErrorText() const { return mErrorText; }
+
+private:
+	std::string mErrorText;
+};
+
+// ---------------------------------------------------------------------------
+/** The BaseImporter defines a common interface for all importer worker 
+ *  classes.
+ *
+ * The interface defines two functions: CanRead() is used to check if the 
+ * importer can handle the format of the given file. If an implementation of 
+ * this function returns true, the importer then calls ReadFile() which 
+ * imports the given file. ReadFile is not overridable, it just calls InternReadFile() 
+ * and catches any ImportErrorException that might occur.
+ */
+class BaseImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	BaseImporter();
+
+	/** Destructor, private as well */
+	virtual ~BaseImporter();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the class can handle the format of the given file.
+	* @param pFile Path and file name of the file to be examined.
+	* @param pIOHandler The IO handler to use for accessing any file.
+	* @return true if the class can read this file, false if not.
+	*/
+	virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const = 0;
+
+	// -------------------------------------------------------------------
+	/** 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 takes care that any
+	* partially constructed data is destroyed beforehand.
+	*
+	* @param pFile Path of the file to be imported. 
+	* @param pIOHandler IO-Handler used to open this and possible other files.
+	* @return The imported data or NULL if failed. If it failed a human-readable
+	*   error description can be retrieved by calling GetErrorText()
+	*
+	* @note This function is not intended to be overridden. Implement InternReadFile()
+	*   to do the import. If an exception is thrown somewhere in InternReadFile(),
+	*   this function will catch it and transform it into a suitable response to the caller.
+	*/
+	aiScene* ReadFile( const std::string& pFile, IOSystem* pIOHandler);
+
+	// -------------------------------------------------------------------
+	/** Returns the error description of the last error that occured. 
+	 * @return A description of the last error that occured. An empty string if no error.
+	 */
+	const std::string& GetErrorText() const { return mErrorText; }
+
+protected:
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. The function is 
+	 * expected to throw an ImportErrorException if there is an error. If it
+	 * terminates normally, the data in aiScene is expected to be correct.
+	 * Override this function to implement the actual importing.
+	 * 
+	 * @param pFile Path of the file to be imported.
+	 * @param pScene The scene object to hold the imported data.
+	 * @param pIOHandler The IO handler to use for any file access.
+	 */
+	virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) = 0;
+
+protected:
+	/** Error description in case there was one. */
+	std::string mErrorText;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC

+ 59 - 0
code/BaseProcess.h

@@ -0,0 +1,59 @@
+/** @file Base class of all import post processing steps */
+#ifndef AI_BASEPROCESS_H_INC
+#define AI_BASEPROCESS_H_INC
+
+struct aiScene;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The BaseProcess defines a common interface for all post processing steps.
+ * A post processing step is run after a successful import if the caller
+ * specified the corresponding flag when calling ReadFile(). Enum #aiPostProcessSteps
+ * defines which flags are available. 
+ * After a successful import the Importer iterates over its internal array of processes
+ * and calls IsActive() on each process to evaluate if the step should be executed.
+ * If the function returns true, the class' Execute() function is called subsequently.
+ */
+class BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	BaseProcess();
+
+	/** Destructor, private as well */
+	virtual ~BaseProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	 * @param pFlags The processing flags the importer was called with. A bitwise
+	 *   combination of #aiPostProcessSteps.
+	 * @return true if the process is present in this flag fields, false if not.
+	*/
+	virtual bool IsActive( unsigned int pFlags) const = 0;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	virtual void Execute( aiScene* pScene) = 0;
+};
+
+/** Constructor, dummy implementation to keep the compiler from complaining */
+inline BaseProcess::BaseProcess()
+{
+}
+
+/** Destructor */
+inline BaseProcess::~BaseProcess()
+{
+}
+
+} // end of namespace Assimp
+
+#endif // AI_BASEPROCESS_H_INC

+ 186 - 0
code/CalcTangentsProcess.cpp

@@ -0,0 +1,186 @@
+/** @file Implementation of the post processing step to calculate tangents and bitangents
+ * for all imported meshes
+ */
+
+#include <vector>
+#include <assert.h>
+#include "CalcTangentsProcess.h"
+#include "SpatialSort.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CalcTangentsProcess::CalcTangentsProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+CalcTangentsProcess::~CalcTangentsProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_CalcTangentSpace) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		ProcessMesh( pScene->mMeshes[a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates tangents and bitangents for the given mesh
+void CalcTangentsProcess::ProcessMesh( aiMesh* pMesh)
+{
+	// we assume that the mesh is still in the verbose vertex format where each face has its own set
+	// of vertices and no vertices are shared between faces. Sadly I don't know any quick test to 
+	// assert() it here.
+    //assert( must be verbose, dammit);
+
+	// what we can check, though, is if the mesh has normals and texture coord. That's a requirement
+	if( pMesh->mNormals == NULL || pMesh->mTextureCoords[0] == NULL)
+		return;
+
+	// calculate the position bounds so we have a reliable epsilon to check position differences against 
+	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+	{
+		minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
+		minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
+		minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
+		maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
+		maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
+		maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
+	}
+
+	// calculate epsilons border
+	const float epsilon = 1e-5f;
+	const float posEpsilon = (maxVec - minVec).Length() * epsilon;
+	const float angleEpsilon = 0.9999f;
+
+	// create space for the tangents and bitangents
+	pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+	pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+
+	const aiVector3D* meshPos = pMesh->mVertices;
+	const aiVector3D* meshNorm = pMesh->mNormals;
+	const aiVector3D* meshTex = pMesh->mTextureCoords[0];
+	aiVector3D* meshTang = pMesh->mTangents;
+	aiVector3D* meshBitang = pMesh->mBitangents;
+	
+	// calculate the tangent and bitangent for every face
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		const aiFace& face = pMesh->mFaces[a];
+
+		// triangle or polygon... we always use only the first three indices. A polygon
+		// is supposed to be planar anyways....
+		// FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
+		const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
+
+		// position differences p1->p2 and p1->p3
+		aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
+
+		// texture offset p1->p2 and p1->p3
+		float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
+        float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
+		float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
+
+		// tangent points in the direction where to positive X axis of the texture coords would point in model space
+		// bitangents points along the positive Y axis of the texture coords, respectively
+		aiVector3D tangent, bitangent;
+		tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
+        tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
+        tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
+        bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
+        bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
+        bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
+
+		// store for every vertex of that face
+		for( unsigned int b = 0; b < face.mNumIndices; b++)
+		{
+			unsigned int p = face.mIndices[b];
+
+			// project tangent and bitangent into the plane formed by the vertex' normal
+			aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
+			aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
+			localTangent.Normalize(); localBitangent.Normalize();
+
+			// and write it into the mesh.
+			meshTang[p] = localTangent;
+			meshBitang[p] = localBitangent;
+		}
+    }
+
+	// create a helper to quickly find locally close vertices among the vertex array
+	SpatialSort vertexFinder( meshPos, pMesh->mNumVertices, sizeof( aiVector3D));
+	std::vector<unsigned int> verticesFound;
+
+	// in the second pass we now smooth out all tangents and bitangents at the same local position 
+	// if they are not too far off.
+	std::vector<bool> vertexDone( pMesh->mNumVertices, false);
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+	{
+		if( vertexDone[a])
+			continue;
+
+		const aiVector3D& origPos = pMesh->mVertices[a];
+		const aiVector3D& origNorm = pMesh->mNormals[a];
+		const aiVector3D& origTang = pMesh->mTangents[a];
+		const aiVector3D& origBitang = pMesh->mBitangents[a];
+		std::vector<unsigned int> closeVertices;
+		closeVertices.push_back( a);
+
+		// find all vertices close to that position
+		vertexFinder.FindPositions( origPos, posEpsilon, verticesFound);
+
+		// look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
+		static const float MAX_DIFF_ANGLE = 0.701f;
+		for( unsigned int b = 0; b < verticesFound.size(); b++)
+		{
+			unsigned int idx = verticesFound[b];
+			if( vertexDone[idx])
+				continue;
+			if( meshNorm[idx] * origNorm < angleEpsilon)
+				continue;
+			if( meshTang[idx] * origTang < MAX_DIFF_ANGLE)
+				continue;
+			if( meshBitang[idx] * origBitang < MAX_DIFF_ANGLE)
+				continue;
+
+			// it's similar enough -> add it to the smoothing group
+			closeVertices.push_back( idx);
+			vertexDone[idx] = true;
+		}
+
+		// smooth the tangents and bitangents of all vertices that were found to be close enough
+		aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
+		for( unsigned int b = 0; b < closeVertices.size(); b++)
+		{
+			smoothTangent += meshTang[ closeVertices[b] ];
+			smoothBitangent += meshBitang[ closeVertices[b] ];
+		}
+		smoothTangent.Normalize();
+		smoothBitangent.Normalize();
+
+		// and write it back into all affected tangents
+		for( unsigned int b = 0; b < closeVertices.size(); b++)
+		{
+			meshTang[ closeVertices[b] ] = smoothTangent;
+			meshBitang[ closeVertices[b] ] = smoothBitangent;
+		}
+	}
+}

+ 55 - 0
code/CalcTangentsProcess.h

@@ -0,0 +1,55 @@
+/** @file Defines a post processing step to calculate tangents and bitangents on all imported meshes.*/
+#ifndef AI_CALCTANGENTSPROCESS_H_INC
+#define AI_CALCTANGENTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex
+ * of all meshes. It is expected to be run before the JoinVerticesProcess runs
+ * because the joining of vertices also considers tangents and bitangents for uniqueness.
+
+ */
+class CalcTangentsProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	CalcTangentsProcess();
+
+	/** Destructor, private as well */
+	~CalcTangentsProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	 * @param pFlags The processing flags the importer was called with. A bitwise
+	 *   combination of #aiPostProcessSteps.
+	 * @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+protected:
+	// -------------------------------------------------------------------
+	/** Calculates tangents and bitangents for the given mesh
+	 * @param pMesh The mesh to process.
+	 */
+	void ProcessMesh( aiMesh* pMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC

+ 155 - 0
code/ConvertToLHProcess.cpp

@@ -0,0 +1,155 @@
+/** @file Implementation of the post processing step to convert all imported data
+ * to a left-handed coordinate system.
+ */
+#include "ConvertToLHProcess.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+#include "../include/aiScene.h"
+
+
+using namespace Assimp;
+
+// The transformation matrix to convert from DirectX coordinates to OpenGL coordinates.
+const aiMatrix3x3 Assimp::ConvertToLHProcess::sToOGLTransform(
+	1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f
+	);
+// The transformation matrix to convert from OpenGL coordinates to DirectX coordinates.
+const aiMatrix3x3 Assimp::ConvertToLHProcess::sToDXTransform(
+	1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f
+	);
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ConvertToLHProcess::ConvertToLHProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ConvertToLHProcess::~ConvertToLHProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ConvertToLHProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_ConvertToLeftHanded) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ConvertToLHProcess::Execute( aiScene* pScene)
+{
+	// Check for an existent root node to proceed
+	if (NULL == pScene->mRootNode)
+		return;
+
+	// transform the root node of the scene, the other nodes will follow then
+	ConvertToDX( pScene->mRootNode->mTransformation);
+
+	// transform all meshes accordingly
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		ProcessMesh( pScene->mMeshes[a]);
+
+	// transform all animation channels affecting the root node as well
+	for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
+	{
+		aiAnimation* anim = pScene->mAnimations[a];
+		for( unsigned int b = 0; b < anim->mNumBones; b++)
+		{
+			aiBoneAnim* boneAnim = anim->mBones[b];
+			if( strcmp( boneAnim->mBoneName.data, pScene->mRootNode->mName.data) == 0)
+				ProcessAnimation( boneAnim);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh to left handed coordinates. 
+void ConvertToLHProcess::ProcessMesh( aiMesh* pMesh)
+{
+	// invert the order of all faces in this mesh
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		aiFace& face = pMesh->mFaces[a];
+		for( unsigned int b = 0; b < face.mNumIndices / 2; b++)
+			std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
+	}
+
+	// mirror texture y coordinate
+	for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+	{
+		if( pMesh->HasTextureCoords( a))
+		{
+			for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
+				pMesh->mTextureCoords[a][b].y = 1.0f - pMesh->mTextureCoords[a][b].y;
+		}
+	}
+
+	// mirror bitangents as well as they're derived from the texture coords
+	if( pMesh->HasTangentsAndBitangents())
+	{
+		for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+			pMesh->mBitangents[a] = -pMesh->mBitangents[a];
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the given animation to LH coordinates. 
+void ConvertToLHProcess::ProcessAnimation( aiBoneAnim* pAnim)
+{
+	// position keys
+	for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
+		ConvertToDX( pAnim->mPositionKeys[a].mValue);
+
+	// rotation keys
+	for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
+	{
+		aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
+		ConvertToDX( rotmat);
+		pAnim->mRotationKeys[a].mValue = aiQuaternion( rotmat);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Static helper function to convert a vector/matrix from DX to OGL coords
+void ConvertToLHProcess::ConvertToOGL( aiVector3D& poVector)
+{
+	poVector = sToOGLTransform * poVector;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToLHProcess::ConvertToOGL( aiMatrix3x3& poMatrix)
+{
+	poMatrix *= sToOGLTransform;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToLHProcess::ConvertToOGL( aiMatrix4x4& poMatrix)
+{
+	poMatrix *= aiMatrix4x4( sToOGLTransform);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Static helper function to convert a vector/matrix from OGL back to DX coords
+void ConvertToLHProcess::ConvertToDX( aiVector3D& poVector)
+{
+	poVector = sToDXTransform * poVector;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToLHProcess::ConvertToDX( aiMatrix3x3& poMatrix)
+{
+	poMatrix *= sToDXTransform;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToLHProcess::ConvertToDX( aiMatrix4x4& poMatrix)
+{
+	aiMatrix4x4 temp(sToDXTransform);
+	poMatrix *= temp;
+}

+ 88 - 0
code/ConvertToLHProcess.h

@@ -0,0 +1,88 @@
+/** @file Defines a post processing step to convert all data to a left-handed coordinate system.*/
+#ifndef AI_CONVERTTOLHPROCESS_H_INC
+#define AI_CONVERTTOLHPROCESS_H_INC
+
+#include "../include/aiTypes.h"
+#include "BaseProcess.h"
+
+struct aiMesh;
+struct aiBoneAnim;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The ConvertToLHProcess converts all imported data to a left-handed coordinate
+ * system. This implies inverting the Z axis for all transformation matrices
+ * invert the orientation of all faces, and adapting skinning and animation 
+ * data in a similar way.
+ */
+class ConvertToLHProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	ConvertToLHProcess();
+
+	/** Destructor, private as well */
+	~ConvertToLHProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	 * @param pFlags The processing flags the importer was called with. A bitwise
+	 *   combination of #aiPostProcessSteps.
+	 * @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+	// -------------------------------------------------------------------
+	/** Static helper function to convert a vector/matrix from DX coords to OGL coords.
+	 * @param poMatrix The matrix to convert.
+	 */
+	static void ConvertToOGL( aiVector3D& poVector);
+	static void ConvertToOGL( aiMatrix3x3& poMatrix);
+	static void ConvertToOGL( aiMatrix4x4& poMatrix);
+
+	// -------------------------------------------------------------------
+	/** Static helper function to convert a vector/matrix from OGL coords back to DX coords.
+	 * @param poMatrix The matrix to convert.
+	 */
+	static void ConvertToDX( aiVector3D& poVector);
+	static void ConvertToDX( aiMatrix3x3& poMatrix);
+	static void ConvertToDX( aiMatrix4x4& poMatrix);
+
+protected:
+	// -------------------------------------------------------------------
+	/** Converts a single mesh to left handed coordinates. 
+	 * This simply means the order of all faces is inverted.
+	 * @param pMesh The mesh to convert.
+	 */
+	void ProcessMesh( aiMesh* pMesh);
+
+	// -------------------------------------------------------------------
+	/** Converts the given animation to LH coordinates. 
+	 * The rotation and translation keys are transformed, the scale keys
+	 * work in local space and can therefore be left untouched.
+	 * @param pAnim The bone animation to transform
+	 */
+	void ProcessAnimation( aiBoneAnim* pAnim);
+
+public:
+	/** The transformation matrix to convert from DirectX coordinates to OpenGL coordinates. */
+	static const aiMatrix3x3 sToOGLTransform;
+	/** The transformation matrix to convert from OpenGL coordinates to DirectX coordinates. */
+	static const aiMatrix3x3 sToDXTransform;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CONVERTTOLHPROCESS_H_INC

+ 92 - 0
code/DefaultIOStream.cpp

@@ -0,0 +1,92 @@
+/** @file Default File I/O implementation for #Importer */
+
+#include "DefaultIOStream.h"
+#include "aiAssert.h"
+#include <sys/types.h> 
+#include <sys/stat.h> 
+
+using namespace Assimp;
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+DefaultIOStream::~DefaultIOStream()
+{
+	if (this->mFile)
+	{
+		fclose(this->mFile);
+	}	
+}
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+size_t DefaultIOStream::Read(void* pvBuffer, 
+								size_t pSize, 
+								size_t pCount)
+{
+	if (!this->mFile)
+		return 0;
+
+	return fread(pvBuffer, pSize, pCount, this->mFile);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+size_t DefaultIOStream::Write(const void* pvBuffer, 
+								 size_t pSize,
+								 size_t pCount)
+{
+	if (!this->mFile)return 0;
+
+	fseek(mFile, 0, SEEK_SET);
+	return fwrite(pvBuffer, pSize, pCount, this->mFile);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn DefaultIOStream::Seek(size_t pOffset,
+						   aiOrigin pOrigin)
+{
+	if (!this->mFile)return AI_FAILURE;
+
+	return (0 == fseek(this->mFile, pOffset,
+		(aiOrigin_CUR == pOrigin ? SEEK_CUR :
+		(aiOrigin_END == pOrigin ? SEEK_END : SEEK_SET))) 
+		? AI_SUCCESS : AI_FAILURE);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+size_t DefaultIOStream::Tell() const
+{
+	if (!this->mFile)return 0;
+
+	return ftell(this->mFile);
+}
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+size_t DefaultIOStream::FileSize() const
+{
+	ai_assert (!mFilename.empty());
+
+	if (NULL == mFile)
+		return 0;
+#if defined _WIN32 && !defined __GNUC__
+	struct __stat64 fileStat; 
+	int err = _stat64(  mFilename.c_str(), &fileStat ); 
+	if (0 != err) 
+		return 0; 
+	return (size_t) (fileStat.st_size); 
+#else
+	struct stat fileStat; 
+	int err = stat(mFilename.c_str(), &fileStat ); 
+	if (0 != err) 
+		return 0; 
+	return (size_t) (fileStat.st_size); 
+#endif
+}
+// ---------------------------------------------------------------------------

+ 82 - 0
code/DefaultIOStream.h

@@ -0,0 +1,82 @@
+/** @file Default file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <string>
+#include <stdio.h>
+#include "IOStream.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+//!	\class	DefaultIOStream
+//!	\brief	Default IO implementation, use standard IO operations
+// ---------------------------------------------------------------------------
+class DefaultIOStream : public IOStream
+{
+	friend class DefaultIOSystem;
+
+protected:
+	DefaultIOStream ();
+	DefaultIOStream (FILE* pFile, const std::string &strFilename);
+
+public:
+	/** Destructor public to allow simple deletion to close the file. */
+	~DefaultIOStream ();
+
+	// -------------------------------------------------------------------
+	// -------------------------------------------------------------------
+    size_t Read(void* pvBuffer, 
+		size_t pSize, 
+		size_t pCount);
+
+
+	// -------------------------------------------------------------------
+	// -------------------------------------------------------------------
+    size_t Write(const void* pvBuffer, 
+		size_t pSize,
+		size_t pCount);
+
+
+	// -------------------------------------------------------------------
+	// -------------------------------------------------------------------
+	aiReturn Seek(size_t pOffset,
+		aiOrigin pOrigin);
+
+
+	// -------------------------------------------------------------------
+	// -------------------------------------------------------------------
+    size_t Tell() const;
+
+	//!	Returns filesize
+	size_t FileSize() const;
+
+private:
+	//!	File datastructure, using clib
+	FILE* mFile;
+	//!	Filename
+	std::string	mFilename;
+};
+// ---------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream () : 
+	mFile(NULL), 
+	mFilename("") 
+{
+	// empty
+}
+
+// ---------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream (FILE* pFile, 
+		const std::string &strFilename) :
+	mFile(pFile), 
+	mFilename(strFilename)
+{
+	// empty
+}
+
+// ---------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC

+ 64 - 0
code/DefaultIOSystem.cpp

@@ -0,0 +1,64 @@
+/** @file Default implementation of IOSystem using the standard C file functions */
+#include <stdlib.h>
+#include <string>
+
+#include "DefaultIOSystem.h"
+#include "DefaultIOStream.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor. 
+DefaultIOSystem::DefaultIOSystem()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor. 
+DefaultIOSystem::~DefaultIOSystem()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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");
+	if( !file)
+		return false;
+
+	fclose( file);
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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());
+	if( NULL == file) 
+		return NULL;
+
+	return new DefaultIOStream( file, strFile);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Closes the given file and releases all resources associated with it.
+void DefaultIOSystem::Close( IOStream* pFile)
+{
+	delete pFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the operation specific directory separator
+std::string DefaultIOSystem::getOsSeparator() const
+{
+#ifndef _WIN32
+	std::string sep = "/";
+#else
+	std::string sep = "\\";
+#endif
+	return sep;
+}

+ 40 - 0
code/DefaultIOSystem.h

@@ -0,0 +1,40 @@
+/** @file Default implementation of IOSystem using the standard C file functions */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include "IOSystem.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** Default implementation of IOSystem using the standard C file functions */
+class DefaultIOSystem : public IOSystem
+{
+public:
+	/** Constructor. */
+    DefaultIOSystem();
+
+	/** Destructor. */
+	~DefaultIOSystem();
+
+	// -------------------------------------------------------------------
+	/** Tests for the existence of a file at the given path. */
+	bool Exists( const std::string& pFile) const;
+
+	// -------------------------------------------------------------------
+	/** Returns the directory separator. */
+	std::string getOsSeparator() const;
+
+	// -------------------------------------------------------------------
+	/** Open a new file with a given path. */
+	IOStream* Open( const std::string& pFile, const std::string& pMode = std::string("rb"));
+
+	// -------------------------------------------------------------------
+	/** Closes the given file and releases all resources associated with it. */
+	void Close( IOStream* pFile);
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC

+ 67 - 0
code/GenFaceNormalsProcess.cpp

@@ -0,0 +1,67 @@
+/** @file Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+#include "GenFaceNormalsProcess.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// Constructor to be privately used by Importer
+GenFaceNormalsProcess::GenFaceNormalsProcess()
+	{
+	}
+
+// Destructor, private as well
+GenFaceNormalsProcess::~GenFaceNormalsProcess()
+	{
+	// nothing to do here
+	}
+
+// -------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const
+{
+	return	(pFlags & aiProcess_GenNormals) != 0;
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenFaceNormalsProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		this->GenMeshFaceNormals( pScene->mMeshes[a]);
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
+{
+	if (NULL != pMesh->mNormals)return;
+
+	pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		const aiFace& face = pMesh->mFaces[a];
+		
+		// assume it is a triangle
+		aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+		aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
+
+		aiVector3D pDelta1 = *pV2 - *pV1;
+		aiVector3D pDelta2 = *pV3 - *pV1;
+		aiVector3D vNor = pDelta1 ^ pDelta2;
+		
+		// NOTE: Never normalize here. Causes problems ...
+		//float fLength = vNor.Length();
+		//if (0.0f != fLength)vNor /= fLength;
+
+		pMesh->mNormals[face.mIndices[0]] = vNor;
+		pMesh->mNormals[face.mIndices[1]] = vNor;
+		pMesh->mNormals[face.mIndices[2]] = vNor;
+	}
+	return;
+}

+ 48 - 0
code/GenFaceNormalsProcess.h

@@ -0,0 +1,48 @@
+/** @file Defines a post processing step to compute face normals for all loaded faces*/
+#ifndef AI_GENFACENORMALPROCESS_H_INC
+#define AI_GENFACENORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
+*/
+class GenFaceNormalsProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	GenFaceNormalsProcess();
+
+	/** Destructor, private as well */
+	~GenFaceNormalsProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	* @param pFlags The processing flags the importer was called with. A bitwise
+	*   combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+
+private:
+	void GenMeshFaceNormals (aiMesh* pcMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC

+ 104 - 0
code/GenVertexNormalsProcess.cpp

@@ -0,0 +1,104 @@
+/** @file Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+#include "GenVertexNormalsProcess.h"
+#include "SpatialSort.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// Constructor to be privately used by Importer
+GenVertexNormalsProcess::GenVertexNormalsProcess()
+{
+}
+
+// Destructor, private as well
+GenVertexNormalsProcess::~GenVertexNormalsProcess()
+{
+	// nothing to do here
+}
+
+// -------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_GenSmoothNormals) != 0;
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		this->GenMeshVertexNormals( pScene->mMeshes[a]);
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
+{
+	if (NULL != pMesh->mNormals)return;
+
+	pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		const aiFace& face = pMesh->mFaces[a];
+
+		// assume it is a triangle
+		aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+		aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+		aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[2]];
+
+		aiVector3D pDelta1 = *pV2 - *pV1;
+		aiVector3D pDelta2 = *pV3 - *pV1;
+		aiVector3D vNor = pDelta1 ^ pDelta2;
+		float fLength = vNor.Length();
+		if (0.0f != fLength)vNor /= fLength;
+
+		pMesh->mNormals[face.mIndices[0]] = vNor;
+		pMesh->mNormals[face.mIndices[1]] = vNor;
+		pMesh->mNormals[face.mIndices[2]] = vNor;
+	}
+
+	// calculate the position bounds so we have a reliable epsilon to 
+	// check position differences against 
+	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+	{
+		minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
+		minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
+		minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
+		maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
+		maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
+		maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
+	}
+
+	const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
+	// set up a SpatialSort to quickly find all vertices close to a given position
+	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+	std::vector<unsigned int> verticesFound;
+
+	aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
+	for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
+	{
+		const aiVector3D& posThis = pMesh->mVertices[i];
+
+		// get all vertices that share this one ...
+		vertexFinder.FindPositions( posThis, posEpsilon, verticesFound);
+
+		aiVector3D pcNor; 
+		for (unsigned int a = 0; a < verticesFound.size(); ++a)
+		{
+			unsigned int vidx = verticesFound[a];
+			pcNor += pMesh->mNormals[vidx];
+		}
+		pcNor /= (float) verticesFound.size();
+		pcNew[i] = pcNor;
+	}
+	delete pMesh->mNormals;
+	pMesh->mNormals = pcNew;
+
+	return;
+}

+ 47 - 0
code/GenVertexNormalsProcess.h

@@ -0,0 +1,47 @@
+/** @file Defines a post processing step to compute vertex normals for all loaded vertizes */
+#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
+#define AI_GENVERTEXNORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+namespace Assimp
+	{
+
+	// ---------------------------------------------------------------------------
+	/** The GenFaceNormalsProcess computes vertex normals for all vertizes of all meshes
+	*/
+	class GenVertexNormalsProcess : public BaseProcess
+		{
+		friend class Importer;
+
+		protected:
+			/** Constructor to be privately used by Importer */
+			GenVertexNormalsProcess();
+
+			/** Destructor, private as well */
+			~GenVertexNormalsProcess();
+
+		public:
+			// -------------------------------------------------------------------
+			/** Returns whether the processing step is present in the given flag field.
+			* @param pFlags The processing flags the importer was called with. A bitwise
+			*   combination of #aiPostProcessSteps.
+			* @return true if the process is present in this flag fields, false if not.
+			*/
+			bool IsActive( unsigned int pFlags) const;
+
+			// -------------------------------------------------------------------
+			/** Executes the post processing step on the given imported data.
+			* At the moment a process is not supposed to fail.
+			* @param pScene The imported data to work at.
+			*/
+			void Execute( aiScene* pScene);
+
+
+		private:
+			void GenMeshVertexNormals (aiMesh* pcMesh);
+		};
+
+	} // end of namespace Assimp
+
+#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC

+ 136 - 0
code/Importer.cpp

@@ -0,0 +1,136 @@
+/** @file Implementation of the CPP-API class #Importer */
+#include <fstream>
+#include <string>
+
+#include "../include/assimp.hpp"
+#include "../include/aiScene.h"
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+#include "DefaultIOStream.h"
+#include "DefaultIOSystem.h"
+#include "XFileImporter.h"
+#include "3DSLoader.h"
+#include "MD3Loader.h"
+#include "MD2Loader.h"
+#include "PlyLoader.h"
+#include "ObjFileImporter.h"
+#include "CalcTangentsProcess.h"
+#include "JoinVerticesProcess.h"
+#include "ConvertToLHProcess.h"
+#include "TriangulateProcess.h"
+#include "GenFaceNormalsProcess.h"
+#include "GenVertexNormalsProcess.h"
+#include "KillNormalsProcess.h"
+#include "SplitLargeMeshes.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor. 
+Importer::Importer() :
+	mIOHandler(NULL),
+	mScene(NULL),
+	mErrorString("")	
+{
+	// default IO handler
+	mIOHandler = new DefaultIOSystem;
+
+	// add an instance of each worker class here
+	mImporter.push_back( new XFileImporter());
+	mImporter.push_back( new ObjFileImporter());
+	mImporter.push_back( new Dot3DSImporter());
+	mImporter.push_back( new MD3Importer());
+	mImporter.push_back( new MD2Importer());
+	mImporter.push_back( new PLYImporter());
+
+	// add an instance of each post processing step here in the order of sequence it is executed
+	mPostProcessingSteps.push_back( new TriangulateProcess());
+	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess());
+	mPostProcessingSteps.push_back( new KillNormalsProcess());
+	mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
+	mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
+	mPostProcessingSteps.push_back( new CalcTangentsProcess());
+	mPostProcessingSteps.push_back( new JoinVerticesProcess());
+	mPostProcessingSteps.push_back( new ConvertToLHProcess());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor. 
+Importer::~Importer()
+{
+	for( unsigned int a = 0; a < mImporter.size(); a++)
+		delete mImporter[a];
+	for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++)
+		delete mPostProcessingSteps[a];
+
+	delete mIOHandler;
+
+	// kill imported scene. Destructors should do that recursivly
+	delete mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Supplies a custom IO handler to the importer to open and access files.
+void Importer::SetIOHandler( IOSystem* pIOHandler)
+{
+	delete mIOHandler;
+	mIOHandler = pIOHandler;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its contents if successful. 
+const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags)
+{
+	// first check if the file is accessable at all
+	if( !mIOHandler->Exists( pFile))
+	{
+		mErrorString = "Unable to open file \"" + pFile + "\".";
+		return NULL;
+	}
+
+	// find an worker class which can handle the file
+	BaseImporter* imp = NULL;
+	for( unsigned int a = 0; a < mImporter.size(); a++)
+	{
+		if( mImporter[a]->CanRead( pFile, mIOHandler))
+		{
+			imp = mImporter[a];
+			break;
+		}
+	}
+
+	// put a proper error message if no suitable importer was found
+	if( !imp)
+	{
+		mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
+		return NULL;
+	}
+
+	// dispatch the reading to the worker class for this format
+	mScene = imp->ReadFile( pFile, mIOHandler);
+	// if failed, extract the error string
+	if( !mScene)
+		mErrorString = imp->GetErrorText();
+
+	// if successful, apply all active post processing steps to the imported data
+	if( mScene)
+	{
+		for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++)
+		{
+			BaseProcess* process = mPostProcessingSteps[a];
+			if( process->IsActive( pFlags))
+				process->Execute( mScene);
+		}
+	}
+
+	// either successful or failure - the pointer expresses it anyways
+	return mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Empty and rpivate copy constructor
+Importer::Importer(const Importer &other)
+{
+	// empty
+}
+

+ 262 - 0
code/JoinVerticesProcess.cpp

@@ -0,0 +1,262 @@
+/** @file Implementation of the post processing step to join identical vertices
+ * for all imported meshes
+ */
+
+#include <vector>
+#include <assert.h>
+#include "JoinVerticesProcess.h"
+#include "SpatialSort.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+JoinVerticesProcess::JoinVerticesProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+JoinVerticesProcess::~JoinVerticesProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void JoinVerticesProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		ProcessMesh( pScene->mMeshes[a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+void JoinVerticesProcess::ProcessMesh( aiMesh* pMesh)
+{
+	// helper structure to hold all the data a single vertex can possibly have
+	typedef struct Vertex vertex;
+	
+	struct Vertex
+	{
+		aiVector3D mPosition;
+		aiVector3D mNormal;
+		aiVector3D mTangent, mBitangent;
+		aiColor4D mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+		aiVector3D mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+	};
+	std::vector<Vertex> uniqueVertices;
+	uniqueVertices.reserve( pMesh->mNumVertices);
+
+	// For each vertex the index of the vertex it was replaced by. 
+	std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
+	// for each vertex whether it was replaced by an existing unique vertex (true) or a new vertex was created for it (false)
+	std::vector<bool> isVertexUnique( pMesh->mNumVertices, false);
+
+	// calculate the position bounds so we have a reliable epsilon to check position differences against 
+	aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+	{
+		minVec.x = std::min( minVec.x, pMesh->mVertices[a].x);
+		minVec.y = std::min( minVec.y, pMesh->mVertices[a].y);
+		minVec.z = std::min( minVec.z, pMesh->mVertices[a].z);
+		maxVec.x = std::max( maxVec.x, pMesh->mVertices[a].x);
+		maxVec.y = std::max( maxVec.y, pMesh->mVertices[a].y);
+		maxVec.z = std::max( maxVec.z, pMesh->mVertices[a].z);
+	}
+
+	// squared because we check against squared length of the vector difference	
+	const float epsilon = 1e-5f; 
+	const float posEpsilon = (maxVec - minVec).Length() * epsilon;
+	const float squareEpsilon = epsilon * epsilon;
+
+	// a little helper to find locally close vertices faster
+	SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+	std::vector<unsigned int> verticesFound;
+
+	// now check each vertex if it brings something new to the table
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+	{
+		// collect the vertex data
+		Vertex v;
+		v.mPosition = pMesh->mVertices[a];
+		v.mNormal = (pMesh->mNormals != NULL) ? pMesh->mNormals[a] : aiVector3D( 0, 0, 0);
+		v.mTangent = (pMesh->mTangents != NULL) ? pMesh->mTangents[a] : aiVector3D( 0, 0, 0);
+		v.mBitangent = (pMesh->mBitangents != NULL) ? pMesh->mBitangents[a] : aiVector3D( 0, 0, 0);
+		for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_COLOR_SETS; b++)
+			v.mColors[b] = (pMesh->mColors[b] != NULL) ? pMesh->mColors[b][a] : aiColor4D( 0, 0, 0, 0);
+		for( unsigned int b = 0; b < AI_MAX_NUMBER_OF_TEXTURECOORDS; b++)
+			v.mTexCoords[b] = (pMesh->mTextureCoords[b] != NULL) ? pMesh->mTextureCoords[b][a] : aiVector3D( 0, 0, 0);
+
+		// collect all vertices that are close enough to the given position
+		vertexFinder.FindPositions( v.mPosition, posEpsilon, verticesFound);
+
+		unsigned int matchIndex = 0xffffffff;
+		// check all unique vertices close to the position if this vertex is already present among them
+		for( unsigned int b = 0; b < verticesFound.size(); b++)
+		{
+			unsigned int vidx = verticesFound[b];
+			unsigned int uidx = replaceIndex[ vidx];
+			if( uidx == 0xffffffff || !isVertexUnique[ vidx])
+				continue;
+
+			const Vertex& uv = uniqueVertices[ uidx];
+			// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
+
+			// We just test the other attributes even if they're not present in the mesh.
+			// In this case they're initialized to 0 so the comparision succeeds. 
+			// By this method the non-present attributes are effectively ignored in the comparision.
+			
+			if( (uv.mNormal - v.mNormal).SquareLength() > squareEpsilon)
+				continue;
+			if( (uv.mTangent - v.mTangent).SquareLength() > squareEpsilon)
+				continue;
+			if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
+				continue;
+			// manually unrolled because continue wouldn't work as desired in an inner loop
+			assert( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+			if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
+				continue;
+			if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
+				continue;
+			if( GetColorDifference( uv.mColors[2], v.mColors[2]) > squareEpsilon)
+				continue;
+			if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
+				continue;
+			// texture coord matching manually unrolled as well
+			assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+			if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
+				continue;
+			if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)
+				continue;
+			if( (uv.mTexCoords[2] - v.mTexCoords[2]).SquareLength() > squareEpsilon)
+				continue;
+			if( (uv.mTexCoords[3] - v.mTexCoords[3]).SquareLength() > squareEpsilon)
+				continue;
+
+			// we're still here -> this vertex perfectly matches our given vertex
+			matchIndex = uidx;
+			break;
+		}
+
+		// found a replacement vertex among the uniques?
+		if( matchIndex != 0xffffffff)
+		{
+			// store where to found the matching unique vertex
+			replaceIndex[a] = matchIndex;
+			isVertexUnique[a] = false;
+		} else
+		{
+			// no unique vertex matches it upto now -> so add it
+			replaceIndex[a] = uniqueVertices.size();
+			uniqueVertices.push_back( v);
+			isVertexUnique[a] = true;
+		}
+	}
+
+	// replace vertex data with the unique data sets
+	pMesh->mNumVertices = uniqueVertices.size();
+	// Position
+	delete [] pMesh->mVertices;
+	pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+		pMesh->mVertices[a] = uniqueVertices[a].mPosition;
+	// Normals, if present
+	if( pMesh->mNormals)
+	{
+		delete [] pMesh->mNormals;
+		pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+		for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+			pMesh->mNormals[a] = uniqueVertices[a].mNormal;
+	}
+	// Tangents, if present
+	if( pMesh->mTangents)
+	{
+		delete [] pMesh->mTangents;
+		pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+		for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+			pMesh->mTangents[a] = uniqueVertices[a].mTangent;
+	}
+	// Bitangents as well
+	if( pMesh->mBitangents)
+	{
+		delete [] pMesh->mBitangents;
+		pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+		for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+			pMesh->mBitangents[a] = uniqueVertices[a].mBitangent;
+	}
+	// Vertex colors
+	for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+	{
+		if( !pMesh->mColors[a])
+			continue;
+
+		delete [] pMesh->mColors[a];
+		pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
+		for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
+			pMesh->mColors[a][b] = uniqueVertices[b].mColors[a];
+	}
+	// Texture coords
+	for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+	{
+		if( !pMesh->mTextureCoords[a])
+			continue;
+
+		delete [] pMesh->mTextureCoords[a];
+		pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
+		for( unsigned int b = 0; b < pMesh->mNumVertices; b++)
+			pMesh->mTextureCoords[a][b] = uniqueVertices[b].mTexCoords[a];
+	}
+
+	// adjust the indices in all faces
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		aiFace& face = pMesh->mFaces[a];
+		for( unsigned int b = 0; b < face.mNumIndices; b++)
+		{
+			const size_t index = face.mIndices[b];
+			face.mIndices[b] = replaceIndex[face.mIndices[b]];
+		}
+	}
+
+	// adjust bone vertex weights.
+	for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+	{
+		aiBone* bone = pMesh->mBones[a];
+		std::vector<aiVertexWeight> newWeights;
+		newWeights.reserve( bone->mNumWeights);
+
+		for( unsigned int b = 0; b < bone->mNumWeights; b++)
+		{
+			const aiVertexWeight& ow = bone->mWeights[b];
+			// if the vertex is a unique one, translate it
+			if( isVertexUnique[ow.mVertexId])
+			{
+				aiVertexWeight nw;
+				nw.mVertexId = replaceIndex[ow.mVertexId];
+				nw.mWeight = ow.mWeight;
+				newWeights.push_back( nw);
+			}
+		}
+
+		// there should be some. At least I think there should be some
+		assert( newWeights.size() > 0);
+
+		// kill the old and replace them with the translated weights
+		delete [] bone->mWeights;
+		bone->mNumWeights = newWeights.size();
+		bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+		memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
+	}
+}

+ 65 - 0
code/JoinVerticesProcess.h

@@ -0,0 +1,65 @@
+/** @file Defines a post processing step to join identical vertices on all imported meshes.*/
+#ifndef AI_JOINVERTICESPROCESS_H_INC
+#define AI_CALCTANGENTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The JoinVerticesProcess unites identical vertices in all imported meshes. 
+ * By default the importer returns meshes where each face addressed its own 
+ * set of vertices even if that means that identical vertices are stored multiple
+ * times. The JoinVerticesProcess finds these identical vertices and 
+ * erases all but one of the copies. This usually reduces the number of vertices
+ * in a mesh by a serious amount and is the standard form to render a mesh.
+ */
+class JoinVerticesProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	JoinVerticesProcess();
+
+	/** Destructor, private as well */
+	~JoinVerticesProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	 * @param pFlags The processing flags the importer was called with. A bitwise
+	 *   combination of #aiPostProcessSteps.
+	 * @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+protected:
+	// -------------------------------------------------------------------
+	/** Unites identical vertices in the given mesh.
+	 * @param pMesh The mesh to process.
+	 */
+	void ProcessMesh( aiMesh* pMesh);
+
+	/** Little helper function to calculate the quadratic difference of two colours. */
+	float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) const
+	{
+		aiColor4D c( pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
+		return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
+	}
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC

+ 43 - 0
code/KillNormalsProcess.cpp

@@ -0,0 +1,43 @@
+/** @file Implementation of the post processing step tokill mesh normals
+*/
+#include "KillNormalsProcess.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// Constructor to be privately used by Importer
+KillNormalsProcess::KillNormalsProcess()
+{
+}
+
+// Destructor, private as well
+KillNormalsProcess::~KillNormalsProcess()
+{
+	// nothing to do here
+}
+
+// -------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool KillNormalsProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_KillNormals) != 0;
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void KillNormalsProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		this->KillMeshNormals( pScene->mMeshes[a]);
+}
+
+// -------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void KillNormalsProcess::KillMeshNormals(aiMesh* pMesh)
+{
+	delete[] pMesh->mNormals;
+	pMesh->mNormals = NULL;
+	return;
+}

+ 47 - 0
code/KillNormalsProcess.h

@@ -0,0 +1,47 @@
+/** @file Defines a post processing step to kill all loaded normals */
+#ifndef AI_KILLNORMALPROCESS_H_INC
+#define AI_KILLNORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+namespace Assimp
+	{
+
+	// ---------------------------------------------------------------------------
+	/** KillNormalsProcess: Class to kill all normals loaded
+	*/
+	class KillNormalsProcess : public BaseProcess
+		{
+		friend class Importer;
+
+		protected:
+			/** Constructor to be privately used by Importer */
+			KillNormalsProcess();
+
+			/** Destructor, private as well */
+			~KillNormalsProcess();
+
+		public:
+			// -------------------------------------------------------------------
+			/** Returns whether the processing step is present in the given flag field.
+			* @param pFlags The processing flags the importer was called with. A bitwise
+			*   combination of #aiPostProcessSteps.
+			* @return true if the process is present in this flag fields, false if not.
+			*/
+			bool IsActive( unsigned int pFlags) const;
+
+			// -------------------------------------------------------------------
+			/** Executes the post processing step on the given imported data.
+			* At the moment a process is not supposed to fail.
+			* @param pScene The imported data to work at.
+			*/
+			void Execute( aiScene* pScene);
+
+
+		private:
+			void KillMeshNormals (aiMesh* pcMesh);
+		};
+
+	} // end of namespace Assimp
+
+#endif // !!AI_KILLNORMALPROCESS_H_INC

+ 137 - 0
code/MD2FileData.h

@@ -0,0 +1,137 @@
+/** @file Defines the helper data structures for importing MD2 files  */
+#ifndef AI_MD2FILEHELPER_H_INC
+#define AI_MD2FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
+#	pragma pack(push,1)
+#	define PACK_STRUCT
+#elif defined( __GNUC__ )
+#	define PACK_STRUCT	__attribute__((packed))
+#else
+#	error Compiler not supported
+#endif
+
+
+namespace Assimp
+{
+//http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
+namespace MD2
+{
+
+#define AI_MD2_MAGIC_NUMBER_BE	'IDP2'
+#define AI_MD2_MAGIC_NUMBER_LE	'2PDI'
+
+// common limitations
+#define AI_MD2_VERSION			15
+#define AI_MD2_MAXQPATH			64
+#define AI_MD2_MAX_FRAMES		512
+#define AI_MD2_MAX_SKINS		32	
+#define AI_MD2_MAX_VERTS		2048	
+#define AI_MD2_MAX_TRIANGLES	4096	
+
+// ---------------------------------------------------------------------------
+/**	\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; 
+
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 OpenGl draw command
+ */
+// ---------------------------------------------------------------------------
+struct GLCommand
+{
+   float s, t;
+   uint32_t vertexIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 triangle
+ */
+// ---------------------------------------------------------------------------
+struct Triangle
+{
+	uint16_t vertexIndices[3];
+	uint16_t textureIndices[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 vertex
+ */
+// ---------------------------------------------------------------------------
+struct Vertex
+{
+	uint8_t vertex[3];
+	uint8_t lightNormalIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 frame
+ */
+// ---------------------------------------------------------------------------
+struct Frame
+{
+	float scale[3];
+	float translate[3];
+	char name[16];
+	Vertex vertices[1];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 texture coordinate
+ */
+// ---------------------------------------------------------------------------
+struct TexCoord
+{
+	int16_t s;
+	int16_t t;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a MD2 skin
+ */
+// ---------------------------------------------------------------------------
+struct Skin
+{
+	char name[AI_MD2_MAXQPATH];              /* texture file name */
+} PACK_STRUCT;
+
+// reset packing to the original value
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+#	pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+};
+};
+
+#endif // !! include guard

+ 310 - 0
code/MD2Loader.cpp

@@ -0,0 +1,310 @@
+/** @file Implementation of the MD2 importer class */
+#include "MD2Loader.h"
+#include "MaterialSystem.h"
+
+#include "MD2NormalTable.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;
+
+// ------------------------------------------------------------------------------------------------
+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);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD2Importer::MD2Importer()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+MD2Importer::~MD2Importer()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool MD2Importer::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);
+
+	// not brilliant but working ;-)
+	if( extension == ".md2" || extension == ".MD2" || 
+		extension == ".mD2" || extension == ".Md2")
+		return true;
+
+	return false;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void MD2Importer::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 md2 file " + pFile + ".");
+	}
+
+	// check whether the md3 file is large enough to contain
+	// at least the file header
+	size_t fileSize = file->FileSize();
+	if( fileSize < sizeof(MD2::Header))
+	{
+		throw new ImportErrorException( ".md2 File is too small.");
+	}
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	this->mBuffer = new unsigned char[fileSize];
+	file->Read( (void*)mBuffer, 1, fileSize);
+
+	this->m_pcHeader = (const MD2::Header*)this->mBuffer;
+
+	// check magic number
+	if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
+		this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
+	{
+		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");
+	}
+
+	// check some values whether they are valid
+	if (0 == this->m_pcHeader->numFrames)
+	{
+		throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
+	}
+	if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
+	{
+		throw new ImportErrorException( "Invalid md2 file: File is too small");
+	}
+
+	// there won't be more than one mesh inside the file
+	pScene->mNumMaterials = 1;
+	pScene->mRootNode = new aiNode();
+	pScene->mRootNode->mNumMeshes = 1;
+	pScene->mRootNode->mMeshes = new unsigned int[1];
+	pScene->mRootNode->mMeshes[0] = 0;
+	pScene->mMaterials = new aiMaterial*[1];
+	pScene->mMaterials[0] = new MaterialHelper();
+	pScene->mNumMeshes = 1;
+	pScene->mMeshes = new aiMesh*[1];
+	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);
+
+	// navigate to the begin of the triangle data
+	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);
+
+	// 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;
+
+	vPositions.resize(this->m_pcHeader->numVertices,aiVector3D());
+	vTexCoords.resize(this->m_pcHeader->numVertices,aiVector3D(
+		std::numeric_limits<float>::quiet_NaN(),
+		std::numeric_limits<float>::quiet_NaN(),0.0f));
+	vNormals.resize(this->m_pcHeader->numVertices,aiVector3D());
+
+	// not sure whether there are MD2 files without texture coordinates
+	if (0 != this->m_pcHeader->numTexCoords && 0 != this->m_pcHeader->numSkins)
+	{
+		// navigate to the first texture associated with the mesh
+		const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader + 
+			this->m_pcHeader->offsetSkins);
+
+		const int iMode = (int)aiShadingMode_Gouraud;
+		MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		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 szString;
+		const size_t iLen = strlen(pcSkins->name);
+		memcpy(szString.data,pcSkins->name,iLen+1);
+		szString.length = iLen-1;
+
+		pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+	}
+	else
+	{
+		// apply a default material
+		const int iMode = (int)aiShadingMode_Gouraud;
+		MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.6f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+		clr.b = clr.g = clr.r = 0.05f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+	}
+
+	// now read all vertices of the frame
+	for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numVertices;++i)
+	{
+		// read x,y, and z component of the vertex
+
+		aiVector3D& vec = vPositions[i];
+
+		vec.x = (float)pcVerts[i].vertex[0] * pcFrame->scale[0];
+		vec.x += pcFrame->translate[0];
+
+		// (flip z and y component)
+		vec.z = (float)pcVerts[i].vertex[1] * pcFrame->scale[1];
+		vec.z += pcFrame->translate[1];
+
+		vec.y = (float)pcVerts[i].vertex[2] * pcFrame->scale[2];
+		vec.y += pcFrame->translate[2];
+
+		// read the normal vector from the precalculated normal table
+		vNormals[i] = *((const aiVector3D*)(&g_avNormals[std::min(
+			int(pcVerts[i].lightNormalIndex),
+			int(sizeof(g_avNormals) / sizeof(g_avNormals[0]))-1)]));
+
+		std::swap ( vNormals[i].y,vNormals[i].z );
+	}
+
+	// now read all triangles of the first frame, apply scaling and translation
+	if (0 != this->m_pcHeader->numTexCoords)
+	{
+		for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
+		{
+			// allocate the face
+			pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
+			pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
+
+			// copy texture coordinates
+			// check whether they are different from the previous value at this index.
+			// In this case, create a full separate set of vertices/normals/texcoords
+			for (unsigned int c = 0; c < 3;++c)
+			{
+				// validate vertex indices
+				if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
+					pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
+
+				// copy face indices
+				pScene->mMeshes[0]->mFaces[i].mIndices[c] = (unsigned int)pcTriangles[i].vertexIndices[c];
+
+				// validate texture coordinates
+				if (pcTriangles[i].textureIndices[c] >= this->m_pcHeader->numTexCoords)
+					pcTriangles[i].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
+
+				aiVector3D* pcOut = &vTexCoords[pScene->mMeshes[0]->mFaces[i].mIndices[c]];
+				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;
+
+				if ( is_not_qnan ( pcOut->x ) && (pcOut->x != u || pcOut->y != v))
+				{
+					// generate a separate vertex/index set
+					vTexCoords.push_back(aiVector3D(u,v,0.0f));
+					vPositions.push_back(vPositions[pcTriangles[i].vertexIndices[c]]);
+					vNormals.push_back(vPositions[pcTriangles[i].vertexIndices[c]]);
+					unsigned int iPos = vTexCoords.size()-1;
+
+					pScene->mMeshes[0]->mFaces[i].mIndices[c] = iPos;
+				}
+				else
+				{
+					pcOut->x = u;
+					pcOut->y = v;
+			
+				}
+			}
+		}
+	}
+	else
+	{
+		for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
+		{
+			// allocate the face
+			pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
+			pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
+
+			// validate vertex indices
+			if (pcTriangles[i].vertexIndices[0] >= this->m_pcHeader->numVertices)
+				pcTriangles[i].vertexIndices[0] = this->m_pcHeader->numVertices-1;
+			if (pcTriangles[i].vertexIndices[1] >= this->m_pcHeader->numVertices)
+				pcTriangles[i].vertexIndices[1] = this->m_pcHeader->numVertices-1;
+			if (pcTriangles[i].vertexIndices[2] >= this->m_pcHeader->numVertices)
+				pcTriangles[i].vertexIndices[2] = this->m_pcHeader->numVertices-1;
+
+			// copy face indices
+			pScene->mMeshes[0]->mFaces[i].mIndices[0] = (unsigned int)pcTriangles[i].vertexIndices[0];
+			pScene->mMeshes[0]->mFaces[i].mIndices[1] = (unsigned int)pcTriangles[i].vertexIndices[1];
+			pScene->mMeshes[0]->mFaces[i].mIndices[2] = (unsigned int)pcTriangles[i].vertexIndices[2];
+		}
+	}
+
+	// allocate output storage
+	pScene->mMeshes[0]->mNumVertices = 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));
+	memcpy(pScene->mMeshes[0]->mTextureCoords[0],	&vTexCoords[0],	vPositions.size() * sizeof(aiVector3D));
+
+	return;
+}

+ 57 - 0
code/MD2Loader.h

@@ -0,0 +1,57 @@
+/** @file Definition of the .MD2 importer class. */
+#ifndef AI_MD2LOADER_H_INCLUDED
+#define AI_MD2LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "MD2FileData.h"
+
+namespace Assimp
+{
+	class MaterialHelper;
+
+	using namespace MD2;
+
+	// ---------------------------------------------------------------------------
+	/** Used to load MD2 files
+	*/
+	class MD2Importer : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		MD2Importer();
+
+		/** Destructor, private as well */
+		~MD2Importer();
+
+	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:
+
+		// -------------------------------------------------------------------
+		/** 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:
+
+		/** Header of the MD2 file */
+		const MD2::Header* m_pcHeader;
+
+		/** Buffer to hold the loaded file */
+		const unsigned char* mBuffer;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 171 - 0
code/MD2NormalTable.h

@@ -0,0 +1,171 @@
+/*
+ *	Slightly modified version of the anorms.h header file released by
+ *	ID software with the Quake 2 source code.
+ *
+ *	Table of normals used by MD2 models
+ */
+
+float g_avNormals[][3] = {
+{ -0.525731f,  0.000000f,  0.850651f }, 
+{ -0.442863f,  0.238856f,  0.864188f }, 
+{ -0.295242f,  0.000000f,  0.955423f }, 
+{ -0.309017f,  0.500000f,  0.809017f }, 
+{ -0.162460f,  0.262866f,  0.951056f }, 
+{  0.000000f,  0.000000f,  1.000000f }, 
+{  0.000000f,  0.850651f,  0.525731f }, 
+{ -0.147621f,  0.716567f,  0.681718f }, 
+{  0.147621f,  0.716567f,  0.681718f }, 
+{  0.000000f,  0.525731f,  0.850651f }, 
+{  0.309017f,  0.500000f,  0.809017f }, 
+{  0.525731f,  0.000000f,  0.850651f }, 
+{  0.295242f,  0.000000f,  0.955423f }, 
+{  0.442863f,  0.238856f,  0.864188f }, 
+{  0.162460f,  0.262866f,  0.951056f }, 
+{ -0.681718f,  0.147621f,  0.716567f }, 
+{ -0.809017f,  0.309017f,  0.500000f }, 
+{ -0.587785f,  0.425325f,  0.688191f }, 
+{ -0.850651f,  0.525731f,  0.000000f }, 
+{ -0.864188f,  0.442863f,  0.238856f }, 
+{ -0.716567f,  0.681718f,  0.147621f }, 
+{ -0.688191f,  0.587785f,  0.425325f }, 
+{ -0.500000f,  0.809017f,  0.309017f }, 
+{ -0.238856f,  0.864188f,  0.442863f }, 
+{ -0.425325f,  0.688191f,  0.587785f }, 
+{ -0.716567f,  0.681718f, -0.147621f }, 
+{ -0.500000f,  0.809017f, -0.309017f }, 
+{ -0.525731f,  0.850651f,  0.000000f }, 
+{  0.000000f,  0.850651f, -0.525731f }, 
+{ -0.238856f,  0.864188f, -0.442863f }, 
+{  0.000000f,  0.955423f, -0.295242f }, 
+{ -0.262866f,  0.951056f, -0.162460f }, 
+{  0.000000f,  1.000000f,  0.000000f }, 
+{  0.000000f,  0.955423f,  0.295242f }, 
+{ -0.262866f,  0.951056f,  0.162460f }, 
+{  0.238856f,  0.864188f,  0.442863f }, 
+{  0.262866f,  0.951056f,  0.162460f }, 
+{  0.500000f,  0.809017f,  0.309017f }, 
+{  0.238856f,  0.864188f, -0.442863f }, 
+{  0.262866f,  0.951056f, -0.162460f }, 
+{  0.500000f,  0.809017f, -0.309017f }, 
+{  0.850651f,  0.525731f,  0.000000f }, 
+{  0.716567f,  0.681718f,  0.147621f }, 
+{  0.716567f,  0.681718f, -0.147621f }, 
+{  0.525731f,  0.850651f,  0.000000f }, 
+{  0.425325f,  0.688191f,  0.587785f }, 
+{  0.864188f,  0.442863f,  0.238856f }, 
+{  0.688191f,  0.587785f,  0.425325f }, 
+{  0.809017f,  0.309017f,  0.500000f }, 
+{  0.681718f,  0.147621f,  0.716567f }, 
+{  0.587785f,  0.425325f,  0.688191f }, 
+{  0.955423f,  0.295242f,  0.000000f }, 
+{  1.000000f,  0.000000f,  0.000000f }, 
+{  0.951056f,  0.162460f,  0.262866f }, 
+{  0.850651f, -0.525731f,  0.000000f }, 
+{  0.955423f, -0.295242f,  0.000000f }, 
+{  0.864188f, -0.442863f,  0.238856f }, 
+{  0.951056f, -0.162460f,  0.262866f }, 
+{  0.809017f, -0.309017f,  0.500000f }, 
+{  0.681718f, -0.147621f,  0.716567f }, 
+{  0.850651f,  0.000000f,  0.525731f }, 
+{  0.864188f,  0.442863f, -0.238856f }, 
+{  0.809017f,  0.309017f, -0.500000f }, 
+{  0.951056f,  0.162460f, -0.262866f }, 
+{  0.525731f,  0.000000f, -0.850651f }, 
+{  0.681718f,  0.147621f, -0.716567f }, 
+{  0.681718f, -0.147621f, -0.716567f }, 
+{  0.850651f,  0.000000f, -0.525731f }, 
+{  0.809017f, -0.309017f, -0.500000f }, 
+{  0.864188f, -0.442863f, -0.238856f }, 
+{  0.951056f, -0.162460f, -0.262866f }, 
+{  0.147621f,  0.716567f, -0.681718f }, 
+{  0.309017f,  0.500000f, -0.809017f }, 
+{  0.425325f,  0.688191f, -0.587785f }, 
+{  0.442863f,  0.238856f, -0.864188f }, 
+{  0.587785f,  0.425325f, -0.688191f }, 
+{  0.688191f,  0.587785f, -0.425325f }, 
+{ -0.147621f,  0.716567f, -0.681718f }, 
+{ -0.309017f,  0.500000f, -0.809017f }, 
+{  0.000000f,  0.525731f, -0.850651f }, 
+{ -0.525731f,  0.000000f, -0.850651f }, 
+{ -0.442863f,  0.238856f, -0.864188f }, 
+{ -0.295242f,  0.000000f, -0.955423f }, 
+{ -0.162460f,  0.262866f, -0.951056f }, 
+{  0.000000f,  0.000000f, -1.000000f }, 
+{  0.295242f,  0.000000f, -0.955423f }, 
+{  0.162460f,  0.262866f, -0.951056f }, 
+{ -0.442863f, -0.238856f, -0.864188f }, 
+{ -0.309017f, -0.500000f, -0.809017f }, 
+{ -0.162460f, -0.262866f, -0.951056f }, 
+{  0.000000f, -0.850651f, -0.525731f }, 
+{ -0.147621f, -0.716567f, -0.681718f }, 
+{  0.147621f, -0.716567f, -0.681718f }, 
+{  0.000000f, -0.525731f, -0.850651f }, 
+{  0.309017f, -0.500000f, -0.809017f }, 
+{  0.442863f, -0.238856f, -0.864188f }, 
+{  0.162460f, -0.262866f, -0.951056f }, 
+{  0.238856f, -0.864188f, -0.442863f }, 
+{  0.500000f, -0.809017f, -0.309017f }, 
+{  0.425325f, -0.688191f, -0.587785f }, 
+{  0.716567f, -0.681718f, -0.147621f }, 
+{  0.688191f, -0.587785f, -0.425325f }, 
+{  0.587785f, -0.425325f, -0.688191f }, 
+{  0.000000f, -0.955423f, -0.295242f }, 
+{  0.000000f, -1.000000f,  0.000000f }, 
+{  0.262866f, -0.951056f, -0.162460f }, 
+{  0.000000f, -0.850651f,  0.525731f }, 
+{  0.000000f, -0.955423f,  0.295242f }, 
+{  0.238856f, -0.864188f,  0.442863f }, 
+{  0.262866f, -0.951056f,  0.162460f }, 
+{  0.500000f, -0.809017f,  0.309017f }, 
+{  0.716567f, -0.681718f,  0.147621f }, 
+{  0.525731f, -0.850651f,  0.000000f }, 
+{ -0.238856f, -0.864188f, -0.442863f }, 
+{ -0.500000f, -0.809017f, -0.309017f }, 
+{ -0.262866f, -0.951056f, -0.162460f }, 
+{ -0.850651f, -0.525731f,  0.000000f }, 
+{ -0.716567f, -0.681718f, -0.147621f }, 
+{ -0.716567f, -0.681718f,  0.147621f }, 
+{ -0.525731f, -0.850651f,  0.000000f }, 
+{ -0.500000f, -0.809017f,  0.309017f }, 
+{ -0.238856f, -0.864188f,  0.442863f }, 
+{ -0.262866f, -0.951056f,  0.162460f }, 
+{ -0.864188f, -0.442863f,  0.238856f }, 
+{ -0.809017f, -0.309017f,  0.500000f }, 
+{ -0.688191f, -0.587785f,  0.425325f }, 
+{ -0.681718f, -0.147621f,  0.716567f }, 
+{ -0.442863f, -0.238856f,  0.864188f }, 
+{ -0.587785f, -0.425325f,  0.688191f }, 
+{ -0.309017f, -0.500000f,  0.809017f }, 
+{ -0.147621f, -0.716567f,  0.681718f }, 
+{ -0.425325f, -0.688191f,  0.587785f }, 
+{ -0.162460f, -0.262866f,  0.951056f }, 
+{  0.442863f, -0.238856f,  0.864188f }, 
+{  0.162460f, -0.262866f,  0.951056f }, 
+{  0.309017f, -0.500000f,  0.809017f }, 
+{  0.147621f, -0.716567f,  0.681718f }, 
+{  0.000000f, -0.525731f,  0.850651f }, 
+{  0.425325f, -0.688191f,  0.587785f }, 
+{  0.587785f, -0.425325f,  0.688191f }, 
+{  0.688191f, -0.587785f,  0.425325f }, 
+{ -0.955423f,  0.295242f,  0.000000f }, 
+{ -0.951056f,  0.162460f,  0.262866f }, 
+{ -1.000000f,  0.000000f,  0.000000f }, 
+{ -0.850651f,  0.000000f,  0.525731f }, 
+{ -0.955423f, -0.295242f,  0.000000f }, 
+{ -0.951056f, -0.162460f,  0.262866f }, 
+{ -0.864188f,  0.442863f, -0.238856f }, 
+{ -0.951056f,  0.162460f, -0.262866f }, 
+{ -0.809017f,  0.309017f, -0.500000f }, 
+{ -0.864188f, -0.442863f, -0.238856f }, 
+{ -0.951056f, -0.162460f, -0.262866f }, 
+{ -0.809017f, -0.309017f, -0.500000f }, 
+{ -0.681718f,  0.147621f, -0.716567f }, 
+{ -0.681718f, -0.147621f, -0.716567f }, 
+{ -0.850651f,  0.000000f, -0.525731f }, 
+{ -0.688191f,  0.587785f, -0.425325f }, 
+{ -0.587785f,  0.425325f, -0.688191f }, 
+{ -0.425325f,  0.688191f, -0.587785f }, 
+{ -0.425325f, -0.688191f, -0.587785f }, 
+{ -0.587785f, -0.425325f, -0.688191f }, 
+{ -0.688191f, -0.587785f, -0.425325f }
+};

+ 273 - 0
code/MD3FileData.h

@@ -0,0 +1,273 @@
+/** @file Defines the helper data structures for importing MD3 files  */
+#ifndef AI_MD3FILEHELPER_H_INC
+#define AI_MD3FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
+#	pragma pack(push,1)
+#	define PACK_STRUCT
+#elif defined( __GNUC__ )
+#	define PACK_STRUCT	__attribute__((packed))
+#else
+#	error Compiler not supported
+#endif
+
+
+namespace Assimp
+{
+// http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
+namespace MD3
+{
+
+#define AI_MD3_MAGIC_NUMBER_BE	'IDP3'
+#define AI_MD3_MAGIC_NUMBER_LE	'3PDI'
+
+// common limitations
+#define AI_MD3_VERSION			15
+#define AI_MD3_MAXQPATH			64
+#define AI_MD3_MAX_FRAMES		1024
+#define AI_MD3_MAX_TAGS			16
+#define AI_MD3_MAX_SURFACES		32
+#define AI_MD3_MAX_SHADERS		256	
+#define AI_MD3_MAX_VERTS		4096	
+#define AI_MD3_MAX_TRIANGLES	8192	
+
+// master scale factor for all vertices in a MD3 model
+#define AI_MD3_XYZ_SCALE		(1.0f/64.0f)
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for the MD3 main header
+ */
+// ---------------------------------------------------------------------------
+struct Header
+{
+	// magic number
+	int32_t IDENT;
+
+	// file format version
+	int32_t VERSION;
+
+	// original name in .pak archive
+	unsigned char NAME[ AI_MD3_MAXQPATH ];
+
+	// unknown
+	int32_t FLAGS;
+
+	// number of frames in the file
+	int32_t NUM_FRAMES;
+
+	// number of tags in the file
+	int32_t NUM_TAGS;
+
+	// number of surfaces in the file
+	int32_t NUM_SURFACES;
+
+	// number of skins in the file
+	int32_t NUM_SKINS;
+
+	// offset of the first frame
+	int32_t OFS_FRAMES;
+
+	// offset of the first tag
+	int32_t OFS_TAGS;
+
+	// offset of the first surface
+	int32_t OFS_SURFACES;
+
+	// end of file
+	int32_t OFS_EOF;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for the frame header
+ */
+// ---------------------------------------------------------------------------
+struct Frame
+{
+	// no need to define this, we won't need
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for the tag header
+ */
+// ---------------------------------------------------------------------------
+struct Tag
+{
+	// no need to define this, we won't need
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for the surface header
+ */
+// ---------------------------------------------------------------------------
+struct Surface
+{
+	// magic number
+	int32_t IDENT;
+
+	// original name of the surface
+	unsigned char NAME[ AI_MD3_MAXQPATH ];
+
+	// unknown
+	int32_t FLAGS;
+
+	// number of frames in the surface
+	int32_t NUM_FRAMES;
+
+	// number of shaders in the surface
+	int32_t NUM_SHADER;
+
+	// number of vertices in the surface
+	int32_t NUM_VERTICES;
+
+	// number of triangles in the surface
+	int32_t NUM_TRIANGLES;
+
+
+	// offset to the triangle data 
+	int32_t OFS_TRIANGLES;
+
+	// offset to the shader data
+	int32_t OFS_SHADERS;
+
+	// offset to the texture coordinate data
+	int32_t OFS_ST;
+
+	// offset to the vertex/normal data
+	int32_t OFS_XYZNORMAL;
+
+	// offset to the end of the Surface object
+	int32_t OFS_END;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a shader
+ */
+// ---------------------------------------------------------------------------
+struct Shader
+{
+	// filename of the shader
+	unsigned char NAME[ AI_MD3_MAXQPATH ];
+
+	// index of the shader
+	int32_t SHADER_INDEX;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a triangle
+ */
+// ---------------------------------------------------------------------------
+struct Triangle
+{
+	// triangle indices
+	int32_t INDEXES[3];
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for an UV coord
+ */
+// ---------------------------------------------------------------------------
+struct TexCoord
+{
+	// UV coordinates
+	float U,V;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Data structure for a vertex
+ */
+// ---------------------------------------------------------------------------
+struct Vertex
+{
+	// X/Y/Z coordinates
+	int16_t X,Y,Z;
+
+	// encoded normal vector
+	int16_t  NORMAL;
+} PACK_STRUCT;
+
+// reset packing to the original value
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+#	pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+// ---------------------------------------------------------------------------
+/**	\brief Unpack a Q3 16 bit vector to its full float3 representation
+ *
+ *	\param p_iNormal Input normal vector in latitude/longitude form
+ *	\param p_afOut Pointer to an array of three floats to receive the result
+ *
+ *	\note This has been taken from q3 source (misc_model.c)
+ */
+// ---------------------------------------------------------------------------
+inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
+{
+	float lat = (float)(( p_iNormal >> 8 ) & 0xff);
+	float lng = (float)(( p_iNormal & 0xff ));
+	lat *= 3.141926f/128.0f;
+	lng *= 3.141926f/128.0f;
+
+	p_afOut[0] = cosf(lat) * sinf(lng);
+	p_afOut[1] = sinf(lat) * sinf(lng);
+	p_afOut[2] = cosf(lng);
+	return;
+}
+
+
+// ---------------------------------------------------------------------------
+/**	\brief Pack a Q3 normal into 16bit latitute/longitude representation
+ *	\param p_vIn Input vector
+ *	\param p_iOut Output normal
+ *
+ *	\note This has been taken from q3 source (mathlib.c)
+ */
+// ---------------------------------------------------------------------------
+inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut ) 
+{
+	// check for singularities
+	if ( 0.0f == p_vIn[0] && 0.0f == p_vIn[1] ) 
+	{
+		if ( p_vIn[2] > 0.0f ) 
+		{
+			((unsigned char*)&p_iOut)[0] = 0;
+			((unsigned char*)&p_iOut)[1] = 0;		// lat = 0, long = 0
+		} 
+		else 
+		{
+			((unsigned char*)&p_iOut)[0] = 128;
+			((unsigned char*)&p_iOut)[1] = 0;		// lat = 0, long = 128
+		}
+	} 
+	else 
+	{
+		int	a, b;
+
+		a = int(57.2957795f * ( atan2f( p_vIn[1], p_vIn[0] ) ) * (255.0f / 360.0f ));
+		a &= 0xff;
+
+		b = int(57.2957795f * ( acosf( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
+		b &= 0xff;
+
+		((unsigned char*)&p_iOut)[0] = b;	// longitude
+		((unsigned char*)&p_iOut)[1] = a;	// lattitude
+	}
+}
+
+};
+};
+
+#endif // !! AI_MD3FILEHELPER_H_INC

+ 328 - 0
code/MD3Loader.cpp

@@ -0,0 +1,328 @@
+/** @file Implementation of the MD3 importer class */
+#include "MD3Loader.h"
+#include "MaterialSystem.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
+MD3Importer::MD3Importer()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+MD3Importer::~MD3Importer()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool MD3Importer::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);
+
+	// not brilliant but working ;-)
+	if( extension == ".md3" || extension == ".MD3" || 
+		extension == ".mD3" || extension == ".Md3")
+		return true;
+
+	return false;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void MD3Importer::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 md3 file " + pFile + ".");
+	}
+
+	// check whether the md3 file is large enough to contain
+	// at least the file header
+	size_t fileSize = file->FileSize();
+	if( fileSize < sizeof(MD3::Header))
+	{
+		throw new ImportErrorException( ".md3 File is too small.");
+	}
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	this->mBuffer = new unsigned char[fileSize];
+	file->Read( (void*)mBuffer, 1, fileSize);
+
+	this->m_pcHeader = (const MD3::Header*)this->mBuffer;
+
+	// check magic number
+	if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
+		this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
+	{
+		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");
+	}
+
+	// check some values whether they are valid
+	if (0 == this->m_pcHeader->NUM_FRAMES)
+	{
+		throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
+	}
+	if (0 == this->m_pcHeader->NUM_SURFACES)
+	{
+		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");
+	}
+
+	// now navigate to the list of surfaces
+	const MD3::Surface* pcSurfaces = (const MD3::Surface*)
+		(this->mBuffer + this->m_pcHeader->OFS_SURFACES);
+
+	// allocate output storage
+	pScene->mNumMeshes = this->m_pcHeader->NUM_SURFACES;
+	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+	pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
+	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
+
+	unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
+	unsigned int iNumMaterials = 0;
+	unsigned int iDefaultMatIndex = 0xFFFFFFFF;
+	while (iNum-- > 0)
+	{
+		// navigate to the vertex list of the surface
+		const MD3::Vertex* pcVertices = (const MD3::Vertex*)
+			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
+
+		// navigate to the triangle list of the surface
+		const MD3::Triangle* pcTriangles = (const MD3::Triangle*)
+			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
+
+		// navigate to the texture coordinate list of the surface
+		const MD3::TexCoord* pcUVs = (const MD3::TexCoord*)
+			(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_ST);
+
+		// navigate to the shader list of the surface
+		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)
+		{
+			pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
+			pScene->mNumMeshes--;
+			continue;
+		}
+
+		// allocate the output mesh
+		pScene->mMeshes[iNum] = new aiMesh();
+		aiMesh* pcMesh = pScene->mMeshes[iNum];
+
+		pcMesh->mNumVertices = pcSurfaces->NUM_VERTICES;
+		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->mNumUVComponents[0] = 2;
+
+		// fill in all vertices and normals
+		// fill in all texture coordinates
+		for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_VERTICES;++i)
+		{
+			pcMesh->mVertices[i].x = pcVertices->X;
+			pcMesh->mVertices[i].y = pcVertices->Y;
+			pcMesh->mVertices[i].z = -1.0f*pcVertices->Z;
+
+			// convert the normal vector to uncompressed float3 format
+			LatLngNormalToVec3(pcVertices->NORMAL,(float*)&pcMesh->mNormals[i]);
+
+			// read texture coordinates
+			pcMesh->mTextureCoords[0][i].x = pcUVs->U;
+			pcMesh->mTextureCoords[0][i].y = 1.0f - pcUVs->V;
+
+			pcVertices++;
+			pcUVs++;
+		}
+
+		// fill in all triangles
+		for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i)
+		{
+			pcMesh->mFaces[i].mIndices = new unsigned int[3];
+			pcMesh->mFaces[i].mNumIndices = 3;
+
+			pcMesh->mFaces[i].mIndices[0] = pcTriangles->INDEXES[0];
+			pcMesh->mFaces[i].mIndices[1] = pcTriangles->INDEXES[1];
+			pcMesh->mFaces[i].mIndices[2] = pcTriangles->INDEXES[2];
+
+			pcTriangles++;
+		}
+
+		// get the first shader (= texture?) assigned to the surface
+		if (0 != pcSurfaces->NUM_SHADER)
+		{
+			// 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* szEndDir2 = strrchr((const char*)pcShaders->NAME,'\\');
+			if (!szEndDir2)szEndDir2 = strrchr((const char*)pcShaders->NAME,'/');
+
+			if (szEndDir1 && szEndDir2)
+			{
+				// both of them are valid
+				const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)this->m_pcHeader->NAME);
+				const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) );
+
+				bool bSuccess = true;
+				for (unsigned int a = 0; a  < iLen2;++a)
+				{
+					char sz = tolower ( pcShaders->NAME[a] );
+					char sz2 = tolower ( this->m_pcHeader->NAME[a] );
+					if (sz != sz2)
+					{
+						bSuccess = false;
+						break;
+					}
+				}
+				if (bSuccess)
+				{
+					// use the file name only
+					szEndDir2++;
+				}
+				else
+				{
+					// use the full path
+					szEndDir2 = (const char*)pcShaders->NAME;
+				}
+			}
+
+			// now try to find out whether we have this shader already
+			bool bHave = false;
+			for (unsigned int p = 0; p < iNumMaterials;++p)
+			{
+				if (iDefaultMatIndex == p)continue;
+
+				aiString szOut;
+				if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
+					AI_MATKEY_TEXBLEND_DIFFUSE(0),&szOut))
+				{
+					if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
+					{
+						// equal. reuse this material (texture)
+						bHave = true;
+						pcMesh->mMaterialIndex = p;
+						break;
+					}
+				}
+			}
+
+			if (!bHave)
+			{
+				MaterialHelper* pcHelper = new MaterialHelper();
+
+				aiString szString;
+				const size_t iLen = strlen(szEndDir2);
+				memcpy(szString.data,szEndDir2,iLen+1);
+				szString.length = iLen-1;
+
+				pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+				int iMode = (int)aiShadingMode_Gouraud;
+				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+				aiColor3D clr;
+				clr.b = clr.g = clr.r = 1.0f;
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+				clr.b = clr.g = clr.r = 0.05f;
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
+				pcMesh->mMaterialIndex = iNumMaterials++;
+			}
+		}
+		else
+		{
+			if (0xFFFFFFFF != iDefaultMatIndex)
+			{
+				pcMesh->mMaterialIndex = iDefaultMatIndex;
+			}
+			else
+			{
+				MaterialHelper* pcHelper = new MaterialHelper();
+
+				// fill in a default material
+				int iMode = (int)aiShadingMode_Gouraud;
+				pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+				aiColor3D clr;
+				clr.b = clr.g = clr.r = 0.6f;
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+				clr.b = clr.g = clr.r = 0.05f;
+				pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+				pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
+				pcMesh->mMaterialIndex = iNumMaterials++;
+			}
+		}
+		pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
+	}
+
+	if (0 == pScene->mNumMeshes)
+	{
+		// cleanup before returning
+		delete pScene;
+		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];
+
+	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;
+	}
+
+	delete[] this->mBuffer;
+	return;
+}

+ 60 - 0
code/MD3Loader.h

@@ -0,0 +1,60 @@
+/** @file Definition of the .MD3 importer class. */
+#ifndef AI_MD3LOADER_H_INCLUDED
+#define AI_MD3LOADER_H_INCLUDED
+
+#include <map>
+
+#include "BaseImporter.h"
+
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+#include "MD3FileData.h"
+namespace Assimp
+{
+	class MaterialHelper;
+
+	using namespace MD3;
+
+	// ---------------------------------------------------------------------------
+	/** Used to load MD3 files
+	*/
+	class MD3Importer : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		MD3Importer();
+
+		/** Destructor, private as well */
+		~MD3Importer();
+
+	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:
+
+		// -------------------------------------------------------------------
+		/** 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:
+
+		/** Header of the MD3 file */
+		const MD3::Header* m_pcHeader;
+
+		/** Buffer to hold the loaded file */
+		const unsigned char* mBuffer;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 0 - 0
code/MD4FileData.h


+ 57 - 0
code/MD4Loader.h

@@ -0,0 +1,57 @@
+/** @file Definition of the .MD4 importer class. */
+#ifndef AI_MD4LOADER_H_INCLUDED
+#define AI_MD4LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "MD4FileData.h"
+
+namespace Assimp
+{
+	class MaterialHelper;
+
+	using namespace MD4;
+
+	// ---------------------------------------------------------------------------
+	/** Used to load MD4 files
+	*/
+	class MD4Importer : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		MD4Importer();
+
+		/** Destructor, private as well */
+		~MD4Importer();
+
+	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:
+
+		// -------------------------------------------------------------------
+		/** 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:
+
+		/** Header of the MD4 file */
+		const MD4::Header* m_pcHeader;
+
+		/** Buffer to hold the loaded file */
+		const unsigned char* mBuffer;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 57 - 0
code/MD5Loader.h

@@ -0,0 +1,57 @@
+/** @file Definition of the .MD5 importer class. */
+#ifndef AI_MD5LOADER_H_INCLUDED
+#define AI_MD5LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "MD5FileData.h"
+
+namespace Assimp
+{
+	class MaterialHelper;
+
+	using namespace MD5;
+
+	// ---------------------------------------------------------------------------
+	/** Used to load MD5 files
+	*/
+	class MD5Importer : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		MD5Importer();
+
+		/** Destructor, private as well */
+		~MD5Importer();
+
+	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:
+
+		// -------------------------------------------------------------------
+		/** 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:
+
+		/** Header of the MD5 file */
+		const MD5::Header* m_pcHeader;
+
+		/** Buffer to hold the loaded file */
+		const unsigned char* mBuffer;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 285 - 0
code/MaterialSystem.cpp

@@ -0,0 +1,285 @@
+
+
+#include "assimp.h"
+#include "aiMaterial.h"
+#include "assimp.hpp"
+#include "MaterialSystem.h"
+
+
+#include "../include/aiAssert.h"
+
+using namespace Assimp;
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialProperty(const aiMaterial* pMat, 
+	const char* pKey,
+	const aiMaterialProperty** pPropOut)
+{
+#if (defined DEBUG)
+
+	ai_assert (pMat != NULL);
+	ai_assert (pKey != NULL);
+	ai_assert (pPropOut != NULL);
+
+#endif // ASSIMP_DEBUG
+
+	for (unsigned int i = 0; i < pMat->mNumProperties;++i)
+		{
+		if (NULL != pMat->mProperties[i])
+			{
+			if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
+				{
+				*pPropOut = pMat->mProperties[i];
+				return AI_SUCCESS;
+				}
+			}
+		}
+	*pPropOut = NULL;
+	return AI_FAILURE;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, 
+	const char* pKey,
+	float* pOut,
+	unsigned int* pMax)
+{
+#if (defined DEBUG)
+
+	ai_assert (pMat != NULL);
+	ai_assert (pKey != NULL);
+	ai_assert (pOut != NULL);
+
+#endif // ASSIMP_DEBUG
+
+	for (unsigned int i = 0; i < pMat->mNumProperties;++i)
+	{
+		if (NULL != pMat->mProperties[i])
+		{
+			if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
+			{
+				// data is given in floats, simply copy it
+				if( aiPTI_Float == pMat->mProperties[i]->mType ||
+					aiPTI_Buffer == pMat->mProperties[i]->mType)
+				{
+					unsigned int iWrite = pMat->mProperties[i]->
+						mDataLength / sizeof(float);
+
+					if (NULL != pMax)
+						iWrite = *pMax < iWrite ? *pMax : iWrite;
+
+					memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (float));
+					
+					if (NULL != pMax)
+						*pMax = iWrite;
+				}
+				// data is given in ints, convert to float
+				else if( aiPTI_Integer == pMat->mProperties[i]->mType)
+				{
+					unsigned int iWrite = pMat->mProperties[i]->
+						mDataLength / sizeof(int);
+
+					if (NULL != pMax)
+						iWrite = *pMax < iWrite ? *pMax : iWrite;
+
+					for (unsigned int a = 0; a < iWrite;++a)
+					{
+						pOut[a] = (float) ((int*)pMat->mProperties[i]->mData)[a];
+					}
+					if (NULL != pMax)
+						*pMax = iWrite;
+				}
+				// it is a string ... no way to read something out of this
+				else
+				{
+					if (NULL != pMax)
+						*pMax = 0;
+					return AI_FAILURE;
+				}
+				return AI_SUCCESS;
+			}
+		}
+	}
+	return AI_FAILURE;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, 
+	const char* pKey,
+	int* pOut,
+	unsigned int* pMax)
+{
+#if (defined DEBUG)
+
+	ai_assert (pMat != NULL);
+	ai_assert (pKey != NULL);
+	ai_assert (pOut != NULL);
+
+#endif // ASSIMP_DEBUG
+
+	for (unsigned int i = 0; i < pMat->mNumProperties;++i)
+	{
+		if (NULL != pMat->mProperties[i])
+		{
+			if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
+			{
+				// data is given in ints, simply copy it
+				if( aiPTI_Integer == pMat->mProperties[i]->mType ||
+					aiPTI_Buffer == pMat->mProperties[i]->mType)
+				{
+					unsigned int iWrite = pMat->mProperties[i]->
+						mDataLength / sizeof(int);
+
+					if (NULL != pMax)
+						iWrite = *pMax < iWrite ? *pMax : iWrite;
+
+					memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (int));
+					
+					if (NULL != pMax)
+						*pMax = iWrite;
+				}
+				// data is given in floats convert to int (lossy!)
+				else if( aiPTI_Float == pMat->mProperties[i]->mType)
+				{
+					unsigned int iWrite = pMat->mProperties[i]->
+						mDataLength / sizeof(float);
+
+					if (NULL != pMax)
+						iWrite = *pMax < iWrite ? *pMax : iWrite;
+
+					for (unsigned int a = 0; a < iWrite;++a)
+					{
+						pOut[a] = (int) ((float*)pMat->mProperties[i]->mData)[a];
+					}
+					if (NULL != pMax)
+						*pMax = iWrite;
+				}
+				// it is a string ... no way to read something out of this
+				else
+				{
+					if (NULL != pMax)
+						*pMax = 0;
+					return AI_FAILURE;
+				}
+				return AI_SUCCESS;
+			}
+		}
+	}
+	return AI_FAILURE;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialColor(const aiMaterial* pMat, 
+	const char* pKey,
+	aiColor4D* pOut)
+{
+	unsigned int iMax = 4;
+	aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,(float*)pOut,&iMax);
+
+	// if no alpha channel is provided set it to 1.0 by default
+	if (3 == iMax)pOut->a = 1.0f;
+	return eRet;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialString(const aiMaterial* pMat, 
+	const char* pKey,
+	aiString* pOut)
+{
+#if (defined DEBUG)
+
+	ai_assert (pMat != NULL);
+	ai_assert (pKey != NULL);
+	ai_assert (pOut != NULL);
+
+#endif // ASSIMP_DEBUG
+
+	for (unsigned int i = 0; i < pMat->mNumProperties;++i)
+	{
+		if (NULL != pMat->mProperties[i])
+		{
+			if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
+			{
+				if( aiPTI_String == pMat->mProperties[i]->mType)
+				{
+					memcpy (pOut, pMat->mProperties[i]->mData, 
+						sizeof(aiString));
+				}
+				// wrong type
+				else return AI_FAILURE;
+				return AI_SUCCESS;
+			}
+		}
+	}
+	return AI_FAILURE;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
+	const unsigned int pSizeInBytes,
+	const char* pKey,
+	aiPropertyTypeInfo pType)
+{
+#if (defined DEBUG)
+
+	ai_assert (pInput != NULL);
+	ai_assert (pKey != NULL);
+	ai_assert (0 != pSizeInBytes);
+
+#endif // ASSIMP_DEBUG
+
+	aiMaterialProperty* pcNew = new aiMaterialProperty();
+
+	// fill this
+	pcNew->mKey = new aiString();
+	pcNew->mType = pType;
+
+	pcNew->mDataLength = pSizeInBytes;
+	pcNew->mData = new char[pSizeInBytes];
+	memcpy (pcNew->mData,pInput,pSizeInBytes);
+
+	pcNew->mKey->length = strlen(pKey);
+	ai_assert ( MAXLEN > pcNew->mKey->length);
+	strcpy( pcNew->mKey->data, pKey );
+
+	// resize the array ... allocate
+	// storage for 5 other properties
+	if (this->mNumProperties == this->mNumAllocated)
+	{
+		unsigned int iOld = this->mNumAllocated;
+		this->mNumAllocated += 5;
+
+		aiMaterialProperty** ppTemp = new aiMaterialProperty*[this->mNumAllocated];
+		if (NULL == ppTemp)return AI_OUTOFMEMORY;
+
+		memcpy (ppTemp,this->mProperties,iOld * sizeof(void*));
+
+		delete[] this->mProperties;
+		this->mProperties = ppTemp;
+	}
+	// push back ...
+	this->mProperties[this->mNumProperties++] = pcNew;
+	return AI_SUCCESS;
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+aiReturn MaterialHelper::AddProperty (const aiString* pInput,
+	const char* pKey)
+{
+	return this->AddBinaryProperty(pInput,
+		sizeof(aiString),pKey,aiPTI_String);
+}

+ 187 - 0
code/MaterialSystem.h

@@ -0,0 +1,187 @@
+/** @file Definition of the base class for all importer worker classes. */
+#ifndef AI_MATERIALSYSTEM_H_INC
+#define AI_MATERIALSYSTEM_H_INC
+
+#include "../include/aiMaterial.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+inline int ASSIMP_stricmp(const char *s1, const char *s2)
+{
+	const char *a1, *a2;
+	a1 = s1;
+	a2 = s2;
+
+	while (true)
+	{
+		char c1 = (char)tolower(*a1); 
+		char c2 = (char)tolower(*a2);
+		if ((0 == c1) && (0 == c2)) return 0;
+		if (c1 < c2) return-1;
+		if (c1 > c2) return 1;
+		++a1; 
+		++a2;
+	}
+}
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n)
+{
+	const char *a1, *a2;
+	a1 = s1;
+	a2 = s2;
+
+	unsigned int p = 0;
+
+	while (true)
+	{
+		if (p >= n)return 0;
+
+		char c1 = (char)tolower(*a1); 
+		char c2 = (char)tolower(*a2);
+		if ((0 == c1) && (0 == c2)) return 0;
+		if (c1 < c2) return-1;
+		if (c1 > c2) return 1;
+		++a1; 
+		++a2;
+		++p;
+	}
+}
+
+
+// ---------------------------------------------------------------------------
+/** Internal material helper class. Can be used to fill an aiMaterial
+    structure easily. */
+class MaterialHelper : public ::aiMaterial
+{
+	public:
+
+	inline MaterialHelper();
+	inline ~MaterialHelper();
+
+	// -------------------------------------------------------------------
+	/** Add a property with a given key and type info to the material
+	    structure  */
+	aiReturn AddBinaryProperty (const void* pInput,
+		const unsigned int pSizeInBytes,
+		const char* pKey,
+		aiPropertyTypeInfo pType);
+
+
+	// -------------------------------------------------------------------
+	/** Add a string property with a given key and type info to the 
+	    material structure  */
+	aiReturn AddProperty (const aiString* pInput,
+		const char* pKey);
+
+
+	// -------------------------------------------------------------------
+	/** Add a property with a given key to the material structure  */
+	template<class TYPE>
+	aiReturn AddProperty (const TYPE* pInput,
+		const unsigned int pNumValues,
+		const char* pKey);
+};
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+inline MaterialHelper::MaterialHelper()
+	{
+	// allocate 5 entries by default
+	this->mNumProperties = 0;
+	this->mNumAllocated = 5;
+	this->mProperties = new aiMaterialProperty*[5];
+	return;
+	}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+inline MaterialHelper::~MaterialHelper()
+	{
+	for (unsigned int i = 0; i < this->mNumProperties;++i)
+		{
+		// be careful ...
+		if(NULL != this->mProperties[i])
+			{
+			delete[] this->mProperties[i]->mKey;
+			delete[] this->mProperties[i]->mData;
+			delete this->mProperties[i];
+			}
+		}
+	return;
+	}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+template<class TYPE>
+aiReturn MaterialHelper::AddProperty (const TYPE* pInput,
+	const unsigned int pNumValues,
+	const char* pKey)
+{
+	return this->AddBinaryProperty((const void*)pInput,
+		pNumValues * sizeof(TYPE),
+		pKey,aiPTI_Buffer);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<float> (const float* pInput,
+	const unsigned int pNumValues,
+	const char* pKey)
+{
+	return this->AddBinaryProperty((const void*)pInput,
+		pNumValues * sizeof(float),
+		pKey,aiPTI_Float);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiColor4D> (const aiColor4D* pInput,
+	const unsigned int pNumValues,
+	const char* pKey)
+{
+	return this->AddBinaryProperty((const void*)pInput,
+		pNumValues * sizeof(aiColor4D),
+		pKey,aiPTI_Float);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiColor3D> (const aiColor3D* pInput,
+	const unsigned int pNumValues,
+	const char* pKey)
+{
+	return this->AddBinaryProperty((const void*)pInput,
+		pNumValues * sizeof(aiColor3D),
+		pKey,aiPTI_Float);
+}
+
+
+// ---------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<int> (const int* pInput,
+	const unsigned int pNumValues,
+	const char* pKey)
+{
+	return this->AddBinaryProperty((const void*)pInput,
+		pNumValues * sizeof(int),
+		pKey,aiPTI_Integer);
+}
+}
+
+
+#endif //!! AI_MATERIALSYSTEM_H_INC

+ 170 - 0
code/ObjFileData.h

@@ -0,0 +1,170 @@
+#ifndef OBJ_FILEDATA_H_INC
+#define OBJ_FILEDATA_H_INC
+
+#include <vector>
+#include <map>
+#include "aiTypes.h"
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+
+struct Object;
+struct Face;
+struct Material;
+
+// ------------------------------------------------------------------------------------------------
+//!	\struct	Face
+//!	\brief	Datastructure for a simple obj-face, descripes discredisation and materials
+struct Face
+{
+	typedef std::vector<unsigned int> IndexArray;
+
+	//!	Primitive type
+	int m_PrimitiveType;
+	//!	Vertex indices
+	IndexArray *m_pVertices;
+	//!	Normal indices
+	IndexArray *m_pNormals;
+	//!	Texture coordinates indices
+	IndexArray *m_pTexturCoords;
+	//!	Pointer to assigned material
+	Material *m_pMaterial;
+	
+	//!	\brief	Default constructor
+	//!	\param	pVertices	Pointer to assigned vertex indexbuffer
+	//!	\param	pNormals	Pointer to assigned normals indexbuffer
+	//!	\param	pTexCoords	Pointer to assigned texture indexbuffer
+	Face(std::vector<unsigned int> *pVertices, 
+			std::vector<unsigned int> *pNormals, 
+			std::vector<unsigned int> *pTexCoords) : 
+		m_PrimitiveType(2), 
+		m_pVertices(pVertices), 
+		m_pNormals(pNormals),
+		m_pTexturCoords(pTexCoords), 
+		m_pMaterial(0L)
+	{
+		// empty
+	}
+	
+	//!	\brief	Destructor	
+	~Face()
+	{	
+		// empty
+	}
+};
+
+// ------------------------------------------------------------------------------------------------
+//!	\struct	Object
+//!	\brief	Stores all objects of an objfile object definition
+struct Object
+{
+	//!	Obejct name
+	std::string m_strObjName;
+	//!	Assigend face instances
+	std::vector<Face*> m_Faces;
+	//!	Transformation matrix, stored in OpenGL format
+	aiMatrix4x4 m_Transformation;
+	//!	All subobjects references by this object
+	std::vector<Object*> m_SubObjects;
+
+	//!	\brief	Default constructor
+	Object() :
+		m_strObjName("")
+	{
+		// empty
+	}
+	
+	//!	\brief	Destructor	
+	~Object()
+	{
+		for (std::vector<Object*>::iterator it = m_SubObjects.begin();
+			it != m_SubObjects.end(); ++it)
+		{
+			delete *it;
+		}
+		m_SubObjects.clear();
+	}
+};
+
+// ------------------------------------------------------------------------------------------------
+//!	\struct	Material
+//!	\brief	Data structure to store all material specific data
+struct Material
+{
+	aiString MaterialName;
+	aiString texture;
+	aiColor3D ambient;
+	aiColor3D diffuse;
+	aiColor3D specular;
+	float alpha;
+	float shineness;
+	int illumination_model;
+};
+
+// ------------------------------------------------------------------------------------------------
+//!	\struct	Model
+//!	\brief	Data structure to store all obj-specific model datas
+struct Model
+{
+	typedef std::map<std::string*, std::vector<unsigned int>* > GroupMap;
+	typedef std::map<std::string*, std::vector<unsigned int>* >::iterator GroupMapIt;
+	typedef std::map<std::string*, std::vector<unsigned int>* >::const_iterator ConstGroupMapIt;
+
+	//!	Model name
+	std::string m_ModelName;
+	//!	List ob assigned objects
+	std::vector<Object*> m_Objects;
+	//!	Pointer to current object
+	ObjFile::Object *m_pCurrent;
+	//!	Pointer to current material
+	ObjFile::Material *m_pCurrentMaterial;
+	//!	Pointer to default material
+	ObjFile::Material *m_pDefaultMaterial;
+	//!	Vector with all generated materials
+	std::vector<std::string> m_MaterialLib;
+	//!	Vector with all generated group
+	std::vector<std::string> m_GroupLib;
+	//!	Vector with all generated vertices
+	std::vector<aiVector3D_t*> m_Vertices;
+	//!	vector with all generated normals
+	std::vector<aiVector3D_t*> m_Normals;
+	//!	Groupmap
+	GroupMap m_Groups;
+	std::vector<unsigned int> *m_pGroupFaceIDs;
+	//!	Active group
+	std::string m_strActiveGroup;
+	//!	Vector with generated texture coordinates
+	std::vector<aiVector2D_t*> m_TextureCoord;
+	//!	Material map
+	std::map<std::string, Material*> m_MaterialMap;
+
+	//!	\brief	Default constructor
+	Model() :
+		m_ModelName(""),
+		m_pCurrent(NULL),
+		m_pCurrentMaterial(NULL),
+		m_pDefaultMaterial(NULL),
+		m_strActiveGroup("")
+	{
+		// empty
+	}
+	
+	//!	\brief	DEstructor
+	~Model()
+	{
+		for (std::vector<Object*>::iterator it = m_Objects.begin();
+		it != m_Objects.end(); ++it)
+		{
+			delete *it;
+		}
+		m_Objects.clear();
+	}
+};
+
+} // Namespace ObjFile
+} // Namespace Assimp
+
+#endif

+ 354 - 0
code/ObjFileImporter.cpp

@@ -0,0 +1,354 @@
+#include "ObjFileImporter.h"
+#include "ObjFileParser.h"
+#include "ObjFileData.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+#include "aiAssert.h"
+#include "MaterialSystem.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/format.hpp>
+
+namespace Assimp
+{
+// ------------------------------------------------------------------------------------------------
+
+using namespace std;
+
+//!	Obj-file-format extention
+const string ObjFileImporter::OBJ_EXT = "obj";
+
+// ------------------------------------------------------------------------------------------------
+//	Default constructor
+ObjFileImporter::ObjFileImporter() :
+	m_pRootObject(NULL),
+	m_strAbsPath("\\")
+{
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Destructor
+ObjFileImporter::~ObjFileImporter()
+{
+	// Release root object instance
+	if (NULL != m_pRootObject)
+	{
+		delete m_pRootObject;
+		m_pRootObject = NULL;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Returns true, fi file is an obj file
+bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
+{
+	if (pFile.empty())
+		return false;
+
+	string::size_type pos = pFile.find_last_of(".");
+	if (string::npos == pos)
+		return false;
+	
+	const string ext = pFile.substr(pos+1, pFile.size() - pos - 1);
+	if (ext == OBJ_EXT)
+		return true;
+
+	return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Obj-file import implementation
+void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+	// Read file into memory
+	const std::string mode  = "rb";
+	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
+	if (NULL == file.get())
+		throw new ImportErrorException( "Failed to open file " + pFile + ".");
+
+	// Get the filesize and vaslidate it, throwing an exception when failes
+	size_t fileSize = file->FileSize();
+	if( fileSize < 16)
+		throw new ImportErrorException( "OBJ-file is too small.");
+
+	// Allocate buffer and read file into it
+	m_Buffer.resize( fileSize );
+	const size_t readsize = file->Read(&m_Buffer.front(), sizeof(char), fileSize);
+	assert (readsize == fileSize);
+
+	//
+	std::string strDirectory("\\"), strModelName;
+	std::string::size_type pos = pFile.find_last_of("\\");
+	if (pos != std::string::npos)
+	{
+		strDirectory = pFile.substr(0, pos);
+		strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
+	}
+	else
+	{
+		strModelName = pFile;
+	}
+	
+	// parse the file into a temporary representation
+	ObjFileParser parser(m_Buffer, strDirectory, strModelName);
+
+	// And create the proper return structures out of it
+	CreateDataFromImport(parser.GetModel(), pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Create the data from parsed obj-file
+void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene)
+{
+	if (0L == pModel)
+		return;
+		
+	// Create the root node of the scene
+	pScene->mRootNode = new aiNode();
+	if (!pModel->m_ModelName.empty())
+	{
+		// Set the name of the scene
+		pScene->mRootNode->mName.Set(pModel->m_ModelName);
+	}
+	else
+	{
+		// This is an error, so break down the application
+		assert (false);
+	}
+
+	// Create nodes for the whole scene	
+	std::vector<aiMesh*> MeshArray;
+	for (size_t index = 0; index < pModel->m_Objects.size(); index++)
+	{
+		createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray);
+	}
+
+	// Create mesh pointer buffer for this scene
+	if (pScene->mNumMeshes > 0)
+	{
+		pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
+		for (size_t index =0; index < pScene->mNumMeshes; index++)
+		{
+			pScene->mMeshes [ index ] = MeshArray[ index ];
+		}
+	}
+
+	// Create all materials
+	for (size_t index = 0; index < pModel->m_Objects.size(); index++)
+	{
+		createMaterial(pModel, pModel->m_Objects[ index ], pScene);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Creates all nodes of the model
+aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+									 aiNode *pParent, aiScene* pScene, 
+									 std::vector<aiMesh*> &MeshArray)
+{
+	if (NULL == pData)
+		return NULL;
+	
+	// Store older mesh size to be able to computate mesh offsets for new mesh instances
+	size_t oldMeshSize = MeshArray.size();
+	aiNode *pNode = new aiNode();
+	
+	if (pParent != NULL)
+		this->appendChildToParentNode(pParent, pNode);
+
+	aiMesh *pMesh = new aiMesh();
+	MeshArray.push_back(pMesh);
+	createTopology(pModel, pData, pMesh);
+
+	// Create all nodes from the subobjects stored in the current object
+	if (!pData->m_SubObjects.empty())
+	{
+		pNode->mNumChildren = pData->m_SubObjects.size();
+		pNode->mChildren = new aiNode*[pData->m_SubObjects.size()];
+		pNode->mNumMeshes = 1;
+		pNode->mMeshes = new unsigned int[1];
+
+		// Loop over all child objects
+		for (size_t index = 0; index < pData->m_SubObjects.size(); index++)
+		{
+			// Create all child nodes
+			pNode->mChildren[index] = createNodes(pModel, pData, pNode, pScene, MeshArray);
+			
+			// Create meshes of this object
+			pMesh = new aiMesh();
+			MeshArray.push_back(pMesh);
+			createTopology(pModel, pData->m_SubObjects[ index ], pMesh);
+
+			// Create material of this object
+			createMaterial(pModel, pData->m_SubObjects[ index ], pScene);
+		}
+	}
+
+	// Set mesh instances into scene- and node-instances
+	const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
+	if (meshSizeDiff > 0 )
+	{
+		pNode->mMeshes = new unsigned int[ meshSizeDiff ];
+		pNode->mNumMeshes++;
+		size_t index = 0;
+		for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
+		{
+			pNode->mMeshes[ index ] = pScene->mNumMeshes;
+			pScene->mNumMeshes++;
+			index++;
+		}
+	}
+	
+	return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Create topology data
+void ObjFileImporter::createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+									 aiMesh* pMesh)
+{
+	if (NULL == pData)
+		return;
+	
+	// Create mesh vertices
+	createVertexArray(pModel, pData, pMesh);
+
+	// Create faces
+	pMesh->mNumFaces = pData->m_Faces.size();
+	pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+	for (size_t index = 0; index < pMesh->mNumFaces; index++)
+	{
+		aiFace *pFace = &pMesh->mFaces[ index ];
+		pFace->mNumIndices = pData->m_Faces[index]->m_pVertices->size();
+		if (pFace->mNumIndices > 0)
+		{
+			pFace->mIndices = new unsigned int[pMesh->mFaces[index].mNumIndices];
+			ObjFile::Face::IndexArray *pArray = pData->m_Faces[index]->m_pVertices;
+			
+			// TODO:	Should be implement much better
+			//memcpy(pFace->mIndices, &pData->m_Faces[index]->m_pVertices[0], pFace->mNumIndices  * sizeof(unsigned int));
+			if (pArray != NULL)
+			{
+				for (size_t a=0; a<pFace->mNumIndices; a++)
+				{
+					pFace->mIndices[a] = pArray->at( a );
+				}
+			}
+			else
+			{
+				ai_assert (false);
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel, 
+										const ObjFile::Object* pCurrentObject, 
+										aiMesh* pMesh)
+{
+	// Break, if no faces are stored in object
+	if (pCurrentObject->m_Faces.empty())
+		return;
+	
+	// Copy all stored vertices, normals and so on
+	pMesh->mNumVertices = pModel->m_Vertices.size();
+	pMesh->mVertices = new aiVector3D_t[pMesh->mNumVertices];
+	for (size_t index=0; index < pModel->m_Vertices.size(); index++)
+	{
+		pMesh->mVertices[ index ] = *pModel->m_Vertices[ index ];
+	}
+	
+	if (!pModel->m_Normals.empty())
+	{
+		pMesh->mNormals = new aiVector3D_t[pModel->m_Normals.size()];
+		for (size_t index = 0; index < pModel->m_Normals.size(); index++)
+		{
+			pMesh->mNormals[ index ] = *pModel->m_Normals[ index ];
+		}
+	}
+
+	if (!pModel->m_TextureCoord.empty())
+	{
+		// TODO: Implement texture coodinates
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
+{
+	iNumMeshes = 0;
+	if (rObjects.empty())	
+		return;
+
+	iNumMeshes += rObjects.size();
+	for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
+		it != rObjects.end(); 
+		++it)
+	{
+		if (!(*it)->m_SubObjects.empty())
+		{
+			countObjects((*it)->m_SubObjects, iNumMeshes);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjFileImporter::createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+									 aiScene* pScene)
+{
+	ai_assert (NULL != pScene);
+	if (NULL == pData)
+		return;
+
+	// Create only a dumy material to enshure a running viewer
+	pScene->mNumMaterials = 1;
+	Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+	
+	// Create a new material
+	pScene->mMaterials = new aiMaterial*[1];
+	pScene->mMaterials[0] = mat;
+}
+
+// ------------------------------------------------------------------------------------------------
+//	Appends this node to the parent node
+void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
+{
+	// Checking preconditions
+	ai_assert (NULL != pParent);
+	ai_assert (NULL != pChild);
+
+	// Assign parent to child
+	pChild->mParent = pParent;
+	size_t sNumChildren = 0;
+	
+	// If already children was assigned to the parent node, store them in a 
+	std::vector<aiNode*> temp;
+	if (pParent->mChildren != NULL)
+	{
+		sNumChildren = pParent->mNumChildren;
+		ai_assert (0 != sNumChildren);
+		for (size_t index = 0; index < pParent->mNumChildren; index++)
+		{
+			temp.push_back(pParent->mChildren [ index ] );
+		}
+		delete [] pParent->mChildren;
+	}
+	
+	// Copy node instances into parent node
+	pParent->mNumChildren++;
+	pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
+	for (size_t index = 0; index < pParent->mNumChildren-1; index++)
+	{
+		pParent->mChildren[ index ] = temp [ index ];
+	}
+	pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
+
+}
+
+// ------------------------------------------------------------------------------------------------
+
+}	// Namespace Assimp

+ 78 - 0
code/ObjFileImporter.h

@@ -0,0 +1,78 @@
+#ifndef OBJ_FILE_IMPORTER_H_INC
+#define OBJ_FILE_IMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Object;
+struct Model;
+}
+
+///	\class	ObjFileImporter
+///	\brief	IMports a waveform obj file
+class ObjFileImporter :
+	BaseImporter
+{	
+	friend class Importer;
+
+	//!	OB file extention
+	static const std::string OBJ_EXT;
+
+protected:
+	///	\brief	Default constructor
+	ObjFileImporter();
+
+	///	\brief	Destructor
+	~ObjFileImporter();
+
+public:
+	/// \brief	Returns whether the class can handle the format of the given file. 
+	/// \remark	See BaseImporter::CanRead() for details.
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
+
+private:
+	//!	\brief
+	void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+	
+	//!	\brief
+	void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene);
+	
+	//!	\brief
+	aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+		aiNode *pParent, aiScene* pScene, std::vector<aiMesh*> &MeshArray);
+
+	//!	\brief
+	void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
+		aiMesh* pMesh);	
+	
+	//!	\brief
+	void createVertexArray(const ObjFile::Model* pModel, 
+		const ObjFile::Object* pCurrentObject, aiMesh* pMesh);
+
+	//!	\brief
+	void countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes);
+
+	//!	\brief
+	void createMaterial(const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+		aiScene* pScene);
+
+	//!	\brief
+	void appendChildToParentNode(aiNode *pParent, aiNode *pChild);
+
+private:
+	std::vector<char> m_Buffer;
+	ObjFile::Object *m_pRootObject;
+	std::string m_strAbsPath;
+};
+
+} // Namespace Assimp
+
+#endif

+ 56 - 0
code/ObjFileMtlImporter.cpp

@@ -0,0 +1,56 @@
+#include "ObjFileMtlImporter.h"
+
+namespace Assimp
+{
+
+// -------------------------------------------------------------------
+ObjFileMtlImporter::ObjFileMtlImporter()
+{
+	// TODO: Inplement this
+}
+
+// -------------------------------------------------------------------
+ObjFileMtlImporter::~ObjFileMtlImporter()
+{
+	// empty
+}
+
+// -------------------------------------------------------------------
+ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter &rOther)
+{
+	// empty
+}
+	
+// -------------------------------------------------------------------
+ObjFileMtlImporter &ObjFileMtlImporter::operator = (const ObjFileMtlImporter &rOther)
+{
+	return *this;
+}
+// -------------------------------------------------------------------
+void ObjFileMtlImporter::getColorRGBA()
+{
+}
+
+// -------------------------------------------------------------------
+void ObjFileMtlImporter::getIlluminationModel()
+{
+}
+
+// -------------------------------------------------------------------
+void ObjFileMtlImporter::getFloatValue()
+{
+}
+
+// -------------------------------------------------------------------
+void ObjFileMtlImporter::createMaterial()
+{
+}
+
+// -------------------------------------------------------------------
+void ObjFileMtlImporter::getTexture()
+{
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp

+ 36 - 0
code/ObjFileMtlImporter.h

@@ -0,0 +1,36 @@
+#ifndef OBJFILEMTLIMPORTER_H_INC
+#define OBJFILEMTLIMPORTER_H_INC
+
+namespace Assimp
+{
+
+/**
+ *	@class	ObjFileMtlImporter
+ *	@brief	Loads the material description from a mtl file.
+ */
+class ObjFileMtlImporter
+{
+public:
+	//!	\brief	Default constructor
+	ObjFileMtlImporter();
+	
+	//!	\brief	DEstructor
+	~ObjFileMtlImporter();
+
+private:
+	//!	\brief	Copy constructor, empty.
+	ObjFileMtlImporter(const ObjFileMtlImporter &rOther);
+	
+	//!	\brief	Assignment operator, returns only a reference of this instance.
+	ObjFileMtlImporter &operator = (const ObjFileMtlImporter &rOther);
+
+	void getColorRGBA();
+	void getIlluminationModel();
+	void getFloatValue();
+	void createMaterial();
+	void getTexture();
+};
+
+} // Namespace Assimp
+
+#endif

+ 551 - 0
code/ObjFileParser.cpp

@@ -0,0 +1,551 @@
+#include "ObjFileParser.h"
+#include "ObjTools.h"
+#include "ObjFileData.h"
+#include "DefaultIOSystem.h"
+#include "IOStream.h"
+#include "aiTypes.h"
+#include "aiVector3D.h"
+#include "aiAssert.h"
+#include "fast_atof.h"
+
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+namespace Assimp
+{
+
+// -------------------------------------------------------------------
+ObjFileParser::ObjFileParser(std::vector<char> &Data, const std::string &strAbsPath, const std::string &strModelName) :
+	m_strAbsPath(strAbsPath),
+	m_DataIt(Data.begin()),
+	m_DataItEnd(Data.end()),
+	m_pModel(NULL),
+	m_uiLine(0)
+{
+	// Create the model instance to store all the data
+	m_pModel = new ObjFile::Model();
+	m_pModel->m_ModelName = strModelName;
+		
+	// Start parsing the file
+	parseFile();
+}
+
+// -------------------------------------------------------------------
+ObjFileParser::~ObjFileParser()
+{
+	// empty
+}
+
+// -------------------------------------------------------------------
+ObjFile::Model *ObjFileParser::GetModel() const
+{
+	return m_pModel;
+}
+
+// -------------------------------------------------------------------
+void ObjFileParser::parseFile()
+{
+	if (m_DataIt == m_DataItEnd)
+		return;
+
+	while (m_DataIt != m_DataItEnd)
+	{
+		switch (*m_DataIt)
+		{
+		case 'v': // Parse a vertex texture coordinate
+			{
+				++m_DataIt;
+				if (*m_DataIt == ' ')
+				{
+					// Read in vertex definition
+					getVector3(m_pModel->m_Vertices);
+				}
+				else if (*m_DataIt == 't')
+				{
+					// Read in texture coordinate (2D)
+					getVector2(m_pModel->m_TextureCoord);
+				}
+				else if (*m_DataIt == 'n')
+				{
+					// Read in normal vector definition
+					getVector3(m_pModel->m_Normals);
+				}
+			}
+			break;
+
+		case 'f': // Parse a face
+			{
+				getFace();
+			}
+			break;
+
+		case '#': // Parse a comment
+			{
+				getComment();
+			}
+			break;
+
+		case 'u': // Parse a material desc. setter
+			{
+				getMaterialDesc();
+			}
+			break;
+
+		case 'm': // Parse a material library
+			{
+				getMaterialLib();
+			}
+			break;
+
+		case 'g': // Parse group name
+			{
+				getGroupName();
+			}
+			break;
+
+		case 's': // Parse group number
+			{
+				getGroupNumber();
+			}
+			break;
+
+		case 'o': // Parse object name
+			{
+				getObjectName();
+			}
+			break;
+		
+		default:
+			{
+				skipLine();
+			}
+			break;
+		}
+	}
+}
+
+// -------------------------------------------------------------------
+//	Copy the next word in a temporary buffer
+void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
+{
+	size_t index = 0;
+	m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+	while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd)
+	{
+		pBuffer[index] = *m_DataIt;
+		index++;
+		if (index == length-1)
+			break;
+		++m_DataIt;
+	}
+	pBuffer[index] = '\0';
+}
+
+// -------------------------------------------------------------------
+// Copy the next line into a temporary buffer
+void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
+{
+	size_t index = 0;
+	while (m_DataIt != m_DataItEnd)
+	{
+		if (*m_DataIt == '\n' || *m_DataIt == '\r')
+			break;
+		assert (index+1 <= length);
+		pBuffer[ index ] = *m_DataIt;
+		++index;
+		++m_DataIt;
+	}
+	pBuffer[ index ] = '\0';
+}
+
+// -------------------------------------------------------------------
+//	Get values for a new 3D vector instance
+void ObjFileParser::getVector3(std::vector<aiVector3D_t*> &point3d_array)
+{
+	float x, y, z;
+	copyNextWord(m_buffer, BUFFERSIZE);
+	x = (float) fast_atof(m_buffer);	
+	
+	copyNextWord(m_buffer, BUFFERSIZE);
+	y = (float) fast_atof(m_buffer);
+
+	copyNextWord(m_buffer, BUFFERSIZE);
+	z = (float) fast_atof(m_buffer);
+
+	point3d_array.push_back(new aiVector3D(x,y,z));
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Get values for a new 2D vector instance
+void ObjFileParser::getVector2(std::vector<aiVector2D_t*> &point2d_array)
+{
+	float x, y;
+	copyNextWord(m_buffer, BUFFERSIZE);
+	x = (float) fast_atof(m_buffer);	
+	
+	copyNextWord(m_buffer, BUFFERSIZE);
+	y = (float) fast_atof(m_buffer);
+
+	point2d_array.push_back(new aiVector2D(x, y));
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Skips a line
+void ObjFileParser::skipLine()
+{
+	while (m_DataIt != m_DataItEnd && *m_DataIt != '\n')
+		++m_DataIt;
+	if (m_DataIt != m_DataItEnd)
+	{
+		++m_DataIt;
+		++m_uiLine;
+	}
+}
+
+// -------------------------------------------------------------------
+//	Get values for a new face instance
+void ObjFileParser::getFace()
+{
+	copyNextLine(m_buffer, BUFFERSIZE);
+	if (m_DataIt == m_DataItEnd)
+		return;
+	char *pPtr = m_buffer;
+	char *pEnd = &pPtr[BUFFERSIZE];
+	pPtr = getNextToken<char*>(pPtr, pEnd);
+	if (pPtr == '\0')
+		return;
+
+	std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
+	std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
+	std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
+	bool vt = (!m_pModel->m_TextureCoord.empty());
+	bool vn = (!m_pModel->m_Normals.empty());
+	int iStep = 0, iPos = 0;
+	while (pPtr != pEnd)
+	{
+		iStep = 1;
+		if (*pPtr == '\0')
+			break;
+
+		if (*pPtr=='\r')
+			break;
+
+		if (*pPtr=='/' )
+		{
+			if (iPos == 0)
+			{
+				//if there are no texturecoordinates in the obj file but normals
+				if (!vt && vn)
+					iPos = 1;
+			}
+			iPos++;
+		}
+		else if (isSpace(*pPtr))
+		{
+			iPos = 0;
+		}
+		else 
+		{
+			//OBJ USES 1 Base ARRAYS!!!!
+			const int iVal = atoi(pPtr);
+			int tmp = iVal;
+			while ((tmp = tmp / 10)!=0)
+				++iStep;
+
+			if (0 != iVal)
+			{
+				// Store parsed index
+				if (0 == iPos)
+				{
+					pIndices->push_back(iVal-1);
+				}
+				else if (1 == iPos)
+				{	
+					pTexID->push_back(iVal-1);
+				}
+				else if (2 == iPos)
+				{
+					pNormalID->push_back(iVal-1);
+				}
+				else
+				{
+					reportErrorTokenInFace();
+				}
+			}
+		}
+		for (int i=0; i<iStep; i++)
+			++pPtr;
+	}
+
+	ObjFile::Face *face = new ObjFile::Face(pIndices, pNormalID, pTexID);
+	
+	// Set active material, if one set
+	if (NULL != m_pModel->m_pCurrentMaterial) 
+		face->m_pMaterial = m_pModel->m_pCurrentMaterial;
+	else 
+		face->m_pMaterial = m_pModel->m_pDefaultMaterial;
+
+	// Create a default object, if nothing there
+	if (NULL == m_pModel->m_pCurrent)
+		createObject("defaultobject");
+
+	// Store the new instance
+	m_pModel->m_pCurrent->m_Faces.push_back(face);
+	
+	// Skip the rest of the line
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Get values for a new material description
+void ObjFileParser::getMaterialDesc()
+{
+	// Get next data for material data
+	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+	if (m_DataIt == m_DataItEnd)
+		return;
+
+	char *pStart = &(*m_DataIt);
+	while (!isSpace(*m_DataIt) && m_DataIt != m_DataItEnd)
+		m_DataIt++;
+
+	// Get name
+	std::string strName(pStart, &(*m_DataIt));
+	if (strName.empty())
+		return;
+
+	// Search for material
+	std::string strFile;
+	std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strName);
+	if (it == m_pModel->m_MaterialMap.end())
+	{
+		m_pModel->m_pCurrentMaterial = new ObjFile::Material();
+		m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial;
+	}
+
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Get a comment, values will be skipped
+void ObjFileParser::getComment()
+{
+	while (true)
+	{
+		if ('\n' == (*m_DataIt) || m_DataIt == m_DataItEnd) 
+		{
+			++m_DataIt;
+			break;
+		}
+		else
+		{
+			++m_DataIt;
+		}
+	}
+}
+
+// -------------------------------------------------------------------
+//	
+void ObjFileParser::getMaterialLib()
+{
+	// Translate tuple
+	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+	if (m_DataIt ==  m_DataItEnd)
+		return;
+	
+	char *pStart = &(*m_DataIt);
+	while (!isSpace(*m_DataIt))
+		m_DataIt++;
+	
+	// Check for existence
+	DefaultIOSystem IOSystem;
+	std::string strMatName(pStart, &(*m_DataIt));
+	std::string absName = m_strAbsPath + IOSystem.getOsSeparator() + strMatName;
+	if (!IOSystem.Exists(absName))
+	{
+		skipLine();
+		return;
+	}
+
+	std::string strExt("");
+	extractExtension(strMatName, strExt);
+	std::string mat = "mtl";
+
+	DefaultIOSystem FileSystem;
+	IOStream *pFile = FileSystem.Open(absName);
+	if (0L != pFile)
+	{
+		size_t size = pFile->FileSize();
+		char *pBuffer = new char[size];
+		size_t read_size = pFile->Read(pBuffer, sizeof(char), size);
+		FileSystem.Close(pFile);
+
+		// TODO: Load mtl file
+		
+		delete [] pBuffer;
+	
+	}
+
+	// Load material library (all materials will be created)
+	m_pModel->m_MaterialLib.push_back(strMatName);
+	
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	
+void ObjFileParser::getNewMaterial()
+{
+	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+	m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+
+	char *pStart = &(*m_DataIt);
+	std::string strMat(pStart, *m_DataIt);
+	while (isSpace(*m_DataIt))
+		m_DataIt++;
+	std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strMat);
+	if (it == m_pModel->m_MaterialMap.end())
+	{
+		// Show a warning, if material was not found
+		std::string strWarn ("Unsupported material requested: ");
+		strWarn += strMat;
+		std::cerr << "Warning : " << strWarn << std::endl;
+		m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+	}
+	else
+	{
+		// Set new material
+		m_pModel->m_pCurrentMaterial = (*it).second;
+	}
+
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	
+void ObjFileParser::getGroupName()
+{
+	// Get next word from data buffer
+	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+	m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+	
+	// Store groupname in group library 
+	char *pStart = &(*m_DataIt);
+	while (!isSpace(*m_DataIt))
+		m_DataIt++;
+	std::string strGroupName(pStart, &(*m_DataIt));
+
+	// Change active group, if necessary
+	if (m_pModel->m_strActiveGroup != strGroupName)
+	{
+		// Search for already existing entry
+		ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName);
+		
+		// New group name, creating a new entry
+		ObjFile::Object *pObject = m_pModel->m_pCurrent;
+		if (it == m_pModel->m_Groups.end())
+		{
+			std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
+			m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray;
+			m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
+		}
+		else
+		{
+			m_pModel->m_pGroupFaceIDs = (*it).second;
+		}
+		m_pModel->m_strActiveGroup = strGroupName;
+	}
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Not supported
+void ObjFileParser::getGroupNumber()
+{
+	// TODO: Implement this
+
+	skipLine();
+}
+
+// -------------------------------------------------------------------
+//	Stores values for a new object instance, name will be used to 
+//	identify it.
+void ObjFileParser::getObjectName()
+{
+	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+	if (m_DataIt == m_DataItEnd)
+		return;
+	char *pStart = &(*m_DataIt);
+	while (!isSpace(*m_DataIt))
+		m_DataIt++;
+
+	std::string strObjectName(pStart, &(*m_DataIt));
+	if (!strObjectName.empty()) 
+	{
+		// Reset current object
+		m_pModel->m_pCurrent = NULL;
+		
+		// Search for actual object
+		for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
+			it != m_pModel->m_Objects.end();
+			++it)
+		{
+			if ((*it)->m_strObjName == strObjectName)
+			{
+				m_pModel->m_pCurrent = *it;
+				break;
+			}
+		}
+
+		// Allocate a new object, if current one wasn´t found before
+		if (m_pModel->m_pCurrent == NULL)
+		{
+			createObject(strObjectName);
+			/*m_pModel->m_pCurrent = new ObjFile::Object();
+			m_pModel->m_pCurrent->m_strObjName = strObjectName;
+			m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);*/
+		}
+	}
+	skipLine();
+}
+// -------------------------------------------------------------------
+//	Creates a new object instance
+void ObjFileParser::createObject(const std::string &strObjectName)
+{
+	ai_assert (NULL != m_pModel);
+	ai_assert (!strObjectName.empty());
+
+	m_pModel->m_pCurrent = new ObjFile::Object();
+	m_pModel->m_pCurrent->m_strObjName = strObjectName;
+	m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);
+}
+
+// -------------------------------------------------------------------
+//	Shows an error in parsing process.
+void ObjFileParser::reportErrorTokenInFace()
+{		
+	std::string strErr("");
+	skipLine();
+	std::cerr <<  "Not supported token in face desc. detected : " << strErr << std::endl;
+}
+
+// -------------------------------------------------------------------
+//	Extracts the extention from a filename
+void ObjFileParser::extractExtension(const std::string strFile, 
+									 std::string &strExt)
+{
+	strExt = "";
+	if (strFile.empty())
+		return;
+
+	std::string::size_type pos = strFile.find_last_of(".");
+	if (pos == std::string::npos)
+		return;
+	strExt = strFile.substr(pos, strFile.size() - pos);
+}
+// -------------------------------------------------------------------
+
+}	// Namespace Assimp

+ 56 - 0
code/ObjTools.h

@@ -0,0 +1,56 @@
+/**	@file	ObjTools.h
+ *	@brief	Some helpful templates for text parsing
+ */
+#ifndef OBJ_TOOLS_H_INC
+#define OBJ_TOOLS_H_INC
+
+namespace Assimp
+{
+
+/** @brief	Returns true, if token is a space on any supported platform
+*	@param	token	Token to search in
+*	@return	true, if token is a space			
+*/
+inline bool isSpace(char token)
+{
+	return (token == ' ' || token == '\n' || token == '\f' || token == '\r' ||
+		token == '\t');
+}
+
+/**	@brief	Returns next word separated by a space
+ *	@param	pBuffer	Pointer to data buffer
+ *	@param	pEnd	Pointer to end of buffer
+ *	@return	Pointer to next space
+ */
+template<class Char_T>
+inline Char_T getNextWord(Char_T pBuffer, Char_T pEnd)
+{
+	while (pBuffer != pEnd)
+	{
+		if (!isSpace(*pBuffer))
+			break;
+		pBuffer++;
+	}
+	return pBuffer;
+}
+
+/**	@brief	Returns ponter a next token
+ *	@param	pBuffer	Pointer to data buffer
+ *	@param	pEnd	Pointer to end of buffer
+ *	@return	Pointer to next token
+*/
+template<class Char_T>
+inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd)
+{
+	while (pBuffer != pEnd)
+	{
+		if (isSpace(*pBuffer))
+			break;
+		pBuffer++;
+	}
+	return getNextWord(pBuffer, pEnd);
+}
+
+} // Namespace Assimp
+
+#endif

+ 990 - 0
code/PlyLoader.cpp

@@ -0,0 +1,990 @@
+/** @file Implementation of the PLY importer class */
+#include "PLYLoader.h"
+#include "MaterialSystem.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
+PLYImporter::PLYImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+PLYImporter::~PLYImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool PLYImporter::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] != 'p' && extension[1] != 'P')return false;
+	if (extension[2] != 'l' && extension[2] != 'L')return false;
+	if (extension[3] != 'y' && extension[3] != 'Y')return false;
+
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void PLYImporter::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 PLY file " + pFile + ".");
+	}
+
+	// check whether the ply file is large enough to contain
+	// at least the file header
+	size_t fileSize = file->FileSize();
+	if( fileSize < 10)
+	{
+		throw new ImportErrorException( ".ply File is too small.");
+	}
+
+	// allocate storage and copy the contents of the file to a memory buffer
+	// (terminate it with zero)
+	this->mBuffer = new unsigned char[fileSize+1];
+	file->Read( (void*)mBuffer, 1, fileSize);
+	this->mBuffer[fileSize] = '\0';
+
+	// the beginning of the file must be PLY
+	if (this->mBuffer[0] != 'P' && this->mBuffer[0] != 'p' ||
+		this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' ||
+		this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y')
+	{
+		throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there");
+	}
+	char* szMe = (char*)&this->mBuffer[3];
+	SkipSpacesAndLineEnd(szMe,(const char**)&szMe);
+	
+	// determine the format of the file data
+	PLY::DOM sPlyDom;
+	if (0 == ASSIMP_strincmp(szMe,"format",6) && IsSpace(*(szMe+6)))
+	{
+		szMe += 7;
+		if (0 == ASSIMP_strincmp(szMe,"ascii",5) && IsSpace(*(szMe+5)))
+		{
+			szMe += 6;
+			SkipLine(szMe,(const char**)&szMe);
+			if(!PLY::DOM::ParseInstance(szMe,&sPlyDom))
+			{
+				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)");
+			}
+		}
+		else if (0 == ASSIMP_strincmp(szMe,"binary_",7))
+		{
+			bool bIsBE = false;
+
+			// binary_little_endian
+			// binary_big_endian
+			szMe += 7;
+#if (defined AI_BUILD_BIG_ENDIAN)
+			if ('l' == *szMe || 'L' == *szMe)bIsBE = true;
+#else
+			if ('b' == *szMe || 'B' == *szMe)bIsBE = true;
+#endif // ! AI_BUILD_BIG_ENDIAN
+
+			// skip the line, parse the rest of the header and build the DOM
+			SkipLine(szMe,(const char**)&szMe);
+			if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
+			{
+				throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)");
+			}
+		}
+		else
+		{
+			throw new ImportErrorException( "Invalid .ply file: Unknown file format");
+		}
+	}
+	else
+	{
+		throw new ImportErrorException( "Invalid .ply file: Missing format specification");
+	}
+	this->pcDOM = &sPlyDom;
+
+	// now load a list of vertices. This must be sucessfull in order to procede
+	std::vector<aiVector3D> avPositions;
+	this->LoadVertices(&avPositions,false);
+
+	if (avPositions.empty())
+	{
+		throw new ImportErrorException( "Invalid .ply file: No vertices found");
+	}
+
+	// now load a list of normals. 
+	std::vector<aiVector3D> avNormals;
+	this->LoadVertices(&avNormals,true);
+
+	// load the face list
+	std::vector<PLY::Face> avFaces;
+	this->LoadFaces(&avFaces);
+
+	// if no face list is existing we assume that the vertex
+	// list is containing a list of triangles
+	if (avFaces.empty())
+	{
+		if (avPositions.size() < 3)
+		{
+			throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build "
+				"a face list. ");
+		}
+
+		unsigned int iNum = avPositions.size() / 3;
+		for (unsigned int i = 0; i< iNum;++i)
+		{
+			PLY::Face sFace;
+			sFace.mIndices.push_back((iNum*3));
+			sFace.mIndices.push_back((iNum*3)+1);
+			sFace.mIndices.push_back((iNum*3)+2);
+			avFaces.push_back(sFace);
+		}
+	}
+
+	// now load a list of all materials
+	std::vector<MaterialHelper*> avMaterials;
+	this->LoadMaterial(&avMaterials);
+
+	// now load a list of all vertex color channels
+	std::vector<aiColor4D> avColors;
+	this->LoadVertexColor(&avColors);
+
+	// now replace the default material in all faces and validate all material indices
+	this->ReplaceDefaultMaterial(&avFaces,&avMaterials);
+
+	// now convert this to a list of aiMesh instances
+	std::vector<aiMesh*> avMeshes;
+	this->ConvertMeshes(&avFaces,&avPositions,&avNormals,
+		&avColors,&avMaterials,&avMeshes);
+
+	if (avMeshes.empty())
+	{
+		throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data ");
+	}
+
+	// now generate the output scene object. Fill the material list
+	pScene->mNumMaterials = avMaterials.size();
+	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+	for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+		pScene->mMaterials[i] = avMaterials[i];
+
+	// fill the mesh list
+	pScene->mNumMeshes = avMeshes.size();
+	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+		pScene->mMeshes[i] = avMeshes[i];
+
+	// generate a simple node structure
+	pScene->mRootNode = new aiNode();
+	pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+	pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+	for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
+		pScene->mRootNode->mMeshes[i] = i;
+
+	// delete the file buffer
+	delete[] this->mBuffer;
+
+	// DOM is lying on the stack, will be deconstructed automatically
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
+	const std::vector<aiVector3D>*			avPositions,
+	const std::vector<aiVector3D>*			avNormals,
+	const std::vector<aiColor4D>*			avColors,
+	const std::vector<MaterialHelper*>*		avMaterials,
+	std::vector<aiMesh*>* avOut)
+{
+	ai_assert(NULL != avFaces);
+	ai_assert(NULL != avPositions);
+	ai_assert(NULL != avMaterials);
+
+	// split by materials
+	std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[
+		avMaterials->size()];
+
+	unsigned int iNum = 0;
+	for (std::vector<PLY::Face>::const_iterator
+		i =  avFaces->begin();
+		i != avFaces->end();++i,++iNum)
+	{
+		// index has already been checked
+		aiSplit[(*i).iMaterialIndex].push_back(iNum);
+	}
+	// now generate submeshes
+	for (unsigned int p = 0; p < avMaterials->size();++p)
+	{
+		if (aiSplit[p].size() != 0)
+		{
+			// allocate the mesh object
+			aiMesh* p_pcOut = new aiMesh();
+			p_pcOut->mMaterialIndex = p;
+
+			p_pcOut->mNumFaces = aiSplit[p].size();
+			p_pcOut->mFaces = new aiFace[aiSplit[p].size()];
+
+			// at first we need to determine the size of the output vector array
+			unsigned int iNum = 0;
+			for (unsigned int i = 0; i < aiSplit[p].size();++i)
+			{
+				iNum += (*avFaces)[aiSplit[p][i]].mIndices.size();
+			}
+			p_pcOut->mNumVertices = iNum;
+			p_pcOut->mVertices = new aiVector3D[iNum];
+
+			if (!avColors->empty())
+				p_pcOut->mColors[0] = new aiColor4D[iNum];
+			if (!avNormals->empty())
+				p_pcOut->mNormals = new aiVector3D[iNum];
+
+			// add all faces
+			iNum = 0;
+			unsigned int iVertex = 0;
+			for (std::vector<unsigned int>::const_iterator
+				i =  aiSplit[p].begin();
+				i != aiSplit[p].end();++i,++iNum)
+			{
+				p_pcOut->mFaces[iNum].mNumIndices = (*avFaces)[*i].mIndices.size(); 
+				p_pcOut->mFaces[iNum].mIndices = new unsigned int[p_pcOut->mFaces[iNum].mNumIndices];
+
+				// build an unique set of vertices/colors for this face
+				// hardcode all combinations to speedup this piece of code
+				if (!avColors->empty())
+				{
+					if (!avNormals->empty())
+					{
+						for (unsigned int q = 0; q <  p_pcOut->mFaces[iNum].mNumIndices;++q)
+						{
+							p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+							p_pcOut->mVertices[iVertex]		= (*avPositions)[(*avFaces)[*i].mIndices[q]];
+							p_pcOut->mColors[0][iVertex]	= (*avColors)[(*avFaces)[*i].mIndices[q]];
+							p_pcOut->mNormals[iVertex]		= (*avNormals)[(*avFaces)[*i].mIndices[q]];
+							iVertex++;
+						}
+					}
+					else
+					{
+						for (unsigned int q = 0; q <  p_pcOut->mFaces[iNum].mNumIndices;++q)
+						{
+							p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+							p_pcOut->mVertices[iVertex]		= (*avPositions)[(*avFaces)[*i].mIndices[q]];
+							p_pcOut->mColors[0][iVertex]	= (*avColors)[(*avFaces)[*i].mIndices[q]];
+							iVertex++;
+						}
+					}
+				}
+				else
+				{
+					if (!avNormals->empty())
+					{
+						for (unsigned int q = 0; q <  p_pcOut->mFaces[iNum].mNumIndices;++q)
+						{
+							p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+							p_pcOut->mVertices[iVertex]		= (*avPositions)[(*avFaces)[*i].mIndices[q]];
+							p_pcOut->mNormals[iVertex]		= (*avNormals)[(*avFaces)[*i].mIndices[q]];
+							iVertex++;
+						}
+					}
+					else
+					{
+						for (unsigned int q = 0; q <  p_pcOut->mFaces[iNum].mNumIndices;++q)
+						{
+							p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+							p_pcOut->mVertices[iVertex]		= (*avPositions)[(*avFaces)[*i].mIndices[q]];
+							iVertex++;
+						}
+					}
+				}
+			}
+			// add the mesh to the output list
+			avOut->push_back(p_pcOut);
+		}
+	}
+	delete[] aiSplit;
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+										 std::vector<MaterialHelper*>* avMaterials)
+{
+	bool bNeedDefaultMat = false;
+
+	for (std::vector<PLY::Face>::iterator
+		i =  avFaces->begin();i != avFaces->end();++i)
+	{
+		if (0xFFFFFFFF == (*i).iMaterialIndex)
+		{
+			bNeedDefaultMat = true;
+			(*i).iMaterialIndex = avMaterials->size();
+		}
+		else if ((*i).iMaterialIndex >= avMaterials->size() )
+		{
+			// clamp the index
+			(*i).iMaterialIndex = avMaterials->size()-1;
+		}
+	}
+
+	if (bNeedDefaultMat)
+	{
+		// generate a default material
+		MaterialHelper* pcHelper = new MaterialHelper();
+
+		// fill in a default material
+		int iMode = (int)aiShadingMode_Gouraud;
+		pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+		aiColor3D clr;
+		clr.b = clr.g = clr.r = 0.6f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+		clr.b = clr.g = clr.r = 0.05f;
+		pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+		avMaterials->push_back(pcHelper);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadVertices(std::vector<aiVector3D>* pvOut, bool p_bNormals)
+{
+	unsigned int aiPositions[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+	PLY::EDataType aiTypes[3];
+	PLY::ElementInstanceList* pcList = NULL;
+	unsigned int cnt = 0;
+
+	// serach in the DOM for a vertex entry
+	unsigned int _i = 0;
+	for (std::vector<PLY::Element>::const_iterator
+		i =  this->pcDOM->alElements.begin();
+		i != this->pcDOM->alElements.end();++i,++_i)
+	{
+		if (PLY::EEST_Vertex == (*i).eSemantic)
+		{
+			pcList = &this->pcDOM->alElementData[_i];
+
+			// load normal vectors?
+			if (p_bNormals)
+			{
+				// now check whether which normal components are available
+				unsigned int _a = 0;
+				for (std::vector<PLY::Property>::const_iterator
+					a =  (*i).alProperties.begin();
+					a != (*i).alProperties.end();++a,++_a)
+				{
+					if ((*a).bIsList)continue;
+					if (PLY::EST_XNormal == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[0] = _a;
+						aiTypes[0] = (*a).eType;
+					}
+					else if (PLY::EST_YNormal == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[1] = _a;
+						aiTypes[1] = (*a).eType;
+					}
+					else if (PLY::EST_ZNormal == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[2] = _a;
+						aiTypes[2] = (*a).eType;
+					}
+					if (3 == cnt)break; 
+				}
+			}
+			// load vertex coordinates
+			else
+			{
+				// now check whether which coordinate sets are available
+				unsigned int _a = 0;
+				for (std::vector<PLY::Property>::const_iterator
+					a =  (*i).alProperties.begin();
+					a != (*i).alProperties.end();++a,++_a)
+				{
+					if ((*a).bIsList)continue;
+					if (PLY::EST_XCoord == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[0] = _a;
+						aiTypes[0] = (*a).eType;
+					}
+					else if (PLY::EST_YCoord == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[1] = _a;
+						aiTypes[1] = (*a).eType;
+					}
+					else if (PLY::EST_ZCoord == (*a).Semantic)
+					{
+						cnt++;
+						aiPositions[2] = _a;
+						aiTypes[2] = (*a).eType;
+					}
+					if (3 == cnt)break; 
+				}
+			}
+			break;
+		}
+	}
+	// check whether we have a valid source for the vertex data
+	if (NULL != pcList && 0 != cnt)
+	{
+		pvOut->reserve(pcList->alInstances.size());
+		for (std::vector<ElementInstance>::const_iterator
+			i =  pcList->alInstances.begin();
+			i != pcList->alInstances.end();++i)
+		{
+			// convert the vertices to sp floats
+			aiVector3D vOut;
+
+			if (0xFFFFFFFF == aiPositions[0])vOut.x = 0.0f;
+			else
+			{
+				vOut.x = PLY::PropertyInstance::ConvertTo<float>(
+					(*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+			}
+
+			if (0xFFFFFFFF == aiPositions[1])vOut.y = 0.0f;
+			else
+			{
+				vOut.y = PLY::PropertyInstance::ConvertTo<float>(
+					(*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+			}
+
+			if (0xFFFFFFFF == aiPositions[2])vOut.z = 0.0f;
+			else
+			{
+				vOut.z = PLY::PropertyInstance::ConvertTo<float>(
+					(*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]);
+			}
+
+			// and add them to our nice list
+			pvOut->push_back(vOut);
+		}
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+float NormalizeColorValue (PLY::PropertyInstance::ValueUnion val,PLY::EDataType eType)
+{
+	switch (eType)
+	{
+	case EDT_Float:
+		return val.fFloat;
+	case EDT_Double:
+		return (float)val.fDouble;
+
+	case EDT_UChar:
+		return (float)val.iUInt / (float)0xFF;
+	case EDT_Char:
+		return (float)(val.iInt+(0xFF/2)) / (float)0xFF;
+	case EDT_UShort:
+		return (float)val.iUInt / (float)0xFFFF;
+	case EDT_Short:
+		return (float)(val.iInt+(0xFFFF/2)) / (float)0xFFFF;
+	case EDT_UInt:
+		return (float)val.iUInt / (float)0xFFFF;
+	case EDT_Int:
+		return ((float)val.iInt / (float)0xFF) + 0.5f;
+	};
+	return 0.0f;
+}
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadVertexColor(std::vector<aiColor4D>* pvOut)
+{
+	unsigned int aiPositions[4] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+	PLY::EDataType aiTypes[4];
+	unsigned int cnt = 0;
+	PLY::ElementInstanceList* pcList = NULL;
+
+	// serach in the DOM for a vertex entry
+	unsigned int _i = 0;
+	for (std::vector<PLY::Element>::const_iterator
+		i =  this->pcDOM->alElements.begin();
+		i != this->pcDOM->alElements.end();++i,++_i)
+	{
+		if (PLY::EEST_Vertex == (*i).eSemantic)
+		{
+			pcList = &this->pcDOM->alElementData[_i];
+
+			// now check whether which coordinate sets are available
+			unsigned int _a = 0;
+			for (std::vector<PLY::Property>::const_iterator
+				a =  (*i).alProperties.begin();
+				a != (*i).alProperties.end();++a,++_a)
+			{
+				if ((*a).bIsList)continue;
+				if (PLY::EST_Red == (*a).Semantic)
+				{
+					cnt++;
+					aiPositions[0] = _a;
+					aiTypes[0] = (*a).eType;
+				}
+				else if (PLY::EST_Green == (*a).Semantic)
+				{
+					cnt++;
+					aiPositions[1] = _a;
+					aiTypes[1] = (*a).eType;
+				}
+				else if (PLY::EST_Blue == (*a).Semantic)
+				{
+					cnt++;
+					aiPositions[2] = _a;
+					aiTypes[2] = (*a).eType;
+				}
+				else if (PLY::EST_Alpha == (*a).Semantic)
+				{
+					cnt++;
+					aiPositions[3] = _a;
+					aiTypes[3] = (*a).eType;
+				}
+				if (4 == cnt)break; 
+			}
+			break;
+		}
+	}
+	// check whether we have a valid source for the vertex data
+	if (NULL != pcList && 0 != cnt)
+	{
+		pvOut->reserve(pcList->alInstances.size());
+		for (std::vector<ElementInstance>::const_iterator
+			i =  pcList->alInstances.begin();
+			i != pcList->alInstances.end();++i)
+		{
+			// convert the vertices to sp floats
+			aiColor4D vOut;
+			
+			if (0xFFFFFFFF == aiPositions[0])vOut.r = 0.0f;
+			else
+			{
+				vOut.r = NormalizeColorValue((*i).alProperties[
+					aiPositions[0]].avList.front(),aiTypes[0]);
+			}
+
+			if (0xFFFFFFFF == aiPositions[1])vOut.g = 0.0f;
+			else
+			{
+				vOut.g = NormalizeColorValue((*i).alProperties[
+					aiPositions[1]].avList.front(),aiTypes[1]);
+			}
+
+			if (0xFFFFFFFF == aiPositions[2])vOut.b = 0.0f;
+			else
+			{
+				vOut.b = NormalizeColorValue((*i).alProperties[
+					aiPositions[2]].avList.front(),aiTypes[2]);
+			}
+
+			// assume 1.0 for the alpha channel ifit is not set
+			if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f;
+			else
+			{
+				vOut.a = NormalizeColorValue((*i).alProperties[
+					aiPositions[3]].avList.front(),aiTypes[3]);
+			}
+
+			// and add them to our nice list
+			pvOut->push_back(vOut);
+		}
+	}
+	return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
+{
+	PLY::ElementInstanceList* pcList = NULL;
+	bool bOne = false;
+
+	// index of the vertex index list
+	unsigned int iProperty = 0xFFFFFFFF;
+	PLY::EDataType eType;
+	bool bIsTristrip = false;
+
+	// index of the material index property
+	unsigned int iMaterialIndex = 0xFFFFFFFF;
+	PLY::EDataType eType2;
+
+	// serach in the DOM for a face entry
+	unsigned int _i = 0;
+	for (std::vector<PLY::Element>::const_iterator 
+		i =  this->pcDOM->alElements.begin();
+		i != this->pcDOM->alElements.end();++i,++_i)
+	{
+		// face = unique number of vertex indices
+		if (PLY::EEST_Face == (*i).eSemantic)
+		{
+			pcList = &this->pcDOM->alElementData[_i];
+			unsigned int _a = 0;
+			for (std::vector<PLY::Property>::const_iterator 
+				a =  (*i).alProperties.begin();
+				a != (*i).alProperties.end();++a,++_a)
+			{
+				if (PLY::EST_VertexIndex == (*a).Semantic)
+				{
+					// must be a dynamic list!
+					if (!(*a).bIsList)continue;
+					iProperty	= _a;
+					bOne		= true;
+					eType		= (*a).eType;		
+				}
+				else if (PLY::EST_MaterialIndex == (*a).Semantic)
+				{
+					if ((*a).bIsList)continue;
+					iMaterialIndex	= _a;
+					bOne			= true;
+					eType2		= (*a).eType;		
+				}
+			}
+			break;
+		}
+		// triangle strip
+		// TODO: triangle strip and material index support???
+		else if (PLY::EEST_TriStrip == (*i).eSemantic)
+		{
+			// find a list property in this ...
+			pcList = &this->pcDOM->alElementData[_i];
+			unsigned int _a = 0;
+			for (std::vector<PLY::Property>::const_iterator 
+				a =  (*i).alProperties.begin();
+				a != (*i).alProperties.end();++a,++_a)
+			{
+				// must be a dynamic list!
+				if (!(*a).bIsList)continue;
+				iProperty	= _a;
+				bOne		= true;
+				bIsTristrip	= true;
+				eType		= (*a).eType;	
+				break;
+			}
+			break;
+		}
+	}
+	// check whether we have at least one per-face information set
+	if (pcList && bOne)
+	{
+		if (!bIsTristrip)
+		{
+			pvOut->reserve(pcList->alInstances.size());
+			for (std::vector<ElementInstance>::const_iterator 
+				i =  pcList->alInstances.begin();
+				i != pcList->alInstances.end();++i)
+			{
+				PLY::Face sFace;
+
+				// parse the list of vertex indices
+				if (0xFFFFFFFF != iProperty)
+				{
+					const unsigned int iNum = (*i).alProperties[iProperty].avList.size();
+					sFace.mIndices.resize(iNum);
+
+					std::list<PLY::PropertyInstance::ValueUnion>::const_iterator p = 
+						(*i).alProperties[iProperty].avList.begin();
+
+					for (unsigned int a = 0; a < iNum;++a,++p)
+					{
+						sFace.mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p,eType);
+					}
+				}
+
+				// parse the material index
+				if (0xFFFFFFFF != iMaterialIndex)
+				{
+					sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo<unsigned int>(
+						(*i).alProperties[iMaterialIndex].avList.front(),eType2);
+				}
+				pvOut->push_back(sFace);
+			}
+		}
+		else // triangle strips
+		{
+			// normally we have only one triangle strip instance where
+			// a value of -1 indicates a restart of the strip
+			for (std::vector<ElementInstance>::const_iterator
+				i =  pcList->alInstances.begin();
+				i != pcList->alInstances.end();++i)
+			{
+				int aiTable[2] = {-1,-1};
+				for (std::list<PLY::PropertyInstance::ValueUnion>::const_iterator 
+					a =  (*i).alProperties[iProperty].avList.begin();
+					a != (*i).alProperties[iProperty].avList.end();++a)
+				{
+					int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
+					if (-1 == p)
+					{
+						// restart the strip ...
+						aiTable[0] = aiTable[1] = -1;
+						continue;
+					}
+					if (-1 == aiTable[0])
+					{
+						aiTable[0] = p;
+						continue;
+					}
+					if (-1 == aiTable[1])
+					{
+						aiTable[1] = p;
+						continue;
+					}
+				
+					PLY::Face sFace;
+					sFace.mIndices.push_back((unsigned int)aiTable[0]);
+					sFace.mIndices.push_back((unsigned int)aiTable[1]);
+					sFace.mIndices.push_back((unsigned int)p);
+					pvOut->push_back(sFace);
+
+					aiTable[0] = aiTable[1];
+					aiTable[1] = p;
+				}
+			}
+		}
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void GetMaterialColor(const std::vector<PLY::PropertyInstance>& avList,
+					  unsigned int aiPositions[4], 
+					  PLY::EDataType aiTypes[4],
+					  aiColor4D* clrOut)
+{
+	ai_assert(NULL != clrOut);
+
+	if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f;
+	else
+	{
+		clrOut->r = NormalizeColorValue(avList[
+			aiPositions[0]].avList.front(),aiTypes[0]);
+	}
+
+	if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f;
+	else
+	{
+		clrOut->g = NormalizeColorValue(avList[
+			aiPositions[1]].avList.front(),aiTypes[1]);
+	}
+
+	if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f;
+	else
+	{
+		clrOut->b = NormalizeColorValue(avList[
+			aiPositions[2]].avList.front(),aiTypes[2]);
+	}
+
+	// assume 1.0 for the alpha channel ifit is not set
+	if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f;
+	else
+	{
+		clrOut->a = NormalizeColorValue(avList[
+			aiPositions[3]].avList.front(),aiTypes[3]);
+	}
+
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadMaterial(std::vector<MaterialHelper*>* pvOut)
+{
+	// diffuse[4], specular[4], ambient[4]
+	// rgba order
+	unsigned int aaiPositions[3][4] = {
+
+		{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+		{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+		{0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+	};
+
+	// dto.
+	PLY::EDataType aaiTypes[3][4];
+	PLY::ElementInstanceList* pcList = NULL;
+
+	unsigned int iPhong = 0xFFFFFFFF;
+	PLY::EDataType ePhong;
+
+	unsigned int iOpacity = 0xFFFFFFFF;
+	PLY::EDataType eOpacity;
+
+	// serach in the DOM for a vertex entry
+	unsigned int _i = 0;
+	for (std::vector<PLY::Element>::const_iterator
+		i =  this->pcDOM->alElements.begin();
+		i != this->pcDOM->alElements.end();++i,++_i)
+	{
+		if (PLY::EEST_Material == (*i).eSemantic)
+		{
+			pcList = &this->pcDOM->alElementData[_i];
+
+			// now check whether which coordinate sets are available
+			unsigned int _a = 0;
+			for (std::vector<PLY::Property>::const_iterator
+				a =  (*i).alProperties.begin();
+				a != (*i).alProperties.end();++a,++_a)
+			{
+				if ((*a).bIsList)continue;
+
+				// pohng specularity      -----------------------------------
+				if (PLY::EST_PhongPower == (*a).Semantic)
+				{
+					iPhong		= _a;
+					ePhong		= (*a).eType;
+				}
+
+				// general opacity        -----------------------------------
+				if (PLY::EST_Opacity == (*a).Semantic)
+				{
+					iOpacity		= _a;
+					eOpacity		= (*a).eType;
+				}
+
+				// diffuse color channels -----------------------------------
+				if (PLY::EST_DiffuseRed == (*a).Semantic)
+				{
+					aaiPositions[0][0]	= _a;
+					aaiTypes[0][0]		= (*a).eType;
+				}
+				else if (PLY::EST_DiffuseGreen == (*a).Semantic)
+				{
+					aaiPositions[0][1]	= _a;
+					aaiTypes[0][1]		= (*a).eType;
+				}
+				else if (PLY::EST_DiffuseBlue == (*a).Semantic)
+				{
+					aaiPositions[0][2]	= _a;
+					aaiTypes[0][2]		= (*a).eType;
+				}
+				else if (PLY::EST_DiffuseAlpha == (*a).Semantic)
+				{
+					aaiPositions[0][3]	= _a;
+					aaiTypes[0][3]		= (*a).eType;
+				}
+				// specular color channels -----------------------------------
+				else if (PLY::EST_SpecularRed == (*a).Semantic)
+				{
+					aaiPositions[1][0]	= _a;
+					aaiTypes[1][0]		= (*a).eType;
+				}
+				else if (PLY::EST_SpecularGreen == (*a).Semantic)
+				{
+					aaiPositions[1][1]	= _a;
+					aaiTypes[1][1]		= (*a).eType;
+				}
+				else if (PLY::EST_SpecularBlue == (*a).Semantic)
+				{
+					aaiPositions[1][2]	= _a;
+					aaiTypes[1][2]		= (*a).eType;
+				}
+				else if (PLY::EST_SpecularAlpha == (*a).Semantic)
+				{
+					aaiPositions[1][3]	= _a;
+					aaiTypes[1][3]		= (*a).eType;
+				}
+				// ambient color channels -----------------------------------
+				else if (PLY::EST_AmbientRed == (*a).Semantic)
+				{
+					aaiPositions[2][0]	= _a;
+					aaiTypes[2][0]		= (*a).eType;
+				}
+				else if (PLY::EST_AmbientGreen == (*a).Semantic)
+				{
+					aaiPositions[2][1]	= _a;
+					aaiTypes[2][1]		= (*a).eType;
+				}
+				else if (PLY::EST_AmbientBlue == (*a).Semantic)
+				{
+					aaiPositions[22][2]	= _a;
+					aaiTypes[2][2]		= (*a).eType;
+				}
+				else if (PLY::EST_AmbientAlpha == (*a).Semantic)
+				{
+					aaiPositions[2][3]	= _a;
+					aaiTypes[2][3]		= (*a).eType;
+				}
+			}
+			break;
+		}
+	}
+	// check whether we have a valid source for the material data
+	if (NULL != pcList)
+	{
+		for (std::vector<ElementInstance>::const_iterator
+			i =  pcList->alInstances.begin();
+			i != pcList->alInstances.end();++i)
+		{
+			aiColor4D clrOut;
+			MaterialHelper* pcHelper = new MaterialHelper();
+	
+			// build the diffuse material color
+			GetMaterialColor((*i).alProperties,aaiPositions[0],aaiTypes[0],&clrOut);
+			pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_DIFFUSE);
+
+			// build the specular material color
+			GetMaterialColor((*i).alProperties,aaiPositions[1],aaiTypes[1],&clrOut);
+			pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_SPECULAR);
+
+			// build the ambient material color
+			GetMaterialColor((*i).alProperties,aaiPositions[2],aaiTypes[2],&clrOut);
+			pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_AMBIENT);
+
+			// handle phong power and shading mode
+			int iMode;
+			if (0xFFFFFFFF != iPhong)
+			{
+				float fSpec = PLY::PropertyInstance::ConvertTo<float>(
+					(*i).alProperties[iPhong].avList.front(),ePhong);
+
+				// if shininess is 0 (and the pow() calculation would therefore always
+				// become 1, not depending on the angle) use gouraud lighting
+				if (0.0f != fSpec)
+				{
+
+					// scale this with 15 ... hopefully this is correct
+					fSpec += 15;
+
+					pcHelper->AddProperty<float>(&fSpec, 1, AI_MATKEY_SHININESS);
+
+					iMode = (int)aiShadingMode_Phong;
+				}
+				else iMode = (int)aiShadingMode_Gouraud;
+			}
+			else iMode = (int)aiShadingMode_Gouraud;
+			pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+			// handle opacity
+			if (0xFFFFFFFF != iOpacity)
+			{
+				float fOpacity = PLY::PropertyInstance::ConvertTo<float>(
+					(*i).alProperties[iPhong].avList.front(),eOpacity);
+
+				pcHelper->AddProperty<float>(&fOpacity, 1, AI_MATKEY_OPACITY);
+			}
+
+			// add the newly created material instance to the list
+			pvOut->push_back(pcHelper);
+		}
+	}
+	return;
+}

+ 99 - 0
code/PlyLoader.h

@@ -0,0 +1,99 @@
+/** @file Definition of the .ply importer class. */
+#ifndef AI_PLYLOADER_H_INCLUDED
+#define AI_PLYLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+#include "PlyParser.h"
+
+namespace Assimp
+{
+	class MaterialHelper;
+
+
+	using namespace PLY;
+
+	// ---------------------------------------------------------------------------
+	/** Used to load PLY files
+	*/
+	class PLYImporter : public BaseImporter
+	{
+		friend class Importer;
+
+	protected:
+		/** Constructor to be privately used by Importer */
+		PLYImporter();
+
+		/** Destructor, private as well */
+		~PLYImporter();
+
+	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:
+
+		// -------------------------------------------------------------------
+		/** 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:
+
+
+		// -------------------------------------------------------------------
+		/** Extract vertices from the DOM
+		*/
+		void LoadVertices(std::vector<aiVector3D>* pvOut,bool p_bNormals = false);
+
+		// -------------------------------------------------------------------
+		/** Extract vertex color channels
+		*/
+		void LoadVertexColor(std::vector<aiColor4D>* pvOut);
+
+		// -------------------------------------------------------------------
+		/** Extract a face list from the DOM
+		*/
+		void LoadFaces(std::vector<PLY::Face>* pvOut);
+
+		// -------------------------------------------------------------------
+		/** Extract a material list from the DOM
+		*/
+		void LoadMaterial(std::vector<MaterialHelper*>* pvOut);
+
+
+		// -------------------------------------------------------------------
+		/** Validate material indices, replace default material identifiers
+		*/
+		void ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+			std::vector<MaterialHelper*>* avMaterials);
+
+
+		// -------------------------------------------------------------------
+		/** Convert all meshes into our ourer representation
+		*/
+		void ConvertMeshes(std::vector<PLY::Face>* avFaces,
+			const std::vector<aiVector3D>* avPositions,
+			const std::vector<aiVector3D>* avNormals,
+			const std::vector<aiColor4D>* avColors,
+			const std::vector<MaterialHelper*>* avMaterials,
+			std::vector<aiMesh*>* avOut);
+
+
+		/** Buffer to hold the loaded file */
+		unsigned char* mBuffer;
+
+		/** Document object model representation extracted from the file */
+		PLY::DOM* pcDOM;
+	};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 874 - 0
code/PlyParser.cpp

@@ -0,0 +1,874 @@
+/** @file Implementation of the PLY parser class */
+#include "PLYLoader.h"
+#include "MaterialSystem.h"
+#include "fast_atof.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;
+
+
+// ------------------------------------------------------------------------------------------------
+PLY::EDataType PLY::Property::ParseDataType(const char* p_szIn,const char** p_szOut)
+{
+	PLY::EDataType eOut = PLY::EDT_INVALID;
+
+	if (0 == ASSIMP_strincmp(p_szIn,"char",4) ||
+		0 == ASSIMP_strincmp(p_szIn,"int8",4))
+	{
+		p_szIn+=4;
+		eOut = PLY::EDT_Char;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"uchar",5) ||
+			 0 == ASSIMP_strincmp(p_szIn,"uint8",5))
+	{
+		p_szIn+=5;
+		eOut = PLY::EDT_UChar;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"short",5) ||
+			 0 == ASSIMP_strincmp(p_szIn,"int16",5))
+	{
+		p_szIn+=5;
+		eOut = PLY::EDT_Short;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"ushort",6) ||
+			 0 == ASSIMP_strincmp(p_szIn,"uint16",6))
+	{
+		p_szIn+=6;
+		eOut = PLY::EDT_UShort;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"int32",5))
+	{
+		p_szIn+=5;
+		eOut = PLY::EDT_Int;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"uint32",6))
+	{
+		p_szIn+=6;
+		eOut = PLY::EDT_UInt;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"int",3))
+	{
+		p_szIn+=3;
+		eOut = PLY::EDT_Int;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"uint",4))
+	{
+		p_szIn+=4;
+		eOut = PLY::EDT_UInt;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"float32",7))
+	{
+		p_szIn+=7;
+		eOut = PLY::EDT_Float;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"float",5))
+	{
+		p_szIn+=5;
+		eOut = PLY::EDT_Float;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"float64",7))
+	{
+		p_szIn+=7;
+		eOut = PLY::EDT_Double;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"double64",8))
+	{
+		p_szIn+=8;
+		eOut = PLY::EDT_Double;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"double",6))
+	{
+		p_szIn+=6;
+		eOut = PLY::EDT_Double;
+	}
+	// either end of line or space, but no other characters allowed
+	if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
+	{
+		eOut = PLY::EDT_INVALID;
+	}
+	*p_szOut = p_szIn;
+	return eOut;
+}
+// ------------------------------------------------------------------------------------------------
+PLY::ESemantic PLY::Property::ParseSemantic(const char* p_szIn,const char** p_szOut)
+{
+	PLY::ESemantic eOut = PLY::EST_INVALID;
+	if (0 == ASSIMP_strincmp(p_szIn,"x",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_XCoord;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"y",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_YCoord;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"z",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_ZCoord;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"red",3))
+	{
+		p_szIn+=3;
+		eOut = PLY::EST_Red;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"green",4))
+	{
+		p_szIn+=5;
+		eOut = PLY::EST_Green;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"blue",4))
+	{
+		p_szIn+=4;
+		eOut = PLY::EST_Blue;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"alpha",5))
+	{
+		p_szIn+=5;
+		eOut = PLY::EST_Alpha;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"vertex_index",12))
+	{
+		p_szIn+=12;
+		eOut = PLY::EST_VertexIndex;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"vertex_indices",14))
+	{
+		p_szIn+=14;
+		eOut = PLY::EST_VertexIndex;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"material_index",14))
+	{
+		p_szIn+=14;
+		eOut = PLY::EST_MaterialIndex;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"ambient_red",11))
+	{
+		p_szIn+=11;
+		eOut = PLY::EST_AmbientRed;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"ambient_green",13))
+	{
+		p_szIn+=13;
+		eOut = PLY::EST_AmbientGreen;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"ambient_blue",12))
+	{
+		p_szIn+=12;
+		eOut = PLY::EST_AmbientBlue;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"ambient_alpha",13))
+	{
+		p_szIn+=13;
+		eOut = PLY::EST_AmbientAlpha;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_red",11))
+	{
+		p_szIn+=11;
+		eOut = PLY::EST_DiffuseRed;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_green",13))
+	{
+		p_szIn+=13;
+		eOut = PLY::EST_DiffuseGreen;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_blue",12))
+	{
+		p_szIn+=12;
+		eOut = PLY::EST_DiffuseBlue;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"diffuse_alpha",13))
+	{
+		p_szIn+=13;
+		eOut = PLY::EST_DiffuseAlpha;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"specular_red",12))
+	{
+		p_szIn+=12;
+		eOut = PLY::EST_SpecularRed;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"specular_green",14))
+	{
+		p_szIn+=14;
+		eOut = PLY::EST_SpecularGreen;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"specular_blue",13))
+	{
+		p_szIn+=13;
+		eOut = PLY::EST_SpecularBlue;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"specular_alpha",14))
+	{
+		p_szIn+=14;
+		eOut = PLY::EST_SpecularAlpha;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"opacity",7))
+	{
+		p_szIn+=7;
+		eOut = PLY::EST_Opacity;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"specular_power",6))
+	{
+		p_szIn+=7;
+		eOut = PLY::EST_PhongPower;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"r",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_Red;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"g",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_Green;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"b",1))
+	{
+		p_szIn++;
+		eOut = PLY::EST_Blue;
+	}
+	else
+	{
+		// ... find the next space or new line
+		while (*p_szIn != ' ' && *p_szIn != '\t'  && 
+			   *p_szIn != '\r' && *p_szIn != '\0' && *p_szIn != '\n')p_szIn++;
+	}
+	// either end of line or space, but no other characters allowed
+	if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
+	{
+		eOut = PLY::EST_INVALID;
+	}
+	*p_szOut = p_szIn;
+	return eOut;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::Property::ParseProperty (const char* p_szIn, const char** p_szOut, PLY::Property* pOut)
+{
+	// Forms supported:
+	// "property float x"
+	// "property list uchar int vertex_index"
+	*p_szOut = p_szIn;
+
+	// skip leading spaces
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+
+	// skip the "property" string at the beginning
+	if (0 != ASSIMP_strincmp(p_szIn,"property",8) || !IsSpace(*(p_szIn+8)))
+	{
+		// seems not to be a valid property entry
+		return false;
+	}
+	// get next word
+	p_szIn += 9;
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+
+	if (0 == ASSIMP_strincmp(p_szIn,"list",4) && IsSpace(*(p_szIn+4)))
+	{
+		pOut->bIsList = true;
+
+		// seems to be a list.
+		p_szIn += 5;
+		if(EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
+		{
+			// unable to parse list size data type
+			SkipLine(p_szIn,&p_szIn);
+			*p_szOut = p_szIn;
+			return false;
+		}
+		if (!SkipSpaces(p_szIn,&p_szIn))return false;
+		if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
+		{
+			// unable to parse list data type
+			SkipLine(p_szIn,&p_szIn);
+			*p_szOut = p_szIn;
+			return false;
+		}
+	}
+	else
+	{
+		if(EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(p_szIn, &p_szIn)))
+		{
+			// unable to parse data type. Skip the property
+			SkipLine(p_szIn,&p_szIn);
+			*p_szOut = p_szIn;
+			return false;
+		}
+	}
+	
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+	const char* szCur = p_szIn;
+	pOut->Semantic = PLY::Property::ParseSemantic(p_szIn, &p_szIn);
+
+	if (PLY::EST_INVALID == pOut->Semantic)
+	{
+		// store the name of the semantic
+		uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur;
+
+		pOut->szName = std::string(szCur,iDiff);
+	}
+
+	SkipSpacesAndLineEnd(p_szIn,&p_szIn);
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+PLY::EElementSemantic PLY::Element::ParseSemantic(const char* p_szIn,const char** p_szOut)
+{
+	PLY::EElementSemantic eOut = PLY::EEST_INVALID;
+	if (0 == ASSIMP_strincmp(p_szIn,"vertex",6))
+	{
+		p_szIn+=6;
+		eOut = PLY::EEST_Vertex;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"face",4))
+	{
+		p_szIn+=4;
+		eOut = PLY::EEST_Face;
+	}
+#if 0
+	else if (0 == ASSIMP_strincmp(p_szIn,"range_grid",10))
+	{
+		p_szIn+=10;
+		eOut = PLY::EEST_Face;
+	}
+#endif
+	else if (0 == ASSIMP_strincmp(p_szIn,"tristrips",9))
+	{
+		p_szIn+=9;
+		eOut = PLY::EEST_TriStrip;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"edge",4))
+	{
+		p_szIn+=4;
+		eOut = PLY::EEST_Edge;
+	}
+	else if (0 == ASSIMP_strincmp(p_szIn,"material",8))
+	{
+		p_szIn+=8;
+		eOut = PLY::EEST_Material;
+	}
+
+	// either end of line or space, but no other characters allowed
+	if (!(IsSpace(*p_szIn) || IsLineEnd(*p_szIn)))
+	{
+		eOut = PLY::EEST_INVALID;
+	}
+	*p_szOut = p_szIn;
+	return eOut;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::Element::ParseElement (const char* p_szIn, const char** p_szOut,
+								 PLY::Element* pOut)
+{
+	// Example format: "element vertex 8"
+	*p_szOut = p_szIn;
+
+	// skip leading spaces
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+
+	// skip the "element" string at the beginning
+	if (0 != ASSIMP_strincmp(p_szIn,"element",7) || !IsSpace(*(p_szIn+7)))
+	{
+		// seems not to be a valid property entry
+		return false;
+	}
+	// get next word
+	p_szIn += 8;
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+
+	// parse the semantic of the element
+	const char* szCur = p_szIn;
+	pOut->eSemantic = PLY::Element::ParseSemantic(p_szIn,&p_szIn);
+
+	if (PLY::EEST_INVALID == pOut->eSemantic)
+	{
+		// store the name of the semantic
+		uintptr_t iDiff = (uintptr_t)p_szIn - (uintptr_t)szCur;
+
+		pOut->szName = std::string(szCur,iDiff);
+	}
+
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+	
+	//parse the number of occurences of this element
+	pOut->NumOccur = strtol10(p_szIn,&p_szIn);
+
+	// go to the next line
+	SkipSpacesAndLineEnd(p_szIn,&p_szIn);
+
+	// now parse all properties of the element
+	while(true)
+	{
+		// skip all comments
+		PLY::DOM::SkipComments(p_szIn,&p_szIn);
+
+		Property prop;
+		if(!PLY::Property::ParseProperty(p_szIn,&p_szIn,&prop))break;
+
+		// add the property to the property list
+		pOut->alProperties.push_back(prop);
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::SkipComments (const char* p_szIn,const char** p_szOut)
+{
+	*p_szOut = p_szIn;
+
+	// skip spaces
+	if (!SkipSpaces(p_szIn,&p_szIn))return false;
+
+	if (0 == ASSIMP_strincmp(p_szIn,"comment",7))
+	{
+		p_szIn += 7;
+
+		SkipLine(p_szIn,&p_szIn);
+
+		SkipComments(p_szIn,&p_szIn);
+		*p_szOut = p_szIn;
+		return true;
+	}
+	return false;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseHeader (const char* p_szIn,const char** p_szOut)
+{
+	// after ply and format line
+	*p_szOut = p_szIn;
+
+	// parse all elements
+	while (true)
+	{
+		// skip all comments
+		PLY::DOM::SkipComments(p_szIn,&p_szIn);
+
+		Element out;
+		if(PLY::Element::ParseElement(p_szIn,&p_szIn,&out))
+		{
+			// add the element to the list of elements
+			this->alElements.push_back(out);
+		}
+		else if (0 == ASSIMP_strincmp(p_szIn,"end_header",10) && IsSpaceOrNewLine(*(p_szIn+10)))
+		{
+			// we have reached the end of the header
+			p_szIn += 11;
+			break;
+		}
+		// ignore unknown header elements
+	}
+	SkipSpacesAndLineEnd(p_szIn,&p_szIn);
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceLists (const char* p_szIn,const char** p_szOut)
+{
+	this->alElementData.resize(this->alElements.size());
+
+	std::vector<PLY::Element>::const_iterator i			=  this->alElements.begin();
+	std::vector<PLY::ElementInstanceList>::iterator a	=  this->alElementData.begin();
+
+	// parse all element instances
+	for (;i != this->alElements.end();++i,++a)
+	{
+		*a = PLY::ElementInstanceList(&(*i)); // reserve enough storage
+		PLY::ElementInstanceList::ParseInstanceList(p_szIn,&p_szIn,&(*i),&(*a));
+	}
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceListsBinary (const char* p_szIn,const char** p_szOut,bool p_bBE)
+{
+	this->alElementData.resize(this->alElements.size());
+
+	std::vector<PLY::Element>::const_iterator i			=  this->alElements.begin();
+	std::vector<PLY::ElementInstanceList>::iterator a	=  this->alElementData.begin();
+
+	// parse all element instances
+	for (;i != this->alElements.end();++i,++a)
+	{
+		*a = PLY::ElementInstanceList(&(*i)); // reserve enough storage
+		PLY::ElementInstanceList::ParseInstanceListBinary(p_szIn,&p_szIn,&(*i),&(*a),p_bBE);
+	}
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstanceBinary (const char* p_szIn,DOM* p_pcOut,bool p_bBE)
+{
+	if(!p_pcOut->ParseHeader(p_szIn,&p_szIn))
+	{
+		return false;
+	}
+	if(!p_pcOut->ParseElementInstanceListsBinary(p_szIn,&p_szIn,p_bBE))
+	{
+		return false;
+	}
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstance (const char* p_szIn,DOM* p_pcOut)
+{
+	if(!p_pcOut->ParseHeader(p_szIn,&p_szIn))
+	{
+		return false;
+	}
+	if(!p_pcOut->ParseElementInstanceLists(p_szIn,&p_szIn))
+	{
+		return false;
+	}
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceList (const char* p_szIn,const char** p_szOut,
+	 const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut)
+{
+	if (EEST_INVALID == pcElement->eSemantic)
+	{
+		// if the element has an unknown semantic we can skip all lines
+		// However, there could be comments
+		for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+		{
+			PLY::DOM::SkipComments(p_szIn,&p_szIn);
+			SkipLine(p_szIn,&p_szIn);
+		}
+	}
+	else
+	{
+		// be sure to have enough storage
+		p_pcOut->alInstances.resize(pcElement->NumOccur);
+		for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+		{
+			PLY::DOM::SkipComments(p_szIn,&p_szIn);
+
+			PLY::ElementInstance out;
+			PLY::ElementInstance::ParseInstance(p_szIn, &p_szIn,pcElement, &out);
+			// add it to the list
+			p_pcOut->alInstances[i] = out;
+		}
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceListBinary (const char* p_szIn,const char** p_szOut,
+	 const PLY::Element* pcElement, PLY::ElementInstanceList* p_pcOut,bool p_bBE)
+{
+	// we can add special handling code for unknown element semantics since
+	// we can't skip it as a whole block (we don't know its exact size
+	// due to the fact that lists could be contained in the property list 
+	// of the unknown element)
+	for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+	{
+		PLY::ElementInstance out;
+		PLY::ElementInstance::ParseInstanceBinary(p_szIn, &p_szIn,pcElement, &out, p_bBE);
+		// add it to the list
+		p_pcOut->alInstances[i] = out;
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstance (const char* p_szIn,const char** p_szOut,
+	const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut)
+{
+	if (!SkipSpaces(p_szIn, &p_szIn))return false;
+
+	p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+	*p_szOut = p_szIn;
+	std::vector<PLY::PropertyInstance>::iterator		i =  p_pcOut->alProperties.begin();
+	std::vector<PLY::Property>::const_iterator			a =  pcElement->alProperties.begin();
+	for (;i != p_pcOut->alProperties.end();++i,++a)
+	{
+		if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i))))
+		{
+			// skip the rest of the instance
+			SkipLine(p_szIn, &p_szIn);
+
+			PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
+			(*i).avList.push_back(v);
+		}
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
+	const PLY::Element* pcElement, PLY::ElementInstance* p_pcOut, bool p_bBE)
+{
+	p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+	*p_szOut = p_szIn;
+	std::vector<PLY::PropertyInstance>::iterator		i =  p_pcOut->alProperties.begin();
+	std::vector<PLY::Property>::const_iterator			a =  pcElement->alProperties.begin();
+	for (;i != p_pcOut->alProperties.end();++i,++a)
+	{
+		if(!(PLY::PropertyInstance::ParseInstance(p_szIn, &p_szIn,&(*a),&(*i))))
+		{
+			PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
+			(*i).avList.push_back(v);
+		}
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstance (const char* p_szIn,const char** p_szOut,
+	const PLY::Property* prop, PLY::PropertyInstance* p_pcOut)
+{
+	*p_szOut = p_szIn;
+
+	// skip spaces at the beginning
+	if (!SkipSpaces(p_szIn, &p_szIn))return false;
+
+	if (prop->bIsList)
+	{
+		// parse the number of elements in the list
+		PLY::PropertyInstance::ValueUnion v;
+		PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eFirstType,&v);
+
+		// convert to unsigned int
+		unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+		// parse all list elements
+		for (unsigned int i = 0; i < iNum;++i)
+		{
+			if (!SkipSpaces(p_szIn, &p_szIn))return false;
+
+			PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v);
+			p_pcOut->avList.push_back(v);
+		}
+	}
+	else
+	{
+		// parse the property
+		PLY::PropertyInstance::ValueUnion v;
+		PLY::PropertyInstance::ParseValue(p_szIn, &p_szIn,prop->eType,&v);
+		p_pcOut->avList.push_back(v);
+	}
+	SkipSpacesAndLineEnd(p_szIn, &p_szIn);
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
+	const PLY::Property* prop, PLY::PropertyInstance* p_pcOut,bool p_bBE)
+{
+	*p_szOut = p_szIn;
+
+	if (prop->bIsList)
+	{
+		// parse the number of elements in the list
+		PLY::PropertyInstance::ValueUnion v;
+		PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eFirstType,&v,p_bBE);
+
+		// convert to unsigned int
+		unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+		// parse all list elements
+		for (unsigned int i = 0; i < iNum;++i)
+		{
+			PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE);
+			p_pcOut->avList.push_back(v);
+		}
+	}
+	else
+	{
+		// parse the property
+		PLY::PropertyInstance::ValueUnion v;
+		PLY::PropertyInstance::ParseValueBinary(p_szIn, &p_szIn,prop->eType,&v,p_bBE);
+		p_pcOut->avList.push_back(v);
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(
+	PLY::EDataType eType)
+{
+	PLY::PropertyInstance::ValueUnion out;
+
+	switch (eType)
+	{
+	case EDT_Float:
+		out.fFloat = 0.0f;
+		return out;
+
+	case EDT_Double:
+		out.fDouble = 0.0;
+		return out;
+	};
+	out.iUInt = 0;
+	return out;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValue(const char* p_szIn,const char** p_szOut,
+	PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out)
+{
+	*p_szOut = p_szIn;
+
+	switch (eType)
+	{
+	case EDT_UInt:
+	case EDT_UShort:
+	case EDT_UChar:
+
+		// simply parse in a full uint
+		out->iUInt = (uint32_t)strtol10(p_szIn, &p_szIn);
+
+		break;
+
+	case EDT_Int:
+	case EDT_Short:
+	case EDT_Char:
+
+		{
+		// simply parse in a full int
+		// Take care of the sign at the beginning
+		bool bMinus = false;
+		if (*p_szIn == '-')
+		{
+			p_szIn++;
+			bMinus = true;
+		}
+		out->iInt = (int32_t)strtol10(p_szIn, &p_szIn);
+		if (bMinus)out->iInt *= -1;
+
+		break;
+		}
+
+	case EDT_Float:
+
+		// parse a simple float
+		p_szIn = fast_atof_move(p_szIn,out->fFloat);
+		break;
+
+	case EDT_Double:
+
+		// Parse a double float. .. TODO: support this
+		float f;
+		p_szIn = fast_atof_move(p_szIn,f);
+		out->fDouble = (double)f;
+
+	default:
+		return false;
+	}
+	*p_szOut = p_szIn;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValueBinary(const char* p_szIn,const char** p_szOut,
+	PLY::EDataType eType,PLY::PropertyInstance::ValueUnion* out, bool p_bBE)
+{
+	*p_szOut = p_szIn;
+
+	switch (eType)
+	{
+	case EDT_UInt:
+		out->iUInt = (uint32_t)*((uint32_t*)p_szIn);
+		p_szIn += 4;
+		
+		if (p_bBE)
+		{
+			std::swap(((unsigned char*)(&out->iUInt))[0],((unsigned char*)(&out->iUInt))[3]);
+			std::swap(((unsigned char*)(&out->iUInt))[1],((unsigned char*)(&out->iUInt))[2]);
+		}
+		break;
+
+	case EDT_UShort:
+		{
+		uint16_t i = *((uint16_t*)p_szIn);
+		if (p_bBE)
+			{
+			std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]);
+			}
+		out->iUInt = (uint32_t)i;
+		p_szIn += 2;
+		break;
+		}
+
+	case EDT_UChar:
+		{
+		uint8_t i = *((uint8_t*)p_szIn);
+		out->iUInt = (uint32_t)i;
+		p_szIn += 2;
+		break;
+		}
+
+	case EDT_Int:
+		out->iInt = *((int32_t*)p_szIn);
+		p_szIn += 4;
+		
+		if (p_bBE)
+		{
+			std::swap(((unsigned char*)(&out->iInt))[0],((unsigned char*)(&out->iInt))[3]);
+			std::swap(((unsigned char*)(&out->iInt))[1],((unsigned char*)(&out->iInt))[2]);
+		}
+		break;
+
+	case EDT_Short:
+		{
+		int16_t i = *((int16_t*)p_szIn);
+		if (p_bBE)
+			{
+			std::swap(((unsigned char*)(&i))[0],((unsigned char*)(&i))[1]);
+			}
+		out->iInt = (int32_t)i;
+		p_szIn += 2;
+		break;
+		}
+
+	case EDT_Char:
+		out->iInt = (int32_t)*((int8_t*)p_szIn);
+		p_szIn += 1;
+		break;
+
+	case EDT_Float:
+		if (p_bBE)
+		{
+			union {char szArray[4]; float fValue; } _X;
+
+			_X.szArray[0] = ((unsigned char*)p_szIn)[3];
+			_X.szArray[1] = ((unsigned char*)p_szIn)[2];
+			_X.szArray[2] = ((unsigned char*)p_szIn)[1];
+			_X.szArray[3] = ((unsigned char*)p_szIn)[0];
+			out->fFloat = _X.fValue;
+		}
+		else out->fFloat = *((float*)p_szIn);
+		p_szIn += 4;
+		break;
+
+	case EDT_Double:
+		if (p_bBE)
+		{
+			union {char szArray[8]; double fValue; } _X;
+
+			_X.szArray[0] = ((unsigned char*)p_szIn)[7];
+			_X.szArray[1] = ((unsigned char*)p_szIn)[6];
+			_X.szArray[2] = ((unsigned char*)p_szIn)[5];
+			_X.szArray[3] = ((unsigned char*)p_szIn)[4];
+			_X.szArray[4] = ((unsigned char*)p_szIn)[3];
+			_X.szArray[5] = ((unsigned char*)p_szIn)[2];
+			_X.szArray[6] = ((unsigned char*)p_szIn)[1];
+			_X.szArray[7] = ((unsigned char*)p_szIn)[0];
+			out->fDouble = _X.fValue;
+		}
+		else out->fDouble = *((double*)p_szIn);
+		p_szIn += 8;
+		break;
+
+	default:
+		return false;
+	}
+	*p_szOut = p_szIn;
+	return true;
+}

+ 483 - 0
code/PlyParser.h

@@ -0,0 +1,483 @@
+/** @file Defines the helper data structures for importing PLY files  */
+#ifndef AI_PLYFILEHELPER_H_INC
+#define AI_PLYFILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <list>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+namespace Assimp
+{
+
+// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
+// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf
+// http://www.okino.com/conv/exp_ply.htm
+namespace PLY
+{
+
+
+// ---------------------------------------------------------------------------------
+/*
+name        type        number of bytes
+---------------------------------------
+char       character                 1
+uchar      unsigned character        1
+short      short integer             2
+ushort     unsigned short integer    2
+int        integer                   4
+uint       unsigned integer          4
+float      single-precision float    4
+double     double-precision float    8
+
+int8
+int16
+uint8 ... forms are also used
+*/
+enum EDataType
+{
+	EDT_Char = 0x0u,
+	EDT_UChar,
+	EDT_Short,
+	EDT_UShort,
+	EDT_Int,
+	EDT_UInt,
+	EDT_Float,
+	EDT_Double,
+
+	// Marks invalid entries
+	EDT_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Specifies semantics for PLY element properties
+ *
+ *	Semantics define the usage of a property, e.g. x coordinate
+*/
+enum ESemantic
+{
+	//! vertex position x coordinate
+	EST_XCoord = 0x0u,
+	//! vertex position x coordinate
+	EST_YCoord,
+	//! vertex position x coordinate
+	EST_ZCoord,
+
+	//! vertex normal x coordinate
+	EST_XNormal,
+	//! vertex normal y coordinate
+	EST_YNormal,
+	//! vertex normal z coordinate
+	EST_ZNormal,
+	
+	//! vertex colors, red channel
+	EST_Red,		
+	//! vertex colors, green channel
+	EST_Green,
+	//! vertex colors, blue channel
+	EST_Blue,
+	//! vertex colors, alpha channel
+	EST_Alpha,
+
+	//! vertex index list 
+	EST_VertexIndex,
+
+	//! texture index 
+	EST_TextureIndex,
+
+	//! texture coordinates (stored as element of a face)
+	EST_TextureCoordinates,
+
+	//! material index 
+	EST_MaterialIndex,
+
+	//! ambient color, red channel
+	EST_AmbientRed,
+	//! ambient color, green channel
+	EST_AmbientGreen,
+	//! ambient color, blue channel
+	EST_AmbientBlue,
+	//! ambient color, alpha channel
+	EST_AmbientAlpha,
+
+	//! diffuse color, red channel
+	EST_DiffuseRed,
+	//! diffuse color, green channel
+	EST_DiffuseGreen,
+	//! diffuse color, blue channel
+	EST_DiffuseBlue,
+	//! diffuse color, alpha channel
+	EST_DiffuseAlpha,
+
+	//! specular color, red channel
+	EST_SpecularRed,
+	//! specular color, green channel
+	EST_SpecularGreen,
+	//! specular color, blue channel
+	EST_SpecularBlue,
+	//! specular color, alpha channel
+	EST_SpecularAlpha,
+
+	//! specular power for phong shading
+	EST_PhongPower,
+
+	//! opacity between 0 and 1
+	EST_Opacity,
+
+	//! Marks invalid entries
+	EST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Specifies semantics for PLY elements
+ *
+ *	Semantics define the usage of an element, e.g. vertex or material
+*/
+enum EElementSemantic
+{
+	//! The element is a vertex
+	EEST_Vertex	= 0x0u,
+
+	//! The element is a face description (index table)
+	EEST_Face,
+
+	//! The element is a tristrip description (index table)
+	EEST_TriStrip,
+
+	//! The element is an edge description (ignored)
+	EEST_Edge,
+
+	//! The element is a material description 
+	EEST_Material,
+
+	//! Marks invalid entries
+	EEST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Helper class for a property in a PLY file.
+ *
+ *	This can e.g. be a part of the vertex declaration
+ */
+class Property
+{
+public:
+
+	//! Default constructor
+	Property()
+		: eType (EDT_Int), bIsList(false), eFirstType(EDT_UChar)
+	{}
+
+	//!	Data type of the property
+	EDataType eType;
+
+	//!	Semantical meaning of the property
+	ESemantic Semantic;
+
+	//! Of the semantic of the property could not be parsed:
+	//! Contains the semantic specified in the file
+	std::string szName;
+
+	//!	Specifies whether the data type is a list where
+	//! the first element specifies the size of the list
+	bool bIsList;
+	EDataType eFirstType;
+
+	//! Parse a property from a string. The end of the
+	//! string is either '\n', '\r' or '\0'. Return valie is false
+	//! if the input string is NOT a valid property (E.g. does
+	//! not start with the "property" keyword)
+	static bool ParseProperty (const char* p_szIn, const char** p_szOut, 
+		Property* pOut);
+
+	//! Parse a data type from a string
+	static EDataType ParseDataType(const char* p_szIn,const char** p_szOut);
+
+	//! Parse a semantic from a string
+	static ESemantic ParseSemantic(const char* p_szIn,const char** p_szOut);
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Helper class for an element in a PLY file.
+ *
+ *	This can e.g. be the vertex declaration. Elements contain a
+ *	well-defined number of properties.
+ */
+class Element
+{
+public:
+
+	//! Default constructor
+	Element()
+		: NumOccur(0), eSemantic (EEST_INVALID)
+	{}
+
+	//! List of properties assigned to the element
+	//! std::vector to support operator[]
+	std::vector<Property> alProperties;
+
+	//! Semantic of the element
+	EElementSemantic eSemantic;
+
+	//! Of the semantic of the element could not be parsed:
+	//! Contains the semantic specified in the file
+	std::string szName;
+
+	//! How many times will the element occur?
+	unsigned int NumOccur;
+
+	//! Parse an element from a string. 
+	//! The function will parse all properties contained in the
+	//! element, too.
+	static bool ParseElement (const char* p_szIn, const char** p_szOut, 
+		Element* pOut);
+
+	//! Parse a semantic from a string
+	static EElementSemantic ParseSemantic(const char* p_szIn,
+		const char** p_szOut);
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Instance of a property in a PLY file
+ */
+class PropertyInstance 
+{
+public:
+
+	//! Default constructor
+	PropertyInstance ()
+	{}
+
+	union ValueUnion
+	{
+
+		//! uInt32 representation of the property. All
+		// uint types are automatically converted to uint32
+		uint32_t iUInt;
+
+		//! Int32 representation of the property. All
+		// int types are automatically converted to int32
+		int32_t iInt;
+
+		//! Float32 representation of the property
+		float fFloat;
+
+		//! Float64 representation of the property
+		double fDouble;
+
+	};
+
+	//! List of all values parsed. Contains only one value
+	// for non-list propertys
+	std::list<ValueUnion> avList;
+
+	//! Parse a property instance 
+	static bool ParseInstance (const char* p_szIn,const char** p_szOut,
+		const Property* prop, PropertyInstance* p_pcOut);
+
+	//! Parse a property instance in binary format
+	static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
+		const Property* prop, PropertyInstance* p_pcOut,bool p_bBE);
+
+	//! Get the default value for a given data type
+	static ValueUnion DefaultValue(EDataType eType);
+
+	//! Parse a value
+	static bool ParseValue(const char* p_szIn,const char** p_szOut,
+		EDataType eType,ValueUnion* out);
+
+	//! Parse a binary value
+	static bool ParseValueBinary(const char* p_szIn,const char** p_szOut,
+		EDataType eType,ValueUnion* out,bool p_bBE);
+
+	//! Convert a property value to a given type TYPE
+	template <typename TYPE>
+	static TYPE ConvertTo(ValueUnion v, EDataType eType);
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Class for an element instance in a PLY file
+ */
+class ElementInstance 
+{
+public:
+
+	//! Default constructor
+	ElementInstance ()
+	{}
+
+	//! List of all parsed properties
+	std::vector< PropertyInstance > alProperties;
+
+	//! Parse an element instance
+	static bool ParseInstance (const char* p_szIn,const char** p_szOut,
+		const Element* pcElement, ElementInstance* p_pcOut);
+
+	//! Parse a binary element instance
+	static bool ParseInstanceBinary (const char* p_szIn,const char** p_szOut,
+		const Element* pcElement, ElementInstance* p_pcOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Class for an element instance list in a PLY file
+ */
+class ElementInstanceList 
+{
+public:
+
+	//! Default constructor
+	ElementInstanceList ()
+	{}
+
+	ElementInstanceList (const Element* pc)
+	{
+		alInstances.reserve(pc->NumOccur);
+	}
+
+	//! List of all element instances
+	std::vector< ElementInstance > alInstances;
+
+	//! Parse an element instance list
+	static bool ParseInstanceList (const char* p_szIn,const char** p_szOut,
+		const Element* pcElement, ElementInstanceList* p_pcOut);
+
+	//! Parse a binary element instance list
+	static bool ParseInstanceListBinary (const char* p_szIn,const char** p_szOut,
+		const Element* pcElement, ElementInstanceList* p_pcOut,bool p_bBE);
+};
+// ---------------------------------------------------------------------------------
+/**	\brief Class to represent the document object model of an ASCII or binary 
+ *		  (both little and big-endian) PLY file
+ */
+class DOM
+{
+public:
+
+	//! Default constructor
+	DOM()
+	{}
+
+
+	std::vector<Element> alElements;
+	std::vector<ElementInstanceList> alElementData;
+
+	//! Parse the DOM for a PLY file. The input string is assumed
+	//! to be terminated with zero
+	static bool ParseInstance (const char* p_szIn,DOM* p_pcOut);
+	static bool ParseInstanceBinary (const char* p_szIn,
+		DOM* p_pcOut,bool p_bBE);
+
+	//! Skip all comment lines after this
+	static bool SkipComments (const char* p_szIn,const char** p_szOut);
+
+private:
+
+	//! Handle the file header and read all element descriptions
+	bool ParseHeader (const char* p_szIn,const char** p_szOut);
+
+	//! Read in all element instance lists
+	bool ParseElementInstanceLists (const char* p_szIn,const char** p_szOut);
+
+	//! Read in all element instance lists for a binary file format
+	bool ParseElementInstanceListsBinary (const char* p_szIn,
+		const char** p_szOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/**	\brief Helper class to represent a loaded face
+ */
+class Face
+{
+public:
+
+	Face()
+		: iMaterialIndex(0xFFFFFFFF)
+	{
+		// set all indices to zero by default
+		mIndices.resize(3,0);
+	}
+
+public:
+
+	//! List of vertex indices
+	std::vector<unsigned int> mIndices;
+
+	//! Material index
+	unsigned int iMaterialIndex;
+};
+
+// ---------------------------------------------------------------------------------
+template <typename TYPE>
+TYPE PLY::PropertyInstance::ConvertTo(
+	PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType)
+{
+	switch (eType)
+	{
+	case EDT_Float:
+		return (TYPE)v.fFloat;
+	case EDT_Double:
+		return (TYPE)v.fDouble;
+
+	case EDT_UInt:
+	case EDT_UShort:
+	case EDT_UChar:
+		return (TYPE)v.iUInt;
+
+	case EDT_Int:
+	case EDT_Short:
+	case EDT_Char:
+		return (TYPE)v.iInt;
+	};
+	return (TYPE)0;
+}
+// ---------------------------------------------------------------------------------
+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

+ 93 - 0
code/SpatialSort.cpp

@@ -0,0 +1,93 @@
+/** @file Implementation of the helper class to quickly find vertices close to a given position */
+#include <algorithm>
+#include "SpatialSort.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructs a spatially sorted representation from the given position array.
+SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset)
+{
+	// define the reference plane. We choose some arbitrary vector away from all basic axises 
+	// in the hope that no model spreads all its vertices along this plane.
+	mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
+	mPlaneNormal.Normalize();
+
+	// store references to all given positions along with their distance to the reference plane
+	mPositions.reserve( pNumPositions);
+	for( unsigned int a = 0; a < pNumPositions; a++)
+	{
+		const char* tempPointer = reinterpret_cast<const char*> (pPositions);
+		const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
+
+		// store position by index and distance
+		float distance = *vec * mPlaneNormal;
+		mPositions.push_back( Entry( a, *vec, distance));
+	}
+
+	// now sort the array ascending by distance.
+	std::sort( mPositions.begin(), mPositions.end());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+SpatialSort::~SpatialSort()
+{
+	// nothing to do here, everything destructs automatically
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns an iterator for all positions close to the given position.
+void SpatialSort::FindPositions( const aiVector3D& pPosition, float pRadius, std::vector<unsigned int>& poResults) const
+{
+	float dist = pPosition * mPlaneNormal;
+	float minDist = dist - pRadius, maxDist = dist + pRadius;
+
+	// clear the array in this strange fashion because a simple clear() would also deallocate
+    // the array which we want to avoid
+	poResults.erase( poResults.begin(), poResults.end());
+
+	// quick check for positions outside the range
+	if( mPositions.size() == 0)
+		return;
+	if( maxDist < mPositions.front().mDistance)
+		return;
+	if( minDist > mPositions.back().mDistance)
+		return;
+
+	// do a binary search for the minimal distance to start the iteration there
+	unsigned int index = mPositions.size() / 2;
+	unsigned int binaryStepSize = mPositions.size() / 4;
+	while( binaryStepSize > 1)
+	{
+		if( mPositions[index].mDistance < minDist)
+			index += binaryStepSize;
+		else
+			index -= binaryStepSize;
+
+		binaryStepSize /= 2;
+	}
+
+	// depending on the direction of the last step we need to single step a bit back or forth
+	// to find the actual beginning element of the range
+	while( index > 0 && mPositions[index].mDistance > minDist)
+		index--;
+	while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
+		index++;
+	
+	// Mow start iterating from there until the first position lays outside of the distance range.
+	// Add all positions inside the distance range within the given radius to the result aray
+	std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+	float squareEpsilon = pRadius * pRadius;
+	while( it->mDistance < maxDist)
+	{
+		if( (it->mPosition - pPosition).SquareLength() < squareEpsilon)
+			poResults.push_back( it->mIndex);
+		++it;
+		if( it == mPositions.end())
+			break;
+	}
+
+	// that's it
+}
+

+ 72 - 0
code/SpatialSort.h

@@ -0,0 +1,72 @@
+/** Small helper classes to optimise finding vertizes close to a given location */
+#ifndef AI_SPATIALSORT_H_INC
+#define AI_SPATIALSORT_H_INC
+
+#include <vector>
+#include "../include/aiVector3D.h"
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------------
+/** A little helper class to quickly find all vertices in the epsilon environment of a given
+ * position. Construct an instance with an array of positions. The class stores the given positions
+ * by their indices and sorts them by their distance to an arbitrary chosen plane.
+ * You can then query the instance for all vertices close to a given position in an average O(log n) 
+ * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen
+ * so that it avoids common planes in usual data sets.
+ */
+class SpatialSort
+{
+public:
+
+	SpatialSort() {/* This is unintialized. This is evil. This is OK. */}
+
+	/** Constructs a spatially sorted representation from the given position array.
+	 * Supply the positions in its layout in memory, the class will only refer to them
+	 * by index.
+	 * @param pPositions Pointer to the first position vector of the array.
+	 * @param pNumPositions Number of vectors to expect in that array.
+	 * @param pElementOffset Offset in bytes from the beginning of one vector in memory to the beginning of the next vector.
+	 */
+	SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, unsigned int pElementOffset);
+
+	/** Destructor */
+	~SpatialSort();
+
+	/** Returns an iterator for all positions close to the given position.
+	 * @param pPosition The position to look for vertices.
+	 * @param pRadius Maximal distance from the position a vertex may have to be counted in.
+	 * @param poResults The container to store the indices of the found positions. Will be emptied
+	 *   by the call so it may contain anything.
+	 * @return An iterator to iterate over all vertices in the given area.
+	 */
+	void FindPositions( const aiVector3D& pPosition, float pRadius, std::vector<unsigned int>& poResults) const;
+
+protected:
+	/** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+	aiVector3D mPlaneNormal;
+
+	/** An entry in a spatially sorted position array. Consists of a vertex index,
+	 * its position and its precalculated distance from the reference plane */
+	struct Entry
+	{
+		unsigned int mIndex; ///< The vertex referred by this entry
+		aiVector3D mPosition; ///< Position
+		float mDistance; ///< Distance of this vertex to the sorting plane
+
+		Entry() { /** intentionally not initialized.*/ }
+		Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance) 
+			: mPosition( pPosition), mIndex( pIndex), mDistance( pDistance)
+		{ 	}
+
+		bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
+	};
+
+	// all positions, sorted by distance to the sorting plane
+	std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC

+ 227 - 0
code/SplitLargeMeshes.cpp

@@ -0,0 +1,227 @@
+/** @file Implementation of the SplitLargeMeshes postprocessing step
+*/
+#include "SplitLargeMeshes.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// Constructor to be privately used by Importer
+SplitLargeMeshesProcess::SplitLargeMeshesProcess()
+	{
+	}
+
+// Destructor, private as well
+SplitLargeMeshesProcess::~SplitLargeMeshesProcess()
+	{
+	// nothing to do here
+	}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SplitLargeMeshesProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_SplitLargeMeshes) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess::Execute( aiScene* pScene)
+{
+	std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		this->SplitMesh(a, pScene->mMeshes[a],avList);
+
+	if (avList.size() != pScene->mNumMeshes)
+	{
+		// it seems something has been splitted. rebuild the mesh list
+		delete[] pScene->mMeshes;
+		pScene->mNumMeshes = avList.size();
+		pScene->mMeshes = new aiMesh*[avList.size()];
+
+		for (unsigned int i = 0; i < avList.size();++i)
+			pScene->mMeshes[i] = avList[i].first;
+
+		// now we need to update all nodes
+		this->UpdateNode(pScene->mRootNode,avList);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+// Update a node after some meshes have been split
+void SplitLargeMeshesProcess::UpdateNode(aiNode* pcNode,
+	const std::vector<std::pair<aiMesh*, unsigned int> >& avList)
+{
+	// for every index in out list build a new entry
+	// TODO: Currently O(n^2)
+
+	std::vector<unsigned int> aiEntries;
+	aiEntries.reserve(pcNode->mNumMeshes + 1);
+	for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+	{
+		for (unsigned int a = 0; a < avList.size();++a)
+		{
+			if (avList[a].second == pcNode->mMeshes[i])
+			{
+				aiEntries.push_back(a);
+			}
+		}
+	}
+
+	// now build the new list
+	delete pcNode->mMeshes;
+	pcNode->mNumMeshes = aiEntries.size();
+	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+
+	for (unsigned int b = 0; b < pcNode->mNumMeshes;++b)
+		pcNode->mMeshes[b] = aiEntries[b];
+
+	// recusively update all other nodes
+	for (unsigned int i = 0; i < pcNode->mNumChildren;++i)
+	{
+		this->UpdateNode ( pcNode->mChildren[i], avList );
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess::SplitMesh(
+	unsigned int a,
+	aiMesh* pMesh,
+	std::vector<std::pair<aiMesh*, unsigned int> >& avList)
+{
+	// TODO: Mesh splitting is currently not supported for meshes
+	// containing bones
+
+	if (pMesh->mNumVertices > AI_SLM_MAX_VERTICES && 0 == pMesh->mNumBones)
+	{
+		// we need to split this mesh into sub meshes
+		// determine the size of a submesh
+		const unsigned int iSubMeshes = (pMesh->mNumVertices / AI_SLM_MAX_VERTICES) + 1;
+
+		const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes;
+		const unsigned int iOutVertexNum = iOutFaceNum * 3;
+
+		// now generate all submeshes
+		for (unsigned int i = 0; i < iSubMeshes;++i)
+		{
+			aiMesh* pcMesh			= new aiMesh;			
+			pcMesh->mNumFaces		= iOutFaceNum;
+			pcMesh->mMaterialIndex	= pMesh->mMaterialIndex;
+
+			if (i == iSubMeshes-1)
+			{
+				pcMesh->mNumFaces = iOutFaceNum + (
+					pMesh->mNumFaces - iOutFaceNum * iSubMeshes);
+			}
+			// copy the list of faces
+			pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+			const unsigned int iBase = iOutFaceNum * i;
+
+			// get the total number of indices
+			unsigned int iCnt = 0;
+			for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p)
+			{
+				iCnt += pMesh->mFaces[p].mNumIndices;
+			}
+			pcMesh->mNumVertices = iCnt;
+
+			// allocate storage
+			if (pMesh->mVertices != NULL)
+				pcMesh->mVertices = new aiVector3D[iCnt];
+
+			if (pMesh->HasNormals())
+				pcMesh->mNormals = new aiVector3D[iCnt];
+
+			if (pMesh->HasTangentsAndBitangents())
+			{
+				pcMesh->mTangents = new aiVector3D[iCnt];
+				pcMesh->mBitangents = new aiVector3D[iCnt];
+			}
+
+			// texture coordinates
+			for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+			{
+				pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
+				if (pMesh->HasTextureCoords( c))
+				{
+					pcMesh->mTextureCoords[c] = new aiVector3D[iCnt];
+				}
+			}
+
+			// vertex colors
+			for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
+			{
+				if (pMesh->HasVertexColors( c))
+				{
+					pcMesh->mColors[c] = new aiColor4D[iCnt];
+				}
+			}
+
+			// (we will also need to copy the array of indices)
+			for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
+			{
+				pcMesh->mFaces[p].mNumIndices = 3;
+
+				// allocate a new array
+				unsigned int* pi = pMesh->mFaces[p + iBase].mIndices;
+				pcMesh->mFaces[p].mIndices = new unsigned int[3];
+
+				// and copy the contents of the old array, offset by current base
+				for (unsigned int v = 0; v < 3;++v)
+				{
+					unsigned int iIndex = pMesh->mFaces[p+iBase].mIndices[v];
+					unsigned int iIndexOut = p*3 + v;
+					pcMesh->mFaces[p].mIndices[v] = iIndexOut;
+
+					// copy positions
+					if (pMesh->mVertices != NULL)
+					{
+						pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex];
+					}
+
+					// copy normals
+					if (pMesh->HasNormals())
+					{
+						pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex];
+					}
+
+					// copy tangents/bitangents
+					if (pMesh->HasTangentsAndBitangents())
+					{
+						pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex];
+						pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex];
+					}
+
+					// texture coordinates
+					for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+					{
+						if (pMesh->HasTextureCoords( c))
+						{
+							pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex];
+						}
+					}
+					// vertex colors 
+					for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
+					{
+						if (pMesh->HasVertexColors( c))
+						{
+							pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex];
+						}
+					}
+				}
+			}
+
+			// add the newly created mesh to the list
+			avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
+		}
+
+		// now delete the old mesh data
+		delete pMesh;
+	}
+	else avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
+	return;
+}

+ 60 - 0
code/SplitLargeMeshes.h

@@ -0,0 +1,60 @@
+/** @file Defines a post processing step to split large meshes into submeshes*/
+#ifndef AI_SPLITLARGEMESHES_H_INC
+#define AI_SPLITLARGEMESHES_H_INC
+
+#include <vector>
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+namespace Assimp
+{
+
+#define AI_SLM_MAX_VERTICES	1000000
+
+// ---------------------------------------------------------------------------
+/** Postprocessing filter to split large meshes into submeshes
+*/
+class SplitLargeMeshesProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	SplitLargeMeshesProcess();
+
+	/** Destructor, private as well */
+	~SplitLargeMeshesProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	* @param pFlags The processing flags the importer was called with. A bitwise
+	*   combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+
+private:
+
+	//! Apply the algorithm to a given mesh
+	void SplitMesh (unsigned int a, aiMesh* pcMesh,
+		std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+	//! Update a node in the asset after a few of its meshes
+	//! have been split
+	void UpdateNode(aiNode* pcNode,
+		const std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_SPLITLARGEMESHES_H_INC

+ 83 - 0
code/TriangulateProcess.cpp

@@ -0,0 +1,83 @@
+/** @file Implementation of the post processing step to split up
+ * all faces with more than three indices into triangles.
+ */
+#include <vector>
+#include <assert.h>
+#include "TriangulateProcess.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TriangulateProcess::TriangulateProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TriangulateProcess::~TriangulateProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool TriangulateProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_Triangulate) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void TriangulateProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		TriangulateMesh( pScene->mMeshes[a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Triangulates the given mesh.
+void TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
+{
+	std::vector<aiFace> newFaces;
+	newFaces.reserve( pMesh->mNumFaces*2);
+	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+	{
+		const aiFace& face = pMesh->mFaces[a];
+
+		// if it's a simple primitive, just copy it
+		if( face.mNumIndices == 3)
+		{
+			newFaces.push_back( aiFace());
+			aiFace& nface = newFaces.back();
+			nface.mNumIndices = face.mNumIndices;
+			nface.mIndices = new unsigned int[nface.mNumIndices];
+			memcpy( nface.mIndices, face.mIndices, nface.mNumIndices * sizeof( unsigned int));
+		} else
+		{
+			assert( face.mNumIndices > 3);
+			for( unsigned int b = 0; b < face.mNumIndices - 2; b++)
+			{
+				newFaces.push_back( aiFace());
+				aiFace& nface = newFaces.back();
+				nface.mNumIndices = 3;
+				nface.mIndices = new unsigned int[3];
+				nface.mIndices[0] = face.mIndices[0];
+				nface.mIndices[1] = face.mIndices[b+1];
+				nface.mIndices[2] = face.mIndices[b+2];
+			}
+		}
+	}
+
+	// kill the old faces
+	delete [] pMesh->mFaces;
+	// and insert our newly generated faces
+	pMesh->mNumFaces = newFaces.size();
+	pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+	for( unsigned int a = 0; a < newFaces.size(); a++)
+		pMesh->mFaces[a] = newFaces[a];
+}

+ 54 - 0
code/TriangulateProcess.h

@@ -0,0 +1,54 @@
+/** @file Defines a post processing step to triangulate all faces with more than three vertices.*/
+#ifndef AI_TRIANGULATEPROCESS_H_INC
+#define AI_TRIANGULATEPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The TriangulateProcess splits up all faces with more than three indices
+ * into triangles. You usually want this to happen because the graphics cards
+ * need their data as triangles.
+ */
+class TriangulateProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	TriangulateProcess();
+
+	/** Destructor, private as well */
+	~TriangulateProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	 * @param pFlags The processing flags the importer was called with. A bitwise
+	 *   combination of #aiPostProcessSteps.
+	 * @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+protected:
+	// -------------------------------------------------------------------
+	/** Triangulates the given mesh.
+	 * @param pMesh The mesh to triangulate.
+	 */
+	void TriangulateMesh( aiMesh* pMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_TRIANGULATEPROCESS_H_INC

+ 147 - 0
code/XFileHelper.h

@@ -0,0 +1,147 @@
+/** @file Defines the helper data structures for importing XFiles */
+#ifndef AI_XFILEHELPER_H_INC
+#define AI_XFILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+#include "../include/aiQuaternion.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+namespace Assimp
+{
+namespace XFile
+{
+
+/** Helper structure representing a XFile mesh face */
+struct Face
+{
+	std::vector<unsigned int> mIndices;
+};
+
+/** Helper structure representing a XFile material */
+struct Material
+{
+	std::string mName;
+	bool mIsReference; // if true, mName holds a name by which the actual material can be found in the material list
+	aiColor4D mDiffuse;
+	float mSpecularExponent;
+	aiColor3D mSpecular;
+	aiColor3D mEmissive;
+	std::vector<std::string> mTextures;
+
+	Material() { mIsReference = false; }
+};
+
+/** Helper structure to represent a bone weight */
+struct BoneWeight
+{
+	unsigned int mVertex;
+	float mWeight;
+};
+
+/** Helper structure to represent a bone in a mesh */
+struct Bone
+{
+	std::string mName;
+	std::vector<BoneWeight> mWeights;
+	aiMatrix4x4 mOffsetMatrix;
+};
+
+/** Helper structure to represent an XFile mesh */
+struct Mesh
+{
+	std::vector<aiVector3D> mPositions;
+	std::vector<Face> mPosFaces;
+	std::vector<aiVector3D> mNormals;
+	std::vector<Face> mNormFaces;
+	unsigned int mNumTextures;
+	std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+	unsigned int mNumColorSets;
+	std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+	std::vector<unsigned int> mFaceMaterials;
+	std::vector<Material> mMaterials;
+
+	std::vector<Bone> mBones;
+
+	Mesh() { mNumTextures = 0; mNumColorSets = 0; }
+};
+
+/** Helper structure to represent a XFile frame */
+struct Node
+{
+	std::string mName;
+	aiMatrix4x4 mTrafoMatrix;
+	Node* mParent;
+	std::vector<Node*> mChildren;
+	std::vector<Mesh*> mMeshes;
+
+	Node() { mParent = NULL; }
+	Node( Node* pParent) { mParent = pParent; }
+	~Node() 
+	{
+		for( unsigned int a = 0; a < mChildren.size(); a++)
+			delete mChildren[a];
+		for( unsigned int a = 0; a < mMeshes.size(); a++)
+			delete mMeshes[a];
+	}
+};
+
+struct MatrixKey
+{
+	double mTime;
+	aiMatrix4x4 mMatrix;
+};
+
+/** Helper structure representing a single animated bone in a XFile */
+struct AnimBone
+{
+	std::string mBoneName;
+	std::vector<aiVectorKey> mPosKeys;  // either three separate key sequences for position, rotation, scaling
+	std::vector<aiQuatKey> mRotKeys;
+	std::vector<aiVectorKey> mScaleKeys;
+	std::vector<MatrixKey> mTrafoKeys; // or a combined key sequence of transformation matrices.
+};
+
+/** Helper structure to represent an animation set in a XFile */
+struct Animation
+{
+	std::string mName;
+	std::vector<AnimBone*> mAnims;
+
+	~Animation() 
+	{
+		for( unsigned int a = 0; a < mAnims.size(); a++)
+			delete mAnims[a];
+	}
+};
+
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+	Node* mRootNode;
+
+	std::vector<Mesh*> mGlobalMeshes; // global meshes found outside of any frames
+	std::vector<Material> mGlobalMaterials; // global materials found outside of any meshes.
+
+	std::vector<Animation*> mAnims;
+	unsigned int mAnimTicksPerSecond;
+
+	Scene() { mRootNode = NULL; mAnimTicksPerSecond = 0; }
+	~Scene()
+	{
+		delete mRootNode;
+		for( unsigned int a = 0; a < mGlobalMeshes.size(); a++)
+			delete mGlobalMeshes[a];
+		for( unsigned int a = 0; a < mAnims.size(); a++)
+			delete mAnims[a];
+	}
+};
+
+} // end of namespace XFile
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC

+ 703 - 0
code/XFileImporter.cpp

@@ -0,0 +1,703 @@
+/** @file Implementation of the XFile importer class */
+#include "XFileImporter.h"
+#include "XFileParser.h"
+#include "MaterialSystem.h"
+#include "ConvertToLHProcess.h"
+
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/format.hpp>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+XFileImporter::XFileImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well 
+XFileImporter::~XFileImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file. 
+bool XFileImporter::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 == ".x" || extension == ".X")
+		return true;
+
+	return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+	// read file into memory
+	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+	if( file.get() == NULL)
+		throw new ImportErrorException( "Failed to open file " + pFile + ".");
+
+	size_t fileSize = file->FileSize();
+	if( fileSize < 16)
+		throw new ImportErrorException( "XFile is too small.");
+
+	mBuffer.resize( fileSize);
+	file->Read( &mBuffer.front(), 1, fileSize);
+
+	// parse the file into a temporary representation
+	XFileParser parser( mBuffer);
+
+	// and create the proper return structures out of it
+	CreateDataRepresentationFromImport( pScene, parser.GetImportedData());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the return data structure out of the imported data.
+void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData)
+{
+	// Read the global materials first so that meshes referring to them can find them later
+	ConvertMaterials( pScene, pData->mGlobalMaterials);
+
+	// copy nodes, extracting meshes and materials on the way
+	pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode);
+
+	// extract animations
+	CreateAnimations( pScene, pData);
+
+	// read the global meshes that were stored outside of any node
+	if( pData->mGlobalMeshes.size() > 0)
+	{
+		// create a root node to hold them if there isn't any, yet
+		if( pScene->mRootNode == NULL)
+		{
+			pScene->mRootNode = new aiNode;
+			pScene->mRootNode->mName.Set( "$dummy_node");
+		}
+
+		// convert all global meshes and store them in the root node.
+		// If there was one before, the global meshes now suddenly have its transformation matrix...
+		// Don't know what to do there, I don't want to insert another node under the present root node
+		// just to avoid this.
+		CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes);
+	}
+
+	// convert the root node's transformation to OGL coords
+	ConvertToLHProcess::ConvertToOGL( pScene->mRootNode->mTransformation);
+
+	// finally: create a dummy material if not material was imported
+	if( pScene->mNumMaterials == 0)
+	{
+		pScene->mNumMaterials = 1;
+		// create the Material
+		Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+		int shadeMode = (int) aiShadingMode_Gouraud;
+		mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+		// material colours
+		int specExp = 1;
+		mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_EMISSIVE);
+		mat->AddProperty( &aiColor3D( 0.5f, 0.5f, 0.5f), 1, AI_MATKEY_COLOR_DIFFUSE);
+		mat->AddProperty( &aiColor3D( 0, 0, 0), 1, AI_MATKEY_COLOR_SPECULAR);
+		mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
+
+		pScene->mMaterials = new aiMaterial*[1];
+		pScene->mMaterials[0] = mat;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively creates scene nodes from the imported hierarchy. 
+aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode)
+{
+	if( !pNode)
+		return NULL;
+
+	// create node
+	aiNode* node = new aiNode;
+	node->mName.length = pNode->mName.length();
+	node->mParent = pParent;
+	memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length());
+	node->mName.data[node->mName.length] = 0;
+	node->mTransformation = pNode->mTrafoMatrix;
+	
+	// convert meshes from the source node 
+	CreateMeshes( pScene, node, pNode->mMeshes);
+
+	// handle childs
+	if( pNode->mChildren.size() > 0)
+	{
+		node->mNumChildren = pNode->mChildren.size();
+		node->mChildren = new aiNode* [node->mNumChildren];
+
+		for( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+			node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]);
+	}
+
+	return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the meshes for the given node. 
+void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes)
+{
+	if( pMeshes.size() == 0)
+		return;
+
+	// create a mesh for each mesh-material combination in the source node
+	std::vector<aiMesh*> meshes;
+	for( unsigned int a = 0; a < pMeshes.size(); a++)
+	{
+		const XFile::Mesh* sourceMesh = pMeshes[a];
+		// first convert its materials so that we can find them when searching by name afterwards
+		ConvertMaterials( pScene, sourceMesh->mMaterials);
+
+		unsigned int numMaterials = std::max( sourceMesh->mMaterials.size(), 1u);
+		for( unsigned int b = 0; b < numMaterials; b++)
+		{
+			// collect the faces belonging to this material
+			std::vector<unsigned int> faces;
+			unsigned int numVertices = 0;
+			if( sourceMesh->mFaceMaterials.size() > 0)
+			{
+				// if there is a per-face material defined, select the faces with the corresponding material
+				for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++)
+				{
+					if( sourceMesh->mFaceMaterials[c] == b)
+					{
+						faces.push_back( c);
+						numVertices += sourceMesh->mPosFaces[c].mIndices.size();
+					}
+				}
+			} else
+			{
+				// if there is no per-face material, place everything into one mesh
+				for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++)
+				{
+					faces.push_back( c);
+					numVertices += sourceMesh->mPosFaces[c].mIndices.size();
+				}
+			}
+
+			// no faces/vertices using this material? strange...
+			if( numVertices == 0)
+				continue;
+
+			// create a submesh using this material
+			aiMesh* mesh = new aiMesh;
+			meshes.push_back( mesh);
+
+			// find the material by name in the scene's material list. Either own material
+			// or referenced material, it should already be found there
+			if( sourceMesh->mFaceMaterials.size() > 0)
+			{
+				std::map<std::string, unsigned int>::const_iterator matIt = mImportedMats.find( sourceMesh->mMaterials[b].mName);
+				if( matIt == mImportedMats.end())
+					mesh->mMaterialIndex = 0;
+				else
+					mesh->mMaterialIndex = matIt->second;
+			} else
+			{
+				mesh->mMaterialIndex = 0;
+			}
+
+			// Create properly sized data arrays in the mesh. We store unique vertices per face,
+			// as specified 
+			mesh->mNumVertices = numVertices;
+			mesh->mVertices = new aiVector3D[numVertices];
+			mesh->mNumFaces = faces.size();
+			mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+			// normals?
+			if( sourceMesh->mNormals.size() > 0)
+				mesh->mNormals = new aiVector3D[numVertices];
+			// texture coords
+			for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++)
+			{
+				if( sourceMesh->mTexCoords[c].size() > 0)
+					mesh->mTextureCoords[c] = new aiVector3D[numVertices];
+			}
+			// vertex colors
+			for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++)
+			{
+				if( sourceMesh->mColors[c].size() > 0)
+					mesh->mColors[c] = new aiColor4D[numVertices];
+			}
+
+			// now collect the vertex data of all data streams present in the imported mesh
+			unsigned int newIndex = 0;
+			std::vector<unsigned int> orgPoints; // from which original point each new vertex stems
+			orgPoints.resize( numVertices, 0);
+
+			for( unsigned int c = 0; c < faces.size(); c++)
+			{
+				unsigned int f = faces[c]; // index of the source face
+				const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face
+
+				// create face. either triangle or triangle fan depending on the index count
+				aiFace& df = mesh->mFaces[c]; // destination face
+				df.mNumIndices = pf.mIndices.size();
+				df.mIndices = new unsigned int[ df.mNumIndices];
+
+				// collect vertex data for indices of this face
+				for( unsigned int d = 0; d < df.mNumIndices; d++)
+				{
+					df.mIndices[df.mNumIndices - 1 - d] = newIndex; // inverted face orientation for OGL
+					orgPoints[newIndex] = pf.mIndices[d];
+
+					// Position
+					mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]];
+					// Normal, if present
+					if( mesh->HasNormals())
+						mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]];
+					// texture coord sets
+					for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++)
+					{
+						if( mesh->HasTextureCoords( e))
+						{
+							aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]];
+							mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f);
+						}
+					}
+					// vertex color sets
+					for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++)
+						if( mesh->HasVertexColors( e))
+							mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]];
+					
+					newIndex++;
+				}
+			}
+
+			// there should be as much new vertices as we calculated before
+			assert( newIndex == numVertices);
+
+			// convert all bones of the source mesh which influence vertices in this newly created mesh
+			const std::vector<XFile::Bone>& bones = sourceMesh->mBones;
+			std::vector<aiBone*> newBones;
+			for( unsigned int c = 0; c < bones.size(); c++)
+			{
+				const XFile::Bone& obone = bones[c];
+				// set up a vertex-linear array of the weights for quick searching if a bone influences a vertex
+				std::vector<float> oldWeights( sourceMesh->mPositions.size(), 0.0f);
+				for( unsigned int d = 0; d < obone.mWeights.size(); d++)
+					oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight;
+
+				// collect all vertex weights that influence a vertex in the new mesh
+				std::vector<aiVertexWeight> newWeights;
+				newWeights.reserve( numVertices);
+				for( unsigned int d = 0; d < orgPoints.size(); d++)
+				{
+					// does the new vertex stem from an old vertex which was influenced by this bone?
+					float w = oldWeights[orgPoints[d]];
+					if( w > 0.0f)
+						newWeights.push_back( aiVertexWeight( d, w));
+				}
+
+				// if the bone has no weights in the newly created mesh, ignore it
+				if( newWeights.size() == 0)
+					continue;
+
+				// create
+				aiBone* nbone = new aiBone;
+				newBones.push_back( nbone);
+				// copy name and matrix
+				nbone->mName.Set( obone.mName);
+				nbone->mOffsetMatrix = obone.mOffsetMatrix;
+				nbone->mNumWeights = newWeights.size();
+				nbone->mWeights = new aiVertexWeight[nbone->mNumWeights];
+				for( unsigned int d = 0; d < newWeights.size(); d++)
+					nbone->mWeights[d] = newWeights[d];
+			}
+
+			// store the bones in the mesh
+			mesh->mNumBones = newBones.size();
+			mesh->mBones = new aiBone*[mesh->mNumBones];
+			for( unsigned int c = 0; c < newBones.size(); c++)
+				mesh->mBones[c] = newBones[c];
+		}
+	}
+
+	// reallocate scene mesh array to be large enough
+	aiMesh** prevArray = pScene->mMeshes;
+	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()];
+	if( prevArray)
+	{
+		memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*));
+		delete [] prevArray;
+	}
+
+	// allocate mesh index array in the node
+	pNode->mNumMeshes = meshes.size();
+	pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+
+	// store all meshes in the mesh library of the scene and store their indices in the node
+	for( unsigned int a = 0; a < meshes.size(); a++)
+	{
+		pScene->mMeshes[pScene->mNumMeshes] = meshes[a];		
+		pNode->mMeshes[a] = pScene->mNumMeshes;
+		pScene->mNumMeshes++;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the animations from the given imported data and creates them in the scene.
+void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData)
+{
+	std::vector<aiAnimation*> newAnims;
+
+	for( unsigned int a = 0; a < pData->mAnims.size(); a++)
+	{
+		const XFile::Animation* anim = pData->mAnims[a];
+		// create a new animation to hold the data
+		aiAnimation* nanim = new aiAnimation;
+		newAnims.push_back( nanim);
+		nanim->mName.Set( anim->mName);
+		// duration will be determined by the maximum length
+		nanim->mDuration = 0;
+		nanim->mTicksPerSecond = pData->mAnimTicksPerSecond;
+		nanim->mNumBones = anim->mAnims.size();
+		nanim->mBones = new aiBoneAnim*[nanim->mNumBones];
+
+		for( unsigned int b = 0; b < anim->mAnims.size(); b++)
+		{
+			const XFile::AnimBone* bone = anim->mAnims[b];
+			aiBoneAnim* nbone = new aiBoneAnim;
+			nbone->mBoneName.Set( bone->mBoneName);
+			nanim->mBones[b] = nbone;
+
+			// apply the LH->RH conversion if the animation affects the root bone
+			bool isRootAnim = (bone->mBoneName == pScene->mRootNode->mName.data);
+
+			// keyframes are given as combined transformation matrix keys
+			if( bone->mTrafoKeys.size() > 0)
+			{
+				nbone->mNumPositionKeys = bone->mTrafoKeys.size();
+				nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+				nbone->mNumRotationKeys = bone->mTrafoKeys.size();
+				nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+				nbone->mNumScalingKeys = bone->mTrafoKeys.size();
+				nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+
+				for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++)
+				{
+					// deconstruct each matrix into separate position, rotation and scaling
+					double time = bone->mTrafoKeys[c].mTime;
+					aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix;
+
+					// extract position
+					aiVector3D pos( trafo.a4, trafo.b4, trafo.c4);
+					if( isRootAnim)
+						ConvertToLHProcess::ConvertToOGL( pos);
+
+					nbone->mPositionKeys[c].mTime = time;
+					nbone->mPositionKeys[c].mValue = pos;
+
+					// extract scaling
+					aiVector3D scale;
+					scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length();
+					scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length();
+					scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length();
+					nbone->mScalingKeys[c].mTime = time;
+					nbone->mScalingKeys[c].mValue = scale;
+
+					// reconstruct rotation matrix without scaling
+					aiMatrix3x3 rotmat( 
+						trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z,
+						trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z,
+						trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z);
+
+					if( isRootAnim)
+						ConvertToLHProcess::ConvertToOGL( rotmat);
+
+					// and convert it into a quaternion
+					nbone->mRotationKeys[c].mTime = time;
+					nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+				}
+
+				// longest lasting key sequence determines duration
+				nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime);
+			} else
+			{
+				// separate key sequences for position, rotation, scaling
+				nbone->mNumPositionKeys = bone->mPosKeys.size(); 
+				nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+				for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++)
+				{
+					aiVector3D pos = bone->mPosKeys[c].mValue;
+					if( isRootAnim)
+						ConvertToLHProcess::ConvertToOGL( pos);
+
+					nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime;
+					nbone->mPositionKeys[c].mValue = pos;
+				}
+
+				// rotation
+				nbone->mNumRotationKeys = bone->mRotKeys.size(); 
+				nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+				for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++)
+				{
+					aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix();
+					if( isRootAnim)
+						ConvertToLHProcess::ConvertToOGL( rotmat);
+
+					nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime;
+					nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+				}
+
+				// scaling
+				nbone->mNumScalingKeys = bone->mScaleKeys.size(); 
+				nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+				for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++)
+					nbone->mScalingKeys[c] = bone->mScaleKeys[c];
+
+				// longest lasting key sequence determines duration
+				if( bone->mPosKeys.size() > 0)
+					nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime);
+				if( bone->mRotKeys.size() > 0)
+					nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime);
+				if( bone->mScaleKeys.size() > 0)
+					nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime);
+			}
+		}
+	}
+
+	// store all converted animations in the scene
+	if( newAnims.size() > 0)
+	{
+		pScene->mNumAnimations = newAnims.size();
+		pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations];
+		for( unsigned int a = 0; a < newAnims.size(); a++)
+			pScene->mAnimations[a] = newAnims[a];
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts all materials in the given array and stores them in the scene's material list.
+void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials)
+{
+	// count the non-referrer materials in the array
+	unsigned int numMaterials = 0;
+	for( unsigned int a = 0; a < pMaterials.size(); a++)
+		if( !pMaterials[a].mIsReference)
+			numMaterials++;
+
+	if( numMaterials == 0)
+		return;
+
+	// resize the scene's material list to offer enough space for the new materials
+	aiMaterial** prevMats = pScene->mMaterials;
+	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numMaterials];
+	if( prevMats)
+	{
+		memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*));
+		delete [] prevMats;
+	}
+
+	// convert all the materials given in the array
+	for( unsigned int a = 0; a < pMaterials.size(); a++)
+	{
+		const XFile::Material& oldMat = pMaterials[a];
+		if( oldMat.mIsReference)
+			continue;
+
+		Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+		aiString name;
+		name.Set( oldMat.mName);
+		mat->AddProperty( &name, AI_MATKEY_NAME);
+		// Shading model: hardcoded to PHONG, there is no such information in an XFile
+		int shadeMode = (int) aiShadingMode_Phong;
+		mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+		// material colours
+		mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+		mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+		mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+		mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+
+
+		// texture, if there is one
+		if (1 == oldMat.mTextures.size())
+			{
+			// 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];
+				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)
+					{
+					s2 = sz.find("BUMP",s);
+					if (std::string::npos == s2)
+						{
+						s2 = sz.find("Bump",s);
+						if (std::string::npos == s2)
+							{
+							s2 = sz.find("height",s);
+							if (std::string::npos == s2)
+								{
+								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_BUMP_ "[%i]",iHM++);
+					}
+				else
+					{
+					// Normal map
+					std::string::size_type s2 = sz.find("normal",s);
+					if (std::string::npos == s2)
+						{
+						s2 = sz.find("NORMAL",s);
+						if (std::string::npos == s2)
+							{
+							s2 = sz.find("nm",s); // not really unique
+							if (std::string::npos == s2)
+								{
+								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++);
+						}
+					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)
+							{
+							s2 = sz.find("Spec",s);
+							if (std::string::npos == s2)
+								{
+								s2 = sz.find("SPEC",s);
+								if (std::string::npos == s2)
+									{
+									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++);
+							}
+						else
+							{
+							// ambient color texture
+							std::string::size_type s2 = sz.find("ambi",s);
+							if (std::string::npos == s2)
+								{
+								s2 = sz.find("AMBI",s);
+								if (std::string::npos == s2)
+									{
+									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++);
+								}
+							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)
+											{
+											s2 = sz.find("Emissive",s);
+											}
+										}
+									}
+								if (std::string::npos != s2)
+									{
+									sprintf(key,AI_MATKEY_TEXTURE_EMISSIVE_ "[%i]",iEM++);
+									}
+								else
+									{
+									// assume it is a diffuse texture
+									sprintf(key,AI_MATKEY_TEXTURE_DIFFUSE_ "[%i]",iDM++);
+									}
+								}
+							}
+						}
+					}
+
+				aiString tex;
+				tex.Set( oldMat.mTextures[b] );
+
+				mat->AddProperty( &tex, key);
+				}
+
+			}
+		pScene->mMaterials[pScene->mNumMaterials] = mat;
+		mImportedMats[oldMat.mName] = pScene->mNumMaterials;
+		pScene->mNumMaterials++;
+	}
+}

+ 97 - 0
code/XFileImporter.h

@@ -0,0 +1,97 @@
+/** @file Definition of the XFile importer class. */
+#ifndef AI_XFILEIMPORTER_H_INC
+#define AI_XFILEIMPORTER_H_INC
+
+#include <map>
+
+#include "XFileHelper.h"
+#include "BaseImporter.h"
+
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+namespace Assimp
+{
+struct XFile::Scene;
+struct XFile::Node;
+
+// ---------------------------------------------------------------------------
+/** The XFileImporter is a worker class capable of importing a scene from a
+ * DirectX file .x
+ */
+class XFileImporter : public BaseImporter
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	XFileImporter();
+
+	/** Destructor, private as well */
+	~XFileImporter();
+
+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:
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. 
+	 * See BaseImporter::InternReadFile() for details
+	 */
+	void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+	// -------------------------------------------------------------------
+	/** Constructs the return data structure out of the imported data.
+	 * @param pScene The scene to construct the return data in.
+	 * @param pData The imported data in the internal temporary representation.
+	 */
+	void CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData);
+
+	// -------------------------------------------------------------------
+	/** Recursively creates scene nodes from the imported hierarchy. The meshes and materials
+	 * of the nodes will be extracted on the way.
+	 * @param pScene The scene to construct the return data in.
+	 * @param pParent The parent node where to create new child nodes
+	 * @param pNode The temporary node to copy.
+	 * @return The created node 
+	 */
+	aiNode* CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode);
+
+	// -------------------------------------------------------------------
+	/** Converts all meshes in the given mesh array. Each mesh is splitted up per material,
+	 * the indices of the generated meshes are stored in the node structure.
+	 * @param pScene The scene to construct the return data in.
+	 * @param pNode The target node structure that references the constructed meshes.
+	 * @param pMeshes The array of meshes to convert
+	 */
+	void CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes);
+
+	// -------------------------------------------------------------------
+	/** Converts the animations from the given imported data and creates them in the scene.
+	 * @param pScene The scene to hold to converted animations
+	 * @param pData The data to read the animations from
+	 */
+	void CreateAnimations( aiScene* pScene, const XFile::Scene* pData);
+
+	// -------------------------------------------------------------------
+	/** Converts all materials in the given array and stores them in the scene's material list.
+	 * @param pScene The scene to hold the converted materials.
+	 * @param pMaterials The material array to convert.
+	 */
+	void ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials);
+
+protected:
+	/** Buffer to hold the loaded file */
+	std::vector<char> mBuffer;
+
+	/** Imported materials: index in the scene's material list by name */
+	std::map<std::string, unsigned int> mImportedMats;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC

+ 1182 - 0
code/XFileParser.cpp

@@ -0,0 +1,1182 @@
+/** @file Implementation of the XFile parser helper class */
+#include "XFileParser.h"
+#include "XFileHelper.h"
+#include "BaseImporter.h"
+#include "fast_atof.h"
+
+#include <boost/format.hpp>
+
+using namespace Assimp;
+using namespace Assimp::XFile;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor. Creates a data structure out of the XFile given in the memory block. 
+XFileParser::XFileParser( const std::vector<char>& pBuffer)
+{
+	mMajorVersion = mMinorVersion = 0;
+	mIsBinaryFormat = false;
+	mBinaryNumCount = 0;
+	P = End = NULL;
+	mLineNumber = 0;
+	mScene = NULL;
+
+	// set up memory pointers
+	P = &pBuffer.front();
+	End = P + pBuffer.size();
+
+	// check header
+	if( strncmp( P, "xof ", 4) != 0)
+		throw new ImportErrorException( "Header mismatch, file is not an XFile.");
+
+	// read version. It comes in a four byte format such as "0302"
+	mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48);
+	mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48);
+
+	// read format
+	if( strncmp( P + 8, "txt ", 4) == 0)
+		mIsBinaryFormat = false;
+	else if( strncmp( P + 8, "bin ", 4) == 0)
+		mIsBinaryFormat = true;
+	else
+		ThrowException( "Unsupported xfile format");
+
+	// float size
+	mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000
+		+ (unsigned int)(P[13] - 48) * 100
+		+ (unsigned int)(P[14] - 48) * 10
+		+ (unsigned int)(P[15] - 48);
+
+	if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
+		ThrowException( boost::str( boost::format( "Unknown float size %1% specified in xfile header.") % mBinaryFloatSize));
+
+	// start reading here
+	P += 16;
+	ReadUntilEndOfLine();
+
+	mScene = new Scene;
+	ParseFile();
+
+	// filter the imported hierarchy for some degenerated cases
+	if( mScene->mRootNode)
+		FilterHierarchy( mScene->mRootNode);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor. Destroys all imported data along with it 
+XFileParser::~XFileParser()
+{
+	// kill everything we created
+	delete mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseFile()
+{
+	while( 1)
+	{
+		// read name of next object
+		std::string objectName = GetNextToken();
+		if (objectName.length() == 0)
+			break;
+
+		// parse specific object
+		if( objectName == "template")
+			ParseDataObjectTemplate();
+		else
+		if( objectName == "Frame")
+			ParseDataObjectFrame( NULL);
+		else
+		if( objectName == "Mesh")
+		{
+			// some meshes have no frames at all
+			Mesh* mesh = new Mesh;
+			ParseDataObjectMesh( mesh);
+			mScene->mGlobalMeshes.push_back( mesh);
+		} else
+		if( objectName == "AnimTicksPerSecond")
+			ParseDataObjectAnimTicksPerSecond();
+		else
+		if( objectName == "AnimationSet")
+			ParseDataObjectAnimationSet();
+		else
+		if( objectName == "Material")
+		{
+			// Material outside of a mesh or node
+			Material material; 
+			ParseDataObjectMaterial( &material);
+			mScene->mGlobalMaterials.push_back( material);
+		} else
+		if( objectName == "}")
+		{
+			// whatever?
+			// os::Printer::log("} found in dataObject", ELL_WARNING);
+		} else
+		{
+			// unknown format
+			//os::Printer::log("Unknown data object in animation of .x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTemplate()
+{
+	// parse a template data object. Currently not stored.
+	std::string name;
+	readHeadOfDataObject( &name);
+
+	// read GUID
+	std::string guid = GetNextToken();
+
+	// read and ignore data members
+	while(true)
+	{
+		std::string s = GetNextToken();
+
+		if( s == "}")
+			break;
+
+		if( s.length() == 0)
+			ThrowException( "Unexpected end of file reached while parsing template definition");
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectFrame( Node* pParent)
+{
+	// A coordinate frame, or "frame of reference." The Frame template
+	// is open and can contain any object. The Direct3D extensions (D3DX)
+	// mesh-loading functions recognize Mesh, FrameTransformMatrix, and
+	// Frame template instances as child objects when loading a Frame
+	// instance.
+	std::string name;
+	readHeadOfDataObject(&name);
+
+	// create a named node and place it at its parent, if given
+	Node* node = new Node( pParent);
+	node->mName = name;
+	if( pParent)
+	{
+		pParent->mChildren.push_back( node);
+	} else
+	{
+		// there might be multiple root nodes
+		if( mScene->mRootNode != NULL)
+		{
+			// place a dummy root if not there
+			if( mScene->mRootNode->mName != "$dummy_root")
+			{
+				Node* exroot = mScene->mRootNode;
+				mScene->mRootNode = new Node( NULL);
+				mScene->mRootNode->mName = "$dummy_root";
+				mScene->mRootNode->mChildren.push_back( exroot);
+				exroot->mParent = mScene->mRootNode;
+			}
+			// put the new node as its child instead
+			mScene->mRootNode->mChildren.push_back( node);
+			node->mParent = mScene->mRootNode;
+		} else
+		{
+			// it's the first node imported. place it as root
+			mScene->mRootNode = node;
+		}
+	}
+
+	// Now inside a frame.
+	// read tokens until closing brace is reached.
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+		if (objectName.size() == 0)
+			ThrowException( "Unexpected end of file reached while parsing frame");
+
+		if( objectName == "}")
+			break; // frame finished
+		else
+		if( objectName == "Frame")
+			ParseDataObjectFrame( node); // child frame
+		else
+		if( objectName == "FrameTransformMatrix")
+			ParseDataObjectTransformationMatrix( node->mTrafoMatrix);
+		else
+		if( objectName == "Mesh")
+		{
+			Mesh* mesh = new Mesh;
+			node->mMeshes.push_back( mesh);
+			ParseDataObjectMesh( mesh);
+		} else
+		{
+			// os::Printer::log("Unknown data object in frame in x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix)
+{
+	// read header, we're not interested if it has a name
+	readHeadOfDataObject();
+
+	// read its components
+	pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat();
+	pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat();
+	pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat();
+	pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat();
+	pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat();
+	pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat();
+	pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat();
+	pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat();
+
+	// trailing symbols
+	CheckForSemicolon();
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
+{
+	std::string name;
+	readHeadOfDataObject( &name);
+
+	// read vertex count
+	unsigned int numVertices = ReadInt();
+	pMesh->mPositions.resize( numVertices);
+
+	// read vertices
+	for( unsigned int a = 0; a < numVertices; a++)
+		pMesh->mPositions[a] = ReadVector3();
+
+	// read position faces
+	unsigned int numPosFaces = ReadInt();
+	pMesh->mPosFaces.resize( numPosFaces);
+	for( unsigned int a = 0; a < numPosFaces; a++)
+	{
+		unsigned int numIndices = ReadInt();
+		if( numIndices < 3)
+			ThrowException( boost::str( boost::format( "Invalid index count %1% for face %2%.") % numIndices % a));
+
+		// read indices
+		Face& face = pMesh->mPosFaces[a];
+		for( unsigned int b = 0; b < numIndices; b++)
+			face.mIndices.push_back( ReadInt());
+		CheckForSeparator();
+	}
+
+	// here, other data objects may follow
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+
+		if( objectName.size() == 0)
+			ThrowException( "Unexpected end of file while parsing mesh structure");
+		else
+		if( objectName == "}")
+			break; // mesh finished
+		else
+		if( objectName == "MeshNormals")
+			ParseDataObjectMeshNormals( pMesh);
+		else
+		if( objectName == "MeshTextureCoords")
+			ParseDataObjectMeshTextureCoords( pMesh);
+		else
+		if( objectName == "MeshVertexColors")
+			ParseDataObjectMeshVertexColors( pMesh);
+		else
+		if( objectName == "MeshMaterialList")
+			ParseDataObjectMeshMaterialList( pMesh);
+		else
+		if( objectName == "VertexDuplicationIndices")
+			ParseUnknownDataObject(); // we'll ignore vertex duplication indices
+		else
+		if( objectName == "XSkinMeshHeader")
+			ParseDataObjectSkinMeshHeader( pMesh);
+		else
+		if( objectName == "SkinWeights")
+			ParseDataObjectSkinWeights( pMesh);
+		else
+		{
+			//os::Printer::log("Unknown data object in mesh in x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh)
+{
+	readHeadOfDataObject();
+
+	std::string transformNodeName;
+	GetNextTokenAsString( transformNodeName);
+
+	pMesh->mBones.push_back( Bone());
+	Bone& bone = pMesh->mBones.back();
+	bone.mName = transformNodeName;
+
+	// read vertex weights
+	unsigned int numWeights = ReadInt();
+	bone.mWeights.reserve( numWeights);
+
+	for( unsigned int a = 0; a < numWeights; a++)
+	{
+		BoneWeight weight;
+		weight.mVertex = ReadInt();
+		bone.mWeights.push_back( weight);
+	}
+
+	// read vertex weights
+	for( unsigned int a = 0; a < numWeights; a++)
+		bone.mWeights[a].mWeight = ReadFloat();
+
+	// read matrix offset
+	bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat();
+	bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat();
+	bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat();
+	bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat();
+	bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat();
+	bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat();
+	bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat();
+	bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat();
+
+	CheckForSemicolon();
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* pMesh)
+{
+	readHeadOfDataObject();
+
+	unsigned int maxSkinWeightsPerVertex = ReadInt();
+	unsigned int maxSkinWeightsPerFace = ReadInt();
+	unsigned int numBonesInMesh = ReadInt();
+
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh)
+{
+	readHeadOfDataObject();
+
+	// read count
+	unsigned int numNormals = ReadInt();
+	pMesh->mNormals.resize( numNormals);
+
+	// read normal vectors
+	for( unsigned int a = 0; a < numNormals; a++)
+		pMesh->mNormals[a] = ReadVector3();
+
+	// read normal indices
+	unsigned int numFaces = ReadInt();
+	if( numFaces != pMesh->mPosFaces.size())
+		ThrowException( "Normal face count does not match vertex face count.");
+
+	for( unsigned int a = 0; a < numFaces; a++)
+	{
+		unsigned int numIndices = ReadInt();
+		pMesh->mNormFaces.push_back( Face());
+		Face& face = pMesh->mNormFaces.back();
+
+		for( unsigned int b = 0; b < numIndices; b++)
+			face.mIndices.push_back( ReadInt());
+
+		CheckForSeparator();
+	}
+
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh)
+{
+	readHeadOfDataObject();
+	std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++];
+
+	unsigned int numCoords = ReadInt();
+	if( numCoords != pMesh->mPositions.size())
+		ThrowException( "Texture coord count does not match vertex count");
+
+	coords.resize( numCoords);
+	for( unsigned int a = 0; a < numCoords; a++)
+		coords[a] = ReadVector2();
+
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh)
+{
+	readHeadOfDataObject();
+	std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++];
+
+	unsigned int numColors = ReadInt();
+	if( numColors != pMesh->mPositions.size())
+		ThrowException( "Vertex color count does not match vertex count");
+
+	colors.resize( numColors, aiColor4D( 0, 0, 0, 1));
+	for( unsigned int a = 0; a < numColors; a++)
+	{
+		unsigned int index = ReadInt();
+		if( index >= pMesh->mPositions.size())
+			ThrowException( "Vertex color index out of bounds");
+
+		colors[index] = ReadRGBA();
+	}
+
+	CheckForSemicolon();
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
+{
+	readHeadOfDataObject();
+
+	// read material count
+	unsigned int numMaterials = ReadInt();
+	// read non triangulated face material index count
+	unsigned int numMatIndices = ReadInt();
+
+	// some models have a material index count of 1... to be able to read them we
+	// replicate this single material index on every face
+	if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
+		ThrowException( "Per-Face material index count does not match face count.");
+
+	// read per-face material indices
+	for( unsigned int a = 0; a < numMatIndices; a++)
+		pMesh->mFaceMaterials.push_back( ReadInt());
+
+	// in version 03.02, the face indices end with two semicolons.
+	// commented out version check, as version 03.03 exported from blender also has 2 semicolons
+	if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
+	{
+		if( *P == ';')
+			++P;
+	}
+
+	// if there was only a single material index, replicate it on all faces
+	while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
+		pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front());
+
+	// read following data objects
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+		if( objectName.size() == 0)
+			ThrowException( "Unexpected end of file while parsing mesh material list.");
+		else
+		if( objectName == "}")
+			break; // material list finished
+		else
+		if( objectName == "{")
+		{
+			// template materials 
+			std::string matName = GetNextToken();
+			Material material;
+			material.mIsReference = true;
+			material.mName = matName;
+			pMesh->mMaterials.push_back( material);
+
+			CheckForClosingBrace(); // skip }
+		} else
+		if( objectName == "Material")
+		{
+			pMesh->mMaterials.push_back( Material());
+			ParseDataObjectMaterial( &pMesh->mMaterials.back());
+		} else
+		if( objectName == ";")
+		{
+			// ignore
+		} else
+		{
+			// os::Printer::log("Unknown data object in material list in x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
+{
+	std::string matName;
+	readHeadOfDataObject( &matName);
+	pMaterial->mName = matName;
+	pMaterial->mIsReference = false;
+
+	// read material values
+	pMaterial->mDiffuse = ReadRGBA(); 
+	pMaterial->mSpecularExponent = ReadFloat();
+	pMaterial->mSpecular = ReadRGB(); 
+	pMaterial->mEmissive = ReadRGB(); 
+
+	// read other data objects
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+		if( objectName.size() == 0)
+			ThrowException( "Unexpected end of file while parsing mesh material");
+		else
+		if( objectName == "}")
+			break; // material finished
+		else
+		if( objectName == "TextureFilename" || objectName == "TextureFileName")
+		{
+			// some exporters write "TextureFileName" instead.
+			std::string texname;
+			ParseDataObjectTextureFilename( texname);
+			pMaterial->mTextures.push_back( texname);
+		} else
+		{
+			// os::Printer::log("Unknown data object in material in .x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimTicksPerSecond()
+{
+	readHeadOfDataObject();
+	mScene->mAnimTicksPerSecond = ReadInt();
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationSet()
+{
+	std::string animName;
+	readHeadOfDataObject( &animName);
+
+	Animation* anim = new Animation;
+	mScene->mAnims.push_back( anim);
+	anim->mName = animName;
+
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+		if( objectName.length() == 0)
+			ThrowException( "Unexpected end of file while parsing animation set.");
+		else
+		if( objectName == "}")
+			break; // animation set finished
+		else
+		if( objectName == "Animation")
+			ParseDataObjectAnimation( anim);
+		else
+		{
+			// os::Printer::log("Unknown data object in animation set in x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
+{
+	readHeadOfDataObject();
+	AnimBone* banim = new AnimBone;
+	pAnim->mAnims.push_back( banim);
+
+	while(true)
+	{
+		std::string objectName = GetNextToken();
+
+		if( objectName.length() == 0)
+			ThrowException( "Unexpected end of file while parsing animation.");
+		else
+		if( objectName == "}")
+			break; // animation finished
+		else
+		if( objectName == "AnimationKey")
+			ParseDataObjectAnimationKey( banim);
+		else
+		if( objectName == "AnimationOptions")
+			ParseUnknownDataObject(); // not interested
+		else
+		if( objectName == "{")
+		{
+			// read frame name
+			banim->mBoneName = GetNextToken();
+			CheckForClosingBrace();
+		} else
+		{
+			//os::Printer::log("Unknown data object in animation in x file", objectName.c_str(), ELL_WARNING);
+			ParseUnknownDataObject();
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone)
+{
+	readHeadOfDataObject();
+
+	// read key type
+	unsigned int keyType = ReadInt();
+
+	// read number of keys
+	unsigned int numKeys = ReadInt();
+
+	for( unsigned int a = 0; a < numKeys; a++)
+	{
+		// read time
+		unsigned int time = ReadInt();
+
+		// read keys
+		switch( keyType)
+		{
+			case 0: // rotation quaternion
+			{
+				// read count
+				if( ReadInt() != 4)
+					ThrowException( "Invalid number of arguments for quaternion key in animation");
+
+				aiQuatKey key;
+				key.mTime = double( time);
+				key.mValue.w = ReadFloat();
+				key.mValue.x = ReadFloat();
+				key.mValue.y = ReadFloat();
+				key.mValue.z = ReadFloat();
+				pAnimBone->mRotKeys.push_back( key);
+
+				CheckForSemicolon();
+				break;
+			}
+
+			case 1: // scale vector
+			case 2: // position vector
+			{
+				// read count
+				if( ReadInt() != 3)
+					ThrowException( "Invalid number of arguments for vector key in animation");
+
+				aiVectorKey key;
+				key.mTime = double( time);
+				key.mValue = ReadVector3();
+
+				if( keyType == 2)
+					pAnimBone->mPosKeys.push_back( key);
+				else
+					pAnimBone->mScaleKeys.push_back( key);
+
+				break;
+			}
+
+			case 3: // combined transformation matrix
+			case 4: // denoted both as 3 or as 4
+			{
+				// read count
+				if( ReadInt() != 16)
+					ThrowException( "Invalid number of arguments for matrix key in animation");
+
+				// read matrix
+				MatrixKey key;
+				key.mTime = double( time);
+				key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat();
+				key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat();
+				key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat();
+				key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat();
+				key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat();
+				key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat();
+				key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat();
+				key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat();
+				pAnimBone->mTrafoKeys.push_back( key);
+
+				CheckForSemicolon();
+				break;
+			}
+
+			default:
+				ThrowException( boost::str( boost::format( "Unknown key type %1% in animation.") % keyType));
+				break;
+		} // end switch
+
+		// key separator
+		CheckForSeparator();
+	}
+
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
+{
+	readHeadOfDataObject();
+	GetNextTokenAsString( pName);
+	CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseUnknownDataObject()
+{
+	// find opening delimiter
+	while( true)
+	{
+		std::string t = GetNextToken();
+		if( t.length() == 0)
+			ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+		if( t == "{")
+			break;
+	}
+
+	unsigned int counter = 1;
+
+	// parse until closing delimiter
+	while( counter > 0)
+	{
+		std::string t = GetNextToken();
+
+		if( t.length() == 0)
+			ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+		if( t == "{")
+			++counter;
+		else
+		if( t == "}")
+			--counter;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for closing curly brace
+void XFileParser::CheckForClosingBrace()
+{
+	if( GetNextToken() != "}")
+		ThrowException( "Closing brace expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for one following semicolon
+void XFileParser::CheckForSemicolon()
+{
+	if( mIsBinaryFormat)
+		return;
+
+	if( GetNextToken() != ";")
+		ThrowException( "Semicolon expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for a separator char, either a ',' or a ';'
+void XFileParser::CheckForSeparator()
+{
+	if( mIsBinaryFormat)
+		return;
+
+	std::string token = GetNextToken();
+	if( token != "," && token != ";")
+		ThrowException( "Separator character (';' or ',') expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::readHeadOfDataObject( std::string* poName)
+{
+	std::string nameOrBrace = GetNextToken();
+	if( nameOrBrace != "{")
+	{
+		if( poName)
+			*poName = nameOrBrace;
+
+		if( GetNextToken() != "{")
+			ThrowException( "Opening brace expected.");
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string XFileParser::GetNextToken()
+{
+	std::string s;
+
+	// process binary-formatted file
+	if( mIsBinaryFormat)
+	{
+		// in binary mode it will only return NAME and STRING token
+		// and (correctly) skip over other tokens.
+
+		unsigned int tok = ReadBinWord();
+		unsigned int len;
+
+		// standalone tokens
+		switch( tok) 
+		{
+			case 1:
+				// name token
+				len = ReadBinDWord();
+				s = std::string(P, len);
+				P += len;
+				return s;
+			case 2:
+				// string token
+				len = ReadBinDWord();
+				s = std::string(P, len);
+				P += (len + 2);
+				return s;
+			case 3:
+				// integer token
+				P += 4;
+				return "<integer>";
+			case 5:
+				// GUID token
+				P += 16;
+				return "<guid>";
+			case 6:
+				len = ReadBinDWord();
+				P += (len * 4);
+				return "<int_list>";
+			case 7:
+				len = ReadBinDWord();
+				P += (len * mBinaryFloatSize);
+				return "<flt_list>";
+			case 0x0a:
+				return "{";
+			case 0x0b:
+				return "}";
+			case 0x0c:
+				return "(";
+			case 0x0d:
+				return ")";
+			case 0x0e:
+				return "[";
+			case 0x0f:
+				return "]";
+			case 0x10:
+				return "<";
+			case 0x11:
+				return ">";
+			case 0x12:
+				return ".";
+			case 0x13:
+				return ",";
+			case 0x14:
+				return ";";
+			case 0x1f:
+				return "template";
+			case 0x28:
+				return "WORD";
+			case 0x29:
+				return "DWORD";
+			case 0x2a:
+				return "FLOAT";
+			case 0x2b:
+				return "DOUBLE";
+			case 0x2c:
+				return "CHAR";
+			case 0x2d:
+				return "UCHAR";
+			case 0x2e:
+				return "SWORD";
+			case 0x2f:
+				return "SDWORD";
+			case 0x30:
+				return "void";
+			case 0x31:
+				return "string";
+			case 0x32:
+				return "unicode";
+			case 0x33:
+				return "cstring";
+			case 0x34:
+				return "array";
+		}
+	}
+	// process text-formatted file
+	else
+	{
+		FindNextNoneWhiteSpace();
+		if( P >= End)
+			return s;
+
+		while( (P < End) && !isspace( (unsigned char) *P))
+		{
+			// either keep token delimiters when already holding a token, or return if first valid char
+			if( *P == ';' || *P == '}' || *P == '{' || *P == ',')
+			{
+				if( !s.size())
+					s.append( P++, 1);
+				break; // stop for delimiter
+			}
+			s.append( P++, 1);
+		}
+	}
+	return s;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::FindNextNoneWhiteSpace()
+{
+	if( mIsBinaryFormat)
+		return;
+
+	while( true)
+	{
+		while( P < End && isspace( (unsigned char) *P))
+		{
+			if( *P == '\n')
+				mLineNumber++;
+			++P;
+		}
+
+		if( P >= End)
+			return;
+
+		// check if this is a comment
+		if( (P[0] == '/' && P[1] == '/') ||	P[0] == '#')
+			ReadUntilEndOfLine();
+		else
+			break;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::GetNextTokenAsString( std::string& poString)
+{
+	if( mIsBinaryFormat)
+	{
+		poString = GetNextToken();
+		return;
+	}
+
+	FindNextNoneWhiteSpace();
+	if( P >= End)
+		ThrowException( "Unexpected end of file while parsing string");
+
+	if( *P != '"')
+		ThrowException( "Expected quotation mark.");
+	++P;
+
+	while( P < End && *P != '"')
+		poString.append( P++, 1);
+
+	if( P >= End-1)
+		ThrowException( "Unexpected end of file while parsing string");
+
+	if( P[1] != ';' || P[0] != '"')
+		ThrowException( "Expected quotation mark and semicolon at the end of a string.");
+	P+=2;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ReadUntilEndOfLine()
+{
+	if( mIsBinaryFormat)
+		return;
+
+	while( P < End)
+	{
+		if( *P == '\n' || *P == '\r')
+		{
+			++P; mLineNumber++;
+			return;
+		}
+
+		++P;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned short XFileParser::ReadBinWord()
+{
+	const unsigned char* q = (const unsigned char*) P;
+	unsigned short tmp = q[0] | (q[1] << 8);
+	P += 2;
+	return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadBinDWord()
+{
+	const unsigned char* q = (const unsigned char*) P;
+	unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
+	P += 4;
+	return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadInt()
+{
+	if( mIsBinaryFormat)
+	{
+		if( mBinaryNumCount == 0)
+		{
+			unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
+			if( tmp == 0x06) // array of ints follows
+				mBinaryNumCount = ReadBinDWord();
+			else // single int follows
+				mBinaryNumCount = 1; 
+		}
+
+		--mBinaryNumCount;
+		return ReadBinDWord();
+	} else
+	{
+		FindNextNoneWhiteSpace();
+		// check preceeding minus sign
+		bool isNegative = false;
+		if( *P == '-')
+		{
+			isNegative = true;
+			P++;
+		}
+
+		// at least one digit expected
+		if( !isdigit( *P))
+			ThrowException( "Number expected.");
+
+		// read digits
+		unsigned int number = 0;
+		while( P < End)
+		{
+			if( !isdigit( *P))
+				break;
+			number = number * 10 + (*P - 48);
+			P++;
+		}
+		
+		CheckForSeparator();
+
+		return isNegative ? ((unsigned int) -int( number)) : number;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+float XFileParser::ReadFloat()
+{
+	if( mIsBinaryFormat)
+	{
+		if( mBinaryNumCount == 0)
+		{
+			unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
+			if( tmp == 0x07) // array of floats following
+				mBinaryNumCount = ReadBinDWord();
+			else // single float following
+				mBinaryNumCount = 1; 
+		}
+
+		--mBinaryNumCount;
+		if( mBinaryFloatSize == 8)
+		{
+			float result = (float) (*(double*) P);
+			P += 8;
+			return result;
+		} else
+		{
+			float result = *(float*) P;
+			P += 4;
+			return result;
+		}
+	}
+
+	// text version
+	FindNextNoneWhiteSpace();
+	// check for various special strings to allow reading files from faulty exporters
+	// I mean you, Blender!
+	if( strncmp( P, "-1.#IND00", 9) == 0)
+	{ 
+		P += 9;
+		CheckForSeparator();
+		return 0.0f;
+	} else
+	if( strncmp( P, "1.#QNAN0", 8) == 0)
+	{
+		P += 8;
+		CheckForSeparator();
+		return 0.0f;
+	}
+
+	float result = 0.0f;
+	P = fast_atof_move( P, result);
+
+	CheckForSeparator();
+
+	return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D XFileParser::ReadVector2()
+{
+	aiVector2D vector;
+	vector.x = ReadFloat();
+	vector.y = ReadFloat();
+	CheckForSeparator();
+
+	return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D XFileParser::ReadVector3()
+{
+	aiVector3D vector;
+	vector.x = ReadFloat();
+	vector.y = ReadFloat();
+	vector.z = ReadFloat();
+	CheckForSeparator();
+
+	return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor4D XFileParser::ReadRGBA()
+{
+	aiColor4D color;
+	color.r = ReadFloat();
+	color.g = ReadFloat();
+	color.b = ReadFloat();
+	color.a = ReadFloat();
+	CheckForSeparator();
+
+	return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor3D XFileParser::ReadRGB()
+{
+	aiColor3D color;
+	color.r = ReadFloat();
+	color.g = ReadFloat();
+	color.b = ReadFloat();
+	CheckForSeparator();
+
+	return color;
+}
+
+// Throws an exception with a line number and the given text.
+void XFileParser::ThrowException( const std::string& pText)
+{
+	if( mIsBinaryFormat)
+		throw new ImportErrorException( pText);
+	else
+		throw new ImportErrorException( boost::str( boost::format( "Line %d: %s") % mLineNumber % pText));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Filters the imported hierarchy for some degenerated cases that some exporters produce.
+void XFileParser::FilterHierarchy( XFile::Node* pNode)
+{
+	// if the node has just a single unnamed child containing a mesh, remove
+	// the anonymous node inbetween. The 3DSMax kwXport plugin seems to produce this
+	// mess in some cases
+	if( pNode->mChildren.size() == 1)
+	{
+		XFile::Node* child = pNode->mChildren.front();
+		if( child->mName.length() == 0 && child->mMeshes.size() > 0)
+		{
+			// transfer its meshes to us
+			for( unsigned int a = 0; a < child->mMeshes.size(); a++)
+				pNode->mMeshes.push_back( child->mMeshes[a]);
+			child->mMeshes.clear();
+
+			// then kill it
+			delete child;
+			pNode->mChildren.clear();
+		}
+	}
+
+	// recurse
+	for( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+		FilterHierarchy( pNode->mChildren[a]);
+}

+ 119 - 0
code/XFileParser.h

@@ -0,0 +1,119 @@
+/** @file Helper class to parse a XFile into a temporary structure */
+#ifndef AI_XFILEPARSER_H_INC
+#define AI_XFILEPARSER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+
+namespace Assimp
+{
+	namespace XFile
+	{
+		struct Node;
+		struct Mesh;
+		struct Scene;
+		struct Material;
+		struct Animation;
+		struct AnimBone;
+	}
+
+/** The XFileParser reads a XFile either in text or binary form and builds a temporary 
+ * data structure out of it. 
+ */
+class XFileParser
+{
+public:
+	/** Constructor. Creates a data structure out of the XFile given in the memory block. 
+	 * @param pBuffer Memory buffer containing the XFile
+	 */
+	XFileParser( const std::vector<char>& pBuffer);
+
+	/** Destructor. Destroys all imported data along with it */
+	~XFileParser();
+
+	/** Returns the temporary representation of the imported data */
+	const XFile::Scene* GetImportedData() const { return mScene; }
+
+protected:
+	void ParseFile();
+	void ParseDataObjectTemplate();
+	void ParseDataObjectFrame( XFile::Node *pParent);
+	void ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix);
+	void ParseDataObjectMesh( XFile::Mesh* pMesh);
+	void ParseDataObjectSkinWeights( XFile::Mesh* pMesh);
+	void ParseDataObjectSkinMeshHeader( XFile::Mesh* pMesh);
+	void ParseDataObjectMeshNormals( XFile::Mesh* pMesh);
+	void ParseDataObjectMeshTextureCoords( XFile::Mesh* pMesh);
+	void ParseDataObjectMeshVertexColors( XFile::Mesh* pMesh);
+	void ParseDataObjectMeshMaterialList( XFile::Mesh* pMesh);
+	void ParseDataObjectMaterial( XFile::Material* pMaterial);
+	void ParseDataObjectAnimTicksPerSecond();
+	void ParseDataObjectAnimationSet();
+	void ParseDataObjectAnimation( XFile::Animation* pAnim);
+	void ParseDataObjectAnimationKey( XFile::AnimBone *pAnimBone);
+	void ParseDataObjectTextureFilename( std::string& pName);
+	void ParseUnknownDataObject();
+
+	//! places pointer to next begin of a token, and ignores comments
+	void FindNextNoneWhiteSpace();
+
+	//! returns next parseable token. Returns empty string if no token there
+	std::string GetNextToken();
+
+	//! reads header of dataobject including the opening brace.
+	//! returns false if error happened, and writes name of object
+	//! if there is one
+	void readHeadOfDataObject( std::string* poName = NULL);
+
+	//! checks for closing curly brace, throws exception if not there
+	void CheckForClosingBrace();
+
+	//! checks for one following semicolon, throws exception if not there
+	void CheckForSemicolon();
+
+	//! checks for a separator char, either a ',' or a ';'
+	void CheckForSeparator();
+
+	//! reads a x file style string
+	void GetNextTokenAsString( std::string& poString);
+
+	void ReadUntilEndOfLine();
+
+	unsigned short ReadBinWord();
+	unsigned int ReadBinDWord();
+	unsigned int ReadInt();
+	float ReadFloat();
+	aiVector2D ReadVector2();
+	aiVector3D ReadVector3();
+	aiColor3D ReadRGB();
+	aiColor4D ReadRGBA();
+
+	/** Throws an exception with a line number and the given text. */
+	void ThrowException( const std::string& pText);
+
+	/** Filters the imported hierarchy for some degenerated cases that some exporters produce.
+	 * @param pData The sub-hierarchy to filter
+	 */
+	void FilterHierarchy( XFile::Node* pNode);
+
+protected:
+	unsigned int mMajorVersion, mMinorVersion; ///< version numbers
+	bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form
+	unsigned int mBinaryFloatSize; ///< float size, either 32 or 64 bits
+	// counter for number arrays in binary format
+	unsigned int mBinaryNumCount;
+
+	const char* P;
+	const char* End;
+
+	/// Line number when reading in text format
+	unsigned int mLineNumber;
+
+	/// Imported data
+	XFile::Scene* mScene;	
+};
+
+}
+#endif // AI_XFILEPARSER_H_INC

+ 26 - 0
code/aiAssert.cpp

@@ -0,0 +1,26 @@
+#include <iostream>
+#include "aiAssert.h"
+#ifdef _WIN32
+#ifndef __GNUC__
+#  include "crtdbg.h"
+#endif //ndef gcc
+#endif
+
+// Set a breakpoint using win32, else line, file and message will be returned and progam ends with 
+// errrocode = 1
+void Assimp::aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file)
+{
+	if (!expression)
+	{
+		std::cerr << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
+
+#ifdef _WIN32
+#ifndef __GNUC__
+		// Set breakpoint
+		__debugbreak();
+#endif //ndef gcc
+#else
+		exit (1);
+#endif
+	}
+}

+ 114 - 0
code/fast_atof.h

@@ -0,0 +1,114 @@
+// Copyright (C) 2002-2007 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine" and the "irrXML" project.
+// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
+// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
+// float inside a large string. Before parsing, it does a strlen on the given point.
+
+#ifndef __FAST_A_TO_F_H_INCLUDED__
+#define __FAST_A_TO_F_H_INCLUDED__
+
+#include <math.h>
+
+namespace Assimp
+{
+
+const float fast_atof_table[16] =	{  // we write [16] here instead of [] to work around a swig bug
+	0.f,
+	0.1f,
+	0.01f,
+	0.001f,
+	0.0001f,
+	0.00001f,
+	0.000001f,
+	0.0000001f,
+	0.00000001f,
+	0.000000001f,
+	0.0000000001f,
+	0.00000000001f,
+	0.000000000001f,
+	0.0000000000001f,
+	0.00000000000001f,
+	0.000000000000001f
+};
+
+inline unsigned int strtol10( const char* in, const char** out=0)
+{
+	unsigned int value = 0;
+
+	while ( 1 )
+	{
+		if ( *in < '0' || *in > '9' )
+			break;
+
+		value = ( value * 10 ) + ( *in - '0' );
+		++in;
+	}
+	if (out)
+		*out = in;
+	return value;
+}
+
+//! Provides a fast function for converting a string into a float,
+//! about 6 times faster than atof in win32.
+// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
+inline const char* fast_atof_move( const char* c, float& out)
+{
+	bool inv = false;
+	const char *t;
+	float f;
+
+	if (*c=='-')
+	{
+		++c;
+		inv = true;
+	}
+
+	//f = (float)strtol(c, &t, 10);
+	f = (float) strtol10 ( c, &c );
+
+	if (*c == '.')
+	{
+		++c;
+
+		//float pl = (float)strtol(c, &t, 10);
+		float pl = (float) strtol10 ( c, &t );
+		pl *= fast_atof_table[t-c];
+
+		f += pl;
+
+		c = t;
+
+		if (*c == 'e')
+		{
+			++c;
+			//float exp = (float)strtol(c, &t, 10);
+			bool einv = (*c=='-');
+			if (einv)
+				++c;
+
+			float exp = (float)strtol10(c, &c);
+			if (einv)
+				exp *= -1.0f;
+
+			f *= pow(10.0f, exp);
+		}
+	}
+
+	if (inv)
+		f *= -1.0f;
+	
+	out = f;
+	return c;
+}
+
+inline float fast_atof(const char* c)
+{
+	float ret;
+	fast_atof_move(c, ret);
+	return ret;
+}
+
+} // end of namespace Assimp
+
+#endif
+

BIN
doc/ImporterNotes.rtf


+ 83 - 0
doc/datastructure.xml

@@ -0,0 +1,83 @@
+<importedScene alias data alias Model?>
+  <version 1.2 />   <smiley code=":-)" />
+  <frame alias object>
+    <name/>
+    <transformation matrix4x4 />
+
+    <mesh id="1" />
+    <mesh ... />
+    
+    <childframe alias childobject as type frame alias object />
+    <childframe... />
+  </frame>
+  <meshes>
+    <mesh id="1">
+      <meshheader with MatID, Vertexcount, Facecount, a.s.o. />
+      <vertice>
+          <vertex xyzw />
+          <vertex ... />
+      </vertice>
+      <texturecoords>
+          <texturecoord uv />
+          <texturecoord ... />
+      </texturcoords>
+      <vertexcolors>
+          <color rgba />
+          <color ... />
+      </vertexcolors>
+      <normals>
+          <normal xyz />
+          <normal ... />
+      </normals>
+      <faces>
+          <face p1, p2, p3 />
+          <face ... />
+      </faces>      
+      <bones>
+        <bone>
+          <name />
+          <weights>
+            <weight v, w />
+            <weight... />
+          </weights>
+        </bone>
+        <bone ... />
+      </bones>      
+    </mesh>
+    <mesh id="x" ... />
+  </meshes>
+  <anims>
+    <anim name="rudern" duration="4500" ticksPerSecond="50">
+      <animbone name="bla">
+        <rotation>
+          <rotkey time="0" rot="xyzw" />
+          <rotkey time="100" rot="xyzw" />
+          ...
+        </rotation>
+        <translation>
+          <transkey time="0" trans="xyz" />
+          <transkey time="100" trans="xyz" />
+          ...
+        </translation>
+        <scaling>
+          <scalekey time="0" scale="xyz" />
+          <scalekey time="100" scale="xyz" />
+          ...
+        <scaling>
+      </animbone>
+      <animbone name="blubb">
+        ...
+      </animbone>
+    </anim>
+    <anim ... />
+  </anims>
+  <materials>
+    <material>
+       <param>
+          <name/>
+          <value/>
+       </param>
+    </material>
+    <material ... />
+  </materials>
+</importedScene alias data alias model?>

+ 222 - 0
include/Compiler/VisualStudio/stdint.h

@@ -0,0 +1,222 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. 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.
+// 
+//   3. The name of the author may be used to endorse or promote products
+//      derived from this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#if (_MSC_VER < 1300) && defined(__cplusplus)
+   extern "C++" {
+#endif 
+#     include <wchar.h>
+#if (_MSC_VER < 1300) && defined(__cplusplus)
+   }
+#endif
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+typedef __int8            int8_t;
+typedef __int16           int16_t;
+typedef __int32           int32_t;
+typedef __int64           int64_t;
+typedef unsigned __int8   uint8_t;
+typedef unsigned __int16  uint16_t;
+typedef unsigned __int32  uint32_t;
+typedef unsigned __int64  uint64_t;
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef __int64           intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef int               intptr_t;
+   typedef unsigned int      uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C   INT64_C
+#define UINTMAX_C  UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]

+ 111 - 0
include/IOStream.h

@@ -0,0 +1,111 @@
+
+/** @file File I/O wrappers for C++. Use interfaces instead of function
+* pointers to be sure even the silliest men on earth can work with this
+*/
+
+#ifndef AI_IOSTREAM_H_INC
+#define AI_IOSTREAM_H_INC
+
+#include <string>
+#include <stddef.h>
+
+#include "aiTypes.h"
+#include "aiFileIO.h"
+
+#ifndef __cplusplus
+#error This header requires C++ to be used.
+#endif
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** Class to handle file I/O for C++
+*
+* Derive an own implementation from this interface to provide custom IO handling
+* to the Importer. If you implement this interface, be sure to also provide an
+* implementation for IOSystem that creates instances of your custom IO class.
+*/
+// ---------------------------------------------------------------------------
+class IOStream 
+{
+protected:
+	/** Constructor protected, use IOSystem::Open() to create an instance. */
+	IOStream(void);
+
+public:
+	// -------------------------------------------------------------------
+	/** Destructor. Deleting the object closes the underlying file, 
+	 * alternatively you may use IOSystem::Close() to release the file. 
+	 */
+	virtual ~IOStream(void);
+
+	// -------------------------------------------------------------------
+	/** Read from the file
+	*
+	* See fread() for more details
+	* This fails for write-only files
+	*/
+	// -------------------------------------------------------------------
+    virtual size_t Read(
+		void* pvBuffer, 
+		size_t pSize, 
+		size_t pCount) = 0;
+
+
+	// -------------------------------------------------------------------
+	/** Write to the file
+	*
+	* See fwrite() for more details
+	* This fails for read-only files
+	*/
+	// -------------------------------------------------------------------
+    virtual size_t Write(
+		const void* pvBuffer, 
+		size_t pSize,
+		size_t pCount) = 0;
+
+
+	// -------------------------------------------------------------------
+	/** Set the read/write cursor of the file
+	*
+	* See fseek() for more details
+	*/
+	// -------------------------------------------------------------------
+	virtual aiReturn Seek(
+		size_t pOffset,
+		aiOrigin pOrigin) = 0;
+
+
+	// -------------------------------------------------------------------
+	/** Get the current position of the read/write cursor
+	*
+	* See ftell() for more details
+	*/
+	// -------------------------------------------------------------------
+    virtual size_t Tell(void) const = 0;
+
+	// -------------------------------------------------------------------
+	/**	Returns filesize
+	*
+	*	Returns the filesize
+	*/
+	// -------------------------------------------------------------------
+	virtual size_t	FileSize() const = 0;
+};
+// ----------------------------------------------------------------------------
+inline IOStream::IOStream()
+{
+	// empty
+}
+
+// ----------------------------------------------------------------------------
+inline IOStream::~IOStream()
+{
+	// empty
+}
+// ----------------------------------------------------------------------------
+
+} //!ns Assimp
+
+#endif //!!AI_IOSTREAM_H_INC

+ 90 - 0
include/IOSystem.h

@@ -0,0 +1,90 @@
+/** @file Filesystem wrapper for C++. Inherit this class to supply custom file handling
+ * logic to the Import library.
+*/
+
+#ifndef AI_IOSYSTEM_H_INC
+#define AI_IOSYSTEM_H_INC
+
+#ifndef __cplusplus
+#error This header requires C++ to be used.
+#endif
+
+#include <string>
+
+namespace Assimp
+{
+
+class IOStream;
+
+// ---------------------------------------------------------------------------
+/** Interface to the file system.
+*
+* Derive an own implementation from this interface to supply custom file handling
+* to the importer library. If you implement this interface, you also want to
+* supply a custom implementation for IOStream.
+*/
+class IOSystem
+{
+public:
+	/** Constructor. Create an instance of your derived class and assign it to 
+	 * the #Importer instance by calling Importer::SetIOHandler().
+	 */
+	IOSystem();
+
+	/** Destructor. */
+	virtual ~IOSystem();
+
+	// -------------------------------------------------------------------
+	/** Tests for the existence of a file at the given path. 
+	*
+	* @param pFile Path to the file
+	* @return true if there is a file with this path, else false.
+	*/
+	virtual bool Exists( const std::string& pFile) const = 0;
+
+	// -------------------------------------------------------------------
+	/**	Returns the system specific directory separator
+	*	@return	System specific directory separator
+	*/
+	virtual std::string getOsSeparator() const = 0;
+
+	// -------------------------------------------------------------------
+	/** Open a new file with a given path. When the access to the file is finished,
+	* call Close() to release all associated resources.
+	*
+	* @param pFile Path to the file
+	* @param pMode Desired file I/O mode. Required are: "wb", "w", "wt",
+	*        "rb", "r", "rt".
+	*
+	* @return New IOStream interface allowing the lib to access
+	*         the underlying file. 
+	* @note When implementing this class to provide custom IO handling, you propably
+	* have to supply an own implementation of IOStream as well. 
+	*/
+	virtual IOStream* Open(
+		const std::string& pFile,
+		const std::string& pMode = std::string("rb")) = 0;
+
+	// -------------------------------------------------------------------
+	/** Closes the given file and releases all resources associated with it.
+	 * @param pFile The file instance previously created by Open().
+	 */
+	virtual void Close( IOStream* pFile) = 0;
+};
+
+// ----------------------------------------------------------------------------
+inline IOSystem::IOSystem() 
+{
+	// empty
+}
+
+// ----------------------------------------------------------------------------
+inline IOSystem::~IOSystem() 
+{
+	// empty
+}
+// ----------------------------------------------------------------------------
+
+} //!ns Assimp
+
+#endif //AI_IOSYSTEM_H_INC

+ 68 - 0
include/ObjFileParser.h

@@ -0,0 +1,68 @@
+#ifndef OBJ_FILEPARSER_H_INC
+#define OBJ_FILEPARSER_H_INC
+
+#include <vector>
+#include <string>
+#include "aiTypes.h"
+
+/*struct aiVector2D_t;
+struct aiVector3D_t;*/
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Model;
+struct Object;
+struct Material;
+struct Point3;
+struct Point2;
+}
+class ObjFileImporter;
+
+class ObjFileParser
+{
+public:
+	static const size_t BUFFERSIZE = 1024;
+	typedef std::vector<char> DataArray;
+	typedef std::vector<char>::iterator DataArrayIt;
+	typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+public:
+	ObjFileParser(std::vector<char> &Data, const std::string &strAbsPath, const std::string &strModelName);
+	~ObjFileParser();
+	ObjFile::Model *GetModel() const;
+
+private:
+	void parseFile();
+	void copyNextWord(char *pBuffer, size_t length);
+	void copyNextLine(char *pBuffer, size_t length);
+	void getVector3(std::vector<aiVector3D_t*> &point3d_array);
+	void getVector2(std::vector<aiVector2D_t*> &point2d_array);
+	void skipLine();
+	void getFace();
+	void getMaterialDesc();
+	void getComment();
+	void getMaterialLib();
+	void getNewMaterial();
+	void getGroupName();
+	void getGroupNumber();
+	void getObjectName();
+	void createObject(const std::string &strObjectName);
+	void reportErrorTokenInFace();
+	void extractExtension(const std::string strFile, std::string &strExt);
+
+private:
+	std::string m_strAbsPath;
+	DataArrayIt m_DataIt;
+	DataArrayIt m_DataItEnd;
+	ObjFile::Model *m_pModel;
+	unsigned int m_uiLine;
+	char m_buffer[BUFFERSIZE];
+
+};
+
+}	// Namespace Assimp
+
+#endif

+ 118 - 0
include/aiAnim.h

@@ -0,0 +1,118 @@
+/** @file Defines the data structures in which the imported animations are returned. */
+#ifndef AI_ANIM_H_INC
+#define AI_ANIM_H_INC
+
+#include "aiTypes.h"
+#include "aiQuaternion.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A time-value pair specifying a certain 3D vector for the given time. */
+struct aiVectorKey
+{
+	double mTime;      ///< The time of this key
+	aiVector3D_t mValue; ///< The value of this key
+};
+
+/** A time-value pair specifying a rotation for the given time. For joint animations
+ * the rotation is usually expressed using a quaternion.
+ */
+struct aiQuatKey
+{
+	double mTime;      ///< The time of this key
+	aiQuaternion_t mValue; ///< The value of this key
+};
+
+/** Describes the animation of a single bone. The name specifies the bone which is affected by this
+ * animation channel. The keyframes are given in three separate series of values, one each for
+ * position, rotation and scaling.
+ */
+struct aiBoneAnim
+{
+	/** The name of the bone affected by this animation. */
+	aiString mBoneName;
+
+	/** The number of position keys */
+	unsigned int mNumPositionKeys;
+	/** The position keys of this animation channel. Positions are specified as 3D vector. 
+	* The array is mNumPositionKeys in size.
+	*/
+	aiVectorKey* mPositionKeys;
+
+	/** The number of rotation keys */
+	unsigned int mNumRotationKeys;
+	/** The rotation keys of this animation channel. Rotations are given as quaternions, 
+	* which are 4D vectors. The array is mNumRotationKeys in size.
+	*/
+	aiQuatKey* mRotationKeys;
+
+	/** The number of scaling keys */
+	unsigned int mNumScalingKeys;
+	/** The scaling keys of this animation channel. Scalings are specified as 3D vector. 
+	* The array is mNumScalingKeys in size.
+	*/
+	aiVectorKey* mScalingKeys;
+
+#ifdef __cplusplus
+	aiBoneAnim()
+	{
+		mNumPositionKeys = 0; mPositionKeys = NULL; 
+		mNumRotationKeys= 0; mRotationKeys = NULL; 
+		mNumScalingKeys = 0; mScalingKeys = NULL; 
+	}
+
+	~aiBoneAnim()
+	{
+		delete [] mPositionKeys;
+		delete [] mRotationKeys;
+		delete [] mScalingKeys;
+	}
+#endif // __cplusplus
+};
+
+/** An animation consists of keyframe data for a number of bones. For each bone affected by the animation
+ * a separate series of data is given.
+ */
+struct aiAnimation
+{
+	/** The name of the animation. If the modelling package this data was exported from does support 
+	* only a single animation channel, this name is usually empty (length is zero).
+	*/
+	aiString mName;
+
+	/** Duration of the animation in ticks. */
+	double mDuration;
+	/** Ticks per second. 0 if not specified in the imported file */
+	double mTicksPerSecond;
+
+	/** The number of bone animation channels. Each channel affects a single bone. */
+	unsigned int mNumBones;
+	/** The bone animation channels. Each channel affects a single bone. The array
+	* is mNumBones in size.
+	*/
+	aiBoneAnim** mBones;
+
+#ifdef __cplusplus
+	aiAnimation()
+	{
+		mDuration = 0;
+		mTicksPerSecond = 0;
+		mNumBones = 0; mBones = NULL;
+	}
+
+	~aiAnimation()
+	{
+		for( unsigned int a = 0; a < mNumBones; a++)
+			delete mBones[a];
+		delete [] mBones;
+	}
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_ANIM_H_INC

+ 28 - 0
include/aiAssert.h

@@ -0,0 +1,28 @@
+#ifndef AI_DEBUG_H_INC
+#define AI_DEBUG_H_INC
+
+#include <string>
+
+#ifndef __cplusplus
+#error This header requires C++ to be used.
+#endif
+
+namespace Assimp {
+
+//!	\brief	ASSIMP specific assertion test, just works in debug mode
+//!	\param	uiLine	Line in file
+//!	\param	file	Source file
+void aiAssert (bool expression, const std::string &message, unsigned int uiLine, const std::string &file);
+
+
+//!	\def	ai_assert
+//!	\brief	ASSIM specific assertion test
+#ifdef DEBUG  
+#  define	ai_assert(expression) aiAssert (expression, #expression, __LINE__, __FILE__);
+#else
+#  define	ai_assert(expression)
+#endif
+
+}	// Namespace Assimp
+
+#endif

+ 23 - 0
include/aiDefs.h

@@ -0,0 +1,23 @@
+
+
+/** @file Helper macros for the library 
+ */
+#ifndef AI_DEF_H_INC
+#define AI_DEF_H_INC
+
+
+/** Namespace helper macros for c++ compilers
+ */
+#ifdef __cplusplus
+  #define AI_NAMESPACE_START namespace Assimp {
+  #define AI_NAMESPACE_END };
+#else
+  #define AI_NAMESPACE_START
+  #define AI_NAMESPACE_END
+#endif
+
+#ifdef _DEBUG
+#  define ASSIMP_DEBUG
+#endif
+
+#endif //!!AI_DEF_H_INC

+ 63 - 0
include/aiFileIO.h

@@ -0,0 +1,63 @@
+
+/** @file Defines generic routines to access memory-mapped files
+ *
+ */
+
+#ifndef AI_FILEIO_H_INC
+#define AI_FILEIO_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct aiFileIO;
+//enum aiOrigin;
+typedef aiFileIO (*aiFileOpenProc)(aiFileIO*, const char*, const char*);
+typedef aiReturn (*aiFileCloseProc)(aiFileIO*);
+typedef unsigned long (*aiFileReadWriteProc)(aiFileIO*, char*, unsigned int, unsigned int);
+typedef unsigned long (*aiFileTellProc)(aiFileIO*);
+
+// ---------------------------------------------------------------------------
+/** Define seek origins in fseek()-style.
+*/
+// ---------------------------------------------------------------------------
+enum aiOrigin
+{
+	aiOrigin_SET = 0x0,		//!< Set position
+	aiOrigin_CUR = 0x1,		//!< Current position
+	aiOrigin_END = 0x2		//!< End of file
+};
+
+typedef aiReturn (*aiFileSeek)(aiFileIO*, unsigned long, aiOrigin);
+
+typedef char* aiUserData;
+
+// ---------------------------------------------------------------------------
+/** Data structure to wrap a set of fXXXX (e.g fopen) replacement functions
+*
+* The functions behave the same way as their appropriate fXXXX 
+* counterparts in the CRT.
+*/
+// ---------------------------------------------------------------------------
+struct aiFileIO
+{
+	aiUserData UserData;
+
+	aiFileOpenProc OpenFunc;
+	aiFileCloseProc CloseFunc;
+	aiFileReadWriteProc ReadFunc;
+	aiFileReadWriteProc WriteFunc;
+	aiFileTellProc TellProc;
+	aiFileSeek SeekProc;
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // AI_FILEIO_H_INC

+ 476 - 0
include/aiMaterial.h

@@ -0,0 +1,476 @@
+
+
+/** @file Defines the material system of the library
+ *
+ */
+
+#ifndef AI_MATERIAL_H_INC
+#define AI_MATERIAL_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Defines type identifiers for use within the material system.
+*
+*/
+// ---------------------------------------------------------------------------
+enum aiPropertyTypeInfo
+{
+	/** Array of single-precision floats
+	*/
+	aiPTI_Float = 0x1,
+
+
+	/** aiString data structure
+	*/
+	aiPTI_String = 0x3,
+
+
+	/** Array of Integers
+	*/
+	aiPTI_Integer = 0x4,
+
+
+	/** Simple binary buffer
+	*/
+	aiPTI_Buffer = 0x5,
+};
+
+
+// ---------------------------------------------------------------------------
+/** Defines algorithms for generating UVW-coords (for texture sampling)
+*  procedurally.
+*/
+// ---------------------------------------------------------------------------
+enum aiTexUVWGen
+{
+	/** The view vector will be reflected to a pixel's normal. 
+	*
+	*  The result is used as UVW-coordinate for 
+	*  accessing a cubemap
+	*/
+	aiTexUVWGen_VIEWREFLEFT = 0x800001,
+
+
+	/** The view vector will be used as UVW-src
+	*
+	*  The view vector is used as UVW-coordinate for 
+	*  accessing a cubemap
+	*/
+	aiTexUVWGen_VIEW = 0x800002,
+
+
+	/** The view vector will be refracted to the pixel's normal. 
+	*
+	*  If this is used, the refraction index to be applied should
+	*  also be contained in the material description.
+	*  The result is used as UVW-coordinate for 
+	*  accessing a cubemap.
+	*/
+	aiTexUVWGen_VIEWREFRACT = 0x800003
+};
+
+
+// ---------------------------------------------------------------------------
+/** Defines all shading models supported by the library
+*
+*  @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)
+*/
+// ---------------------------------------------------------------------------
+enum aiShadingMode
+{
+	/** Flat shading. Shading is done on per-face base, 
+	*  diffuse only.
+	*/
+	aiShadingMode_Flat = 0x1,
+
+
+	/** Diffuse gouraud shading. Shading on per-vertex base
+	*/
+	aiShadingMode_Gouraud =	0x2,
+
+
+	/** Diffuse/Specular Phong-Shading
+	*
+	*  Shading is applied on per-pixel base. This is the
+	*  slowest algorithm, but generates the best results.
+	*/
+	aiShadingMode_Phong = 0x3,
+
+
+	/** Diffuse/Specular Phong-Blinn-Shading
+	*
+	*  Shading is applied on per-pixel base. This is a little
+	*  bit faster than phong and in some cases even
+	*  more realistic
+	*/
+	aiShadingMode_Blinn	= 0x4,
+
+
+	/** Toon-Shading per pixel
+	*
+	*  Shading is applied on per-pixel base. The output looks
+	*  like a comic. Often combined with edge detection.
+	*/
+	aiShadingMode_Toon = 0x5,
+
+
+	/** OrenNayar-Shading per pixel
+	*
+	*  Extension to standard lambertian shading, taking the
+	*  roughness of the material into account
+	*	
+	*/
+	aiShadingMode_OrenNayar = 0x6,
+
+
+	/** Minnaert-Shading per pixel
+	*
+	*  Extension to standard lambertian shading, taking the
+	*  "darkness" of the material into account
+	*/
+	aiShadingMode_Minnaert = 0x7,
+
+
+	/** CookTorrance-Shading per pixel
+	*/
+	aiShadingMode_CookTorrance = 0x8,
+
+
+	/** No shading at all
+	*/
+	aiShadingMode_NoShading = 0x8
+};
+
+
+// ---------------------------------------------------------------------------
+/** Data structure for a single property inside a material
+*
+*  @see aiMaterial
+*/
+// ---------------------------------------------------------------------------
+struct aiMaterialProperty
+{
+	/** Specifies the name of the property (key)
+	*
+	*	Keys are case insensitive.
+	*/
+	aiString* mKey;
+
+
+	/**	Size of the buffer mData is pointing to, in bytes
+	*/
+	unsigned int mDataLength;
+
+
+	/** Type information for the property.
+	*
+	*  Defines the data layout inside the
+	*  data buffer. This is used by the library
+	*  internally to perform debug checks.
+	*/
+	aiPropertyTypeInfo mType;
+
+
+	/**	Binary buffer to hold the property's value
+	*
+	*  The buffer has no terminal character. However,
+	*  if a string is stored inside it may use 0 as terminal,
+	*  but it would be contained in mDataLength.
+	*/
+	char* mData;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Data structure for a material
+*
+*  Material data is stored using a key-value structure, called property
+*  (to guarant that the system is maximally flexible).
+*  The library defines a set of standard keys, which should be enough
+*  for nearly all purposes. 
+*/
+// ---------------------------------------------------------------------------
+#ifdef __cplusplus
+class aiMaterial
+{
+protected:
+	aiMaterial() {}
+public:
+#else
+struct aiMaterial
+{
+#endif // __cplusplus
+	/** List of all material properties loaded.
+	*/
+	aiMaterialProperty** mProperties;
+
+	/** Number of properties loaded
+	*/
+	unsigned int mNumProperties;
+	unsigned int mNumAllocated;
+};
+
+
+// ---------------------------------------------------------------------------
+/** @def AI_MATKEY_NAME
+*  Defines the name of the material (aiString)
+*/
+#define AI_MATKEY_NAME "$mat.name"
+
+/** @def AI_MATKEY_SHADING_MODE
+*  Defines the shading model to use (aiShadingMode)
+*/
+#define AI_MATKEY_SHADING_MODEL "$mat.shadingm"
+
+/** @def AI_MATKEY_OPACITY
+*  Defines the base opacity of the material
+*/
+#define AI_MATKEY_OPACITY "$mat.opacity"
+
+/** @def AI_MATKEY_BUMPSCALING
+*  Defines the height scaling of a bump map (for stuff like Parallax
+*  Occlusion Mapping)
+*/
+#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling"
+
+/** @def AI_MATKEY_SHININESS
+*  Defines the base shininess of the material
+*/
+#define AI_MATKEY_SHININESS "$mat.shininess"
+
+/** @def AI_MATKEY_COLOR_DIFFUSE
+*  Defines the diffuse base color of the material
+*/
+#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse"
+
+/** @def AI_MATKEY_COLOR_AMBIENT
+*  Defines the ambient base color of the material
+*/
+#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient"
+
+/** @def AI_MATKEY_COLOR_SPECULAR
+*  Defines the specular base color of the material
+*/
+#define AI_MATKEY_COLOR_SPECULAR "$clr.specular"
+
+/** @def AI_MATKEY_COLOR_EMISSIVE
+*  Defines the emissive base color of the material
+*/
+#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive"
+
+/** @def AI_MATKEY_TEXTURE_DIFFUSE
+*  Defines a specified diffuse texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_DIFFUSE(N) "$tex.file.diffuse["#N"]"
+#define AI_MATKEY_TEXTURE_DIFFUSE_  "$tex.file.diffuse"
+
+/** @def AI_MATKEY_TEXTURE_AMBIENT
+*  Defines a specified ambient texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_AMBIENT(N) "$tex.file.ambient["#N"]"
+#define AI_MATKEY_TEXTURE_AMBIENT_   "$tex.file.ambient"
+
+/** @def AI_MATKEY_TEXTURE_SPECULAR
+*  Defines a specified specular texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_SPECULAR(N) "$tex.file.specular["#N"]"
+#define AI_MATKEY_TEXTURE_SPECULAR_   "$tex.file.specular"
+
+/** @def AI_MATKEY_TEXTURE_EMISSIVE
+*  Defines a specified emissive texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_EMISSIVE(N) "$tex.file.emissive["#N"]"
+#define AI_MATKEY_TEXTURE_EMISSIVE_   "$tex.file.emissive"
+
+/** @def AI_MATKEY_TEXTURE_NORMALS
+*  Defines a specified normal texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_NORMALS(N) "$tex.file.normals["#N"]"
+#define AI_MATKEY_TEXTURE_NORMALS_   "$tex.file.normals"
+
+/** @def AI_MATKEY_TEXTURE_BUMP
+* Defines a specified bumpmap texture (=heightmap) channel of the material
+* This is very similar to #AI_MATKEY_TEXTURE_NORMALS. It is provided
+* to allow applications to determine whether the input data for
+* normal mapping is already a normal map or needs to be converted to
+* a heightmap.
+*/
+#define AI_MATKEY_TEXTURE_BUMP(N) "$tex.file.bump["#N"]"
+#define AI_MATKEY_TEXTURE_BUMP_   "$tex.file.bump"
+
+/** @def AI_MATKEY_TEXTURE_SHININESS
+*  Defines a specified shininess texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_SHININESS(N) "$tex.file.shininess["#N"]"
+#define AI_MATKEY_TEXTURE_SHININESS_   "$tex.file.shininess"
+
+/** @def AI_MATKEY_TEXTURE_OPACITY
+*  Defines a specified opacity texture channel of the material
+*/
+#define AI_MATKEY_TEXTURE_OPACITY(N) "$tex.file.opacity["#N"]"
+#define AI_MATKEY_TEXTURE_OPACITY_   "$tex.file.opacity"
+
+
+#define AI_MATKEY_TEXOP_DIFFUSE(N)		"$tex.op.diffuse["#N"]"
+#define AI_MATKEY_TEXOP_AMBIENT(N)		"$tex.op.ambient["#N"]"
+#define AI_MATKEY_TEXOP_SPECULAR(N)		"$tex.op.specular["#N"]"
+#define AI_MATKEY_TEXOP_EMISSIVE(N)		"$tex.op.emissive["#N"]"
+#define AI_MATKEY_TEXOP_NORMALS(N)		"$tex.op.normals["#N"]"
+#define AI_MATKEY_TEXOP_BUMP(N)			"$tex.op.bump["#N"]"
+#define AI_MATKEY_TEXOP_SHININESS(N)	"$tex.op.shininess["#N"]"
+#define AI_MATKEY_TEXOP_OPACITY(N)		"$tex.op.opacity["#N"]"
+
+#define AI_MATKEY_UVWSRC_DIFFUSE(N)		"$tex.uvw.diffuse["#N"]"
+#define AI_MATKEY_UVWSRC_AMBIENT(N)		"$tex.uvw.ambient["#N"]"
+#define AI_MATKEY_UVWSRC_SPECULAR(N)	"$tex.uvw.specular["#N"]"
+#define AI_MATKEY_UVWSRC_EMISSIVE(N)	"$tex.uvw.emissive["#N"]"
+#define AI_MATKEY_UVWSRC_NORMALS(N)		"$tex.uvw.normals["#N"]"
+#define AI_MATKEY_UVWSRC_BUMP(N)		"$tex.uvw.bump["#N"]"
+#define AI_MATKEY_UVWSRC_SHININESS(N)	"$tex.uvw.shininess["#N"]"
+#define AI_MATKEY_UVWSRC_OPACITY(N)		"$tex.uvw.opacity["#N"]"
+
+#define AI_MATKEY_REFRACTI_DIFFUSE(N)	"$tex.refracti.diffuse["#N"]"
+#define AI_MATKEY_REFRACTI_AMBIENT(N)	"$tex.refracti.ambient["#N"]"
+#define AI_MATKEY_REFRACTI_SPECULAR(N)	"$tex.refracti.specular["#N"]"
+#define AI_MATKEY_REFRACTI_EMISSIVE(N)	"$tex.refracti.emissive["#N"]"
+#define AI_MATKEY_REFRACTI_NORMALS(N)	"$tex.refracti.normals["#N"]"
+#define AI_MATKEY_REFRACTI_BUMP(N)		"$tex.refracti.bump["#N"]"
+#define AI_MATKEY_REFRACTI_SHININESS(N)	"$tex.refracti.shininess["#N"]"
+#define AI_MATKEY_REFRACTI_OPACITY(N)	"$tex.refracti.opacity["#N"]"
+
+#define AI_MATKEY_TEXBLEND_DIFFUSE(N)	"$tex.blend.diffuse["#N"]"
+#define AI_MATKEY_TEXBLEND_AMBIENT(N)	"$tex.blend.ambient["#N"]"
+#define AI_MATKEY_TEXBLEND_SPECULAR(N)	"$tex.blend.specular["#N"]"
+#define AI_MATKEY_TEXBLEND_EMISSIVE(N)	"$tex.blend.emissive["#N"]"
+#define AI_MATKEY_TEXBLEND_NORMALS(N)	"$tex.blend.normals["#N"]"
+#define AI_MATKEY_TEXBLEND_BUMP(N)		"$tex.blend.bump["#N"]"
+#define AI_MATKEY_TEXBLEND_SHININESS(N)	"$tex.blend.shininess["#N"]"
+#define AI_MATKEY_TEXBLEND_OPACITY(N)	"$tex.blend.opacity["#N"]"
+
+
+#define AI_MATKEY_ORENNAYAR_ROUGHNESS	 "$shading.orennayar.roughness"
+#define AI_MATKEY_MINNAERT_DARKNESS		 "$shading.minnaert.darkness"
+#define AI_MATKEY_COOK_TORRANCE_REFRACTI "$shading.cookt.refracti"
+#define AI_MATKEY_COOK_TORRANCE_PARAM	 "$shading.cookt.param"
+
+
+/** @def AI_MATKEY_GLOBAL_BACKGROUND_IMAGE
+*  Global property defined by some loaders. Contains the path to 
+*  the image file to be used as background image.
+*/
+#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "$global.bg.image2d"
+
+
+// ---------------------------------------------------------------------------
+/** Retrieve a material property with a specific key from the material
+*
+*  @param pMat Pointer to the input material. May not be NULL
+*  @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+*  @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty
+*         structure or NULL if the key has not been found. 
+*/
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialProperty(const aiMaterial* pMat, 
+							   const char* pKey,
+							   const aiMaterialProperty** pPropOut);
+
+
+// ---------------------------------------------------------------------------
+/** Retrieve an array of float values with a specific key 
+*  from the material
+*
+* @param pMat Pointer to the input material. May not be NULL
+* @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 float's.
+*        Receives the number of values (not bytes!) read. 
+*/
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, 
+								 const char* pKey,
+								 float* pOut,
+								 unsigned int* pMax);
+
+#ifdef __cplusplus
+// inline it
+inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat, 
+								   const char* pKey,
+								   float* pOut)
+	{return aiGetMaterialFloatArray(pMat,pKey,pOut,(unsigned int*)0x0);}
+#else 
+// use our friend, the C preprocessor
+#define aiGetMaterialFloat (pMat, pKey, pOut) \
+	aiGetMaterialFloatArray(pMat, pKey, pOut, NULL)
+#endif //!__cplusplus
+
+
+// ---------------------------------------------------------------------------
+/** Retrieve an array of integer values with a specific key 
+*  from the material
+*
+* @param pMat Pointer to the input material. May not be NULL
+* @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 int's.
+*        Receives the number of values (not bytes!) read. 
+*/
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, 
+								   const char* pKey,
+								   int* pOut,
+								   unsigned int* pMax);
+
+#ifdef __cplusplus
+// inline it
+inline aiReturn aiGetMaterialInteger(const aiMaterial* pMat, 
+									 const char* pKey,
+									 int* pOut)
+	{return aiGetMaterialIntegerArray(pMat,pKey,pOut,(unsigned int*)0x0);}
+#else 
+// use our friend, the C preprocessor
+#define aiGetMaterialInteger (pMat, pKey, pOut) \
+	aiGetMaterialIntegerArray(pMat, pKey, pOut, NULL)
+#endif //!__cplusplus
+
+
+
+// ---------------------------------------------------------------------------
+/** Retrieve a color value from the material property table
+*
+*	@param pMat Pointer to the input material. May not be NULL
+*	@param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+*	@param pOut Pointer to a buffer to receive the result. 
+*/
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialColor(const aiMaterial* pMat, 
+							const char* pKey,
+							aiColor4D* pOut);
+
+
+// ---------------------------------------------------------------------------
+/** Retrieve a string from the material property table
+*
+*	@param pMat Pointer to the input material. May not be NULL
+*	@param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+*	@param pOut Pointer to a buffer to receive the result. 
+*/
+// ---------------------------------------------------------------------------
+aiReturn aiGetMaterialString(const aiMaterial* pMat, 
+							 const char* pKey,
+							 aiString* pOut);
+
+
+#ifdef __cplusplus
+}
+#endif //!__cplusplus
+
+#endif //!!AI_MATERIAL_H_INC

+ 50 - 0
include/aiMatrix3x3.h

@@ -0,0 +1,50 @@
+/** @file Definition of a 3x3 matrix, including operators when compiling in C++ */
+#ifndef AI_MATRIX3x3_H_INC
+#define AI_MATRIX3x3_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiMatrix4x4;
+
+// ---------------------------------------------------------------------------
+/** Represents a column-major 3x3 matrix 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiMatrix3x3
+{
+#ifdef __cplusplus
+	aiMatrix3x3 () :	
+		a1(1.0f), a2(0.0f), a3(0.0f), 
+		b1(0.0f), b2(1.0f), b3(0.0f), 
+		c1(0.0f), c2(0.0f), c3(1.0f) {}
+
+	aiMatrix3x3 (	float _a1, float _a2, float _a3,
+					float _b1, float _b2, float _b3,
+					float _c1, float _c2, float _c3) :	
+		a1(_a1), a2(_a2), a3(_a3), 
+		b1(_b1), b2(_b2), b3(_b3), 
+		c1(_c1), c2(_c2), c3(_c3)
+	{}
+
+	/** Construction from a 4x4 matrix. The remaining parts of the matrix are ignored. */
+	explicit aiMatrix3x3( const aiMatrix4x4& pMatrix);
+
+	aiMatrix3x3& operator *= (const aiMatrix3x3& m);
+	aiMatrix3x3 operator* (const aiMatrix3x3& m) const;
+	aiMatrix3x3& Transpose();
+
+#endif // __cplusplus
+
+
+	float a1, a2, a3;
+	float b1, b2, b3;
+	float c1, c2, c3;
+} aiMatrix3x3_t;
+
+#ifdef __cplusplus
+} // end of extern C
+#endif
+
+#endif // AI_MATRIX3x3_H_INC

+ 54 - 0
include/aiMatrix3x3.inl

@@ -0,0 +1,54 @@
+/** @file Inline implementation of the 3x3 matrix operators */
+#ifndef AI_MATRIX3x3_INL_INC
+#define AI_MATRIX3x3_INL_INC
+
+#include "aiMatrix3x3.h"
+
+#ifdef __cplusplus
+#include "aiMatrix4x4.h"
+#include <algorithm>
+
+// ------------------------------------------------------------------------------------------------
+// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored.
+inline aiMatrix3x3::aiMatrix3x3( const aiMatrix4x4& pMatrix)
+{
+	a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3;
+	b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3;
+	c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::operator *= (const aiMatrix3x3& m)
+{
+	*this = aiMatrix3x3(
+		m.a1 * a1 + m.b1 * a2 + m.c1 * a3,
+		m.a2 * a1 + m.b2 * a2 + m.c2 * a3,
+		m.a3 * a1 + m.b3 * a2 + m.c3 * a3,
+		m.a1 * b1 + m.b1 * b2 + m.c1 * b3,
+		m.a2 * b1 + m.b2 * b2 + m.c2 * b3,
+		m.a3 * b1 + m.b3 * b2 + m.c3 * b3,
+		m.a1 * c1 + m.b1 * c2 + m.c1 * c3,
+		m.a2 * c1 + m.b2 * c2 + m.c2 * c3,
+		m.a3 * c1 + m.b3 * c2 + m.c3 * c3);
+	return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3 aiMatrix3x3::operator* (const aiMatrix3x3& m) const
+{
+	aiMatrix3x3 temp( *this);
+	temp *= m;
+	return temp;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline aiMatrix3x3& aiMatrix3x3::Transpose()
+{
+	std::swap( a2, b1);
+	std::swap( a3, c1);
+	std::swap( b3, c2);
+}
+
+
+#endif // __cplusplus
+#endif // AI_MATRIX3x3_INL_INC

+ 77 - 0
include/aiMatrix4x4.h

@@ -0,0 +1,77 @@
+/** @file 4x4 matrix structure, including operators when compiling in C++ */
+#ifndef AI_MATRIX4X4_H_INC
+#define AI_MATRIX4X4_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiMatrix3x3;
+
+// Set packing to 4
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) ||	defined (__BCPLUSPLUS__)
+	#pragma pack(push,4)
+	#define PACK_STRUCT
+#elif defined( __GNUC__ )
+	#define PACK_STRUCT	__attribute__((packed))
+#else
+	#error Compiler not supported
+#endif
+
+// ---------------------------------------------------------------------------
+/** Represents a column-major 4x4 matrix, 
+*  use this for homogenious coordinates 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiMatrix4x4
+{
+#ifdef __cplusplus
+	aiMatrix4x4 () :	
+		a1(1.0f), a2(0.0f), a3(0.0f), a4(0.0f), 
+		b1(0.0f), b2(1.0f), b3(0.0f), b4(0.0f), 
+		c1(0.0f), c2(0.0f), c3(1.0f), c4(0.0f),
+		d1(0.0f), d2(0.0f), d3(0.0f), d4(1.0f){}
+
+	aiMatrix4x4 (	float _a1, float _a2, float _a3, float _a4,
+					float _b1, float _b2, float _b3, float _b4,
+					float _c1, float _c2, float _c3, float _c4,
+					float _d1, float _d2, float _d3, float _d4) :	
+		a1(_a1), a2(_a2), a3(_a3), a4(_a4),  
+		b1(_b1), b2(_b2), b3(_b3), b4(_b4), 
+		c1(_c1), c2(_c2), c3(_c3), c4(_c4),
+		d1(_d1), d2(_d2), d3(_d3), d4(_d4)
+		{}
+
+	/** Constructor from 3x3 matrix. The remaining elements are set to identity. */
+	explicit aiMatrix4x4( const aiMatrix3x3& m);
+
+	aiMatrix4x4& operator *= (const aiMatrix4x4& m);
+	aiMatrix4x4 operator* (const aiMatrix4x4& m) const;
+	aiMatrix4x4& Transpose();
+	aiMatrix4x4& Inverse();
+	float Determinant() const;
+
+	float* operator[](unsigned int p_iIndex);
+	const float* operator[](unsigned int p_iIndex) const;
+#endif // __cplusplus
+
+	float a1, a2, a3, a4;
+	float b1, b2, b3, b4;
+	float c1, c2, c3, c4;
+	float d1, d2, d3, d4;
+
+} PACK_STRUCT aiMatrix4x4_t;
+
+
+// Reset packing
+#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+#pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+#ifdef __cplusplus
+} // end extern "C"
+
+
+#endif // __cplusplus
+#endif // AI_MATRIX4X4_H_INC

+ 136 - 0
include/aiMatrix4x4.inl

@@ -0,0 +1,136 @@
+/** @file Inline implementation of the 4x4 matrix operators */
+#ifndef AI_MATRIX4x4_INL_INC
+#define AI_MATRIX4x4_INL_INC
+
+#include "aiMatrix4x4.h"
+
+#ifdef __cplusplus
+#include "aiMatrix3x3.h"
+
+#include <algorithm>
+#include <limits>
+#include <math.h>
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4::aiMatrix4x4( const aiMatrix3x3& m)
+{
+	a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = 0.0f;
+	b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = 0.0f;
+	c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = 0.0f;
+	d1 = 0.0f; d2 = 0.0f; d3 = 0.0f; d4 = 1.0f;
+}
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::operator *= (const aiMatrix4x4& m)
+{
+	*this = aiMatrix4x4(
+		m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4,
+		m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4,
+		m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4,
+		m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4,
+		m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4,
+		m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4,
+		m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4,
+		m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4,
+		m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4,
+		m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4,
+		m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4,
+		m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4,
+		m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4,
+		m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4,
+		m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4,
+		m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4);
+	return *this;
+}
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4 aiMatrix4x4::operator* (const aiMatrix4x4& m) const
+{
+	aiMatrix4x4 temp( *this);
+	temp *= m;
+	return temp;
+}
+
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Transpose()
+{
+	std::swap( (float&)b1, (float&)a2);
+	std::swap( (float&)c1, (float&)a3);
+	std::swap( (float&)c2, (float&)b3);
+	std::swap( (float&)d1, (float&)a4);
+	std::swap( (float&)d2, (float&)b4);
+	std::swap( (float&)d3, (float&)c4);
+	return *this;
+}
+
+
+// ---------------------------------------------------------------------------
+inline float aiMatrix4x4::Determinant() const
+{
+	return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4 
+		+ a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4 
+		- a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3 
+		+ a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2 
+		+ a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2
+		- a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1;
+}
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Inverse()
+{
+	// Compute the reciprocal determinant
+	float det = Determinant();
+	if(det == 0.0f) 
+	{
+		*this = aiMatrix4x4(
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
+			std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN());
+		return *this;
+	}
+
+	float invdet = 1.0f / det;
+
+	aiMatrix4x4 res;
+	res.a1 = invdet  * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2));
+	res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2));
+	res.a3 = invdet  * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2));
+	res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2));
+	res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1));
+	res.b2 = invdet  * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1));
+	res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1));
+	res.b4 = invdet  * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1));
+	res.c1 = invdet  * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1));
+	res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1));
+	res.c3 = invdet  * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1));
+	res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1));
+	res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1));
+	res.d2 = invdet  * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1));
+	res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1));
+	res.d4 = invdet  * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1)); 
+	*this = res;
+
+	return *this;
+}
+
+// ---------------------------------------------------------------------------
+inline float* aiMatrix4x4::operator[](unsigned int p_iIndex)
+{
+	return &this->a1 + p_iIndex * 4;
+}
+
+
+// ---------------------------------------------------------------------------
+inline const float* aiMatrix4x4::operator[](unsigned int p_iIndex) const
+{
+	return &this->a1 + p_iIndex * 4;
+}
+
+#endif // __cplusplus
+#endif // AI_MATRIX4x4_INL_INC

+ 268 - 0
include/aiMesh.h

@@ -0,0 +1,268 @@
+/** @file Defines the data structures in which the imported geometry is returned. */
+#ifndef AI_MESH_H_INC
+#define AI_MESH_H_INC
+
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** A single face in a mesh, referring to multiple vertices. 
+*  If mNumIndices is 3, the face is a triangle, for mNumIndices > 3 it's a polygon.
+*/
+// ---------------------------------------------------------------------------
+struct aiFace
+{
+	unsigned int mNumIndices; ///< Number of indices defining this face. 3 for a triangle, >3 for polygon
+	unsigned int* mIndices;   ///< Pointer to the indices array. Size of the array is given in numIndices.
+
+#ifdef __cplusplus
+	aiFace()
+	{
+		mNumIndices = 0; mIndices = NULL;
+	}
+
+	~aiFace()
+	{
+		delete [] mIndices;
+	}
+
+	aiFace( const aiFace& o)
+	{
+		mIndices = NULL;
+		*this = o;
+	}
+
+	const aiFace& operator = ( const aiFace& o)
+	{
+		if (&o == this)
+			return *this;
+
+		delete mIndices;
+		mNumIndices = o.mNumIndices;
+		mIndices = new unsigned int[mNumIndices];
+		memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int));
+		return *this;
+	}
+
+#endif // __cplusplus
+};
+
+
+// ---------------------------------------------------------------------------
+/** A single influence of a bone on a vertex. */
+// ---------------------------------------------------------------------------
+struct aiVertexWeight
+{
+	unsigned int mVertexId; ///< Index of the vertex which is influenced by the bone.
+	float mWeight;     ///< The strength of the influence in the range (0...1). The influence from all bones at one vertex amounts to 1.
+
+#ifdef __cplusplus
+	aiVertexWeight() { }
+	aiVertexWeight( unsigned int pID, float pWeight) : mVertexId( pID), mWeight( pWeight) { }
+#endif // __cplusplus
+};
+
+
+// ---------------------------------------------------------------------------
+/** A single bone of a mesh. A bone has a name by which it can be found 
+* in the frame hierarchy and by which it can be addressed by animations. 
+* In addition it has a number of influences on vertices.
+*/
+// ---------------------------------------------------------------------------
+struct aiBone
+{
+	aiString mName; ///< The name of the bone. 
+	unsigned int mNumWeights; ///< The number of vertices affected by this bone
+	aiVertexWeight* mWeights; ///< The vertices affected by this bone
+	aiMatrix4x4 mOffsetMatrix; ///< Matrix that transforms from mesh space to bone space in bind pose
+
+#ifdef __cplusplus
+	aiBone()
+	{
+		mNumWeights = 0; mWeights = NULL;
+	}
+
+	~aiBone()
+	{
+		delete [] mWeights;
+	}
+#endif // __cplusplus
+};
+
+
+/** Maximum number of vertex color sets per mesh.
+*
+* Diffuse, specular, ambient and emissive
+*/
+#define AI_MAX_NUMBER_OF_COLOR_SETS 0x4
+
+
+/** Maximum number of texture coord sets (UV channels) per mesh 
+*/
+#define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x4
+
+// ---------------------------------------------------------------------------
+/** A mesh represents a geometry or model with a single material. 
+*
+* It usually consists of a number of vertices and a series of primitives/faces 
+* referencing the vertices. In addition there might be a series of bones, each 
+* of them addressing a number of vertices with a certain weight. Vertex data is
+* presented in channels with each channel containing a single per-vertex 
+* information such as a set of texture coords or a normal vector.
+* If a data pointer is non-null, the corresponding data stream is present.
+* From C++-programs you can also use the comfort functions Has*() to
+* test for the presence of various data streams.
+*
+* A Mesh uses only a single material which is referenced by a material ID.
+*/
+struct aiMesh
+{
+	/** The number of vertices in this mesh. 
+	* This is also the size of all of the per-vertex data arrays
+	*/
+	unsigned int mNumVertices;
+
+	/** The number of primitives (triangles, polygones, lines) in this  mesh. 
+	* This is also the size of the mFaces array 
+	*/
+	unsigned int mNumFaces;
+
+	/** Vertex positions. 
+	* This array is always present in a mesh. The array is 
+	* mNumVertices in size. 
+	*/
+	aiVector3D_t* mVertices;
+
+	/** Vertex normals. 
+	* The array contains normalized vectors, NULL if not present. 
+	* The array is mNumVertices in size. 
+	*/
+	aiVector3D_t* mNormals;
+
+	/** Vertex tangents. 
+	* The tangent of a vertex points in the direction of the positive 
+	* X texture axis. The array contains normalized vectors, NULL if
+	* not present. The array is mNumVertices in size. 
+	* @note If the mesh contains tangents, it automatically also 
+	* contains bitangents. 
+	*/
+	aiVector3D_t* mTangents;
+
+	/** Vertex bitangents. 
+	* The bitangent of a vertex points in the direction of the positive 
+	* Y texture axis. The array contains normalized vectors, NULL if not
+	* present. The array is mNumVertices in size. 
+	* @note If the mesh contains tangents, it automatically also contains
+	* bitangents. 
+	*/
+	aiVector3D_t* mBitangents;
+
+	/** Vertex color sets. 
+	* A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex 
+	* colors per vertex. NULL if not present. Each array is
+	* mNumVertices in size if present.
+	*/
+	aiColor4D_t* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+	/** Vertex texture coords, also known as UV channels.
+	* A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per
+	* vertex. NULL if not present. The array is mNumVertices in size. 
+	*/
+	aiVector3D_t* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+	/** Specifies the number of components for a given UV channel.
+	* Up to three channels are supported (UVW, for accessing volume
+	* or cube maps). If the value is 2 for a given channel n, the
+	* component p.z of mTextureCoords[n][p] is set to 0.0f.
+	* If the value is 1 for a given channel, p.y is set to 0.0f, too.
+	* @note 4D coords are not supported 
+	*/
+	unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+	/** The faces the mesh is contstructed from. 
+	* Each face referres to a number of vertices by their indices. 
+	* This array is always present in a mesh, its size is given 
+	* in mNumFaces.
+	*/
+	aiFace* mFaces;
+
+	/** The number of bones this mesh contains. 
+	* Can be 0, in which case the mBones array is NULL. 
+	*/
+	unsigned int mNumBones;
+
+	/** The bones of this mesh. 
+	* A bone consists of a name by which it can be found in the
+	* frame hierarchy and a set of vertex weights.
+	*/
+	aiBone** mBones;
+
+	/** The material used by this mesh. 
+	 * A mesh does use only a single material. If an imported model uses multiple materials,
+	 * the import splits up the mesh. Use this value as index into the scene's material list.
+	 */
+	unsigned int mMaterialIndex;
+
+#ifdef __cplusplus
+	aiMesh()
+	{
+		mNumVertices = 0; mNumFaces = 0;
+		mVertices = NULL; mFaces = NULL;
+		mNormals = NULL; mTangents = NULL;
+		mBitangents = NULL;
+		for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+		{
+			mNumUVComponents[a] = 0;
+			mTextureCoords[a] = NULL;
+		}
+		for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+			mColors[a] = NULL;
+		mNumBones = 0; mBones = NULL;
+		mMaterialIndex = 0;
+	}
+
+	~aiMesh()
+	{
+		delete [] mVertices; 
+		delete [] mFaces;
+		delete [] mNormals;
+		delete [] mTangents;
+		delete [] mBitangents;
+		for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+			delete [] mTextureCoords[a];
+		for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+			delete [] mColors[a];
+		for( unsigned int a = 0; a < mNumBones; a++)
+			delete mBones[a];
+		delete [] mBones;
+	}
+
+	bool HasNormals() const { return mNormals != NULL; }
+	bool HasTangentsAndBitangents() const { return mTangents != NULL && mBitangents != NULL; }
+	bool HasVertexColors( unsigned int pIndex) 
+	{ 
+		if( pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS) 
+			return false; 
+		else 
+			return mColors[pIndex] != NULL; 
+	}
+	bool HasTextureCoords( unsigned int pIndex) 
+	{ 
+		if( pIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) 
+			return false; 
+		else 
+			return mTextureCoords[pIndex] != NULL; 
+	}
+	bool HasBones() const { return mBones != NULL; }
+
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_MESH_H_INC

+ 78 - 0
include/aiPostProcess.h

@@ -0,0 +1,78 @@
+/** @file Definitions for import post processing steps */
+#ifndef AI_POSTPROCESS_H_INC
+#define AI_POSTPROCESS_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Defines the flags for all possible post processing steps. */
+enum aiPostProcessSteps
+{
+	/** Calculates the binormals and tangents for the imported meshes. Does nothing
+	 * if a mesh does not have normals. You might want this post processing step to be
+	 * executed if you plan to use tangent space calculations such as normal mapping 
+	 * applied to the meshes.
+	 */
+	aiProcess_CalcTangentSpace = 1,
+
+	/** Identifies and joins identical vertex data sets within all imported meshes. 
+	 * After this step is run each mesh does contain only unique vertices anymore,
+	 * so a vertex is possibly used by multiple faces. You propably always want
+	 * to use this post processing step.*/
+	aiProcess_JoinIdenticalVertices = 2,
+
+	/** Converts all the imported data to a left-handed coordinate space such as 
+	 * the DirectX coordinate system. By default the data is returned in a right-handed
+	 * coordinate space which for example OpenGL preferres. In this space, +X points to the
+	 * right, +Y points upwards and +Z points to the viewer. In the DirectX coordinate space
+	 * +X points to the right, +Y points upwards and +Z points away from the viewer 
+	 * into the screen.
+	 */
+	aiProcess_ConvertToLeftHanded = 4,
+
+	/** Triangulates all faces of all meshes. By default the imported mesh data might 
+	 * contain faces with more than 3 indices. For rendering a mesh you usually need
+	 * all faces to be triangles. This post processing step splits up all higher faces
+	 * to triangles.
+	 */
+	aiProcess_Triangulate = 8,
+
+
+	/** Omits all normals found in the file. This can be used together
+	 * with either the aiProcess_GenNormals or the aiProcess_GenSmoothNormals
+	 * flag to force the recomputation of the normals.
+	 */
+	aiProcess_KillNormals = 0x10,
+
+
+	/** Generates normals for all faces of all meshes. The normals are shared
+	* between the three vertices of a face. This is ignored
+	* if normals are already existing. This flag may not be specified together
+	* with aiProcess_GenSmoothNormals
+	*/
+	aiProcess_GenNormals = 0x20,
+
+
+	/** Generates smooth normals for all vertices in the mesh. This is ignored
+	* if normals are already existing. This flag may not be specified together
+	* with aiProcess_GenNormals
+	*/
+	aiProcess_GenSmoothNormals = 0x40,
+
+
+	/** Splits large meshes into submeshes
+	* This is quite useful for realtime rendering where the number of vertices
+	* is usually limited by the video driver.
+	*
+	* A mesh is split if it consists of more than 1 * 10^6 vertices. This is defined
+	* in the internal SplitLargeMeshes.h header as AI_SLM_MAX_VERTICES.
+	*/
+	aiProcess_SplitLargeMeshes = 0x80
+};
+
+#ifdef __cplusplus
+} // end of extern "C"
+#endif
+
+#endif // AI_POSTPROCESS_H_INC

+ 96 - 0
include/aiQuaternion.h

@@ -0,0 +1,96 @@
+/** @file Quaternion structure, including operators when compiling in C++ */
+#ifndef AI_QUATERNION_H_INC
+#define AI_QUATERNION_H_INC
+
+#include <math.h>
+#include "aiTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Represents a quaternion in a 4D vector. */
+typedef 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);
+
+	/** Returns a matrix representation of the quaternion */
+	aiMatrix3x3 GetMatrix() const;
+#endif // __cplusplus
+
+	float w, x, y, z;	
+} aiQuaternion_t;
+
+
+#ifdef __cplusplus
+
+// ---------------------------------------------------------------------------
+// Constructs a quaternion from a rotation matrix
+inline aiQuaternion::aiQuaternion( const aiMatrix3x3 &pRotMatrix)
+{
+  float t = 1 + pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3;
+
+  // large enough
+  if( t > 0.00001f)
+  {
+    float s = sqrt( t) * 2.0f;
+    x = (pRotMatrix.b3 - pRotMatrix.c2) / s;
+    y = (pRotMatrix.c1 - pRotMatrix.a3) / s;
+    z = (pRotMatrix.a2 - pRotMatrix.b1) / s;
+    w = 0.25f * s;
+  } // else we have to check several cases
+  else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 )  
+  {	
+    // Column 0: 
+    float s = sqrt( 1.0f + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * 2.0f;
+    x = 0.25f * s;
+    y = (pRotMatrix.a2 + pRotMatrix.b1) / s;
+    z = (pRotMatrix.c1 + pRotMatrix.a3) / s;
+    w = (pRotMatrix.b3 - pRotMatrix.c2) / s;
+  } else 
+  if( pRotMatrix.b2 > pRotMatrix.c3) 
+  { 
+    // Column 1: 
+    float s = sqrt( 1.0f + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * 2.0f;
+    x = (pRotMatrix.a2 + pRotMatrix.b1) / s;
+    y = 0.25f * s;
+    z = (pRotMatrix.b3 + pRotMatrix.c2) / s;
+    w = (pRotMatrix.c1 - pRotMatrix.a3) / s;
+  } else 
+  { 
+    // Column 2:
+    float s = sqrt( 1.0f + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * 2.0f;
+    x = (pRotMatrix.c1 + pRotMatrix.a3) / s;
+    y = (pRotMatrix.b3 + pRotMatrix.c2) / s;
+    z = 0.25f * s;
+    w = (pRotMatrix.a2 - pRotMatrix.b1) / s;
+  }
+}
+
+// ---------------------------------------------------------------------------
+// Returns a matrix representation of the quaternion
+inline aiMatrix3x3 aiQuaternion::GetMatrix() const
+{
+	aiMatrix3x3 resMatrix;
+	resMatrix.a1 = 1.0f - 2.0f * (y * y + z * z);
+	resMatrix.a2 = 2.0f * (x * y + z * w);
+	resMatrix.a3 = 2.0f * (x * z - y * w);
+	resMatrix.b1 = 2.0f * (x * y - z * w);
+	resMatrix.b2 = 1.0f - 2.0f * (x * x + z * z);
+	resMatrix.b3 = 2.0f * (y * z + x * w);
+	resMatrix.c1 = 2.0f * (x * z + y * w);
+	resMatrix.c2 = 2.0f * (y * z - x * w);
+	resMatrix.c3 = 1.0f - 2.0f * (x * x + y * y);
+
+	return resMatrix;
+}
+
+} // end extern "C"
+#endif // __cplusplus
+
+#endif // AI_QUATERNION_H_INC

+ 147 - 0
include/aiScene.h

@@ -0,0 +1,147 @@
+/** @file Defines the data structures in which the imported scene is returned. */
+#ifndef AI_SCENE_H_INC
+#define AI_SCENE_H_INC
+
+#include "aiTypes.h"
+#include "aiMesh.h"
+#include "aiMaterial.h"
+#include "aiAnim.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// ---------------------------------------------------------------------------
+/** A node in the imported hierarchy. 
+*
+* 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.
+*/
+// ---------------------------------------------------------------------------
+struct aiNode
+{
+	/** 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.
+	*/
+	aiString mName;
+
+	/** The transformation relative to the node's parent. */
+	aiMatrix4x4 mTransformation;
+
+	/** Parent node. NULL if this node is the root node. */
+	aiNode* mParent;
+
+	/** The number of child nodes of this node. */
+	unsigned int mNumChildren;
+	/** The child nodes of this node. NULL if mNumChildren is 0. */
+
+	aiNode** mChildren;
+
+	/** The number of meshes of this node. */
+	unsigned int mNumMeshes;
+
+	/** The meshes of this node. Each entry is an index into the mesh */
+	unsigned int* mMeshes;
+
+#ifdef __cplusplus
+	/** Constructor */
+	aiNode() 
+	{ 
+		mParent = NULL; 
+		mNumChildren = 0; mChildren = NULL;
+		mNumMeshes = 0; mMeshes = NULL;
+	}
+
+	/** Destructor */
+	~aiNode()
+	{
+		for( unsigned int a = 0; a < mNumChildren; a++)
+			delete mChildren[a];
+		delete [] mChildren;
+		delete [] mMeshes;
+	}
+#endif // __cplusplus
+};
+
+
+// ---------------------------------------------------------------------------
+/** The root structure of the imported data. 
+* 
+* Everything that was imported from the given file can be accessed from here.
+*/
+// ---------------------------------------------------------------------------
+struct aiScene
+{
+	/** The root node of the hierarchy. 
+	* 
+	* There will always be at least the root node if the import
+	* was successful. Presence of further nodes depends on the 
+	* format and content of the imported file.
+	*/
+	aiNode* mRootNode;
+
+	/** The number of meshes in the scene. */
+	unsigned int mNumMeshes;
+
+	/** The array of meshes. 
+	*
+	* Use the indices given in the aiNode structure to access 
+	* this array. The array is mNumMeshes in size.
+	*/
+	aiMesh** mMeshes;
+
+	/** The number of materials in the scene. */
+	unsigned int mNumMaterials;
+
+	/** The array of materials. 
+	* 
+	* Use the index given in each aiMesh structure to access this
+	* array. The array is mNumMaterials in size.
+	*/
+	aiMaterial** mMaterials;
+
+	/** The number of animations in the scene. */
+	unsigned int mNumAnimations; 
+
+	/** The array of animations. 
+	*
+	* All animations imported from the given file are listed here.
+	* The array is mNumAnimations in size.
+	*/
+	aiAnimation** mAnimations;
+
+#ifdef __cplusplus
+	aiScene()
+	{
+		mRootNode = NULL;
+		mNumMeshes = 0; mMeshes = NULL;
+		mNumMaterials = 0; mMaterials = NULL;
+		mNumAnimations = 0; mAnimations = NULL;
+	}
+
+	~aiScene()
+	{
+		delete mRootNode;
+		for( unsigned int a = 0; a < mNumMeshes; a++)
+			delete mMeshes[a];
+		delete [] mMeshes;
+		for( unsigned int a = 0; a < mNumMaterials; a++)
+			delete mMaterials[a];
+		delete [] mMaterials;
+		for( unsigned int a = 0; a < mNumAnimations; a++)
+			delete mAnimations[a];
+		delete [] mAnimations;
+	}
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_SCENE_H_INC

+ 140 - 0
include/aiTypes.h

@@ -0,0 +1,140 @@
+#ifndef AI_TYPES_H_INC
+#define AI_TYPES_H_INC
+
+#include <sys/types.h>
+#include <memory.h>
+
+#if (defined _MSC_VER)
+#	include "Compiler/VisualStudio/stdint.h"
+#endif // (defined _MSC_VER)
+
+#include "aiVector3D.h"
+#include "aiMatrix3x3.h"
+#include "aiMatrix4x4.h"
+#include "aiVector3D.inl"
+#include "aiMatrix3x3.inl"
+#include "aiMatrix4x4.inl"
+
+#ifdef __cplusplus
+#include <string>
+extern "C" {
+#endif
+
+/** Maximum dimension for strings, ASSIMP strings are zero terminated */
+const size_t MAXLEN = 1024;
+
+// ---------------------------------------------------------------------------
+/** Represents a two-dimensional vector. 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiVector2D
+{
+#ifdef __cplusplus
+	aiVector2D () : x(0.0f), y(0.0f) {}
+	aiVector2D (float _x, float _y) : x(_x), y(_y) {}
+	aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {}
+	
+#endif // __cplusplus
+
+	float x, y;
+} aiVector2D_t;
+
+// aiVector3D type moved to separate header due to size of operators
+
+// aiQuaternion type moved to separate header due to size of operators
+
+// aiMatrix4x4 type moved to separate header due to size of operators
+
+// ---------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space. 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiColor3D
+{
+#ifdef __cplusplus
+	aiColor3D () : r(0.0f), g(0.0f), b(0.0f) {}
+	aiColor3D (float _r, float _g, float _b) : r(_r), g(_g), b(_b) {}
+	aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {}
+	
+#endif // __cplusplus
+
+	float r, g, b;
+} aiColor3D_t;
+
+
+// ---------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space including an 
+*   alpha component. 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiColor4D
+{
+#ifdef __cplusplus
+	aiColor4D () : r(0.0f), g(0.0f), b(0.0f), a(0.0f) {}
+	aiColor4D (float _r, float _g, float _b, float _a) 
+		: r(_r), g(_g), b(_b), a(_a) {}
+	aiColor4D (const aiColor4D& o) 
+		: r(o.r), g(o.g), b(o.b), a(o.a) {}
+	
+#endif // __cplusplus
+
+	float r, g, b, a;
+} aiColor4D_t;
+
+
+// ---------------------------------------------------------------------------
+/** Represents a string, zero byte terminated 
+*/
+// ---------------------------------------------------------------------------
+typedef struct aiString
+{
+#ifdef __cplusplus
+	inline aiString() :
+		length(0) 
+	{
+		// empty
+	}
+
+	inline aiString(const aiString& rOther) : 
+		length(rOther.length) 
+	{
+		memcpy( data, rOther.data, rOther.length);
+		this->data[this->length] = '\0';
+	}
+
+	void Set( const std::string& pString)
+	{
+		if( pString.length() > MAXLEN - 1)
+			return;
+		length = pString.length();
+		memcpy( data, pString.c_str(), length);
+		data[length] = 0;
+	}
+#endif // __cplusplus
+
+	size_t length;
+	char data[MAXLEN];
+} aiString_t;
+
+
+// ---------------------------------------------------------------------------
+/**	Standard return type for all library functions.
+*
+* To check whether a function failed or not check against
+* AI_SUCCESS.
+*/
+// ---------------------------------------------------------------------------
+enum aiReturn
+{
+	AI_SUCCESS = 0x0,
+	AI_FAILURE = -0x1,
+	AI_INVALIDFILE = -0x2,
+	AI_OUTOFMEMORY = -0x3,
+	AI_INVALIDARG = -0x4
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+

+ 99 - 0
include/aiVector3D.h

@@ -0,0 +1,99 @@
+/** @file 3D vector structure, including operators when compiling in C++ */
+#ifndef AI_VECTOR3D_H_INC
+#define AI_VECTOR3D_H_INC
+
+#include <math.h>
+
+#include "aiAssert.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Represents a three-dimensional vector. */
+typedef struct aiVector3D
+{
+#ifdef __cplusplus
+	aiVector3D () : x(0.0f), y(0.0f), z(0.0f) {}
+	aiVector3D (float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
+	aiVector3D (const aiVector3D& o) : x(o.x), y(o.y), z(o.z) {}
+
+	void Set( float pX, float pY, float pZ) { x = pX; y = pY; z = pZ; }
+	float SquareLength() const { return x*x + y*y + z*z; }
+	float Length() const { return sqrt( SquareLength()); }
+	aiVector3D& Normalize() { *this /= Length(); return *this; }
+	const aiVector3D& operator += (const aiVector3D& o) { x += o.x; y += o.y; z += o.z; return *this; }
+	const aiVector3D& operator -= (const aiVector3D& o) { x -= o.x; y -= o.y; z -= o.z; return *this; }
+	const aiVector3D& operator *= (float f) { x *= f; y *= f; z *= f; return *this; }
+	const aiVector3D& operator /= (float f) { x /= f; y /= f; z /= f; return *this; }
+
+	inline float operator[](unsigned int i) const {return *(&x + i);}
+	inline float& operator[](unsigned int i) {return *(&x + i);}
+#endif // __cplusplus
+
+	float x, y, z;	
+} aiVector3D_t;
+
+#ifdef __cplusplus
+} // end extern "C"
+
+// symmetric addition
+inline aiVector3D operator + (const aiVector3D& v1, const aiVector3D& v2)
+{
+	return aiVector3D( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
+}
+
+// symmetric subtraction
+inline aiVector3D operator - (const aiVector3D& v1, const aiVector3D& v2)
+{
+	return aiVector3D( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+}
+
+// scalar product
+inline float operator * (const aiVector3D& v1, const aiVector3D& v2)
+{
+	return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
+}
+
+// scalar multiplication
+inline aiVector3D operator * ( float f, const aiVector3D& v)
+{
+	return aiVector3D( f*v.x, f*v.y, f*v.z);
+}
+
+// and the other way around
+inline aiVector3D operator * ( const aiVector3D& v, float f)
+{
+	return aiVector3D( f*v.x, f*v.y, f*v.z);
+}
+
+// scalar division
+inline aiVector3D operator / ( const aiVector3D& v, float f)
+{
+	//ai_assert(0.0f != f);
+	return v * (1/f);
+}
+
+// vector division
+inline aiVector3D operator / ( const aiVector3D& v, const aiVector3D& v2)
+	{
+	//ai_assert(0.0f != v2.x && 0.0f != v2.y && 0.0f != v2.z);
+	return aiVector3D(v.x / v2.x,v.y / v2.y,v.z / v2.z);
+	}
+
+// cross product
+inline aiVector3D operator ^ ( const aiVector3D& v1, const aiVector3D& v2)
+{
+	return aiVector3D( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
+}
+
+// vector inversion
+inline aiVector3D operator - ( const aiVector3D& v)
+{
+	return aiVector3D( -v.x, -v.y, -v.z);
+}
+
+#endif // __cplusplus
+
+#endif // AI_VECTOR3D_H_INC

+ 33 - 0
include/aiVector3D.inl

@@ -0,0 +1,33 @@
+/** @file Inline implementation of vector3D operators */
+#ifndef AI_VECTOR3D_INL_INC
+#define AI_VECTOR3D_INL_INC
+
+#include "aiVector3D.h"
+
+#ifdef __cplusplus
+#include "aiMatrix3x3.h"
+#include "aiMatrix4x4.h"
+
+/** Transformation of a vector by a 3x3 matrix */
+inline aiVector3D operator * (const aiMatrix3x3& pMatrix, const aiVector3D& pVector)
+{
+	aiVector3D res;
+	res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z;
+	res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z;
+	res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z;
+	return res;
+}
+
+/** Transformation of a vector by a 4x4 matrix */
+inline aiVector3D operator * (const aiMatrix4x4& pMatrix, const aiVector3D& pVector)
+{
+	aiVector3D res;
+	res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4;
+	res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4;
+	res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4;
+	return res;
+}
+
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_INL_INC

+ 77 - 0
include/assimp.h

@@ -0,0 +1,77 @@
+/** @file Defines the C-API to the Asset Import Library. */
+#ifndef AI_ASSIMP_H_INC
+#define AI_ASSIMP_H_INC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiScene;
+struct aiFileIO;
+//enum aiOrigin;
+
+// ---------------------------------------------------------------------------
+/** Reads the given file and returns its content.
+* 
+* If the call succeeds, the imported data is returned in an aiScene structure. 
+* The data is intended to be read-only, it stays property of the ASSIMP 
+* library and will be stable until aiReleaseImport() is called. After you're 
+* done with it, call aiReleaseImport() to free the resources associated with 
+* this file. If the import fails, NULL is returned instead. Call 
+* aiGetErrorString() to retrieve a human-readable error text.
+* @param pFile Path and filename of the file to be imported, 
+*        expected to be a null-terminated c-string.
+* @param pFlags Optional post processing steps to be executed after 
+*   a successful import. Provide a bitwise combination of the #aiPostProcessSteps
+*   flags.
+* @return Pointer to the imported data or NULL if the import failed. 
+*/
+// ---------------------------------------------------------------------------
+const aiScene* aiImportFile( const char* pFile, unsigned int pFlags);
+
+
+// ---------------------------------------------------------------------------
+/** Reads the given file using user-defined I/O functions and returns 
+*   its content.
+* 
+* If the call succeeds, the imported data is returned in an aiScene structure. 
+* The data is intended to be read-only, it stays property of the ASSIMP 
+* library and will be stable until aiReleaseImport() is called. After you're 
+* done with it, call aiReleaseImport() to free the resources associated with 
+* this file. If the import fails, NULL is returned instead. Call 
+* aiGetErrorString() to retrieve a human-readable error text.
+* @param pFile aiFileIO structure.  All functions pointers must be
+*        initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc()
+*        will be used to open other files in the fs if the asset to be 
+*        loaded depends on them.
+* @return Pointer to the imported data or NULL if the import failed. 
+*/
+// ---------------------------------------------------------------------------
+const aiScene* aiImportFileEx( const aiFileIO* pFile);
+
+
+
+// ---------------------------------------------------------------------------
+/** Releases all resources associated with the given import process.
+*
+* Call this function after you're done with the imported data.
+* @param pScene The imported data to release.
+*/
+// ---------------------------------------------------------------------------
+void aiReleaseImport( const aiScene* pScene);
+
+
+// ---------------------------------------------------------------------------
+/** Returns the error text of the last failed import process. 
+*
+* @return A textual description of the error that occured at the last
+* import process. NULL if there was no error.
+*/
+// ---------------------------------------------------------------------------
+const char* aiGetErrorString();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_ASSIMP_H_INC

+ 123 - 0
include/assimp.hpp

@@ -0,0 +1,123 @@
+/** @file Defines the CPP-API to the Asset Import Library. */
+#ifndef AI_ASSIMP_HPP_INC
+#define AI_ASSIMP_HPP_INC
+
+#ifndef __cplusplus
+#error This header requires C++ to be used.
+#endif
+
+#include <string>
+#include <vector>
+
+struct aiScene;
+
+namespace Assimp
+{
+
+class BaseImporter;
+class BaseProcess;
+class IOStream;
+class IOSystem;
+
+// ---------------------------------------------------------------------------
+/** The Importer class forms an C++ interface to the functionality of the 
+*   Asset Import library.
+*
+* Create an object of this class and call ReadFile() to import a file. 
+* If the import succeeds, the function returns a pointer to the imported data. 
+* The data remains property of the object, it is intended to be accessed 
+* read-only. The imported data will be destroyed along with the Importer 
+* object. If the import failes, ReadFile() returns a NULL pointer. In this
+* case you can retrieve a human-readable error description be calling 
+* GetErrorString().
+*
+* If you need the Importer to do custom file handling to access the files,
+* implement IOSystem and IOStream and supply an instance of your custom IOSystem
+* implementation by calling SetIOHandler() before calling ReadFile(). If you
+* do not assign a custion IO handler, a default handler using the standard C++
+* IO logic will be used.
+*/
+class Importer
+{
+public:
+
+	// -------------------------------------------------------------------
+	/** Constructor. Creates an empty importer object. 
+	 * 
+	 * Call ReadFile() to start the import process.
+	 */
+	Importer();
+
+	// -------------------------------------------------------------------
+	/** Destructor. The object kept ownership of the imported data,
+	 * which now will be destroyed along with the object. 
+	 */
+	~Importer();
+
+	// -------------------------------------------------------------------
+	/** Supplies a custom IO handler to the importer to open and access files.
+	 * If you need the importer to use custion IO logic to access the files,
+	 * you need to provide a custom implementation of IOSystem and IOFile
+	 * to the importer. Then create an instance of your custion IOSystem
+	 * implementation and supply it by this function.
+	 *
+	 * The Importer takes ownership of the object and will destroy it afterwards.
+	 * The previously assigned handler will be deleted.
+	 *
+	 * @param pIOHandler The IO handler to be used in all file accesses of the Importer.
+	 */
+	void SetIOHandler( IOSystem* pIOHandler);
+
+	// -------------------------------------------------------------------
+	/** Reads the given file and returns its contents if successful. 
+	* 
+	* If the call succeeds, the contents of the file are returned as a 
+	* pointer to an aiScene object. The returned data is intended to be 
+	* read-only, the importer object keeps ownership of the data and will
+    * destroy it upon destruction. If the import failes, NULL is returned.
+	* A human-readable error description can be retrieved by calling 
+	* 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.
+	* @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.
+	*/
+	inline const std::string& GetErrorString() const 
+		{ return mErrorString; }
+
+private:
+	/** Empty copy constructor. */
+	Importer(const Importer &other);
+
+protected:
+	/** IO handler to use for all file accesses. */
+	IOSystem* mIOHandler;
+
+	/** Format-specific importer worker objects - 
+	 * one for each format we can read. */
+	std::vector<BaseImporter*> mImporter;
+
+	/** Post processing steps we can apply at the imported data. */
+	std::vector<BaseProcess*> mPostProcessingSteps;
+
+	/** The imported data, if ReadFile() was successful,
+	 * NULL otherwise. */
+	aiScene* mScene;
+
+	/** The error description, if there was one. */
+	std::string mErrorString;
+};
+
+} // End of namespace Assimp
+
+#endif // AI_ASSIMP_HPP_INC

BIN
test/3DSFiles/CWALL02.jpg


BIN
test/3DSFiles/IMAGE1.bmp


BIN
test/3DSFiles/IMAGE2.jpg


BIN
test/3DSFiles/cube_with_diffuse_texture.3DS


BIN
test/3DSFiles/cube_with_specular_texture.3DS


BIN
test/3DSFiles/test.png


BIN
test/3DSFiles/test1.3ds


+ 1 - 0
test/3DSFiles/textures.txt

@@ -0,0 +1 @@
+All textures are from cgtextures.com and are free for commercial use

+ 135 - 0
tools/assimp_view/AssetHelper.h

@@ -0,0 +1,135 @@
+//-------------------------------------------------------------------------------
+/**
+*	This program is distributed under the terms of the GNU Lesser General
+*	Public License (LGPL). 
+*
+*	ASSIMP Viewer Utility
+*
+*/
+//-------------------------------------------------------------------------------
+
+#if (!defined AV_ASSET_HELPER_H_INCLUDED)
+#define AV_ASSET_HELPER_H_INCLUDED
+
+
+//-------------------------------------------------------------------------------
+/**	\brief Class to wrap ASSIMP's asset output structures
+*/
+//-------------------------------------------------------------------------------
+class AssetHelper
+	{
+	public:
+
+		//---------------------------------------------------------------
+		// default vertex data structure
+		// (even if tangents, bitangents or normals aren't
+		// required by the shader they will be committed to the GPU)
+		//---------------------------------------------------------------
+		struct Vertex
+			{
+			aiVector3D vPosition;
+			aiVector3D vNormal;
+
+			D3DCOLOR dColorDiffuse;
+			aiVector3D vTangent;
+			aiVector3D vBitangent;
+			aiVector2D vTextureUV;
+
+			// retrieves the FVF code of the vertex type
+			static DWORD GetFVF()
+				{
+				return D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_NORMAL |
+					D3DFVF_TEX1 | D3DFVF_TEX2 | D3DFVF_TEX3 |
+					D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEXCOORDSIZE3(1);
+				}
+			};
+
+		//---------------------------------------------------------------
+		// FVF vertex structure used for normals
+		//---------------------------------------------------------------
+		struct LineVertex
+			{
+			aiVector3D vPosition;
+			DWORD dColorDiffuse;
+
+			// retrieves the FVF code of the vertex type
+			static DWORD GetFVF()
+				{
+				return D3DFVF_DIFFUSE | D3DFVF_XYZ;
+				}
+			};
+
+		//---------------------------------------------------------------
+		// Helper class to store GPU related resources created for
+		// a given aiMesh
+		//---------------------------------------------------------------
+		class MeshHelper
+			{
+			public:
+
+				MeshHelper ()
+					: 
+					piVB				(NULL),
+					piIB				(NULL),
+					piVBNormals			(NULL),
+					piDiffuseTexture	(NULL),
+					piSpecularTexture	(NULL),
+					piAmbientTexture	(NULL),
+					piNormalTexture		(NULL),
+					piEmissiveTexture	(NULL),
+					piOpacityTexture	(NULL),
+					bSharedFX(false) {}
+
+				~MeshHelper ()
+					{
+					// NOTE: This is done in DeleteAssetData()
+					// TODO: Make this a proper d'tor
+					}
+
+				// shading mode to use. Either Lambert or otherwise phong
+				// will be used in every case
+				aiShadingMode eShadingMode;
+
+				// vertex buffer
+				IDirect3DVertexBuffer9* piVB;
+
+				// index buffer
+				IDirect3DIndexBuffer9* piIB;
+
+				// vertex buffer to be used to draw vertex normals
+				// (vertex normals are generated in every case)
+				IDirect3DVertexBuffer9* piVBNormals;
+
+				// shader to be used
+				ID3DXEffect* piEffect;
+				bool bSharedFX;
+
+				// material textures
+				IDirect3DTexture9* piDiffuseTexture;
+				IDirect3DTexture9* piSpecularTexture;
+				IDirect3DTexture9* piAmbientTexture;
+				IDirect3DTexture9* piEmissiveTexture;
+				IDirect3DTexture9* piNormalTexture;
+				IDirect3DTexture9* piOpacityTexture;
+
+				// material colors
+				D3DXVECTOR4 vDiffuseColor;
+				D3DXVECTOR4 vSpecularColor;
+				D3DXVECTOR4 vAmbientColor;
+				D3DXVECTOR4 vEmissiveColor;
+
+				// opacity for the material
+				float fOpacity;
+
+				// shininess for the material
+				float fShininess;
+			};
+
+		// One instance per aiMesh in the globally loaded asset
+		MeshHelper** apcMeshes;
+
+		// Scene wrapper instance
+		const aiScene* pcScene;
+	};
+
+#endif // !! IG

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