Sfoglia il codice sorgente

Small update to the ASE loader.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@64 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
aramis_acg 17 anni fa
parent
commit
6ad0892396

+ 180 - 65
code/ASELoader.cpp

@@ -126,6 +126,7 @@ void ASEImporter::InternReadFile(
 	// allocate storage and copy the contents of the file to a memory buffer
 	// (terminate it with zero)
 	this->mBuffer = new unsigned char[fileSize+1];
+	this->pcScene = pScene;
 	file->Read( (void*)mBuffer, 1, fileSize);
 	this->mBuffer[fileSize] = '\0';
 
@@ -135,10 +136,7 @@ void ASEImporter::InternReadFile(
 
 	// if absolutely no material has been loaded from the file
 	// we need to generate a default material
-	if (this->mParser->m_vMaterials.empty())
-	{
-		this->GenerateDefaultMaterial();
-	}
+	this->GenerateDefaultMaterial();
 
 	// process all meshes
 	std::vector<aiMesh*> avOutMeshes;
@@ -165,17 +163,26 @@ void ASEImporter::InternReadFile(
 		this->ConvertMeshes(*i,avOutMeshes);
 	}
 	
-	// now build the output mesh list
+	// now build the output mesh list. remove dummies
 	pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
-	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
-	for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-		pScene->mMeshes[i] = avOutMeshes[i];
+	aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+	for (std::vector<aiMesh*>::const_iterator
+		i =  avOutMeshes.begin();
+		i != avOutMeshes.end();++i)
+	{
+		if (!(*i)->mNumFaces)continue;
+		*pp++ = *i;
+	}
+	pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
 
 	// buil final material indices (remove submaterials and make the final list)
-	this->BuildMaterialIndices(pScene);
+	this->BuildMaterialIndices();
 
 	// build the final node graph
-	this->BuildNodes(pScene);
+	this->BuildNodes();
+
+	// build output animations
+	this->BuildAnimations();
 
 	// delete the ASE parser
 	delete this->mParser;
@@ -187,28 +194,120 @@ void ASEImporter::GenerateDefaultMaterial()
 {
 	ai_assert(NULL != this->mParser);
 
-	// add a simple material without sub materials to the parser's list
-	this->mParser->m_vMaterials.push_back ( ASE::Material() );
-	ASE::Material& mat = this->mParser->m_vMaterials.back();
+	bool bHas = false;
+	for (std::vector<ASE::Mesh>::iterator
+		i =  this->mParser->m_vMeshes.begin();
+		i != this->mParser->m_vMeshes.end();++i)
+	{
+		if ((*i).bSkip)continue;
+		if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex)
+		{
+			(*i).iMaterialIndex = (unsigned int)this->mParser->m_vMaterials.size();
+			bHas = true;
+		}
+	}
+	if (bHas || this->mParser->m_vMaterials.empty())
+	{
+		// add a simple material without sub materials to the parser's list
+		this->mParser->m_vMaterials.push_back ( ASE::Material() );
+		ASE::Material& mat = this->mParser->m_vMaterials.back();
+
+		mat.mDiffuse = aiColor3D(0.5f,0.5f,0.5f);
+		mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
+		mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
+		mat.mShading = Dot3DSFile::Gouraud;
+		mat.mName = AI_DEFAULT_MATERIAL_NAME;
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::BuildAnimations()
+{
+	// check whether we have at least one mesh which has animations
+	std::vector<ASE::Mesh>::iterator i =  this->mParser->m_vMeshes.begin();
+	unsigned int iNum = 0;
+	for (;i != this->mParser->m_vMeshes.end();++i)
+	{
+		if ((*i).bSkip)continue;
+		if ((*i).mAnim.akeyPositions.size() > 1 || (*i).mAnim.akeyRotations.size() > 1)
+			++iNum;
+	}
+	if (iNum)
+	{
+		this->pcScene->mNumAnimations = 1;
+		this->pcScene->mAnimations = new aiAnimation*[1];
+		aiAnimation* pcAnim = this->pcScene->mAnimations[0] = new aiAnimation();
+		pcAnim->mNumBones = iNum;
+		pcAnim->mBones = new aiBoneAnim*[iNum];
+		pcAnim->mTicksPerSecond = this->mParser->iFrameSpeed * this->mParser->iTicksPerFrame;
+
+		iNum = 0;
+		i =  this->mParser->m_vMeshes.begin();
+		for (;i != this->mParser->m_vMeshes.end();++i)
+		{
+			if ((*i).bSkip)continue;
+			if ((*i).mAnim.akeyPositions.size() > 1 || (*i).mAnim.akeyRotations.size() > 1)
+			{
+				aiBoneAnim* pcBoneAnim = pcAnim->mBones[iNum++] = new aiBoneAnim();
+				pcBoneAnim->mBoneName.Set((*i).mName);
+
+				// copy position keys
+				if ((*i).mAnim.akeyPositions.size() > 1 )
+				{
+					pcBoneAnim->mNumPositionKeys = (unsigned int) (*i).mAnim.akeyPositions.size();
+					pcBoneAnim->mPositionKeys = new aiVectorKey[pcBoneAnim->mNumPositionKeys];
+
+					::memcpy(pcBoneAnim->mPositionKeys,&(*i).mAnim.akeyPositions[0],
+						pcBoneAnim->mNumPositionKeys * sizeof(aiVectorKey));
 
-	mat.mDiffuse = aiColor3D(0.5f,0.5f,0.5f);
-	mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
-	mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
-	mat.mShading = Dot3DSFile::Gouraud;
-	mat.mName = AI_DEFAULT_MATERIAL_NAME;
+					for (unsigned int qq = 0; qq < pcBoneAnim->mNumPositionKeys;++qq)
+					{
+						double dTime = pcBoneAnim->mPositionKeys[qq].mTime;
+						pcAnim->mDuration = std::max(pcAnim->mDuration,dTime);
+					}
+				}
+				// copy rotation keys
+				if ((*i).mAnim.akeyRotations.size() > 1 )
+				{
+					pcBoneAnim->mNumRotationKeys = (unsigned int) (*i).mAnim.akeyPositions.size();
+					pcBoneAnim->mRotationKeys = new aiQuatKey[pcBoneAnim->mNumPositionKeys];
+
+					::memcpy(pcBoneAnim->mRotationKeys,&(*i).mAnim.akeyRotations[0],
+						pcBoneAnim->mNumRotationKeys * sizeof(aiQuatKey));
+
+					for (unsigned int qq = 0; qq < pcBoneAnim->mNumRotationKeys;++qq)
+					{
+						double dTime = pcBoneAnim->mRotationKeys[qq].mTime;
+						pcAnim->mDuration = std::max(pcAnim->mDuration,dTime);
+					}
+				}
+			}
+		}
+	}
 }
 // ------------------------------------------------------------------------------------------------
-void ASEImporter::AddNodes(aiScene* pcScene,aiNode* pcParent,
-	const char* szName)
+void ASEImporter::AddNodes(aiNode* pcParent,const char* szName)
+{
+	aiMatrix4x4 m;
+	ASE::DecompTransform dec(m);
+	this->AddNodes(pcParent,szName,dec);
+}
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::AddNodes(aiNode* pcParent,const char* szName,
+	const ASE::DecompTransform& decompTrafo)
 {
 	const size_t len = szName ? strlen(szName) : 0;
 
 	ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
 	std::vector<aiNode*> apcNodes;
+	aiMesh** pcMeshes = pcScene->mMeshes;
 	for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
 	{
-		// get the name of the mesh ([0] = name, [1] = parent)
-		std::string* szMyName = (std::string*)pcScene->mMeshes[i]->mColors[1];
+		// get the name of the mesh
+		aiMesh* pcMesh = *pcMeshes++;
+		const ASE::Mesh& mesh = *((const ASE::Mesh*)pcMesh->mColors[2]);
+
+		// TODO: experimental quick'n'dirty, clean this up ...
+		std::string szMyName[2] = {mesh.mName,mesh.mParent} ;
 		if (!szMyName)
 		{
 			continue;
@@ -226,25 +325,39 @@ void ASEImporter::AddNodes(aiScene* pcScene,aiNode* pcParent,
 		apcNodes.push_back(new aiNode());
 		aiNode* node = apcNodes.back();
 
-		// get the transformation matrix of the mesh
-		aiMatrix4x4* pmTransform = (aiMatrix4x4*)pcScene->mMeshes[i]->mColors[2];
-
 		node->mName.Set(szMyName[0]);
 		node->mNumMeshes = 1;
 		node->mMeshes = new unsigned int[1];
 		node->mMeshes[0] = i;
 		node->mParent = pcParent;
-		node->mTransformation = *pmTransform;
+
+		aiMatrix4x4 mParentAdjust  = decompTrafo.mMatrix;
+		mParentAdjust.Inverse();
+		//if(ComputeLocalToWorldShift(mParentAdjust, decompTrafo, mesh.inherit))
+		{
+			node->mTransformation = mParentAdjust*mesh.mTransform;
+		}
+		//else node->mTransformation = mesh.mTransform;
 		
-		// delete the matrix (a mesh is always the child of ONE node, so this is safe)
-		delete pmTransform;
-		pcScene->mMeshes[i]->mColors[2] = NULL;
+		// Transform all vertices of the mesh back into their local space -> 
+		// at the moment they are pretransformed
+		aiMatrix4x4 mInverse = mesh.mTransform;
+		mInverse.Inverse();
+
+		aiVector3D* pvCurPtr = pcMesh->mVertices;
+		const aiVector3D* const pvEndPtr = pcMesh->mVertices + pcMesh->mNumVertices;
+		while (pvCurPtr != pvEndPtr)
+		{
+			*pvCurPtr = mInverse * (*pvCurPtr);
+			pvCurPtr++;
+		}
 
-		delete[] szMyName;
-		pcScene->mMeshes[i]->mColors[1] = NULL;
+		//pcMesh->mColors[2] = NULL;
 
 		// add sub nodes
-		this->AddNodes(pcScene,node,node->mName.data);
+		aiMatrix4x4 mNewAbs = decompTrafo.mMatrix * node->mTransformation;
+		ASE::DecompTransform dec( mNewAbs);
+		this->AddNodes(node,node->mName.data,dec);
 	}
 
 	// allocate enough space for the child nodes
@@ -259,7 +372,7 @@ void ASEImporter::AddNodes(aiScene* pcScene,aiNode* pcParent,
 	return;
 }
 // ------------------------------------------------------------------------------------------------
-void ASEImporter::BuildNodes(aiScene* pcScene)
+void ASEImporter::BuildNodes()
 {
 	ai_assert(NULL != pcScene);
 
@@ -270,15 +383,18 @@ void ASEImporter::BuildNodes(aiScene* pcScene)
 	pcScene->mRootNode->mName.Set("<root>");
 
 	// add all nodes
-	this->AddNodes(pcScene,pcScene->mRootNode,NULL);
+	this->AddNodes(pcScene->mRootNode,NULL);
 
 	// now iterate through al meshes and find those that have not yet
 	// been added to the nodegraph (= their parent could not be recognized)
 	std::vector<unsigned int> aiList;
 	for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
 	{
-		// get the name of the mesh ([0] = name, [1] = parent)
-		std::string* szMyName = (std::string*)pcScene->mMeshes[i]->mColors[1];
+		// get the name of the mesh
+		const ASE::Mesh& mesh = *((const ASE::Mesh*)pcScene->mMeshes[i]->mColors[2]);
+		// TODO: experimental quick'n'dirty, clean this up ...
+		std::string szMyName[2] = {mesh.mName,mesh.mParent} ;
+
 		if (!szMyName)
 		{
 			continue;
@@ -289,8 +405,9 @@ void ASEImporter::BuildNodes(aiScene* pcScene)
 		for (unsigned int i2 = 0; i2 < pcScene->mNumMeshes;++i2)
 		{
 			if (i2 == i)continue;
-			// get the name of the mesh ([0] = name, [1] = parent)
-			std::string* szMyName2 = (std::string*)pcScene->mMeshes[i2]->mColors[1];
+			const ASE::Mesh& mesh2 = *((const ASE::Mesh*)pcScene->mMeshes[i2]->mColors[2]);
+			// TODO: experimental quick'n'dirty, clean this up ...
+			std::string szMyName2[2] = {mesh2.mName,mesh2.mParent} ;
 			if (!szMyName2)
 			{
 				continue;
@@ -328,7 +445,7 @@ void ASEImporter::BuildNodes(aiScene* pcScene)
 			aiNode* pcNode = new aiNode();
 			pcNode->mParent = pcScene->mRootNode;
 			pcNode->mName.Set(szMyName[1]);
-			this->AddNodes(pcScene,pcNode,szMyName[1].c_str());
+			this->AddNodes(pcNode,szMyName[1].c_str());
 			apcNodes.push_back(pcNode);
 		}
 		pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()];
@@ -338,6 +455,9 @@ void ASEImporter::BuildNodes(aiScene* pcScene)
 		pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size();
 	}
 
+	for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
+		pcScene->mMeshes[i]->mColors[2] = NULL;
+
 	// if there is only one subnode, set it as root node
 	if (1 == pcScene->mRootNode->mNumChildren)
 	{
@@ -456,15 +576,15 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh)
 
 	// now need to transform all vertices with the inverse of their
 	// transformation matrix ...
-	aiMatrix4x4 mInverse = mesh.mTransform;
-	mInverse.Inverse();
+	//aiMatrix4x4 mInverse = mesh.mTransform;
+	//mInverse.Inverse();
 
-	for (std::vector<aiVector3D>::iterator
-		i =  mesh.mPositions.begin();
-		i != mesh.mPositions.end();++i)
-	{
-		(*i) = mInverse * (*i);
-	}
+	//for (std::vector<aiVector3D>::iterator
+	//	i =  mesh.mPositions.begin();
+	//	i != mesh.mPositions.end();++i)
+	//{
+	//	(*i) = mInverse * (*i);
+	//}
 
 	return;
 }
@@ -669,15 +789,8 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
 				// store the real index here ... color channel 3
 				p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
 
-				// store the real transformation matrix in color channel 2
-				p_pcOut->mColors[2] = (aiColor4D*) new aiMatrix4x4(mesh.mTransform);
-
-				// store the name of the mesh and the
-				// name of its parent in color channel 1
-				p_pcOut->mColors[1] = (aiColor4D*) new std::string[2];
-				((std::string*)p_pcOut->mColors[1])[0] = mesh.mName;
-				((std::string*)p_pcOut->mColors[1])[1] = mesh.mParent;
-
+				// store a pointer to the mesh in color channel 2
+				p_pcOut->mColors[2] = (aiColor4D*) &mesh;
 				avOutMeshes.push_back(p_pcOut);
 
 				// convert vertices
@@ -821,15 +934,17 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
 		// store the real index here ... in color channel 3
 		p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
 
-		// store the transformation matrix in color channel 2
-		p_pcOut->mColors[2] = (aiColor4D*) new aiMatrix4x4(mesh.mTransform);
+		// store a pointer to the mesh in color channel 2
+		p_pcOut->mColors[2] = (aiColor4D*) &mesh;
 		avOutMeshes.push_back(p_pcOut);
 
-		// store the name of the mesh and the
-		// name of its parent in color channel 1
-		p_pcOut->mColors[1] = (aiColor4D*) new std::string[2];
-		((std::string*)p_pcOut->mColors[1])[0] = mesh.mName;
-		((std::string*)p_pcOut->mColors[1])[1] = mesh.mParent;
+		// if the mesh hasn't faces or vertices, there are two cases
+		// possible: 1. the model is invalid. 2. This is a dummy
+		// helper object which we are going to remove later ...
+		if (mesh.mFaces.empty() || mesh.mPositions.empty())
+		{
+			return;
+		}
 
 		// convert vertices
 		p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size();
@@ -951,7 +1066,7 @@ void ComputeBounds(ASE::Mesh& mesh,aiVector3D& minVec, aiVector3D& maxVec,
 	return;
 }
 // ------------------------------------------------------------------------------------------------
-void ASEImporter::BuildMaterialIndices(aiScene* pcScene)
+void ASEImporter::BuildMaterialIndices()
 {
 	ai_assert(NULL != pcScene);
 
@@ -1033,7 +1148,7 @@ void ASEImporter::BuildMaterialIndices(aiScene* pcScene)
 		}
 	}
 	// prepare for the next step
-	for (unsigned int hans = 0; hans < pcScene->mNumMaterials;++hans)
+	for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans)
 	{
 		TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]);
 	}

+ 14 - 7
code/ASELoader.h

@@ -130,24 +130,28 @@ protected:
 
 	// -------------------------------------------------------------------
 	/** Setup the final material indices for each mesh
-	 *  \param pcScene Scene object to be filled
 	 */
-	void BuildMaterialIndices(aiScene* pcScene);
+	void BuildMaterialIndices();
 
 	// -------------------------------------------------------------------
 	/** Build the node graph
-	 *  \param pcScene Scene object to be filled
 	 */
-	void BuildNodes(aiScene* pcScene);
+	void BuildNodes();
+
+	// -------------------------------------------------------------------
+	/** Build output animations
+	 */
+	void BuildAnimations();
 
 	// -------------------------------------------------------------------
 	/** Add sub nodes to a node
-	 *  \param pcScene Scene object to be filled
 	 *  \param pcParent parent node to be filled
 	 *  \param szName Name of the parent node
+	 *  \param decompTrafo Decomposed absolute parent transformation mat
 	 */
-	void AddNodes(aiScene* pcScene,aiNode* pcParent,
-		const char* szName);
+	void AddNodes(aiNode* pcParent,const char* szName);
+	void AddNodes(aiNode* pcParent,const char* szName,
+		const ASE::DecompTransform& decompTrafo);
 
 	// -------------------------------------------------------------------
 	/** Generate a default material and add it to the parser's list
@@ -166,6 +170,9 @@ protected:
 
 	/** true if this is an .ask file */
 	bool mIsAsk;
+
+	/** Scene to be filled */
+	aiScene* pcScene;
 };
 
 } // end of namespace Assimp

+ 246 - 293
code/ASEParser.cpp

@@ -67,6 +67,44 @@ using namespace Assimp::ASE;
 #define BLUBB(_message_) \
 	{this->LogError(_message_);return;}
 
+
+#define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \
+	else if ('{' == *this->m_szFile)iDepth++; \
+	else if ('}' == *this->m_szFile) \
+	{ \
+		if (0 == --iDepth) \
+		{ \
+			++this->m_szFile; \
+			this->SkipToNextToken(); \
+			return; \
+		} \
+	} \
+	else if ('\0' == *this->m_szFile) \
+	{ \
+		return; \
+	} \
+	else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; \
+	++this->m_szFile; 
+
+#define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
+	else 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) \
+	{ \
+		this->LogError("Encountered unexpected EOL while parsing a " msg \
+		" chunk (Level " level ")"); \
+	} \
+	else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; \
+	++this->m_szFile; 
+
 // ------------------------------------------------------------------------------------------------
 Parser::Parser (const char* szFile)
 {
@@ -78,6 +116,10 @@ Parser::Parser (const char* szFile)
 	this->m_clrAmbient.r = std::numeric_limits<float>::quiet_NaN();
 
 	this->iLineNumber = 0;
+	this->iFirstFrame = 0;
+	this->iLastFrame = 0;
+	this->iFrameSpeed = 30;    // use 30 as default value for this property
+	this->iTicksPerFrame = 1;  // use 1 as default value for this property
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::LogWarning(const char* szWarn)
@@ -234,6 +276,15 @@ void Parser::Parse()
 				this->ParseLV1GeometryObjectBlock(this->m_vMeshes.back());
 				continue;
 			}
