diff options
Diffstat (limited to 'src/3rdparty/assimp/code/MD5Loader.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/MD5Loader.cpp | 1223 |
1 files changed, 615 insertions, 608 deletions
diff --git a/src/3rdparty/assimp/code/MD5Loader.cpp b/src/3rdparty/assimp/code/MD5Loader.cpp index 65911f728..7927c9ab1 100644 --- a/src/3rdparty/assimp/code/MD5Loader.cpp +++ b/src/3rdparty/assimp/code/MD5Loader.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2016, assimp 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 +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 @@ -25,25 +25,25 @@ conditions are met: derived from this software without specific prior written permission of the assimp team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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 MD5Loader.cpp - * @brief Implementation of the MD5 importer class + * @brief Implementation of the MD5 importer class */ -#include "AssimpPCH.h" + #ifndef ASSIMP_BUILD_NO_MD5_IMPORTER // internal headers @@ -52,6 +52,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "StringComparison.h" #include "fast_atof.h" #include "SkeletonMeshBuilder.h" +#include <assimp/Importer.hpp> +#include <assimp/scene.h> +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <memory> using namespace Assimp; @@ -60,495 +65,500 @@ using namespace Assimp; static const aiImporterDesc desc = { - "Doom 3 / MD5 Mesh Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 0, - 0, - "md5mesh md5camera md5anim" + "Doom 3 / MD5 Mesh Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "md5mesh md5camera md5anim" }; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer MD5Importer::MD5Importer() -: mBuffer() -, configNoAutoLoad (false) + : mIOHandler() + , mBuffer() + , fileSize() + , iLineNumber() + , pScene() + , pIOHandler() + , bHadMD5Mesh() + , bHadMD5Anim() + , bHadMD5Camera() + , configNoAutoLoad (false) {} // ------------------------------------------------------------------------------------------------ -// Destructor, private as well +// Destructor, private as well MD5Importer::~MD5Importer() {} // ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. +// Returns whether the class can handle the format of the given file. bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera") - return true; - else if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - const char* tokens[] = {"MD5Version"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; + const std::string extension = GetExtension(pFile); + + if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera") + return true; + else if (!extension.length() || checkSig) { + if (!pIOHandler) { + return true; + } + const char* tokens[] = {"MD5Version"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; } // ------------------------------------------------------------------------------------------------ // Get list of all supported extensions const aiImporterDesc* MD5Importer::GetInfo () const { - return &desc; + return &desc; } // ------------------------------------------------------------------------------------------------ // Setup import properties void MD5Importer::SetupProperties(const Importer* pImp) { - // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD - configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0)); + // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD + configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0)); } // ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void MD5Importer::InternReadFile( const std::string& pFile, - aiScene* _pScene, IOSystem* _pIOHandler) +// Imports the given file into the given scene structure. +void MD5Importer::InternReadFile( const std::string& pFile, + aiScene* _pScene, IOSystem* _pIOHandler) { - pIOHandler = _pIOHandler; - pScene = _pScene; - bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false; - - // remove the file extension - const std::string::size_type pos = pFile.find_last_of('.'); - mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1)); - - const std::string extension = GetExtension(pFile); - try { - if (extension == "md5camera") { - LoadMD5CameraFile(); - } - else if (configNoAutoLoad || extension == "md5anim") { - // determine file extension and process just *one* file - if (extension.length() == 0) { - throw DeadlyImportError("Failure, need file extension to determine MD5 part type"); - } - if (extension == "md5anim") { - LoadMD5AnimFile(); - } - else if (extension == "md5mesh") { - LoadMD5MeshFile(); - } - } - else { - LoadMD5MeshFile(); - LoadMD5AnimFile(); - } - } - catch ( ... ) { // std::exception, Assimp::DeadlyImportError - UnloadFileFromMemory(); - throw; - } - - // make sure we have at least one file - if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) { - throw DeadlyImportError("Failed to read valid contents out of this MD5* file"); - } - - // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system - pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, - 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); - - // the output scene wouldn't pass the validation without this flag - if (!bHadMD5Mesh) { - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } - - // clean the instance -- the BaseImporter instance may be reused later. - UnloadFileFromMemory(); + pIOHandler = _pIOHandler; + pScene = _pScene; + bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false; + + // remove the file extension + const std::string::size_type pos = pFile.find_last_of('.'); + mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1)); + + const std::string extension = GetExtension(pFile); + try { + if (extension == "md5camera") { + LoadMD5CameraFile(); + } + else if (configNoAutoLoad || extension == "md5anim") { + // determine file extension and process just *one* file + if (extension.length() == 0) { + throw DeadlyImportError("Failure, need file extension to determine MD5 part type"); + } + if (extension == "md5anim") { + LoadMD5AnimFile(); + } + else if (extension == "md5mesh") { + LoadMD5MeshFile(); + } + } + else { + LoadMD5MeshFile(); + LoadMD5AnimFile(); + } + } + catch ( ... ) { // std::exception, Assimp::DeadlyImportError + UnloadFileFromMemory(); + throw; + } + + // make sure we have at least one file + if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) { + throw DeadlyImportError("Failed to read valid contents out of this MD5* file"); + } + + // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system + pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, + 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); + + // the output scene wouldn't pass the validation without this flag + if (!bHadMD5Mesh) { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + + // clean the instance -- the BaseImporter instance may be reused later. + UnloadFileFromMemory(); } // ------------------------------------------------------------------------------------------------ // Load a file into a memory buffer void MD5Importer::LoadFileIntoMemory (IOStream* file) { - // unload the previous buffer, if any - UnloadFileFromMemory(); + // unload the previous buffer, if any + UnloadFileFromMemory(); - ai_assert(NULL != file); - fileSize = (unsigned int)file->FileSize(); - ai_assert(fileSize); + ai_assert(NULL != file); + fileSize = (unsigned int)file->FileSize(); + ai_assert(fileSize); - // allocate storage and copy the contents of the file to a memory buffer - mBuffer = new char[fileSize+1]; - file->Read( (void*)mBuffer, 1, fileSize); - iLineNumber = 1; + // allocate storage and copy the contents of the file to a memory buffer + mBuffer = new char[fileSize+1]; + file->Read( (void*)mBuffer, 1, fileSize); + iLineNumber = 1; - // append a terminal 0 - mBuffer[fileSize] = '\0'; + // append a terminal 0 + mBuffer[fileSize] = '\0'; - // now remove all line comments from the file - CommentRemover::RemoveLineComments("//",mBuffer,' '); + // now remove all line comments from the file + CommentRemover::RemoveLineComments("//",mBuffer,' '); } // ------------------------------------------------------------------------------------------------ // Unload the current memory buffer void MD5Importer::UnloadFileFromMemory () { - // delete the file buffer - delete[] mBuffer; - mBuffer = NULL; - fileSize = 0; + // delete the file buffer + delete[] mBuffer; + mBuffer = NULL; + fileSize = 0; } // ------------------------------------------------------------------------------------------------ // Build unique vertices void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc) { - std::vector<bool> abHad(meshSrc.mVertices.size(),false); - - // allocate enough storage to keep the output structures - const unsigned int iNewNum = meshSrc.mFaces.size()*3; - unsigned int iNewIndex = meshSrc.mVertices.size(); - meshSrc.mVertices.resize(iNewNum); - - // try to guess how much storage we'll need for new weights - const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex; - const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); - meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer - - for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){ - const aiFace& face = *iter; - for (unsigned int i = 0; i < 3;++i) { - if (face.mIndices[0] >= meshSrc.mVertices.size()) { - throw DeadlyImportError("MD5MESH: Invalid vertex index"); - } - - if (abHad[face.mIndices[i]]) { - // generate a new vertex - meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]]; - face.mIndices[i] = iNewIndex++; - } - else abHad[face.mIndices[i]] = true; - } - // swap face order - std::swap(face.mIndices[0],face.mIndices[2]); - } + std::vector<bool> abHad(meshSrc.mVertices.size(),false); + + // allocate enough storage to keep the output structures + const unsigned int iNewNum = meshSrc.mFaces.size()*3; + unsigned int iNewIndex = meshSrc.mVertices.size(); + meshSrc.mVertices.resize(iNewNum); + + // try to guess how much storage we'll need for new weights + const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex; + const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); + meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer + + for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){ + const aiFace& face = *iter; + for (unsigned int i = 0; i < 3;++i) { + if (face.mIndices[0] >= meshSrc.mVertices.size()) { + throw DeadlyImportError("MD5MESH: Invalid vertex index"); + } + + if (abHad[face.mIndices[i]]) { + // generate a new vertex + meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]]; + face.mIndices[i] = iNewIndex++; + } + else abHad[face.mIndices[i]] = true; + } + // swap face order + std::swap(face.mIndices[0],face.mIndices[2]); + } } // ------------------------------------------------------------------------------------------------ // Recursive node graph construction from a MD5MESH void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones) { - ai_assert(NULL != piParent && !piParent->mNumChildren); - - // First find out how many children we'll have - for (int i = 0; i < (int)bones.size();++i) { - if (iParentID != i && bones[i].mParentIndex == iParentID) { - ++piParent->mNumChildren; - } - } - if (piParent->mNumChildren) { - piParent->mChildren = new aiNode*[piParent->mNumChildren]; - for (int i = 0; i < (int)bones.size();++i) { - // (avoid infinite recursion) - if (iParentID != i && bones[i].mParentIndex == iParentID) { - aiNode* pc; - // setup a new node - *piParent->mChildren++ = pc = new aiNode(); - pc->mName = aiString(bones[i].mName); - pc->mParent = piParent; - - // get the transformation matrix from rotation and translational components - aiQuaternion quat; - MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat ); - - // FIX to get to Assimp's quaternion conventions - quat.w *= -1.f; - - bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix()); - bones[i].mTransform.a4 = bones[i].mPositionXYZ.x; - bones[i].mTransform.b4 = bones[i].mPositionXYZ.y; - bones[i].mTransform.c4 = bones[i].mPositionXYZ.z; - - // store it for later use - pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform; - bones[i].mInvTransform.Inverse(); - - // the transformations for each bone are absolute, so we need to multiply them - // with the inverse of the absolute matrix of the parent joint - if (-1 != iParentID) { - pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation; - } - - // add children to this node, too - AttachChilds_Mesh( i, pc, bones); - } - } - // undo offset computations - piParent->mChildren -= piParent->mNumChildren; - } + ai_assert(NULL != piParent && !piParent->mNumChildren); + + // First find out how many children we'll have + for (int i = 0; i < (int)bones.size();++i) { + if (iParentID != i && bones[i].mParentIndex == iParentID) { + ++piParent->mNumChildren; + } + } + if (piParent->mNumChildren) { + piParent->mChildren = new aiNode*[piParent->mNumChildren]; + for (int i = 0; i < (int)bones.size();++i) { + // (avoid infinite recursion) + if (iParentID != i && bones[i].mParentIndex == iParentID) { + aiNode* pc; + // setup a new node + *piParent->mChildren++ = pc = new aiNode(); + pc->mName = aiString(bones[i].mName); + pc->mParent = piParent; + + // get the transformation matrix from rotation and translational components + aiQuaternion quat; + MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat ); + + bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix()); + bones[i].mTransform.a4 = bones[i].mPositionXYZ.x; + bones[i].mTransform.b4 = bones[i].mPositionXYZ.y; + bones[i].mTransform.c4 = bones[i].mPositionXYZ.z; + + // store it for later use + pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform; + bones[i].mInvTransform.Inverse(); + + // the transformations for each bone are absolute, so we need to multiply them + // with the inverse of the absolute matrix of the parent joint + if (-1 != iParentID) { + pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation; + } + + // add children to this node, too + AttachChilds_Mesh( i, pc, bones); + } + } + // undo offset computations + piParent->mChildren -= piParent->mNumChildren; + } } // ------------------------------------------------------------------------------------------------ // Recursive node graph construction from a MD5ANIM void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims) { - ai_assert(NULL != piParent && !piParent->mNumChildren); - - // First find out how many children we'll have - for (int i = 0; i < (int)bones.size();++i) { - if (iParentID != i && bones[i].mParentIndex == iParentID) { - ++piParent->mNumChildren; - } - } - if (piParent->mNumChildren) { - piParent->mChildren = new aiNode*[piParent->mNumChildren]; - for (int i = 0; i < (int)bones.size();++i) { - // (avoid infinite recursion) - if (iParentID != i && bones[i].mParentIndex == iParentID) - { - aiNode* pc; - // setup a new node - *piParent->mChildren++ = pc = new aiNode(); - pc->mName = aiString(bones[i].mName); - pc->mParent = piParent; - - // get the corresponding animation channel and its first frame - const aiNodeAnim** cur = node_anims; - while ((**cur).mNodeName != pc->mName)++cur; - - aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation); - pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ; - - // add children to this node, too - AttachChilds_Anim( i, pc, bones,node_anims); - } - } - // undo offset computations - piParent->mChildren -= piParent->mNumChildren; - } + ai_assert(NULL != piParent && !piParent->mNumChildren); + + // First find out how many children we'll have + for (int i = 0; i < (int)bones.size();++i) { + if (iParentID != i && bones[i].mParentIndex == iParentID) { + ++piParent->mNumChildren; + } + } + if (piParent->mNumChildren) { + piParent->mChildren = new aiNode*[piParent->mNumChildren]; + for (int i = 0; i < (int)bones.size();++i) { + // (avoid infinite recursion) + if (iParentID != i && bones[i].mParentIndex == iParentID) + { + aiNode* pc; + // setup a new node + *piParent->mChildren++ = pc = new aiNode(); + pc->mName = aiString(bones[i].mName); + pc->mParent = piParent; + + // get the corresponding animation channel and its first frame + const aiNodeAnim** cur = node_anims; + while ((**cur).mNodeName != pc->mName)++cur; + + aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation); + pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ; + + // add children to this node, too + AttachChilds_Anim( i, pc, bones,node_anims); + } + } + // undo offset computations + piParent->mChildren -= piParent->mNumChildren; + } } // ------------------------------------------------------------------------------------------------ // Load a MD5MESH file void MD5Importer::LoadMD5MeshFile () { - std::string pFile = mFile + "md5mesh"; - boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( file.get() == NULL || !file->FileSize()) { - DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile); - return; - } - bHadMD5Mesh = true; - LoadFileIntoMemory(file.get()); - - // now construct a parser and parse the file - MD5::MD5Parser parser(mBuffer,fileSize); - - // load the mesh information from it - MD5::MD5MeshParser meshParser(parser.mSections); - - // create the bone hierarchy - first the root node and dummy nodes for all meshes - pScene->mRootNode = new aiNode("<MD5_Root>"); - pScene->mRootNode->mNumChildren = 2; - pScene->mRootNode->mChildren = new aiNode*[2]; - - // build the hierarchy from the MD5MESH file - aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); - pcNode->mName.Set("<MD5_Hierarchy>"); - pcNode->mParent = pScene->mRootNode; - AttachChilds_Mesh(-1,pcNode,meshParser.mJoints); - - pcNode = pScene->mRootNode->mChildren[0] = new aiNode(); - pcNode->mName.Set("<MD5_Mesh>"); - pcNode->mParent = pScene->mRootNode; + std::string pFile = mFile + "md5mesh"; + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL || !file->FileSize()) { + DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile); + return; + } + bHadMD5Mesh = true; + LoadFileIntoMemory(file.get()); + + // now construct a parser and parse the file + MD5::MD5Parser parser(mBuffer,fileSize); + + // load the mesh information from it + MD5::MD5MeshParser meshParser(parser.mSections); + + // create the bone hierarchy - first the root node and dummy nodes for all meshes + pScene->mRootNode = new aiNode("<MD5_Root>"); + pScene->mRootNode->mNumChildren = 2; + pScene->mRootNode->mChildren = new aiNode*[2]; + + // build the hierarchy from the MD5MESH file + aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); + pcNode->mName.Set("<MD5_Hierarchy>"); + pcNode->mParent = pScene->mRootNode; + AttachChilds_Mesh(-1,pcNode,meshParser.mJoints); + + pcNode = pScene->mRootNode->mChildren[0] = new aiNode(); + pcNode->mName.Set("<MD5_Mesh>"); + pcNode->mParent = pScene->mRootNode; #if 0 - if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */ - SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]); + if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */ + SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]); #else - // FIX: MD5 files exported from Blender can have empty meshes - for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { - if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) - ++pScene->mNumMaterials; - } - - // generate all meshes - pScene->mNumMeshes = pScene->mNumMaterials; - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; - - // storage for node mesh indices - pcNode->mNumMeshes = pScene->mNumMeshes; - pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; - for (unsigned int m = 0; m < pcNode->mNumMeshes;++m) - pcNode->mMeshes[m] = m; - - unsigned int n = 0; - for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { - MD5::MeshDesc& meshSrc = *it; - if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) - continue; - - aiMesh* mesh = pScene->mMeshes[n] = new aiMesh(); - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // generate unique vertices in our internal verbose format - MakeDataUnique(meshSrc); - - mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size(); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; - mesh->mNumUVComponents[0] = 2; - - // copy texture coordinates - aiVector3D* pv = mesh->mTextureCoords[0]; - for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { - pv->x = (*iter).mUV.x; - pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL - pv->z = 0.0f; - } - - // sort all bone weights - per bone - unsigned int* piCount = new unsigned int[meshParser.mJoints.size()]; - ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size()); - - for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { - for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) - { - MD5::WeightDesc& desc = meshSrc.mWeights[w]; - /* FIX for some invalid exporters */ - if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON )) - ++piCount[desc.mBone]; - } - } - - // check how many we will need - for (unsigned int p = 0; p < meshParser.mJoints.size();++p) - if (piCount[p])mesh->mNumBones++; - - if (mesh->mNumBones) // just for safety - { - mesh->mBones = new aiBone*[mesh->mNumBones]; - for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q) - { - if (!piCount[q])continue; - aiBone* p = mesh->mBones[h] = new aiBone(); - p->mNumWeights = piCount[q]; - p->mWeights = new aiVertexWeight[p->mNumWeights]; - p->mName = aiString(meshParser.mJoints[q].mName); - p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform; - - // store the index for later use - MD5::BoneDesc& boneSrc = meshParser.mJoints[q]; - boneSrc.mMap = h++; - - // compute w-component of quaternion - MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted ); - } - - //unsigned int g = 0; - pv = mesh->mVertices; - for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { - // compute the final vertex position from all single weights - *pv = aiVector3D(); - - // there are models which have weights which don't sum to 1 ... - float fSum = 0.0f; - for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) - fSum += meshSrc.mWeights[w].mWeight; - if (!fSum) { - DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0"); - continue; - } - - // process bone weights - for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) { - if (w >= meshSrc.mWeights.size()) - throw DeadlyImportError("MD5MESH: Invalid weight index"); - - MD5::WeightDesc& desc = meshSrc.mWeights[w]; - if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { - continue; - } - - const float fNewWeight = desc.mWeight / fSum; - - // transform the local position into worldspace - MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone]; - const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition); - - // use the original weight to compute the vertex position - // (some MD5s seem to depend on the invalid weight values ...) - *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight); - - aiBone* bone = mesh->mBones[boneSrc.mMap]; - *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight); - } - } - - // undo our nice offset tricks ... - for (unsigned int p = 0; p < mesh->mNumBones;++p) { - mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights; - } - } - - delete[] piCount; - - // now setup all faces - we can directly copy the list - // (however, take care that the aiFace destructor doesn't delete the mIndices array) - mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size(); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - for (unsigned int c = 0; c < mesh->mNumFaces;++c) { - mesh->mFaces[c].mNumIndices = 3; - mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices; - meshSrc.mFaces[c].mIndices = NULL; - } - - // generate a material for the mesh - aiMaterial* mat = new aiMaterial(); - pScene->mMaterials[n] = mat; - - // insert the typical doom3 textures: - // nnn_local.tga - normal map - // nnn_h.tga - height map - // nnn_s.tga - specular map - // nnn_d.tga - diffuse map - if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) { - - aiString temp(meshSrc.mShader); - temp.Append("_local.tga"); - mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0)); - - temp = aiString(meshSrc.mShader); - temp.Append("_s.tga"); - mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0)); - - temp = aiString(meshSrc.mShader); - temp.Append("_d.tga"); - mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0)); - - temp = aiString(meshSrc.mShader); - temp.Append("_h.tga"); - mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0)); - - // set this also as material name - mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME); - } - else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0)); - mesh->mMaterialIndex = n++; - } + // FIX: MD5 files exported from Blender can have empty meshes + for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { + if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) + ++pScene->mNumMaterials; + } + + // generate all meshes + pScene->mNumMeshes = pScene->mNumMaterials; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; + + // storage for node mesh indices + pcNode->mNumMeshes = pScene->mNumMeshes; + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; + for (unsigned int m = 0; m < pcNode->mNumMeshes;++m) + pcNode->mMeshes[m] = m; + + unsigned int n = 0; + for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { + MD5::MeshDesc& meshSrc = *it; + if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) + continue; + + aiMesh* mesh = pScene->mMeshes[n] = new aiMesh(); + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // generate unique vertices in our internal verbose format + MakeDataUnique(meshSrc); + + mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size(); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + mesh->mNumUVComponents[0] = 2; + + // copy texture coordinates + aiVector3D* pv = mesh->mTextureCoords[0]; + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { + pv->x = (*iter).mUV.x; + pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL + pv->z = 0.0f; + } + + // sort all bone weights - per bone + unsigned int* piCount = new unsigned int[meshParser.mJoints.size()]; + ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size()); + + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) + { + MD5::WeightDesc& desc = meshSrc.mWeights[w]; + /* FIX for some invalid exporters */ + if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON )) + ++piCount[desc.mBone]; + } + } + + // check how many we will need + for (unsigned int p = 0; p < meshParser.mJoints.size();++p) + if (piCount[p])mesh->mNumBones++; + + if (mesh->mNumBones) // just for safety + { + mesh->mBones = new aiBone*[mesh->mNumBones]; + for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q) + { + if (!piCount[q])continue; + aiBone* p = mesh->mBones[h] = new aiBone(); + p->mNumWeights = piCount[q]; + p->mWeights = new aiVertexWeight[p->mNumWeights]; + p->mName = aiString(meshParser.mJoints[q].mName); + p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform; + + // store the index for later use + MD5::BoneDesc& boneSrc = meshParser.mJoints[q]; + boneSrc.mMap = h++; + + // compute w-component of quaternion + MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted ); + } + + //unsigned int g = 0; + pv = mesh->mVertices; + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { + // compute the final vertex position from all single weights + *pv = aiVector3D(); + + // there are models which have weights which don't sum to 1 ... + float fSum = 0.0f; + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) + fSum += meshSrc.mWeights[w].mWeight; + if (!fSum) { + DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0"); + continue; + } + + // process bone weights + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) { + if (w >= meshSrc.mWeights.size()) + throw DeadlyImportError("MD5MESH: Invalid weight index"); + + MD5::WeightDesc& desc = meshSrc.mWeights[w]; + if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + continue; + } + + const float fNewWeight = desc.mWeight / fSum; + + // transform the local position into worldspace + MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone]; + const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition); + + // use the original weight to compute the vertex position + // (some MD5s seem to depend on the invalid weight values ...) + *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight); + + aiBone* bone = mesh->mBones[boneSrc.mMap]; + *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight); + } + } + + // undo our nice offset tricks ... + for (unsigned int p = 0; p < mesh->mNumBones;++p) { + mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights; + } + } + + delete[] piCount; + + // now setup all faces - we can directly copy the list + // (however, take care that the aiFace destructor doesn't delete the mIndices array) + mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size(); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + for (unsigned int c = 0; c < mesh->mNumFaces;++c) { + mesh->mFaces[c].mNumIndices = 3; + mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices; + meshSrc.mFaces[c].mIndices = NULL; + } + + // generate a material for the mesh + aiMaterial* mat = new aiMaterial(); + pScene->mMaterials[n] = mat; + + // insert the typical doom3 textures: + // nnn_local.tga - normal map + // nnn_h.tga - height map + // nnn_s.tga - specular map + // nnn_d.tga - diffuse map + if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) { + + aiString temp(meshSrc.mShader); + temp.Append("_local.tga"); + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0)); + + temp = aiString(meshSrc.mShader); + temp.Append("_s.tga"); + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0)); + + temp = aiString(meshSrc.mShader); + temp.Append("_d.tga"); + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + temp = aiString(meshSrc.mShader); + temp.Append("_h.tga"); + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0)); + + // set this also as material name + mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME); + } + else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0)); + mesh->mMaterialIndex = n++; + } #endif } @@ -556,193 +566,190 @@ void MD5Importer::LoadMD5MeshFile () // Load an MD5ANIM file void MD5Importer::LoadMD5AnimFile () { - std::string pFile = mFile + "md5anim"; - boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( !file.get() || !file->FileSize()) { - DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile); - return; - } - LoadFileIntoMemory(file.get()); - - // parse the basic file structure - MD5::MD5Parser parser(mBuffer,fileSize); - - // load the animation information from the parse tree - MD5::MD5AnimParser animParser(parser.mSections); - - // generate and fill the output animation - if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() || - animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) { - - DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded"); - } - else { - bHadMD5Anim = true; - - pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1]; - aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation(); - anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size(); - anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; - for (unsigned int i = 0; i < anim->mNumChannels;++i) { - aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim(); - node->mNodeName = aiString( animParser.mAnimatedBones[i].mName ); - - // allocate storage for the keyframes - node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()]; - node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()]; - } - - // 1 tick == 1 frame - anim->mTicksPerSecond = animParser.fFrameRate; - - for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){ - double dTime = (double)(*iter).iIndex; - aiNodeAnim** pcAnimNode = anim->mChannels; - if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */ - { - // now process all values in there ... read all joints - MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0]; - for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2, - ++pcAnimNode,++pcBaseFrame) - { - if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) { - - // Allow for empty frames - if ((*iter2).iFlags != 0) { - throw DeadlyImportError("MD5: Keyframe index is out of range"); - - } - continue; - } - const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex]; - aiNodeAnim* pcCurAnimBone = *pcAnimNode; - - aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++]; - aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++]; - aiVector3D vTemp; - - // translational component - for (unsigned int i = 0; i < 3; ++i) { - if ((*iter2).iFlags & (1u << i)) { - vKey->mValue[i] = *fpCur++; - } - else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i]; - } - - // orientation component - for (unsigned int i = 0; i < 3; ++i) { - if ((*iter2).iFlags & (8u << i)) { - vTemp[i] = *fpCur++; - } - else vTemp[i] = pcBaseFrame->vRotationQuat[i]; - } - - MD5::ConvertQuaternion(vTemp, qKey->mValue); - qKey->mTime = vKey->mTime = dTime; - - // we need this to get to Assimp quaternion conventions - qKey->mValue.w *= -1.f; - } - } - - // compute the duration of the animation - anim->mDuration = std::max(dTime,anim->mDuration); - } - - // If we didn't build the hierarchy yet (== we didn't load a MD5MESH), - // construct it now from the data given in the MD5ANIM. - if (!pScene->mRootNode) { - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mName.Set("<MD5_Hierarchy>"); - - AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels); - - // Call SkeletonMeshBuilder to construct a mesh to represent the shape - if (pScene->mRootNode->mNumChildren) { - SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]); - } - } - } + std::string pFile = mFile + "md5anim"; + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( !file.get() || !file->FileSize()) { + DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile); + return; + } + LoadFileIntoMemory(file.get()); + + // parse the basic file structure + MD5::MD5Parser parser(mBuffer,fileSize); + + // load the animation information from the parse tree + MD5::MD5AnimParser animParser(parser.mSections); + + // generate and fill the output animation + if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() || + animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) { + + DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded"); + } + else { + bHadMD5Anim = true; + + pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1]; + aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation(); + anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size(); + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + for (unsigned int i = 0; i < anim->mNumChannels;++i) { + aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim(); + node->mNodeName = aiString( animParser.mAnimatedBones[i].mName ); + + // allocate storage for the keyframes + node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()]; + node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()]; + } + + // 1 tick == 1 frame + anim->mTicksPerSecond = animParser.fFrameRate; + + for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){ + double dTime = (double)(*iter).iIndex; + aiNodeAnim** pcAnimNode = anim->mChannels; + if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */ + { + // now process all values in there ... read all joints + MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0]; + for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2, + ++pcAnimNode,++pcBaseFrame) + { + if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) { + + // Allow for empty frames + if ((*iter2).iFlags != 0) { + throw DeadlyImportError("MD5: Keyframe index is out of range"); + + } + continue; + } + const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex]; + aiNodeAnim* pcCurAnimBone = *pcAnimNode; + + aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++]; + aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++]; + aiVector3D vTemp; + + // translational component + for (unsigned int i = 0; i < 3; ++i) { + if ((*iter2).iFlags & (1u << i)) { + vKey->mValue[i] = *fpCur++; + } + else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i]; + } + + // orientation component + for (unsigned int i = 0; i < 3; ++i) { + if ((*iter2).iFlags & (8u << i)) { + vTemp[i] = *fpCur++; + } + else vTemp[i] = pcBaseFrame->vRotationQuat[i]; + } + + MD5::ConvertQuaternion(vTemp, qKey->mValue); + qKey->mTime = vKey->mTime = dTime; + } + } + + // compute the duration of the animation + anim->mDuration = std::max(dTime,anim->mDuration); + } + + // If we didn't build the hierarchy yet (== we didn't load a MD5MESH), + // construct it now from the data given in the MD5ANIM. + if (!pScene->mRootNode) { + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mName.Set("<MD5_Hierarchy>"); + + AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels); + + // Call SkeletonMeshBuilder to construct a mesh to represent the shape + if (pScene->mRootNode->mNumChildren) { + SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]); + } + } + } } // ------------------------------------------------------------------------------------------------ // Load an MD5CAMERA file void MD5Importer::LoadMD5CameraFile () { - std::string pFile = mFile + "md5camera"; - boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( !file.get() || !file->FileSize()) { - throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile); - } - bHadMD5Camera = true; - LoadFileIntoMemory(file.get()); - - // parse the basic file structure - MD5::MD5Parser parser(mBuffer,fileSize); - - // load the camera animation data from the parse tree - MD5::MD5CameraParser cameraParser(parser.mSections); - - if (cameraParser.frames.empty()) { - throw DeadlyImportError("MD5CAMERA: No frames parsed"); - } - - std::vector<unsigned int>& cuts = cameraParser.cuts; - std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames; - - // Construct output graph - a simple root with a dummy child. - // The root node performs the coordinate system conversion - aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>"); - root->mChildren = new aiNode*[root->mNumChildren = 1]; - root->mChildren[0] = new aiNode("<MD5Camera>"); - root->mChildren[0]->mParent = root; - - // ... but with one camera assigned to it - pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1]; - aiCamera* cam = pScene->mCameras[0] = new aiCamera(); - cam->mName = "<MD5Camera>"; - - // FIXME: Fov is currently set to the first frame's value - cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV ); - - // every cut is written to a separate aiAnimation - if (!cuts.size()) { - cuts.push_back(0); - cuts.push_back(frames.size()-1); - } - else { - cuts.insert(cuts.begin(),0); - - if (cuts.back() < frames.size()-1) - cuts.push_back(frames.size()-1); - } - - pScene->mNumAnimations = cuts.size()-1; - aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations]; - for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) { - - aiAnimation* anim = *tmp++ = new aiAnimation(); - anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1)); - - anim->mTicksPerSecond = cameraParser.fFrameRate; - anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1]; - aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim(); - nd->mNodeName.Set("<MD5Camera>"); - - nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it); - nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; - nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys]; - for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) { - - nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ; - MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue); - nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i; - } - } + std::string pFile = mFile + "md5camera"; + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( !file.get() || !file->FileSize()) { + throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile); + } + bHadMD5Camera = true; + LoadFileIntoMemory(file.get()); + + // parse the basic file structure + MD5::MD5Parser parser(mBuffer,fileSize); + + // load the camera animation data from the parse tree + MD5::MD5CameraParser cameraParser(parser.mSections); + + if (cameraParser.frames.empty()) { + throw DeadlyImportError("MD5CAMERA: No frames parsed"); + } + + std::vector<unsigned int>& cuts = cameraParser.cuts; + std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames; + + // Construct output graph - a simple root with a dummy child. + // The root node performs the coordinate system conversion + aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>"); + root->mChildren = new aiNode*[root->mNumChildren = 1]; + root->mChildren[0] = new aiNode("<MD5Camera>"); + root->mChildren[0]->mParent = root; + + // ... but with one camera assigned to it + pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1]; + aiCamera* cam = pScene->mCameras[0] = new aiCamera(); + cam->mName = "<MD5Camera>"; + + // FIXME: Fov is currently set to the first frame's value + cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV ); + + // every cut is written to a separate aiAnimation + if (!cuts.size()) { + cuts.push_back(0); + cuts.push_back(frames.size()-1); + } + else { + cuts.insert(cuts.begin(),0); + + if (cuts.back() < frames.size()-1) + cuts.push_back(frames.size()-1); + } + + pScene->mNumAnimations = cuts.size()-1; + aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations]; + for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) { + + aiAnimation* anim = *tmp++ = new aiAnimation(); + anim->mName.length = ::ai_snprintf(anim->mName.data, MAXLEN, "anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1)); + + anim->mTicksPerSecond = cameraParser.fFrameRate; + anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1]; + aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim(); + nd->mNodeName.Set("<MD5Camera>"); + + nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it); + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys]; + for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) { + + nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ; + MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue); + nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i; + } + } } #endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER |