Browse Source

First half-working version of the ASE loader

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@46 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
aramis_acg 17 years ago
parent
commit
74dfe61c1e
4 changed files with 530 additions and 136 deletions
  1. 174 6
      code/ASELoader.cpp
  2. 10 0
      code/ASELoader.h
  3. 278 130
      code/ASEParser.cpp
  4. 68 0
      code/ASEParser.h

+ 174 - 6
code/ASELoader.cpp

@@ -49,13 +49,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
+#include "../include/DefaultLogger.h"
 
 #include <boost/scoped_ptr.hpp>
 
 using namespace Assimp;
 using namespace Assimp::ASE;
 
-#define LOGOUT_WARN(x)
+#define LOGOUT_WARN(x) DefaultLogger::get()->warn(x);
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
@@ -85,6 +86,8 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
 	if (extension[2] != 's' && extension[2] != 'S')return false;
 
 	// NOTE: Sometimes the extension .ASK is also used
+	// however, often it only contains static animation skeletons
+	// without the real animations.
 	if (extension[3] != 'e' && extension[3] != 'E' &&
 		extension[3] != 'k' && extension[3] != 'K')return false;
 
@@ -105,6 +108,14 @@ void ASEImporter::InternReadFile(
 
 	size_t fileSize = file->FileSize();
 
+	std::string::size_type pos = pFile.find_last_of('.');
+	std::string extension = pFile.substr( pos);
+	if(extension[3] == 'k' || extension[3] == 'K')
+	{
+		this->mIsAsk = true;
+	}
+	else this->mIsAsk = false;
+
 	// allocate storage and copy the contents of the file to a memory buffer
 	// (terminate it with zero)
 	this->mBuffer = new unsigned char[fileSize+1];
@@ -120,12 +131,19 @@ void ASEImporter::InternReadFile(
 		i =  this->mParser->m_vMeshes.begin();
 		i != this->mParser->m_vMeshes.end();++i)
 	{
-		// need to generate proper vertex normals if necessary
-		this->GenerateNormals(*i);
+		// transform all vertices into worldspace
+		// world2obj transform is specified in the
+		// transformation matrix of a scenegraph node
+		this->TransformVertices(*i);
 
 		// now we need to create proper meshes from the import
 		// we need to split them by materials, build valid vertex/face lists ...
 		this->BuildUniqueRepresentation(*i);
+
+		// need to generate proper vertex normals if necessary
+		this->GenerateNormals(*i);
+
+		// convert all meshes to aiMesh objects
 		this->ConvertMeshes(*i,pScene);
 	}
 	// buil final material indices (remove submaterials and make the final list)
@@ -206,6 +224,23 @@ void ASEImporter::BuildNodes(aiScene* pcScene)
 	return;
 }
 // ------------------------------------------------------------------------------------------------
+void ASEImporter::TransformVertices(ASE::Mesh& mesh)
+{
+	// the matrix data is stored in column-major format,
+	// but we need row major
+	mesh.mTransform.Transpose();
+
+	aiMatrix4x4 m = mesh.mTransform;
+	m.Inverse();
+
+	for (std::vector<aiVector3D>::iterator
+		i =  mesh.mPositions.begin();
+		i != mesh.mPositions.end();++i)
+	{
+		(*i) = m * (*i);
+	}
+}
+// ------------------------------------------------------------------------------------------------
 void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
 {
 	// allocate output storage
@@ -213,6 +248,7 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
 	std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 	std::vector<aiColor4D> mVertexColors;
 	std::vector<aiVector3D> mNormals;
+	std::vector<BoneVertex> mBoneVertices;
 
 	unsigned int iSize = mesh.mFaces.size() * 3;
 	mPositions.resize(iSize);
@@ -236,6 +272,11 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
 	{
 		mNormals.resize(iSize);
 	}
+	// bone vertices. There is no need to change the bone list
+	if (!mesh.mBoneVertices.empty())
+	{
+		mBoneVertices.resize(iSize);
+	}
 
 	// iterate through all faces in the mesh
 	unsigned int iCurrent = 0;
@@ -265,6 +306,16 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
 			{
 				mNormals[iCurrent] = mesh.mNormals[(*i).mIndices[n]];
 			}
+
+			// handle bone vertices
+			if ((*i).mIndices[n] < mesh.mBoneVertices.size())
+			{
+				// (sometimes this will cause bone verts to be duplicated
+				//  however, I' quite sure Schrompf' JoinVerticesStep
+				//  will fix that again ...)
+				mBoneVertices[iCurrent] =  mesh.mBoneVertices[(*i).mIndices[n]];
+			}
+
 			// assign a new valid index to the face
 			(*i).mIndices[n] = iCurrent;
 		}