+			// helper object = dummy in the hierarchy
+			if (0 == strncmp(this->m_szFile,"*HELPEROBJECT",13) &&
+				IsSpaceOrNewLine(*(this->m_szFile+13)))
+			{
+				this->m_szFile+=14;
+				this->m_vMeshes.push_back(Mesh());
+				this->ParseLV1GeometryObjectBlock(this->m_vMeshes.back());
+				continue;
+			}
 			// ignore comments, lights and cameras
 			// (display comments on the console)
 			if (0 == strncmp(this->m_szFile,"*LIGHTOBJECT",12) &&
@@ -260,18 +311,7 @@ void Parser::Parse()
 				continue;
 			}
 		}
-		else 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 ... why not?
-			return;
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth);
 	}
 	return;
 }
@@ -301,19 +341,36 @@ void Parser::ParseLV1SceneBlock()
 				this->ParseLV4MeshFloatTriple( &this->m_clrAmbient.r );
 				continue;
 			}
+			if (0 == strncmp(this->m_szFile,"*SCENE_FIRSTFRAME",17) &&
+				IsSpaceOrNewLine(*(this->m_szFile+17)))
+			{
+				this->m_szFile+=18;
+				this->ParseLV4MeshLong(this->iFirstFrame);
+				continue;
+			}
+			if (0 == strncmp(this->m_szFile,"*SCENE_LASTFRAME",16) &&
+				IsSpaceOrNewLine(*(this->m_szFile+16)))
+			{
+				this->m_szFile+=17;
+				this->ParseLV4MeshLong(this->iLastFrame);
+				continue;
+			}
+			if (0 == strncmp(this->m_szFile,"*SCENE_FRAMESPEED",17) &&
+				IsSpaceOrNewLine(*(this->m_szFile+17)))
+			{
+				this->m_szFile+=18;
+				this->ParseLV4MeshLong(this->iFrameSpeed);
+				continue;
+			}
+			if (0 == strncmp(this->m_szFile,"*SCENE_TICKSPERFRAME",20) &&
+				IsSpaceOrNewLine(*(this->m_szFile+20)))
+			{
+				this->m_szFile+=21;
+				this->ParseLV4MeshLong(this->iTicksPerFrame);
+				continue;
+			}
 		}
