From 6ad0892396640b6d143bbf2645f968e5e7221985 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Fri, 18 Jul 2008 20:29:46 +0000 Subject: [PATCH] Small update to the ASE loader. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@64 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/ASELoader.cpp | 247 +++++++++++---- code/ASELoader.h | 21 +- code/ASEParser.cpp | 545 +++++++++++++++------------------ code/ASEParser.h | 111 ++++++- code/CalcTangentsProcess.cpp | 3 +- code/ValidateDataStructure.cpp | 14 +- include/aiAnim.h | 5 +- include/aiMatrix4x4.h | 33 ++ include/aiMatrix4x4.inl | 59 ++++ include/aiQuaternion.h | 59 ++++ include/aiScene.h | 1 + 11 files changed, 715 insertions(+), 383 deletions(-) diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index af0da6acc..7d0db7f3b 100644 --- a/code/ASELoader.cpp +++ b/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 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::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::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; + 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::AddNodes(aiScene* pcScene,aiNode* pcParent, - const char* szName) +void ASEImporter::BuildAnimations() +{ + // check whether we have at least one mesh which has animations + std::vector::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)); + + 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(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 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; - - // delete the matrix (a mesh is always the child of ONE node, so this is safe) - delete pmTransform; - pcScene->mMeshes[i]->mColors[2] = NULL; - delete[] szMyName; - pcScene->mMeshes[i]->mColors[1] = NULL; + aiMatrix4x4 mParentAdjust = decompTrafo.mMatrix; + mParentAdjust.Inverse(); + //if(ComputeLocalToWorldShift(mParentAdjust, decompTrafo, mesh.inherit)) + { + node->mTransformation = mParentAdjust*mesh.mTransform; + } + //else node->mTransformation = mesh.mTransform; + + // 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++; + } + + //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(""); // 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 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::iterator - i = mesh.mPositions.begin(); - i != mesh.mPositions.end();++i) - { - (*i) = mInverse * (*i); - } + //for (std::vector::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& 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& 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]); } diff --git a/code/ASELoader.h b/code/ASELoader.h index 7c5bbf8d0..c2d849664 100644 --- a/code/ASELoader.h +++ b/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 diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index 2d0825569..dfefacf66 100644 --- a/code/ASEParser.cpp +++ b/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::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,23 +812,99 @@ 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; + } } - 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 level1 block, this can be - return; - } - else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; - ++this->m_szFile; + 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; + } + } + AI_ASE_HANDLE_SECTION(iDepth,"2","TM_ANIMATION"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3PosAnimationBlock(ASE::Mesh& mesh) +{ + int iDepth = 0; + while (true) + { + if ('*' == *this->m_szFile) + { + // 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; + } + } + AI_ASE_HANDLE_SECTION(iDepth,"3","*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3RotAnimationBlock(ASE::Mesh& mesh) +{ + int iDepth = 0; + while (true) + { + if ('*' == *this->m_szFile) + { + // 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); + } + } + AI_ASE_HANDLE_SECTION(iDepth,"3","*CONTROL_ROT_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ void Parser::ParseLV2NodeTransformBlock(ASE::Mesh& mesh) { int iDepth = 0; @@ -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)) + for (unsigned int i = 0; i < 3;++i) { - // LOG - this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]"); - ++this->iLineNumber; - apOut[0] = apOut[1] = apOut[2] = 0; - 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[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)) - { - // LOG - this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#3]"); - apOut[2] = 0; - ++this->iLineNumber; - return; - } - 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)) + + for (unsigned int i = 0; i < 3;++i) { - // LOG - this->LogWarning("Unable to parse float triple: unexpected EOL [#5]"); - apOut[0] = apOut[1] = 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 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)) - { - // LOG - this->LogWarning("Unable to parse float triple: unexpected EOL [#7]"); - apOut[2] = 0.0f; - ++this->iLineNumber; - return; - } - // parse the third float - this->m_szFile = fast_atof_move(this->m_szFile,apOut[2]); - // go to the next valid sequence - //this->SkipToNextToken(); return; } // ------------------------------------------------------------------------------------------------ diff --git a/code/ASEParser.h b/code/ASEParser.h index a26313708..8ca471821 100644 --- a/code/ASEParser.h +++ b/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 #include #include -#include +// 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::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; }; diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp index 01794cedd..890187707 100644 --- a/code/CalcTangentsProcess.cpp +++ b/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; } diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index 807db8f88..ca0ed4201 100644 --- a/code/ValidateDataStructure.cpp +++ b/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 #include +// 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 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) { diff --git a/include/aiAnim.h b/include/aiAnim.h index 3605a7730..fc8c3b47c 100644 --- a/include/aiAnim.h +++ b/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. + *
+ * 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 { diff --git a/include/aiMatrix4x4.h b/include/aiMatrix4x4.h index 3502741c4..4f1d032be 100644 --- a/include/aiMatrix4x4.h +++ b/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; diff --git a/include/aiMatrix4x4.inl b/include/aiMatrix4x4.inl index bafb0dda7..252bdf4b6 100644 --- a/include/aiMatrix4x4.inl +++ b/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 diff --git a/include/aiQuaternion.h b/include/aiQuaternion.h index ea221525b..963886d82 100644 --- a/include/aiQuaternion.h +++ b/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" diff --git a/include/aiScene.h b/include/aiScene.h index e140d749b..469f4330b 100644 --- a/include/aiScene.h +++ b/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. *