@@ -312,9 +363,21 @@ void ASEImporter::ConvertMaterial(ASE::Material& mat)
 	mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
 	mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
 	mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
-	mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
 	mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
 
+	// shininess
+	if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength)
+	{
+		mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+		mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+	}
+	// if there is no shininess, we can disable phong lighting
+	else if (Dot3DS::Dot3DSFile::Metal == mat.mShading ||
+		Dot3DS::Dot3DSFile::Phong == mat.mShading)
+	{
+		mat.mShading = Dot3DS::Dot3DSFile::Gouraud;
+	}
+
 	// opacity
 	mat.pcInstance->AddProperty<float>( &mat.mTransparency,1,AI_MATKEY_OPACITY);
 
@@ -489,6 +552,13 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene)
 				p_pcOut->mNumVertices = aiSplit[p].size()*3;
 				p_pcOut->mNumFaces = aiSplit[p].size();
 
+				// receive output vertex weights
+				std::vector<std::pair<unsigned int, float>>* avOutputBones;
+				if (!mesh.mBones.empty())
+				{
+					avOutputBones = new std::vector<std::pair<unsigned int, float>>[mesh.mBones.size()];
+				}
+				
 				// allocate enough storage for faces
 				p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
 
@@ -507,8 +577,29 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene)
 
 						for (unsigned int t = 0; t < 3;++t)
 						{
-							p_pcOut->mVertices[iBase] = mesh.mPositions[mesh.mFaces[iIndex].mIndices[t]];
-							p_pcOut->mNormals[iBase++] = mesh.mNormals[mesh.mFaces[iIndex].mIndices[t]];
+							const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t];
+
+							p_pcOut->mVertices[iBase] = mesh.mPositions[iIndex2];
+							p_pcOut->mNormals[iBase] = mesh.mNormals[iIndex2];
+
+							// convert bones, if existing
+							if (!mesh.mBones.empty())
+							{
+								// check whether there is a vertex weight that is using
+								// this vertex index ...
+								if (iIndex2 < mesh.mBoneVertices.size())
+								{
+									for (std::vector<std::pair<int,float>>::const_iterator
+										blubb =  mesh.mBoneVertices[iIndex2].mBoneWeights.begin();
+										blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb)
+									{
+										// NOTE: illegal cases have already been filtered out
+										avOutputBones[(*blubb).first].push_back(std::pair<unsigned int, float>(
+											iBase,(*blubb).second));
+									}
+								}
+							}
+							++iBase;
 						}
 						p_pcOut->mFaces[q].mIndices[0] = iBase-2;
 						p_pcOut->mFaces[q].mIndices[1] = iBase-1;
@@ -549,6 +640,38 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene)
 						}
 					}
 				}
+				if (!mesh.mBones.empty())
+				{
+					p_pcOut->mNumBones = 0;
+					for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+						if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++;
+
+					p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ];
+					aiBone** pcBone = &p_pcOut->mBones[0];
+					for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+					{
+						if (!avOutputBones[mrspock].empty())
+						{
+							// we will need this bone. add it to the output mesh and
+							// add all per-vertex weights
+							*pcBone = new aiBone();
+							(**pcBone).mName.Set(mesh.mBones[mrspock].mName);
+
+							(**pcBone).mNumWeights = avOutputBones[mrspock].size();
+							(**pcBone).mWeights = new aiVertexWeight[(**pcBone).mNumWeights];
+
+							for (unsigned int captainkirk = 0; captainkirk < (**pcBone).mNumWeights;++captainkirk)
+							{
+								const std::pair<unsigned int,float>& ref = avOutputBones[mrspock][captainkirk];
+								(**pcBone).mWeights[captainkirk].mVertexId = ref.first;
+								(**pcBone).mWeights[captainkirk].mWeight = ref.second;
+							}
+							++pcBone;
+						}
+					}
+					// delete allocated storage
+					delete[] avOutputBones;
+				}
 			}
 		}
 		// delete storage
@@ -619,6 +742,51 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene)
 			p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1];
 			p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2];
 		}