-		else 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 ... why not?
-			return;
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth);
 	}
 	return;
 }
@@ -361,18 +418,7 @@ void Parser::ParseLV1MaterialListBlock()
 				continue;
 			}
 		}
-		else 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 ... why not?
-			return;
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth);
 	}
 	return;
 }
@@ -594,18 +640,7 @@ void Parser::ParseLV2MaterialBlock(ASE::Material& mat)
 				continue;
 			}
 		}
-		else 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 level2 block, this can't be
-			BLUBB("Unable to finish parsing a lv2 material block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"2","*MATERIAL");
 	}
 	return;
 }
@@ -669,18 +704,7 @@ void Parser::ParseLV3MapBlock(Texture& map)
 				this->ParseLV4MeshFloat(map.mTextureBlend);continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing a lv3 map block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MAP_XXXXXX");
 	}
 	return;
 }
@@ -788,21 +812,97 @@ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh)
 				this->m_szFile+=14;
 				this->ParseLV4MeshLong(mesh.iMaterialIndex);continue;
 			}
+			// animation data of the node
+			if (0 == strncmp(this->m_szFile,"*TM_ANIMATION" ,13) &&
+				IsSpaceOrNewLine(*(this->m_szFile+13)))
+			{
+				this->m_szFile+=14;
+				this->ParseLV2AnimationBlock(mesh);continue;
+			}
+		}
+		AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth);
+	}
+	return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2AnimationBlock(ASE::Mesh& mesh)
+{
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
+		{
+			// position keyframes
+			if (0 == strncmp(this->m_szFile,"*CONTROL_POS_TRACK" ,18) &&
+				IsSpaceOrNewLine(*(this->m_szFile+18)))
+			{
+				this->m_szFile+=19;
+				this->ParseLV3PosAnimationBlock(mesh);continue;
+			}
+			// rotation keyframes
+			if (0 == strncmp(this->m_szFile,"*CONTROL_ROT_TRACK" ,18) &&
+				IsSpaceOrNewLine(*(this->m_szFile+18)))
+			{
+				this->m_szFile+=19;
+				this->ParseLV3RotAnimationBlock(mesh);continue;
+			}
 		}
-		else if ('{' == *this->m_szFile)iDepth++;
-		else if ('}' == *this->m_szFile)
+		AI_ASE_HANDLE_SECTION(iDepth,"2","TM_ANIMATION");
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3PosAnimationBlock(ASE::Mesh& mesh)
+{
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
 		{
-			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
+			// position keyframe
+			if (0 == strncmp(this->m_szFile,"*CONTROL_POS_SAMPLE" ,19) &&
+				IsSpaceOrNewLine(*(this->m_szFile+19)))
+			{
+				this->m_szFile+=20;
+				
+				unsigned int iIndex;
+				mesh.mAnim.akeyPositions.push_back(aiVectorKey());
+				aiVectorKey& key = mesh.mAnim.akeyPositions.back();
+
+				this->ParseLV4MeshFloatTriple(&key.mValue.x,iIndex);
+				key.mTime = (double)iIndex;
+			}
 		}
-		else if ('\0' == *this->m_szFile)
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*CONTROL_POS_TRACK");
+	}
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3RotAnimationBlock(ASE::Mesh& mesh)
+{
+	int iDepth = 0;
+	while (true)
+	{
+		if ('*' == *this->m_szFile)
 		{
-			// END OF FILE ... this is a level1 block, this can be
-			return;
+			// rotation keyframe
+			if (0 == strncmp(this->m_szFile,"*CONTROL_ROT_SAMPLE" ,19) &&
+				IsSpaceOrNewLine(*(this->m_szFile+19)))
+			{
+				this->m_szFile+=20;
+
+				unsigned int iIndex;
+				mesh.mAnim.akeyRotations.push_back(aiQuatKey());
+				aiQuatKey& key = mesh.mAnim.akeyRotations.back();
+
+				// first read the axis, then the angle in radians
+				aiVector3D v;float f;
+				this->ParseLV4MeshFloatTriple(&v.x,iIndex);
+				this->ParseLV4MeshFloat(f);
+				key.mTime = (double)iIndex;
+				key.mValue = aiQuaternion(v,f);
+			}
 		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*CONTROL_ROT_TRACK");
 	}
-	return;
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV2NodeTransformBlock(ASE::Mesh& mesh)
@@ -840,19 +940,44 @@ void Parser::ParseLV2NodeTransformBlock(ASE::Mesh& mesh)
 				this->m_szFile+=9;
 				this->ParseLV4MeshFloatTriple(mesh.mTransform[3]);continue;
 			}
+			// inherited position axes
+			if (0 == strncmp(this->m_szFile,"*INHERIT_POS" ,12) &&
+				IsSpaceOrNewLine(*(this->m_szFile+12)))
+			{
+				this->m_szFile+=13;
+				unsigned int aiVal[3];
+				this->ParseLV4MeshLongTriple(aiVal);
+				
+				for (unsigned int i = 0; i < 3;++i)
+					mesh.inherit.abInheritPosition[i] = aiVal[i] != 0;
+				continue;
+			}
+			// inherited rotation axes
+			if (0 == strncmp(this->m_szFile,"*INHERIT_ROT" ,12) &&
+				IsSpaceOrNewLine(*(this->m_szFile+12)))
+			{
+				this->m_szFile+=13;
+				unsigned int aiVal[3];
+				this->ParseLV4MeshLongTriple(aiVal);
+				
+				for (unsigned int i = 0; i < 3;++i)
+					mesh.inherit.abInheritRotation[i] = aiVal[i] != 0;
+				continue;
+			}
+			// inherited scaling axes
+			if (0 == strncmp(this->m_szFile,"*INHERIT_SCL" ,12) &&
+				IsSpaceOrNewLine(*(this->m_szFile+12)))
+			{
+				this->m_szFile+=13;
+				unsigned int aiVal[3];
+				this->ParseLV4MeshLongTriple(aiVal);
+				
+				for (unsigned int i = 0; i < 3;++i)
+					mesh.inherit.abInheritScaling[i] = aiVal[i] != 0;
+				continue;
+			}
 		}
-		else 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 level2 block, this can't be
-			BLUBB("Unable to finish parsing a lv2 node transform block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"2","*NODE_TM");
 	}
 	return;
 }
