From 74dfe61c1e9c9ba5367f658def4e071e3467feea Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Wed, 28 May 2008 21:05:05 +0000 Subject: [PATCH] First half-working version of the ASE loader git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@46 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/ASELoader.cpp | 180 +++++++++++++++++++- code/ASELoader.h | 10 ++ code/ASEParser.cpp | 410 ++++++++++++++++++++++++++++++--------------- code/ASEParser.h | 68 ++++++++ 4 files changed, 531 insertions(+), 137 deletions(-) diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 430bb0ded..8694fd860 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -49,13 +49,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/aiMesh.h" #include "../include/aiScene.h" #include "../include/aiAssert.h" +#include "../include/DefaultLogger.h" #include using namespace Assimp; using namespace Assimp::ASE; -#define LOGOUT_WARN(x) +#define LOGOUT_WARN(x) DefaultLogger::get()->warn(x); // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer @@ -85,6 +86,8 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const if (extension[2] != 's' && extension[2] != 'S')return false; // NOTE: Sometimes the extension .ASK is also used + // however, often it only contains static animation skeletons + // without the real animations. if (extension[3] != 'e' && extension[3] != 'E' && extension[3] != 'k' && extension[3] != 'K')return false; @@ -105,6 +108,14 @@ void ASEImporter::InternReadFile( size_t fileSize = file->FileSize(); + std::string::size_type pos = pFile.find_last_of('.'); + std::string extension = pFile.substr( pos); + if(extension[3] == 'k' || extension[3] == 'K') + { + this->mIsAsk = true; + } + else this->mIsAsk = false; + // allocate storage and copy the contents of the file to a memory buffer // (terminate it with zero) this->mBuffer = new unsigned char[fileSize+1]; @@ -120,12 +131,19 @@ void ASEImporter::InternReadFile( i = this->mParser->m_vMeshes.begin(); i != this->mParser->m_vMeshes.end();++i) { - // need to generate proper vertex normals if necessary - this->GenerateNormals(*i); + // transform all vertices into worldspace + // world2obj transform is specified in the + // transformation matrix of a scenegraph node + this->TransformVertices(*i); // now we need to create proper meshes from the import // we need to split them by materials, build valid vertex/face lists ... this->BuildUniqueRepresentation(*i); + + // need to generate proper vertex normals if necessary + this->GenerateNormals(*i); + + // convert all meshes to aiMesh objects this->ConvertMeshes(*i,pScene); } // buil final material indices (remove submaterials and make the final list) @@ -206,6 +224,23 @@ void ASEImporter::BuildNodes(aiScene* pcScene) return; } // ------------------------------------------------------------------------------------------------ +void ASEImporter::TransformVertices(ASE::Mesh& mesh) +{ + // the matrix data is stored in column-major format, + // but we need row major + mesh.mTransform.Transpose(); + + aiMatrix4x4 m = mesh.mTransform; + m.Inverse(); + + for (std::vector::iterator + i = mesh.mPositions.begin(); + i != mesh.mPositions.end();++i) + { + (*i) = m * (*i); + } +} +// ------------------------------------------------------------------------------------------------ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { // allocate output storage @@ -213,6 +248,7 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; std::vector mVertexColors; std::vector mNormals; + std::vector mBoneVertices; unsigned int iSize = mesh.mFaces.size() * 3; mPositions.resize(iSize); @@ -236,6 +272,11 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { mNormals.resize(iSize); } + // bone vertices. There is no need to change the bone list + if (!mesh.mBoneVertices.empty()) + { + mBoneVertices.resize(iSize); + } // iterate through all faces in the mesh unsigned int iCurrent = 0; @@ -265,6 +306,16 @@ void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { mNormals[iCurrent] = mesh.mNormals[(*i).mIndices[n]]; } + + // handle bone vertices + if ((*i).mIndices[n] < mesh.mBoneVertices.size()) + { + // (sometimes this will cause bone verts to be duplicated + // however, I' quite sure Schrompf' JoinVerticesStep + // will fix that again ...) + mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; + } + // assign a new valid index to the face (*i).mIndices[n] = iCurrent; } @@ -312,9 +363,21 @@ void ASEImporter::ConvertMaterial(ASE::Material& mat) mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + // shininess + if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) + { + mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); + } + // if there is no shininess, we can disable phong lighting + else if (Dot3DS::Dot3DSFile::Metal == mat.mShading || + Dot3DS::Dot3DSFile::Phong == mat.mShading) + { + mat.mShading = Dot3DS::Dot3DSFile::Gouraud; + } + // opacity mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); @@ -489,6 +552,13 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene) p_pcOut->mNumVertices = aiSplit[p].size()*3; p_pcOut->mNumFaces = aiSplit[p].size(); + // receive output vertex weights + std::vector>* avOutputBones; + if (!mesh.mBones.empty()) + { + avOutputBones = new std::vector>[mesh.mBones.size()]; + } + // allocate enough storage for faces p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; @@ -507,8 +577,29 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene) for (unsigned int t = 0; t < 3;++t) { - p_pcOut->mVertices[iBase] = mesh.mPositions[mesh.mFaces[iIndex].mIndices[t]]; - p_pcOut->mNormals[iBase++] = mesh.mNormals[mesh.mFaces[iIndex].mIndices[t]]; + const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; + + p_pcOut->mVertices[iBase] = mesh.mPositions[iIndex2]; + p_pcOut->mNormals[iBase] = mesh.mNormals[iIndex2]; + + // convert bones, if existing + if (!mesh.mBones.empty()) + { + // check whether there is a vertex weight that is using + // this vertex index ... + if (iIndex2 < mesh.mBoneVertices.size()) + { + for (std::vector>::const_iterator + blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); + blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) + { + // NOTE: illegal cases have already been filtered out + avOutputBones[(*blubb).first].push_back(std::pair( + iBase,(*blubb).second)); + } + } + } + ++iBase; } p_pcOut->mFaces[q].mIndices[0] = iBase-2; p_pcOut->mFaces[q].mIndices[1] = iBase-1; @@ -549,6 +640,38 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene) } } } + if (!mesh.mBones.empty()) + { + p_pcOut->mNumBones = 0; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; + aiBone** pcBone = &p_pcOut->mBones[0]; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + { + if (!avOutputBones[mrspock].empty()) + { + // we will need this bone. add it to the output mesh and + // add all per-vertex weights + *pcBone = new aiBone(); + (**pcBone).mName.Set(mesh.mBones[mrspock].mName); + + (**pcBone).mNumWeights = avOutputBones[mrspock].size(); + (**pcBone).mWeights = new aiVertexWeight[(**pcBone).mNumWeights]; + + for (unsigned int captainkirk = 0; captainkirk < (**pcBone).mNumWeights;++captainkirk) + { + const std::pair& ref = avOutputBones[mrspock][captainkirk]; + (**pcBone).mWeights[captainkirk].mVertexId = ref.first; + (**pcBone).mWeights[captainkirk].mWeight = ref.second; + } + ++pcBone; + } + } + // delete allocated storage + delete[] avOutputBones; + } } } // delete storage @@ -619,6 +742,51 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, aiScene* pcScene) p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; } + + // copy vertex bones + if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) + { + std::vector* avBonesOut = new + std::vector[mesh.mBones.size()]; + + // find all vertex weights for this bone + unsigned int quak = 0; + for (std::vector::const_iterator + harrypotter = mesh.mBoneVertices.begin(); + harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) + { + for (std::vector>::const_iterator + ronaldweasley = (*harrypotter).mBoneWeights.begin(); + ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) + { + aiVertexWeight weight; + weight.mVertexId = quak; + weight.mWeight = (*ronaldweasley).second; + avBonesOut[(*ronaldweasley).first].push_back(weight); + } + } + + // now build a final bone list + p_pcOut->mNumBones = 0; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) + if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; + aiBone** pcBone = &p_pcOut->mBones[0]; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) + { + if (!avBonesOut[jfkennedy].empty()) + { + *pcBone = new aiBone(); + (**pcBone).mName.Set(mesh.mBones[jfkennedy].mName); + (**pcBone).mNumWeights = avBonesOut[jfkennedy].size(); + (**pcBone).mWeights = new aiVertexWeight[(**pcBone).mNumWeights]; + memcpy((**pcBone).mWeights,&avBonesOut[jfkennedy][0], + sizeof(aiVertexWeight) * (**pcBone).mNumWeights); + ++pcBone; + } + } + } } // now build the output mesh list diff --git a/code/ASELoader.h b/code/ASELoader.h index 6ba01cf5a..0016db39b 100644 --- a/code/ASELoader.h +++ b/code/ASELoader.h @@ -108,6 +108,13 @@ protected: */ void BuildUniqueRepresentation(ASE::Mesh& mesh); + // ------------------------------------------------------------------- + /** Transform all vertices with the inverse transformation + * matrix of the mesh + * \param mesh Mesh to work on + */ + void TransformVertices(ASE::Mesh& mesh); + // ------------------------------------------------------------------- /** Create one-material-per-mesh meshes ;-) * \param mesh Mesh to work with @@ -140,6 +147,9 @@ protected: /** Buffer to hold the loaded file */ unsigned char* mBuffer; + + /** true if this is an .ask file */ + bool mIsAsk; }; } // end of namespace Assimp diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index cf7dcdc55..8ed32335c 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -204,12 +204,7 @@ void Parser::Parse() if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } if ('\0' == *this->m_szFile) { @@ -249,12 +244,7 @@ void Parser::ParseLV1SceneBlock() if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -310,12 +300,7 @@ void Parser::ParseLV1MaterialListBlock() if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -437,6 +422,13 @@ void Parser::ParseLV2MaterialBlock(ASE::Material& mat) this->ParseLV4MeshFloat(mat.mSpecularExponent); mat.mSpecularExponent *= 15; } + // material shininess strength + if (0 == strncmp(this->m_szFile,"*MATERIAL_SHINESTRENGTH",23) && + IsSpaceOrNewLine(*(this->m_szFile+23))) + { + this->m_szFile+=24; + this->ParseLV4MeshFloat(mat.mShininessStrength); + } // diffuse color map if (0 == strncmp(this->m_szFile,"*MAP_DIFFUSE",12) && IsSpaceOrNewLine(*(this->m_szFile+12))) @@ -546,12 +538,7 @@ void Parser::ParseLV2MaterialBlock(ASE::Material& mat) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -649,12 +636,7 @@ void Parser::ParseLV3MapBlock(Texture& map) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -667,6 +649,47 @@ void Parser::ParseLV3MapBlock(Texture& map) return; } // ------------------------------------------------------------------------------------------------ +bool Parser::ParseString(std::string& out,const char* szName) +{ + char szBuffer[1024]; + ai_assert(strlen(szName < 750)); + + // NOTE: The name could also be the texture in some cases + // be prepared that this might occur ... + if (!SkipSpaces(this->m_szFile,&this->m_szFile)) + { + sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName); + this->LogWarning(szBuffer); + return false; + } + // there must be " + if ('\"' != *this->m_szFile) + { + sprintf(szBuffer,"Unable to parse %s block: String is expected " + "to be enclosed in double quotation marks",szName); + this->LogWarning(szBuffer); + return false; + } + ++this->m_szFile; + const char* sz = this->m_szFile; + while (true) + { + if ('\"' == *sz)break; + else if ('\0' == sz) + { + sprintf(szBuffer,"Unable to parse %s block: String is expected to be " + "enclosed in double quotation marks but EOF was reached before a closing " + "quotation mark was found",szName); + this->LogWarning(szBuffer); + return false; + } + sz++; + } + out = std::string(this->m_szFile,(uintptr_t)sz-(uintptr_t)this->m_szFile); + this->m_szFile = sz; + return true; +} +// ------------------------------------------------------------------------------------------------ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh) { int iDepth = 0; @@ -679,31 +702,22 @@ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh) IsSpaceOrNewLine(*(this->m_szFile+10))) { this->m_szFile+=11; - - // NOTE: The name could also be the texture in some cases - // be prepared that this might occur ... - if (!SkipSpaces(this->m_szFile,&this->m_szFile)) - BLUBB("Unable to parse *NODE_NAME block: Unexpected EOL") - - // there must be " - if ('\"' != *this->m_szFile) - BLUBB("Unable to parse *NODE_NAME block: Name is expected to be enclosed in double quotation marks") - - ++this->m_szFile; - const char* sz = this->m_szFile; - while (true) + if(!this->ParseString(mesh.mName,"*NODE_NAME")) { - if ('\"' == *sz)break; - else if ('\0' == sz) - { - BLUBB("Unable to parse *NODE_NAME block: Name is expected to be enclosed in double quotation marks \ - but EOF was reached before a closing quotation mark was found") - } - sz++; + this->SkipToNextToken(); + continue; + } + } + // name of the parent of the node + if (0 == strncmp(this->m_szFile,"*NODE_PARENT" ,12) && + IsSpaceOrNewLine(*(this->m_szFile+12))) + { + this->m_szFile+=13; + if(!this->ParseString(mesh.mParent,"*NODE_PARENT")) + { + this->SkipToNextToken(); + continue; } - - mesh.mName = std::string(this->m_szFile,(uintptr_t)sz-(uintptr_t)this->m_szFile); - this->m_szFile = sz; } // transformation matrix of the node if (0 == strncmp(this->m_szFile,"*NODE_TM" ,8) && @@ -719,16 +733,18 @@ void Parser::ParseLV1GeometryObjectBlock(ASE::Mesh& mesh) this->m_szFile+=6; this->ParseLV2MeshBlock(mesh); } + // mesh material index + else if (0 == strncmp(this->m_szFile,"*MATERIAL_REF" ,13) && + IsSpaceOrNewLine(*(this->m_szFile+13))) + { + this->m_szFile+=14; + this->ParseLV4MeshLong(mesh.iMaterialIndex); + } } if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -780,12 +796,7 @@ void Parser::ParseLV2NodeTransformBlock(ASE::Mesh& mesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -932,23 +943,28 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) this->ParseLV3MappingChannel(iIndex-1,mesh); } } - // mesh material index - else if (0 == strncmp(this->m_szFile,"*MATERIAL_REF" ,13) && + // mesh animation keyframe. Not supported + else if (0 == strncmp(this->m_szFile,"*MESH_ANIMATION" ,15) && + IsSpaceOrNewLine(*(this->m_szFile+15))) + { + this->m_szFile+=16; + + this->LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " + "Keyframe animation is not supported by Assimp, this element " + "will be ignored"); + } + // mesh animation keyframe. Not supported + else if (0 == strncmp(this->m_szFile,"*MESH_WEIGHTS" ,13) && IsSpaceOrNewLine(*(this->m_szFile+13))) { this->m_szFile+=14; - this->ParseLV4MeshLong(mesh.iMaterialIndex); + this->ParseLV3MeshWeightsBlock(mesh); } } if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -961,6 +977,174 @@ void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) return; } // ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh) +{ + unsigned int iNumVertices = 0; + unsigned int iNumBones = 0; + int iDepth = 0; + while (true) + { + if ('*' == *this->m_szFile) + { + // Number of bone vertices ... + if (0 == strncmp(this->m_szFile,"*MESH_NUMVERTEX" ,15) && + IsSpaceOrNewLine(*(this->m_szFile+15))) + { + this->m_szFile+=16; + this->ParseLV4MeshLong(iNumVertices); + } + // Number of bones + if (0 == strncmp(this->m_szFile,"*MESH_NUMBONE" ,13) && + IsSpaceOrNewLine(*(this->m_szFile+13))) + { + this->m_szFile+=14; + this->ParseLV4MeshLong(iNumBones); + } + // parse the list of bones + if (0 == strncmp(this->m_szFile,"*MESH_BONE_LIST" ,15) && + IsSpaceOrNewLine(*(this->m_szFile+15))) + { + this->m_szFile+=16; + this->SkipOpeningBracket(); + this->ParseLV4MeshBones(iNumBones,mesh); + } + // parse the list of bones vertices + if (0 == strncmp(this->m_szFile,"*MESH_BONE_VERTEX_LIST" ,22) && + IsSpaceOrNewLine(*(this->m_szFile+22))) + { + this->m_szFile+=23; + this->SkipOpeningBracket(); + this->ParseLV4MeshBonesVertices(iNumVertices,mesh); + } + } + if ('{' == *this->m_szFile)iDepth++; + if ('}' == *this->m_szFile) + { + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} + } + else if ('\0' == *this->m_szFile) + { + // END OF FILE ... this is a level2 block, this can't be + BLUBB("Unable to finish parsing a lv2 *MESH_WEIGHTS block. Unexpected EOF") + } + else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; + ++this->m_szFile; + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh) +{ + mesh.mBones.resize(iNumBones); + int iDepth = 0; + while (true) + { + if ('*' == *this->m_szFile) + { + // Mesh bone with name ... + if (0 == strncmp(this->m_szFile,"*MESH_BONE_NAME" ,17) && + IsSpaceOrNewLine(*(this->m_szFile+17))) + { + this->m_szFile+=18; + + // parse an index ... + if(SkipSpaces(this->m_szFile,&this->m_szFile)) + { + unsigned int iIndex = strtol10(this->m_szFile,&this->m_szFile); + if (iIndex >= iNumBones) + { + iIndex = iNumBones-1; + this->LogWarning("Bone index is out of bounds. Using the largest valid " + "bone index instead"); + } + if (!this->ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME")) + { + this->SkipToNextToken(); + continue; + } + } + } + } + if ('{' == *this->m_szFile)iDepth++; + else if ('}' == *this->m_szFile) + { + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} + } + else if ('\0' == *this->m_szFile) + { + // END OF FILE ... this is a level4 block, this can't be + BLUBB("Unable to finish parsing a lv4 *MESH_BONE_LIST block. Unexpected EOF") + } + else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; + ++this->m_szFile; + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh) +{ + mesh.mBoneVertices.resize(iNumVertices); + int iDepth = 0; + while (true) + { + if ('*' == *this->m_szFile) + { + // Mesh bone vertex + if (0 == strncmp(this->m_szFile,"*MESH_BONE_VERTEX" ,17) && + IsSpaceOrNewLine(*(this->m_szFile+17))) + { + this->m_szFile+=18; + + // read the vertex index + unsigned int iIndex = strtol10(this->m_szFile,&this->m_szFile); + if (iIndex >= mesh.mPositions.size()) + { + iIndex = mesh.mPositions.size()-1; + this->LogWarning("Bone vertex index is out of bounds. Using the largest valid " + "bone vertex index instead"); + } + + // now there there are 3 normal floats, the + // should be identical to the vertex positions + // contained in the *VERTEX_LIST block. Well, we check this + // in debug builds to be sure ;-) + float afVert[3]; + this->ParseLV4MeshFloatTriple(afVert); + + std::pair pairOut; + while (true) + { + // first parse the bone index ... + if (!SkipSpaces(this->m_szFile,&this->m_szFile))break; + pairOut.first = strtol10(this->m_szFile,&this->m_szFile); + + // then parse the vertex weight + if (!SkipSpaces(this->m_szFile,&this->m_szFile))break; + this->m_szFile = fast_atof_move(this->m_szFile,pairOut.second); + + // -1 designates unused entries + if (-1 != pairOut.first) + { + mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); + } + } + } + } + if ('{' == *this->m_szFile)iDepth++; + else if ('}' == *this->m_szFile) + { + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} + } + else if ('\0' == *this->m_szFile) + { + // END OF FILE ... this is a level4 block, this can't be + BLUBB("Unable to finish parsing a lv4 *MESH_BONE_VERTEX_LIST block. Unexpected EOF") + } + else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; + ++this->m_szFile; + } + return; +} +// ------------------------------------------------------------------------------------------------ void Parser::ParseLV3MeshVertexListBlock( unsigned int iNumVertices, ASE::Mesh& mesh) { @@ -989,14 +1173,9 @@ void Parser::ParseLV3MeshVertexListBlock( } } if ('{' == *this->m_szFile)iDepth++; - if ('}' == *this->m_szFile) + else if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1037,12 +1216,7 @@ void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1091,12 +1265,7 @@ void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1143,12 +1312,7 @@ void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1205,12 +1369,7 @@ void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1253,12 +1412,7 @@ void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1304,12 +1458,7 @@ void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } else if ('\0' == *this->m_szFile) { @@ -1356,12 +1505,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh) if ('{' == *this->m_szFile)iDepth++; if ('}' == *this->m_szFile) { - if (0 == --iDepth) - { - ++this->m_szFile; - this->SkipToNextToken(); - return; - } + if (0 == --iDepth){++this->m_szFile;this->SkipToNextToken();return;} } // seems we have reached the end of the file ... else if ('\0' == *this->m_szFile) @@ -1432,7 +1576,7 @@ __EARTHQUAKE_XXL: if ('*' == *this->m_szFile)break; if (IsLineEnd(*this->m_szFile)) { - this->iLineNumber++; + //this->iLineNumber++; return; } this->m_szFile++; @@ -1446,9 +1590,13 @@ __EARTHQUAKE_XXL: BLUBB("Unable to parse *MESH_SMOOTHING Element: Unexpected EOL. Smoothing group(s) expected [#5]") // parse smoothing groups until we don_t anymore see commas + // FIX: There needn't always be a value, sad but true while (true) { - out.iSmoothGroup |= (1 << strtol10(this->m_szFile,&this->m_szFile)); + if (*this->m_szFile < '9' && *this->m_szFile >= '0') + { + out.iSmoothGroup |= (1 << strtol10(this->m_szFile,&this->m_szFile)); + } SkipSpaces(this->m_szFile,&this->m_szFile); if (',' != *this->m_szFile) { @@ -1465,7 +1613,7 @@ __EARTHQUAKE_XXL: if ('*' == *this->m_szFile)break; if (IsLineEnd(*this->m_szFile)) { - this->iLineNumber++; + //this->iLineNumber++; return; } this->m_szFile++; @@ -1478,7 +1626,7 @@ __EARTHQUAKE_XXL: BLUBB("Unable to parse *MESH_MTLID Element: Unexpected EOL. Material index expected [#6]") out.iMaterial = strtol10(this->m_szFile,&this->m_szFile); } - this->SkipToNextToken(); + //this->SkipToNextToken(); return; } // ------------------------------------------------------------------------------------------------ @@ -1519,7 +1667,7 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) } apOut[2] = strtol10(this->m_szFile,&this->m_szFile); // go to the next valid sequence - SkipSpacesAndLineEnd(this->m_szFile,&this->m_szFile); + //SkipSpacesAndLineEnd(this->m_szFile,&this->m_szFile); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) @@ -1603,7 +1751,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut) // parse the third float this->m_szFile = fast_atof_move(this->m_szFile,apOut[2]); // go to the next valid sequence - this->SkipToNextToken(); + //this->SkipToNextToken(); return; } // ------------------------------------------------------------------------------------------------ @@ -1621,7 +1769,7 @@ void Parser::ParseLV4MeshFloat(float& fOut) // parse the first float this->m_szFile = fast_atof_move(this->m_szFile,fOut); // go to the next valid sequence - this->SkipToNextToken(); + //this->SkipToNextToken(); return; } // ------------------------------------------------------------------------------------------------ @@ -1639,6 +1787,6 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut) // parse the value iOut = strtol10(this->m_szFile,&this->m_szFile); // go to the next valid sequence - this->SkipToNextToken(); + //this->SkipToNextToken(); return; } \ No newline at end of file diff --git a/code/ASEParser.h b/code/ASEParser.h index 278277003..83215dbdf 100644 --- a/code/ASEParser.h +++ b/code/ASEParser.h @@ -125,6 +125,36 @@ struct Face : public Dot3DS::Face unsigned int iFace; }; +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone */ +struct Bone +{ + //! Constructor + Bone() + { + static int iCnt = 0; + std::stringstream ss(mName); + ss << "%%_UNNAMED_" << iCnt++ << "_%%"; + ss.flush(); + } + + //! Name of the bone + std::string mName; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone vertex */ +struct BoneVertex +{ + //! Bone and corresponding vertex weight. + //! -1 for unrequired bones .... + std::vector > mBoneWeights; + + //! Position of the bone vertex. + //! MUST be identical to the vertex position + //aiVector3D mPosition; +}; + // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file mesh */ struct Mesh @@ -135,6 +165,7 @@ struct Mesh static int iCnt = 0; std::stringstream ss(mName); ss << "%%_UNNAMED_" << iCnt++ << "_%%"; + ss.flush(); // use 2 texture vertex components by default for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) @@ -143,8 +174,14 @@ struct Mesh // setup the default material index by default iMaterialIndex = Face::DEFAULT_MATINDEX; } + + //! Name of the mesh std::string mName; + //! Name of the parent of the mesh + //! "" if there is no parent ... + std::string mParent; + //! vertex positions std::vector mPositions; @@ -160,6 +197,12 @@ struct Mesh //! List of normal vectors std::vector mNormals; + //! List of all bone vertices + std::vector mBoneVertices; + + //! List of all bones + std::vector mBones; + //! Transformation matrix of the mesh aiMatrix4x4 mTransform; @@ -295,6 +338,23 @@ private: //! \param mesh Mesh object to be filled void ParseLV3MeshNormalListBlock(Mesh& mesh); + // ------------------------------------------------------------------- + //! Parse a *MESH_WEIGHTSblock in a file + //! \param mesh Mesh object to be filled + void ParseLV3MeshWeightsBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone list of a file + //! \param mesh Mesh object to be filled + //! \param iNumBones Number of bones in the mesh + void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone vertices list of a file + //! \param mesh Mesh object to be filled + //! \param iNumVertices Number of vertices to be parsed + void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh); + // ------------------------------------------------------------------- //! Parse a *MESH_FACE block in a file //! \param out receive the face data @@ -359,6 +419,14 @@ private: //! \param szWarn Error message void LogError(const char* szWarn); + // ------------------------------------------------------------------- + //! Parse a string, enclosed in double quotation marks + //! \param out Output string + //! \param szName Name of the enclosing element -> used in error + //! messages. + //! \return false if an error occured + bool Parser::ParseString(std::string& out,const char* szName); + public: //! Pointer to current data