+
+		// copy vertex bones
+		if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty())
+		{
+			std::vector<aiVertexWeight>* avBonesOut = new
+				std::vector<aiVertexWeight>[mesh.mBones.size()];
+
+			// find all vertex weights for this bone
+			unsigned int quak = 0;
+			for (std::vector<BoneVertex>::const_iterator
+				harrypotter =  mesh.mBoneVertices.begin();
+				harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak)
+			{
+				for (std::vector<std::pair<int,float>>::const_iterator
+					ronaldweasley  = (*harrypotter).mBoneWeights.begin();
+					ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley)
+				{
+					aiVertexWeight weight;
+					weight.mVertexId = quak;
+					weight.mWeight = (*ronaldweasley).second;
+					avBonesOut[(*ronaldweasley).first].push_back(weight);
+				}
+			}
+
+			// now build a final bone list
+			p_pcOut->mNumBones = 0;
+			for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy)
+				if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++;
+
+			p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones];
+			aiBone** pcBone = &p_pcOut->mBones[0];
+			for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy)
+			{
+				if (!avBonesOut[jfkennedy].empty())
+				{
+					*pcBone = new aiBone();
+					(**pcBone).mName.Set(mesh.mBones[jfkennedy].mName);
+					(**pcBone).mNumWeights = avBonesOut[jfkennedy].size();
+					(**pcBone).mWeights = new aiVertexWeight[(**pcBone).mNumWeights];
+					memcpy((**pcBone).mWeights,&avBonesOut[jfkennedy][0],
+						sizeof(aiVertexWeight) * (**pcBone).mNumWeights);
+					++pcBone;
+				}
+			}
+		}
 	}
 
 	// now build the output mesh list

+ 10 - 0
code/ASELoader.h

@@ -108,6 +108,13 @@ protected:
 	 */
 	void BuildUniqueRepresentation(ASE::Mesh& mesh);
 
+	// -------------------------------------------------------------------
+	/** Transform all vertices with the inverse transformation
+	 *  matrix of the mesh
+	 * \param mesh Mesh to work on
+	 */
+	void TransformVertices(ASE::Mesh& mesh);
+
 	// -------------------------------------------------------------------
 	/** Create one-material-per-mesh meshes ;-)
 	 * \param mesh Mesh to work with
@@ -140,6 +147,9 @@ protected:
 
 	/** Buffer to hold the loaded file */
 	unsigned char* mBuffer;
+
+	/** true if this is an .ask file */
+	bool mIsAsk;
 };
 
 } // end of namespace Assimp

+ 278 - 130
code/ASEParser.cpp

@@ -204,12 +204,7 @@ void Parser::Parse()
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		if ('\0' == *this->m_szFile)
 		{
@@ -249,12 +244,7 @@ void Parser::ParseLV1SceneBlock()
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -310,12 +300,7 @@ void Parser::ParseLV1MaterialListBlock()
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -437,6 +422,13 @@ void Parser::ParseLV2MaterialBlock(ASE::Material& mat)
 				this->ParseLV4MeshFloat(mat.mSpecularExponent);
 				mat.mSpecularExponent *= 15;
 			}
+			// material shininess strength
+			if (0 == strncmp(this->m_szFile,"*MATERIAL_SHINESTRENGTH",23) &&
+				IsSpaceOrNewLine(*(this->m_szFile+23)))
+			{
+				this->m_szFile+=24;
+				this->ParseLV4MeshFloat(mat.mShininessStrength);
+			}
 			// diffuse color map
 			if (0 == strncmp(this->m_szFile,"*MAP_DIFFUSE",12) &&
 				IsSpaceOrNewLine(*(this->m_szFile+12)))
@@ -546,12 +538,7 @@ void Parser::ParseLV2MaterialBlock(ASE::Material& mat)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -649,12 +636,7 @@ void Parser::ParseLV3MapBlock(Texture& map)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -667,6 +649,47 @@ void Parser::ParseLV3MapBlock(Texture& map)
 	return;
 }
 // ------------------------------------------------------------------------------------------------