@@ -1015,18 +1140,7 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh)
 				this->ParseLV3MeshWeightsBlock(mesh);continue;
 			}
 		}
-		else 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 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;
+		AI_ASE_HANDLE_SECTION(iDepth,"2","*MESH");
 	}
 	return;
 }
@@ -1072,18 +1186,7 @@ void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh)
 				continue;
 			}
 		}
-		else 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 level2 block, this can't be
-			BLUBB("Unable to finish parsing a lv2 *MESH_WEIGHTS block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_WEIGHTS");
 	}
 	return;
 }
@@ -1118,18 +1221,7 @@ void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh)
 				}
 			}
 		}
-		else 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;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_BONE_LIST");
 	}
 }
 // ------------------------------------------------------------------------------------------------
@@ -1183,18 +1275,7 @@ void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh
 				continue;
 			}
 		}
-		else 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;
+		AI_ASE_HANDLE_SECTION(iDepth,"4","*MESH_BONE_VERTEX");
 	}
 	return;
 }
@@ -1227,18 +1308,7 @@ void Parser::ParseLV3MeshVertexListBlock(
 				continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing a lv3 vertex list block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_VERTEX_LIST");
 	}
 	return;
 }
@@ -1269,18 +1339,7 @@ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
 				continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing LV3 *MESH_FACE_LIST block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_FACE_LIST");
 	}
 	return;
 }
@@ -1319,18 +1378,7 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
 				continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing LV3 *MESH_VERTEX_LIST block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_TVERT_LIST");
 	}
 	return;
 }
@@ -1367,18 +1415,7 @@ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
 				continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing LV3 *MESH_TFACELIST block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_TFACE_LIST");
 	}
 	return;
 }
@@ -1426,18 +1463,7 @@ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh)
 				continue;
 			}
 		}
-		else 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 level2 block, this can't be
-			BLUBB("Unable to finish parsing a LV3 *MESH_MAPPINGCHANNEL block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_MAPPING_CHANNEL");
 	}
 	return;
 }
@@ -1470,18 +1496,7 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh)
 				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 level3 block, this can't be
-			BLUBB("Unable to finish parsing LV3 *MESH_CVERTLIST block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_CVERTEX_LIST");
 	}
 	return;
 }
@@ -1517,18 +1532,7 @@ void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
 				continue;
 			}
 		}
-		else 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 level3 block, this can't be
-			BLUBB("Unable to finish parsing LV3 *MESH_CFACELIST block. Unexpected EOF")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		++this->m_szFile;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_CFACE_LIST");
 	}
 	return;
 }
@@ -1545,7 +1549,8 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
 	{
 		if ('*' == *this->m_szFile)
 		{
-			if (0 == strncmp(this->m_szFile,"*MESH_VERTEXNORMAL",18) && IsSpaceOrNewLine(*(this->m_szFile+18)))
+			if (0 == strncmp(this->m_szFile,"*MESH_VERTEXNORMAL",18) && 
+				IsSpaceOrNewLine(*(this->m_szFile+18)))
 			{
 				this->m_szFile += 19;
 
@@ -1565,18 +1570,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
 				continue;
 			}
 		}
-		else if ('{' == *this->m_szFile)iDepth++;
-		else if ('}' == *this->m_szFile)
-		{
-			if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;}
-		}
-		// seems we have reached the end of the file ... 
-		else if ('\0' == *this->m_szFile)
-		{
-			BLUBB("Unable to parse *MESH_NORMALS Element: Unexpected EOL [#1]")
-		}
-		else if(IsLineEnd(*this->m_szFile))++this->iLineNumber;
-		this->m_szFile++;
+		AI_ASE_HANDLE_SECTION(iDepth,"3","*MESH_NORMALS");
 	}
 	return;
 }