+bool Parser::ParseString(std::string& out,const char* szName)
+{
+	char szBuffer[1024];
+	ai_assert(strlen(szName < 750));
+
+	// NOTE: The name could also be the texture in some cases
+	// be prepared that this might occur ...
+	if (!SkipSpaces(this->m_szFile,&this->m_szFile))
+	{
+		sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName);
+		this->LogWarning(szBuffer);
+		return false;
+	}
+	// there must be "
+	if ('\"' != *this->m_szFile)
+	{
+		sprintf(szBuffer,"Unable to parse %s block: String is expected "
+			"to be enclosed in double quotation marks",szName);
+		this->LogWarning(szBuffer);
+		return false;
+	}
+	++this->m_szFile;
+	const char* sz = this->m_szFile;
+	while (true)
+	{
+		if ('\"' == *sz)break;
+		else if ('\0' == sz)
+		{
+			sprintf(szBuffer,"Unable to parse %s block: String is expected to be "
+				"enclosed in double quotation marks but EOF was reached before a closing "
+				"quotation mark was found",szName);
+			this->LogWarning(szBuffer);
+			return false;
+		}
+		sz++;
+	}
+	out = std::string(this->m_szFile,(uintptr_t)sz-(uintptr_t)this->m_szFile);
+	this->m_szFile = sz;
+	return true;
+}
+// ------------------------------------------------------------------------------------------------
 void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh)
 {
 	int iDepth = 0;
@@ -679,31 +702,22 @@ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh)
 				IsSpaceOrNewLine(*(this->m_szFile+10)))
 			{
 				this->m_szFile+=11;
-
-				// NOTE: The name could also be the texture in some cases
-				// be prepared that this might occur ...
-				if (!SkipSpaces(this->m_szFile,&this->m_szFile))
-					BLUBB("Unable to parse *NODE_NAME block: Unexpected EOL")
-
-				// there must be "
-				if ('\"' != *this->m_szFile)
-					BLUBB("Unable to parse *NODE_NAME block: Name is expected to be enclosed in double quotation marks")
-
-				++this->m_szFile;
-				const char* sz = this->m_szFile;
-				while (true)
+				if(!this->ParseString(mesh.mName,"*NODE_NAME"))
 				{
-					if ('\"' == *sz)break;
-					else if ('\0' == sz)
-					{
-						BLUBB("Unable to parse *NODE_NAME block: Name is expected to be enclosed in double quotation marks \
-							  but EOF was reached before a closing quotation mark was found")
-					}
-					sz++;
+					this->SkipToNextToken();
+					continue;
+				}
+			}
+			// name of the parent of the node
+			if (0 == strncmp(this->m_szFile,"*NODE_PARENT" ,12) &&
+				IsSpaceOrNewLine(*(this->m_szFile+12)))
+			{
+				this->m_szFile+=13;
+				if(!this->ParseString(mesh.mParent,"*NODE_PARENT"))
+				{
+					this->SkipToNextToken();
+					continue;
 				}
-
-				mesh.mName = std::string(this->m_szFile,(uintptr_t)sz-(uintptr_t)this->m_szFile);
-				this->m_szFile = sz;
 			}
 			// transformation matrix of the node
 			if (0 == strncmp(this->m_szFile,"*NODE_TM" ,8) &&
@@ -719,16 +733,18 @@ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh)
 				this->m_szFile+=6;
 				this->ParseLV2MeshBlock(mesh);
 			}
+			// mesh material index
+			else if (0 == strncmp(this->m_szFile,"*MATERIAL_REF" ,13) &&
+				IsSpaceOrNewLine(*(this->m_szFile+13)))
+			{
+				this->m_szFile+=14;
+				this->ParseLV4MeshLong(mesh.iMaterialIndex);
+			}
 		}
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -780,12 +796,7 @@ void Parser::ParseLV2NodeTransformBlock(ASE::Mesh& mesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -932,28 +943,201 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh)
 					this->ParseLV3MappingChannel(iIndex-1,mesh);
 				}
 			}
-			// mesh material index
-			else if (0 == strncmp(this->m_szFile,"*MATERIAL_REF" ,13) &&
+			// mesh animation keyframe. Not supported
+			else if (0 == strncmp(this->m_szFile,"*MESH_ANIMATION" ,15) &&
+				IsSpaceOrNewLine(*(this->m_szFile+15)))
+			{
+				this->m_szFile+=16;
+				
+				this->LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. "
+					"Keyframe animation is not supported by Assimp, this element "
+					"will be ignored");
+			}
+			// mesh animation keyframe. Not supported
+			else if (0 == strncmp(this->m_szFile,"*MESH_WEIGHTS" ,13) &&
 				IsSpaceOrNewLine(*(this->m_szFile+13)))
 			{
 				this->m_szFile+=14;
-				this->ParseLV4MeshLong(mesh.iMaterialIndex);
+				this->ParseLV3MeshWeightsBlock(mesh);
 			}
 		}
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
+		}
+		else if ('\0' == *this->m_szFile)
+		{
+			// END OF FILE ... this is a level2 block, this can't be
+			BLUBB("Unable to finish parsing a lv2 mesh block. Unexpected EOF")
+		}
+		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
+		++this->m_szFile;
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh)
+{
+	unsigned int iNumVertices = 0;
+	unsigned int iNumBones = 0;
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
+		{
+			// Number of bone vertices ...
+			if (0 == strncmp(this->m_szFile,"*MESH_NUMVERTEX" ,15) &&
+				IsSpaceOrNewLine(*(this->m_szFile+15)))
 			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
+				this->m_szFile+=16;
+				this->ParseLV4MeshLong(iNumVertices);
+			}
+			// Number of bones
+			if (0 == strncmp(this->m_szFile,"*MESH_NUMBONE" ,13) &&
+				IsSpaceOrNewLine(*(this->m_szFile+13)))
+			{
+				this->m_szFile+=14;
+				this->ParseLV4MeshLong(iNumBones);
+			}
+			// parse the list of bones
+			if (0 == strncmp(this->m_szFile,"*MESH_BONE_LIST" ,15) &&
+				IsSpaceOrNewLine(*(this->m_szFile+15)))
+			{
+				this->m_szFile+=16;
+				this->SkipOpeningBracket();
+				this->ParseLV4MeshBones(iNumBones,mesh);
+			}
+			// parse the list of bones vertices
+			if (0 == strncmp(this->m_szFile,"*MESH_BONE_VERTEX_LIST" ,22) &&
+				IsSpaceOrNewLine(*(this->m_szFile+22)))
+			{
+				this->m_szFile+=23;
+				this->SkipOpeningBracket();
+				this->ParseLV4MeshBonesVertices(iNumVertices,mesh);
 			}
 		}
+		if ('{' == *this->m_szFile)iDepth++;
+		if ('}' == *this->m_szFile)
+		{
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
+		}
 		else if ('\0' == *this->m_szFile)
 		{
 			// END OF FILE ... this is a level2 block, this can't be
-			BLUBB("Unable to finish parsing a lv2 mesh block. Unexpected EOF")
+			BLUBB("Unable to finish parsing a lv2 *MESH_WEIGHTS block. Unexpected EOF")
+		}
+		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
+		++this->m_szFile;
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh)
+{
+	mesh.mBones.resize(iNumBones);
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
+		{
+			// Mesh bone with name ...
+			if (0 == strncmp(this->m_szFile,"*MESH_BONE_NAME" ,17) &&
+				IsSpaceOrNewLine(*(this->m_szFile+17)))
+			{
+				this->m_szFile+=18;
+
+				// parse an index ...
+				if(SkipSpaces(this->m_szFile,&this->m_szFile))
+				{
+					unsigned int iIndex = strtol10(this->m_szFile,&this->m_szFile);
+					if (iIndex >= iNumBones)
+					{
+						iIndex = iNumBones-1;
+						this->LogWarning("Bone index is out of bounds. Using the largest valid "
+							"bone index instead");
+					}
+					if (!this->ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME"))						
+					{
+						this->SkipToNextToken();
+						continue;
+					}
+				}
+			}
+		}
+		if ('{' == *this->m_szFile)iDepth++;
+		else if ('}' == *this->m_szFile)
+		{
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
+		}
+		else if ('\0' == *this->m_szFile)
+		{
+			// END OF FILE ... this is a level4 block, this can't be
+			BLUBB("Unable to finish parsing a lv4 *MESH_BONE_LIST block. Unexpected EOF")
+		}
+		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
+		++this->m_szFile;
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh)
+{
+	mesh.mBoneVertices.resize(iNumVertices);
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
+		{
+			// Mesh bone vertex
+			if (0 == strncmp(this->m_szFile,"*MESH_BONE_VERTEX" ,17) &&
+				IsSpaceOrNewLine(*(this->m_szFile+17)))
+			{
+				this->m_szFile+=18;
+
+				// read the vertex index
+				unsigned int iIndex = strtol10(this->m_szFile,&this->m_szFile);
+				if (iIndex >= mesh.mPositions.size())
+				{
+					iIndex = mesh.mPositions.size()-1;
+					this->LogWarning("Bone vertex index is out of bounds. Using the largest valid "
+						"bone vertex index instead");
+				}
+
+				// now there there are 3 normal floats, the
+				// should be identical to the vertex positions
+				// contained in the *VERTEX_LIST block. Well, we check this
+				// in debug builds to be sure ;-)
+				float afVert[3];
+				this->ParseLV4MeshFloatTriple(afVert);
+
+				std::pair<int,float> pairOut;
+				while (true)
+				{
+					// first parse the bone index ...
+					if (!SkipSpaces(this->m_szFile,&this->m_szFile))break;
+					pairOut.first = strtol10(this->m_szFile,&this->m_szFile);
+
+					// then parse the vertex weight
+					if (!SkipSpaces(this->m_szFile,&this->m_szFile))break;
+					this->m_szFile = fast_atof_move(this->m_szFile,pairOut.second);
+
+					// -1 designates unused entries
+					if (-1 != pairOut.first)
+					{
+						mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut);
+					}
+				}
+			}
+		}
+		if ('{' == *this->m_szFile)iDepth++;
+		else if ('}' == *this->m_szFile)
+		{
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
+		}
+		else if ('\0' == *this->m_szFile)
+		{
+			// END OF FILE ... this is a level4 block, this can't be
+			BLUBB("Unable to finish parsing a lv4 *MESH_BONE_VERTEX_LIST block. Unexpected EOF")
 		}
 		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
 		++this->m_szFile;
@@ -989,14 +1173,9 @@ void Parser::ParseLV3MeshVertexListBlock(
 			}
 		}
 		if ('{' == *this->m_szFile)iDepth++;