@@ -1729,40 +1723,19 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
 {
 	ai_assert(NULL != apOut);
 
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]");
-		++this->iLineNumber;
-		apOut[0] = apOut[1] = apOut[2] = 0;
-		return;
-	}
-	apOut[0] = strtol10(this->m_szFile,&this->m_szFile);
-
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#2]");
-		++this->iLineNumber;
-		apOut[1] = apOut[2] = 0;
-		return;
-	}
-	apOut[1] = strtol10(this->m_szFile,&this->m_szFile);
-
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
+	for (unsigned int i = 0; i < 3;++i)
 	{
-		// LOG 
-		this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#3]");
-		apOut[2] = 0;
-		++this->iLineNumber;
-		return;
+		// skip spaces and tabs
+		if(!SkipSpaces(this->m_szFile,&this->m_szFile))
+		{
+			// LOG 
+			this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]");
+			++this->iLineNumber;
+			apOut[0] = apOut[1] = apOut[2] = 0;
+			return;
+		}
+		apOut[i] = strtol10(this->m_szFile,&this->m_szFile);
 	}
-	apOut[2] = strtol10(this->m_szFile,&this->m_szFile);
-	// go to the next valid sequence
-	//SkipSpacesAndLineEnd(this->m_szFile,&this->m_szFile);
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
@@ -1773,7 +1746,7 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut
 	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
 	{
 		// LOG 
-		this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#4]");
+		this->LogWarning("Unable to parse indexed long triple: unexpected EOL [#4]");
 		rIndexOut = 0;
 		apOut[0] = apOut[1] = apOut[2] = 0;
 		++this->iLineNumber;
@@ -1795,7 +1768,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
 	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
 	{
 		// LOG 
-		this->LogWarning("Unable to parse indexable float triple: unexpected EOL [#1]");
+		this->LogWarning("Unable to parse indexed float triple: unexpected EOL [#1]");
 		rIndexOut = 0;
 		apOut[0] = apOut[1] = apOut[2] = 0.0f;
 		++this->iLineNumber;
@@ -1812,41 +1785,21 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
 void Parser::ParseLV4MeshFloatTriple(float* apOut)
 {
 	ai_assert(NULL != apOut);
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse float triple: unexpected EOL [#5]");
-		apOut[0] = apOut[1] = apOut[2] = 0.0f;
-		++this->iLineNumber;
-		return;
-	}
-	// parse the first float
-	this->m_szFile = fast_atof_move(this->m_szFile,apOut[0]);
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
-	{
-		// LOG 
-		this->LogWarning("Unable to parse float triple: unexpected EOL [#6]");
-		apOut[1] = apOut[2] = 0.0f;
-		++this->iLineNumber;
-		return;
-	}
-	// parse the second float
-	this->m_szFile = fast_atof_move(this->m_szFile,apOut[1]);
-	// skip spaces and tabs
-	if(!SkipSpaces(this->m_szFile,&this->m_szFile))
+
+	for (unsigned int i = 0; i < 3;++i)
 	{
-		// LOG 
-		this->LogWarning("Unable to parse float triple: unexpected EOL [#7]");
-		apOut[2] = 0.0f;
-		++this->iLineNumber;
-		return;
+		// skip spaces and tabs
+		if(!SkipSpaces(this->m_szFile,&this->m_szFile))
+		{
+			// LOG 
+			this->LogWarning("Unable to parse float triple: unexpected EOL [#5]");
+			apOut[0] = apOut[1] = apOut[2] = 0.0f;
+			++this->iLineNumber;
+			return;
+		}
+		// parse the float
+		this->m_szFile = fast_atof_move(this->m_szFile,apOut[i]);
 	}
-	// parse the third float
-	this->m_szFile = fast_atof_move(this->m_szFile,apOut[2]);
-	// go to the next valid sequence
-	//this->SkipToNextToken();
 	return;
 }
 // ------------------------------------------------------------------------------------------------

+ 104 - 7
code/ASEParser.h

@@ -43,17 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_ASEFILEHELPER_H_INC
 #define AI_ASEFILEHELPER_H_INC
 
+// STL/CRT headers
 #include <string>
 #include <vector>
 #include <list>
-#include <sstream>
 
+// public ASSIMP headers
 #include "../include/aiTypes.h"
 #include "../include/aiMesh.h"
 #include "../include/aiAnim.h"
 
 // for some helper routines like IsSpace()
 #include "PlyParser.h"
+#include "qnan.h"
 
 // ASE is quite similar to 3ds. We can reuse some structures
 #include "3DSLoader.h"
@@ -130,9 +132,10 @@ struct Bone
 	Bone()
 	{
 		static int iCnt = 0;
-		std::stringstream ss(mName);
-		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
-		ss.flush();
+		
+		char szTemp[128];
+		::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+		mName = szTemp;
 	}
 
 	//! Name of the bone
@@ -152,6 +155,73 @@ struct BoneVertex
 	//aiVector3D mPosition;
 };
 
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file animation */
+struct Animation
+{
+	//! List of rotation keyframes
+	std::vector< aiQuatKey > akeyRotations;
+
+	//! List of position keyframes
+	std::vector< aiVectorKey > akeyPositions;
+
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent the inheritance information of an ASE node */
+struct InheritanceInfo
+{
+	//! Default constructor
+	InheritanceInfo()
+	{
+		// set the inheritance flag for all axes by default to true
+		for (unsigned int i = 0; i < 3;++i)
+			abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true;
+	}
+
+	//! Inherit the parent's position?, axis order is x,y,z
+	bool abInheritPosition[3];
+
+	//! Inherit the parent's rotation?, axis order is x,y,z
+	bool abInheritRotation[3];
+
+	//! Inherit the parent's scaling?, axis order is x,y,z
+	bool abInheritScaling[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Stores a decomposed transformation matrix */
+struct DecompTransform
+{
+	//! Construction from a reference to an existing matrix
+	DecompTransform(aiMatrix4x4& ref) 
+		: vScaling(1.0f,1.0f,1.0f)
+		, vPosition(std::numeric_limits<float>::quiet_NaN(),0.0f,0.0f)
+		, mMatrix(ref)
+	{}
+
+	//! Translational component
+	mutable aiVector3D vPosition;
+
+	//! Rotational component
+	mutable aiQuaternion qRotation;
+
+	//! Scaling component
+	mutable aiVector3D vScaling;
+
+	//! Reference to the matrix being decomposed
+	const aiMatrix4x4& mMatrix;
+
+	//! Decomposes the matrix if this has not yet been done
+	inline void NeedDecomposedMatrixNOW() const
+	{
+		if (is_qnan(vPosition.x))
+		{
+			mMatrix.Decompose(vScaling,qRotation,vPosition);
+		}
+	}
+};
+
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file mesh */
 struct Mesh
@@ -160,9 +230,9 @@ struct Mesh
 	Mesh() : bSkip(false)
 	{
 		static int iCnt = 0;
-		std::stringstream ss(mName);
-		ss << "%%_UNNAMED_" << iCnt++ << "_%%"; 
-		ss.flush();
+		char szTemp[128];
+		::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+		mName = szTemp;
 
 		// use 2 texture vertex components by default
 		for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
@@ -203,6 +273,9 @@ struct Mesh
 	//! Transformation matrix of the mesh
 	aiMatrix4x4 mTransform;
 
+	//! Animation channels for the node
+	Animation mAnim;
+
 	//! Material index of the mesh
 	unsigned int iMaterialIndex;
 
@@ -211,6 +284,10 @@ struct Mesh
 
 	//! used internally
 	bool bSkip;
+
+	//! Specifies which axes transformations a node inherits
+	//! from its parent ...
+	InheritanceInfo inherit;
 };
 
 // ---------------------------------------------------------------------------------
@@ -259,6 +336,13 @@ private:
 	//! \param mesh Mesh object to be filled
 	void ParseLV2NodeTransformBlock(Mesh& mesh);
 
+	// -------------------------------------------------------------------
+	//! Parse a *TM_ANIMATION block in a file
+	//! \param mesh Mesh object to be filled
+	void ParseLV2AnimationBlock(Mesh& mesh);
+	void ParseLV3PosAnimationBlock(Mesh& mesh);
+	void ParseLV3RotAnimationBlock(Mesh& mesh);
+
 	// -------------------------------------------------------------------
 	//! Parse a *MESH block in a file
 	//! \param mesh Mesh object to be filled
@@ -453,6 +537,19 @@ public:
 
 	//! Current line in the file
 	unsigned int iLineNumber;
+
+
+	//! First frame
+	unsigned int iFirstFrame;
+
+	//! Last frame
+	unsigned int iLastFrame;
+
+	//! Frame speed - frames per second
+	unsigned int iFrameSpeed;
+
+	//! Ticks per frame
+	unsigned int iTicksPerFrame;
 };
 
 

+ 1 - 2
code/CalcTangentsProcess.cpp

@@ -110,8 +110,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh)
 	// 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)
 	{
-		DefaultLogger::get()->error("Normal vectors and at least one "
-			"texture coordinate set are required to calculate tangents. ");
+		DefaultLogger::get()->error("Unable to compute tangents: UV0 and normals must be there ");
 		return false;
 	}
 

+ 10 - 4
code/ValidateDataStructure.cpp

@@ -42,20 +42,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file Implementation of the post processing step to validate 
  * the data structure returned by Assimp
  */
+
+// STL headers
 #include <vector>
 #include <assert.h>
 
+// internal headers
 #include "ValidateDataStructure.h"
 #include "BaseImporter.h"
 #include "StringComparison.h"
 #include "fast_atof.h"
 
+// public ASSIMP headers
 #include "../include/DefaultLogger.h"
 #include "../include/aiPostProcess.h"
 #include "../include/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiAssert.h"
 
+// CRT headers
 #include <stdarg.h>
 
 using namespace Assimp;
@@ -128,7 +133,7 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
 		throw new ImportErrorException("Idiot ... learn coding!");
 	}
 	va_end(args);
-	DefaultLogger::get()->warn("Validation failed: " + std::string(szBuffer,iLen));
+	DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
 }
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
@@ -286,7 +291,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
 			{
 				if (face.mIndices[a] >= pMesh->mNumVertices)
 				{
-					this->ReportError("aiMesh::mFaces[%i]::mIndices[%a] is out of range",i,a);
+					this->ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
 				}
 				if (abRefList[face.mIndices[a]])
 				{
@@ -636,6 +641,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 {
 	this->Validate(&pBoneAnim->mBoneName);
 
+#if 0
 	// check whether there is a bone with this name ...
 	unsigned int i = 0;
 	for (; i < this->mScene->mNumMeshes;++i)
@@ -650,14 +656,14 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 __break_out:
 	if (i == this->mScene->mNumMeshes)
 	{
-		this->ReportWarning("aiBoneAnim::mBoneName is %s. However, no bone with this name was found",
+		this->ReportWarning("aiBoneAnim::mBoneName is \"%s\". However, no bone with this name was found",
 			pBoneAnim->mBoneName.data);
 	}
 	if (!pBoneAnim->mNumPositionKeys && !pBoneAnim->mNumRotationKeys && !pBoneAnim->mNumScalingKeys)
 	{
 		this->ReportWarning("A bone animation channel has no keys");
 	}
-
+#endif
 	// otherwise check whether one of the keys exceeds the total duration of the animation
 	if (pBoneAnim->mNumPositionKeys)
 	{

+ 4 - 1
include/aiAnim.h

@@ -66,9 +66,12 @@ struct aiQuatKey
 	C_STRUCT aiQuaternion mValue; ///< The value of this key
 };
 
-/** Describes the animation of a single bone. The name specifies the bone which is affected by this
+/** Describes the animation of a single node. The name specifies the bone/node which is affected by this
  * animation channel. The keyframes are given in three separate series of values, one each for
  * position, rotation and scaling.
+ * <br>
+ * NOTE: The name "BoneAnim" is misleading. This structure is also used to describe
+ * the animation of regular nodes on the node graph. They needn't be nodes.
  */
 struct aiBoneAnim
 {

+ 33 - 0
include/aiMatrix4x4.h

@@ -52,6 +52,8 @@ struct aiMatrix4x4
 	aiMatrix4x4& Inverse();
 	float Determinant() const;
 
+	inline bool IsIdentity() const;
+
 	float* operator[](unsigned int p_iIndex);
 	const float* operator[](unsigned int p_iIndex) const;
 
@@ -87,6 +89,37 @@ struct aiMatrix4x4
 	inline void FromEulerAngles(float x, float y, float z);
 
 
+	/** \brief Returns a rotation matrix for a rotation around the x axis
+	 *  \param a Rotation angle, in radians
+	 *  \param out Receives the output matrix
+	 *  \return Reference to the output matrix
+	 */
+	static aiMatrix4x4& RotationX(float a, aiMatrix4x4& out);
+
+
+	/** \brief Returns a rotation matrix for a rotation around the y axis
+	 *  \param a Rotation angle, in radians
+	 *  \param out Receives the output matrix
+	 *  \return Reference to the output matrix
+	 */
+	static aiMatrix4x4& RotationY(float a, aiMatrix4x4& out);
+
+
+	/** \brief Returns a rotation matrix for a rotation around the z axis
+	 *  \param a Rotation angle, in radians
+	 *  \param out Receives the output matrix
+	 *  \return Reference to the output matrix
+	 */
+	static aiMatrix4x4& RotationZ(float a, aiMatrix4x4& out);
+
+
+	/** \brief Returns a translation matrix 
+	 *  \param v Translation vector
+	 *  \param out Receives the output matrix
+	 *  \return Reference to the output matrix
+	 */
+	static aiMatrix4x4& Translation(aiVector3D v, aiMatrix4x4& out);
+
 #endif // __cplusplus
 
 	float a1, a2, a3, a4;

+ 59 - 0
include/aiMatrix4x4.inl

@@ -237,6 +237,65 @@ inline void aiMatrix4x4::FromEulerAngles(float x, float y, float z)
     _this.a4 = _this.b4 = _this.c4 = _this.d1 = _this.d2 = _this.d3 =  0.0f;
     _this.d4 = 1.0f;
 }
+// ---------------------------------------------------------------------------
+inline bool aiMatrix4x4::IsIdentity() const
+{
+	return !(a1 != 1.0f || a2 || a3 || a4 ||
+		b1 || b2 != 1.0f || b3 || b4 ||
+		c1 || c2 || c3 != 1.0f || a4 ||
+		d1 || d2 || d3 || d4 != 1.0f);
+}
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationX(float a, aiMatrix4x4& out)
+{
+	/*
+	     |  1  0       0       0 |
+     M = |  0  cos(A) -sin(A)  0 |
+         |  0  sin(A)  cos(A)  0 |
+         |  0  0       0       1 |	*/
+	out.aiMatrix4x4::aiMatrix4x4();
+	out.b2 = out.c3 = cos(a);
+	out.b3 = -(out.c2 = sin(a));
+	return out;
+}
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationY(float a, aiMatrix4x4& out)
+{
+	/*
+	     |  cos(A)  0   sin(A)  0 |
+     M = |  0       1   0       0 |
+         | -sin(A)  0   cos(A)  0 |
+         |  0       0   0       1 |
+		*/
+	out.aiMatrix4x4::aiMatrix4x4();
+	out.a1 = out.c3 = cos(a);
+	out.c1 = -(out.a3 = sin(a));
+	return out;
+}
+
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::RotationZ(float a, aiMatrix4x4& out)
+{
+	/*
+	     |  cos(A)  -sin(A)   0   0 |
+     M = |  sin(A)   cos(A)   0   0 |
+         |  0        0        1   0 |
+         |  0        0        0   1 |	*/
+	out.aiMatrix4x4::aiMatrix4x4();
+	out.a1 = out.b2 = cos(a);
+	out.a2 = -(out.b1 = sin(a));
+	return out;
+}
+// ---------------------------------------------------------------------------
+inline aiMatrix4x4& aiMatrix4x4::Translation(aiVector3D v, aiMatrix4x4& out)
+{
+	out.aiMatrix4x4::aiMatrix4x4();
+	out.d1 = v.x;
+	out.d2 = v.y;
+	out.d3 = v.z;
+	return out;
+}
+
 
 #endif // __cplusplus
 #endif // AI_MATRIX4x4_INL_INC

+ 59 - 0
include/aiQuaternion.h

@@ -1,3 +1,43 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
 /** @file Quaternion structure, including operators when compiling in C++ */
 #ifndef AI_QUATERNION_H_INC
 #define AI_QUATERNION_H_INC
@@ -23,8 +63,12 @@ struct aiQuaternion
 	/** Construct from euler angles */
 	aiQuaternion( float rotx, float roty, float rotz);
 
+	/** Construct from an axis angle pair */
+	aiQuaternion( aiVector3D axis, float angle);
+
 	/** Returns a matrix representation of the quaternion */
 	aiMatrix3x3 GetMatrix() const;
+
 #endif // __cplusplus
 
 	//! w,x,y,z components of the quaternion
@@ -113,6 +157,21 @@ inline aiMatrix3x3 aiQuaternion::GetMatrix() const
 	return resMatrix;
 }
 
+// ---------------------------------------------------------------------------
+// Construction from an axis-angle pair
+inline aiQuaternion::aiQuaternion( aiVector3D axis, float angle)
+{
+	axis.Normalize();
+
+	const float sin_a = sin( angle / 2 );
+    const float cos_a = cos( angle / 2 );
+    x    = axis.x * sin_a;
+    y    = axis.y * sin_a;
+    z    = axis.z * sin_a;
+    w    = cos_a;
+}
+
+
 
 
 } // end extern "C"

+ 1 - 0
include/aiScene.h

@@ -119,6 +119,7 @@ struct aiNode
 //! and propably meshes with bones.
 #define AI_SCENE_FLAGS_ANIM_SKELETON_ONLY	0x1
 
+
 // ---------------------------------------------------------------------------
 /** The root structure of the imported data. 
 *