-		if ('}' == *this->m_szFile)
+		else if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1037,12 +1216,7 @@ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1091,12 +1265,7 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1143,12 +1312,7 @@ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1205,12 +1369,7 @@ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1253,12 +1412,7 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1304,12 +1458,7 @@ void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		else if ('\0' == *this->m_szFile)
 		{
@@ -1356,12 +1505,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
 		if ('{' == *this->m_szFile)iDepth++;
 		if ('}' == *this->m_szFile)
 		{
-			if (0 == --iDepth)
-			{
-				++this->m_szFile;
-				this->SkipToNextToken();
-				return;
-			}
+			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
 		}
 		// seems we have reached the end of the file ... 
 		else if ('\0' == *this->m_szFile)
@@ -1432,7 +1576,7 @@ __EARTHQUAKE_XXL:
 		if ('*' == *this->m_szFile)break;
 		if (IsLineEnd(*this->m_szFile))
 		{
-			this->iLineNumber++;
+			//this->iLineNumber++;
 			return;
 		}
 		this->m_szFile++;
@@ -1446,9 +1590,13 @@ __EARTHQUAKE_XXL:
 			BLUBB("Unable to parse *MESH_SMOOTHING Element: Unexpected EOL. Smoothing group(s) expected [#5]")
 		
 		// parse smoothing groups until we don_t anymore see commas
+		// FIX: There needn't always be a value, sad but true
 		while (true)
 		{
-			out.iSmoothGroup |= (1 << strtol10(this->m_szFile,&this->m_szFile));
+			if (*this->m_szFile < '9' && *this->m_szFile >= '0')
+			{
+				out.iSmoothGroup |= (1 << strtol10(this->m_szFile,&this->m_szFile));
+			}
 			SkipSpaces(this->m_szFile,&this->m_szFile);
 			if (',' != *this->m_szFile)
 			{
@@ -1465,7 +1613,7 @@ __EARTHQUAKE_XXL:
 		if ('*' == *this->m_szFile)break;
 		if (IsLineEnd(*this->m_szFile))
 		{
-			this->iLineNumber++;
+			//this->iLineNumber++;
 			return;
 		}
 		this->m_szFile++;
@@ -1478,7 +1626,7 @@ __EARTHQUAKE_XXL:
 			BLUBB("Unable to parse *MESH_MTLID Element: Unexpected EOL. Material index expected [#6]")
 		out.iMaterial = strtol10(this->m_szFile,&this->m_szFile);
 	}
-	this->SkipToNextToken();
+	//this->SkipToNextToken();
 	return;
 }
 // ------------------------------------------------------------------------------------------------
@@ -1519,7 +1667,7 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
 	}
 	apOut[2] = strtol10(this->m_szFile,&this->m_szFile);
 	// go to the next valid sequence
-	SkipSpacesAndLineEnd(this->m_szFile,&this->m_szFile);
+	//SkipSpacesAndLineEnd(this->m_szFile,&this->m_szFile);
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
@@ -1603,7 +1751,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut)
 	// parse the third float
 	this->m_szFile = fast_atof_move(this->m_szFile,apOut[2]);
 	// go to the next valid sequence
-	this->SkipToNextToken();
+	//this->SkipToNextToken();
 	return;
 }
 // ------------------------------------------------------------------------------------------------
@@ -1621,7 +1769,7 @@ void Parser::ParseLV4MeshFloat(float& fOut)
 	// parse the first float
 	this->m_szFile = fast_atof_move(this->m_szFile,fOut);
 	// go to the next valid sequence
-	this->SkipToNextToken();
+	//this->SkipToNextToken();
 	return;
 }
 // ------------------------------------------------------------------------------------------------
@@ -1639,6 +1787,6 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut)
 	// parse the value
 	iOut = strtol10(this->m_szFile,&this->m_szFile);
 	// go to the next valid sequence
-	this->SkipToNextToken();
+	//this->SkipToNextToken();
 	return;
 }

+ 68 - 0
code/ASEParser.h

@@ -125,6 +125,36 @@ struct Face : public Dot3DS::Face
 	unsigned int iFace;
 };
 
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone */
+struct Bone
+{
+	//! Constructor
+	Bone()
+	{
+		static int iCnt = 0;
+		std::stringstream ss(mName);
+		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
+		ss.flush();
+	}
+
+	//! Name of the bone
+	std::string mName;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone vertex */
+struct BoneVertex
+{
+	//! Bone and corresponding vertex weight.
+	//! -1 for unrequired bones ....
+	std::vector<std::pair<int,float> > mBoneWeights;
+
+	//! Position of the bone vertex.
+	//! MUST be identical to the vertex position
+	//aiVector3D mPosition;
+};
+
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file mesh */
 struct Mesh
@@ -135,6 +165,7 @@ struct Mesh
 		static int iCnt = 0;
 		std::stringstream ss(mName);
 		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
+		ss.flush();
 
 		// use 2 texture vertex components by default
 		for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
@@ -143,8 +174,14 @@ struct Mesh
 		// setup the default material index by default
 		iMaterialIndex = Face::DEFAULT_MATINDEX;
 	}
+
+	//! Name of the mesh
 	std::string mName;
 
+	//! Name of the parent of the mesh
+	//! "" if there is no parent ...
+	std::string mParent;
+
 	//! vertex positions
 	std::vector<aiVector3D> mPositions;
 
@@ -160,6 +197,12 @@ struct Mesh
 	//! List of normal vectors
 	std::vector<aiVector3D> mNormals;
 
+	//! List of all bone vertices
+	std::vector<BoneVertex> mBoneVertices;
+
+	//! List of all bones
+	std::vector<Bone> mBones;
+
 	//! Transformation matrix of the mesh
 	aiMatrix4x4 mTransform;
 
@@ -295,6 +338,23 @@ private:
 	//! \param mesh Mesh object to be filled
 	void ParseLV3MeshNormalListBlock(Mesh& mesh);
 
+	// -------------------------------------------------------------------
+	//! Parse a *MESH_WEIGHTSblock in a file
+	//! \param mesh Mesh object to be filled
+	void ParseLV3MeshWeightsBlock(Mesh& mesh);
+
+	// -------------------------------------------------------------------
+	//! Parse the bone list of a file
+	//! \param mesh Mesh object to be filled
+	//! \param iNumBones Number of bones in the mesh
+	void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh);
+
+	// -------------------------------------------------------------------
+	//! Parse the bone vertices list of a file
+	//! \param mesh Mesh object to be filled
+	//! \param iNumVertices Number of vertices to be parsed
+	void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh);
+
 	// -------------------------------------------------------------------
 	//! Parse a *MESH_FACE block in a file
 	//! \param out receive the face data
@@ -359,6 +419,14 @@ private:
 	//! \param szWarn Error message
 	void LogError(const char* szWarn);
 
+	// -------------------------------------------------------------------
+	//! Parse a string, enclosed in double quotation marks
+	//! \param out Output string
+	//! \param szName Name of the enclosing element -> used in error
+	//! messages.
+	//! \return false if an error occured
+	bool Parser::ParseString(std::string& out,const char* szName);
+
 public:
 
 	//! Pointer to current data