vrshoot
diff libs/assimp/MD5Loader.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/MD5Loader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,748 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file MD5Loader.cpp 1.46 + * @brief Implementation of the MD5 importer class 1.47 + */ 1.48 + 1.49 +#include "AssimpPCH.h" 1.50 +#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER 1.51 + 1.52 +// internal headers 1.53 +#include "RemoveComments.h" 1.54 +#include "MD5Loader.h" 1.55 +#include "StringComparison.h" 1.56 +#include "fast_atof.h" 1.57 +#include "SkeletonMeshBuilder.h" 1.58 + 1.59 +using namespace Assimp; 1.60 + 1.61 +// Minimum weight value. Weights inside [-n ... n] are ignored 1.62 +#define AI_MD5_WEIGHT_EPSILON 1e-5f 1.63 + 1.64 + 1.65 +static const aiImporterDesc desc = { 1.66 + "Doom 3 / MD5 Mesh Importer", 1.67 + "", 1.68 + "", 1.69 + "", 1.70 + aiImporterFlags_SupportBinaryFlavour, 1.71 + 0, 1.72 + 0, 1.73 + 0, 1.74 + 0, 1.75 + "md5mesh md5camera md5anim" 1.76 +}; 1.77 + 1.78 +// ------------------------------------------------------------------------------------------------ 1.79 +// Constructor to be privately used by Importer 1.80 +MD5Importer::MD5Importer() 1.81 +: mBuffer() 1.82 +, configNoAutoLoad (false) 1.83 +{} 1.84 + 1.85 +// ------------------------------------------------------------------------------------------------ 1.86 +// Destructor, private as well 1.87 +MD5Importer::~MD5Importer() 1.88 +{} 1.89 + 1.90 +// ------------------------------------------------------------------------------------------------ 1.91 +// Returns whether the class can handle the format of the given file. 1.92 +bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const 1.93 +{ 1.94 + const std::string extension = GetExtension(pFile); 1.95 + 1.96 + if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera") 1.97 + return true; 1.98 + else if (!extension.length() || checkSig) { 1.99 + if (!pIOHandler) { 1.100 + return true; 1.101 + } 1.102 + const char* tokens[] = {"MD5Version"}; 1.103 + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); 1.104 + } 1.105 + return false; 1.106 +} 1.107 + 1.108 +// ------------------------------------------------------------------------------------------------ 1.109 +// Get list of all supported extensions 1.110 +const aiImporterDesc* MD5Importer::GetInfo () const 1.111 +{ 1.112 + return &desc; 1.113 +} 1.114 + 1.115 +// ------------------------------------------------------------------------------------------------ 1.116 +// Setup import properties 1.117 +void MD5Importer::SetupProperties(const Importer* pImp) 1.118 +{ 1.119 + // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD 1.120 + configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0)); 1.121 +} 1.122 + 1.123 +// ------------------------------------------------------------------------------------------------ 1.124 +// Imports the given file into the given scene structure. 1.125 +void MD5Importer::InternReadFile( const std::string& pFile, 1.126 + aiScene* _pScene, IOSystem* _pIOHandler) 1.127 +{ 1.128 + pIOHandler = _pIOHandler; 1.129 + pScene = _pScene; 1.130 + bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false; 1.131 + 1.132 + // remove the file extension 1.133 + const std::string::size_type pos = pFile.find_last_of('.'); 1.134 + mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1)); 1.135 + 1.136 + const std::string extension = GetExtension(pFile); 1.137 + try { 1.138 + if (extension == "md5camera") { 1.139 + LoadMD5CameraFile(); 1.140 + } 1.141 + else if (configNoAutoLoad || extension == "md5anim") { 1.142 + // determine file extension and process just *one* file 1.143 + if (extension.length() == 0) { 1.144 + throw DeadlyImportError("Failure, need file extension to determine MD5 part type"); 1.145 + } 1.146 + if (extension == "md5anim") { 1.147 + LoadMD5AnimFile(); 1.148 + } 1.149 + else if (extension == "md5mesh") { 1.150 + LoadMD5MeshFile(); 1.151 + } 1.152 + } 1.153 + else { 1.154 + LoadMD5MeshFile(); 1.155 + LoadMD5AnimFile(); 1.156 + } 1.157 + } 1.158 + catch ( ... ) { // std::exception, Assimp::DeadlyImportError 1.159 + UnloadFileFromMemory(); 1.160 + throw; 1.161 + } 1.162 + 1.163 + // make sure we have at least one file 1.164 + if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) { 1.165 + throw DeadlyImportError("Failed to read valid contents out of this MD5* file"); 1.166 + } 1.167 + 1.168 + // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system 1.169 + pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, 1.170 + 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); 1.171 + 1.172 + // the output scene wouldn't pass the validation without this flag 1.173 + if (!bHadMD5Mesh) { 1.174 + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; 1.175 + } 1.176 + 1.177 + // clean the instance -- the BaseImporter instance may be reused later. 1.178 + UnloadFileFromMemory(); 1.179 +} 1.180 + 1.181 +// ------------------------------------------------------------------------------------------------ 1.182 +// Load a file into a memory buffer 1.183 +void MD5Importer::LoadFileIntoMemory (IOStream* file) 1.184 +{ 1.185 + // unload the previous buffer, if any 1.186 + UnloadFileFromMemory(); 1.187 + 1.188 + ai_assert(NULL != file); 1.189 + fileSize = (unsigned int)file->FileSize(); 1.190 + ai_assert(fileSize); 1.191 + 1.192 + // allocate storage and copy the contents of the file to a memory buffer 1.193 + mBuffer = new char[fileSize+1]; 1.194 + file->Read( (void*)mBuffer, 1, fileSize); 1.195 + iLineNumber = 1; 1.196 + 1.197 + // append a terminal 0 1.198 + mBuffer[fileSize] = '\0'; 1.199 + 1.200 + // now remove all line comments from the file 1.201 + CommentRemover::RemoveLineComments("//",mBuffer,' '); 1.202 +} 1.203 + 1.204 +// ------------------------------------------------------------------------------------------------ 1.205 +// Unload the current memory buffer 1.206 +void MD5Importer::UnloadFileFromMemory () 1.207 +{ 1.208 + // delete the file buffer 1.209 + delete[] mBuffer; 1.210 + mBuffer = NULL; 1.211 + fileSize = 0; 1.212 +} 1.213 + 1.214 +// ------------------------------------------------------------------------------------------------ 1.215 +// Build unique vertices 1.216 +void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc) 1.217 +{ 1.218 + std::vector<bool> abHad(meshSrc.mVertices.size(),false); 1.219 + 1.220 + // allocate enough storage to keep the output structures 1.221 + const unsigned int iNewNum = meshSrc.mFaces.size()*3; 1.222 + unsigned int iNewIndex = meshSrc.mVertices.size(); 1.223 + meshSrc.mVertices.resize(iNewNum); 1.224 + 1.225 + // try to guess how much storage we'll need for new weights 1.226 + const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex; 1.227 + const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); 1.228 + meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer 1.229 + 1.230 + for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){ 1.231 + const aiFace& face = *iter; 1.232 + for (unsigned int i = 0; i < 3;++i) { 1.233 + if (face.mIndices[0] >= meshSrc.mVertices.size()) { 1.234 + throw DeadlyImportError("MD5MESH: Invalid vertex index"); 1.235 + } 1.236 + 1.237 + if (abHad[face.mIndices[i]]) { 1.238 + // generate a new vertex 1.239 + meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]]; 1.240 + face.mIndices[i] = iNewIndex++; 1.241 + } 1.242 + else abHad[face.mIndices[i]] = true; 1.243 + } 1.244 + // swap face order 1.245 + std::swap(face.mIndices[0],face.mIndices[2]); 1.246 + } 1.247 +} 1.248 + 1.249 +// ------------------------------------------------------------------------------------------------ 1.250 +// Recursive node graph construction from a MD5MESH 1.251 +void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones) 1.252 +{ 1.253 + ai_assert(NULL != piParent && !piParent->mNumChildren); 1.254 + 1.255 + // First find out how many children we'll have 1.256 + for (int i = 0; i < (int)bones.size();++i) { 1.257 + if (iParentID != i && bones[i].mParentIndex == iParentID) { 1.258 + ++piParent->mNumChildren; 1.259 + } 1.260 + } 1.261 + if (piParent->mNumChildren) { 1.262 + piParent->mChildren = new aiNode*[piParent->mNumChildren]; 1.263 + for (int i = 0; i < (int)bones.size();++i) { 1.264 + // (avoid infinite recursion) 1.265 + if (iParentID != i && bones[i].mParentIndex == iParentID) { 1.266 + aiNode* pc; 1.267 + // setup a new node 1.268 + *piParent->mChildren++ = pc = new aiNode(); 1.269 + pc->mName = aiString(bones[i].mName); 1.270 + pc->mParent = piParent; 1.271 + 1.272 + // get the transformation matrix from rotation and translational components 1.273 + aiQuaternion quat; 1.274 + MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat ); 1.275 + 1.276 + // FIX to get to Assimp's quaternion conventions 1.277 + quat.w *= -1.f; 1.278 + 1.279 + bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix()); 1.280 + bones[i].mTransform.a4 = bones[i].mPositionXYZ.x; 1.281 + bones[i].mTransform.b4 = bones[i].mPositionXYZ.y; 1.282 + bones[i].mTransform.c4 = bones[i].mPositionXYZ.z; 1.283 + 1.284 + // store it for later use 1.285 + pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform; 1.286 + bones[i].mInvTransform.Inverse(); 1.287 + 1.288 + // the transformations for each bone are absolute, so we need to multiply them 1.289 + // with the inverse of the absolute matrix of the parent joint 1.290 + if (-1 != iParentID) { 1.291 + pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation; 1.292 + } 1.293 + 1.294 + // add children to this node, too 1.295 + AttachChilds_Mesh( i, pc, bones); 1.296 + } 1.297 + } 1.298 + // undo offset computations 1.299 + piParent->mChildren -= piParent->mNumChildren; 1.300 + } 1.301 +} 1.302 + 1.303 +// ------------------------------------------------------------------------------------------------ 1.304 +// Recursive node graph construction from a MD5ANIM 1.305 +void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims) 1.306 +{ 1.307 + ai_assert(NULL != piParent && !piParent->mNumChildren); 1.308 + 1.309 + // First find out how many children we'll have 1.310 + for (int i = 0; i < (int)bones.size();++i) { 1.311 + if (iParentID != i && bones[i].mParentIndex == iParentID) { 1.312 + ++piParent->mNumChildren; 1.313 + } 1.314 + } 1.315 + if (piParent->mNumChildren) { 1.316 + piParent->mChildren = new aiNode*[piParent->mNumChildren]; 1.317 + for (int i = 0; i < (int)bones.size();++i) { 1.318 + // (avoid infinite recursion) 1.319 + if (iParentID != i && bones[i].mParentIndex == iParentID) 1.320 + { 1.321 + aiNode* pc; 1.322 + // setup a new node 1.323 + *piParent->mChildren++ = pc = new aiNode(); 1.324 + pc->mName = aiString(bones[i].mName); 1.325 + pc->mParent = piParent; 1.326 + 1.327 + // get the corresponding animation channel and its first frame 1.328 + const aiNodeAnim** cur = node_anims; 1.329 + while ((**cur).mNodeName != pc->mName)++cur; 1.330 + 1.331 + aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation); 1.332 + pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ; 1.333 + 1.334 + // add children to this node, too 1.335 + AttachChilds_Anim( i, pc, bones,node_anims); 1.336 + } 1.337 + } 1.338 + // undo offset computations 1.339 + piParent->mChildren -= piParent->mNumChildren; 1.340 + } 1.341 +} 1.342 + 1.343 +// ------------------------------------------------------------------------------------------------ 1.344 +// Load a MD5MESH file 1.345 +void MD5Importer::LoadMD5MeshFile () 1.346 +{ 1.347 + std::string pFile = mFile + "md5mesh"; 1.348 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); 1.349 + 1.350 + // Check whether we can read from the file 1.351 + if( file.get() == NULL || !file->FileSize()) { 1.352 + DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile); 1.353 + return; 1.354 + } 1.355 + bHadMD5Mesh = true; 1.356 + LoadFileIntoMemory(file.get()); 1.357 + 1.358 + // now construct a parser and parse the file 1.359 + MD5::MD5Parser parser(mBuffer,fileSize); 1.360 + 1.361 + // load the mesh information from it 1.362 + MD5::MD5MeshParser meshParser(parser.mSections); 1.363 + 1.364 + // create the bone hierarchy - first the root node and dummy nodes for all meshes 1.365 + pScene->mRootNode = new aiNode("<MD5_Root>"); 1.366 + pScene->mRootNode->mNumChildren = 2; 1.367 + pScene->mRootNode->mChildren = new aiNode*[2]; 1.368 + 1.369 + // build the hierarchy from the MD5MESH file 1.370 + aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); 1.371 + pcNode->mName.Set("<MD5_Hierarchy>"); 1.372 + pcNode->mParent = pScene->mRootNode; 1.373 + AttachChilds_Mesh(-1,pcNode,meshParser.mJoints); 1.374 + 1.375 + pcNode = pScene->mRootNode->mChildren[0] = new aiNode(); 1.376 + pcNode->mName.Set("<MD5_Mesh>"); 1.377 + pcNode->mParent = pScene->mRootNode; 1.378 + 1.379 +#if 0 1.380 + if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */ 1.381 + SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]); 1.382 +#else 1.383 + 1.384 + // FIX: MD5 files exported from Blender can have empty meshes 1.385 + for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { 1.386 + if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) 1.387 + ++pScene->mNumMaterials; 1.388 + } 1.389 + 1.390 + // generate all meshes 1.391 + pScene->mNumMeshes = pScene->mNumMaterials; 1.392 + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; 1.393 + pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; 1.394 + 1.395 + // storage for node mesh indices 1.396 + pcNode->mNumMeshes = pScene->mNumMeshes; 1.397 + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; 1.398 + for (unsigned int m = 0; m < pcNode->mNumMeshes;++m) 1.399 + pcNode->mMeshes[m] = m; 1.400 + 1.401 + unsigned int n = 0; 1.402 + for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) { 1.403 + MD5::MeshDesc& meshSrc = *it; 1.404 + if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) 1.405 + continue; 1.406 + 1.407 + aiMesh* mesh = pScene->mMeshes[n] = new aiMesh(); 1.408 + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; 1.409 + 1.410 + // generate unique vertices in our internal verbose format 1.411 + MakeDataUnique(meshSrc); 1.412 + 1.413 + mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size(); 1.414 + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; 1.415 + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; 1.416 + mesh->mNumUVComponents[0] = 2; 1.417 + 1.418 + // copy texture coordinates 1.419 + aiVector3D* pv = mesh->mTextureCoords[0]; 1.420 + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { 1.421 + pv->x = (*iter).mUV.x; 1.422 + pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL 1.423 + pv->z = 0.0f; 1.424 + } 1.425 + 1.426 + // sort all bone weights - per bone 1.427 + unsigned int* piCount = new unsigned int[meshParser.mJoints.size()]; 1.428 + ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size()); 1.429 + 1.430 + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { 1.431 + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) 1.432 + { 1.433 + MD5::WeightDesc& desc = meshSrc.mWeights[w]; 1.434 + /* FIX for some invalid exporters */ 1.435 + if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON )) 1.436 + ++piCount[desc.mBone]; 1.437 + } 1.438 + } 1.439 + 1.440 + // check how many we will need 1.441 + for (unsigned int p = 0; p < meshParser.mJoints.size();++p) 1.442 + if (piCount[p])mesh->mNumBones++; 1.443 + 1.444 + if (mesh->mNumBones) // just for safety 1.445 + { 1.446 + mesh->mBones = new aiBone*[mesh->mNumBones]; 1.447 + for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q) 1.448 + { 1.449 + if (!piCount[q])continue; 1.450 + aiBone* p = mesh->mBones[h] = new aiBone(); 1.451 + p->mNumWeights = piCount[q]; 1.452 + p->mWeights = new aiVertexWeight[p->mNumWeights]; 1.453 + p->mName = aiString(meshParser.mJoints[q].mName); 1.454 + p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform; 1.455 + 1.456 + // store the index for later use 1.457 + MD5::BoneDesc& boneSrc = meshParser.mJoints[q]; 1.458 + boneSrc.mMap = h++; 1.459 + 1.460 + // compute w-component of quaternion 1.461 + MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted ); 1.462 + } 1.463 + 1.464 + //unsigned int g = 0; 1.465 + pv = mesh->mVertices; 1.466 + for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { 1.467 + // compute the final vertex position from all single weights 1.468 + *pv = aiVector3D(); 1.469 + 1.470 + // there are models which have weights which don't sum to 1 ... 1.471 + float fSum = 0.0f; 1.472 + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) 1.473 + fSum += meshSrc.mWeights[w].mWeight; 1.474 + if (!fSum) { 1.475 + DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0"); 1.476 + continue; 1.477 + } 1.478 + 1.479 + // process bone weights 1.480 + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) { 1.481 + if (w >= meshSrc.mWeights.size()) 1.482 + throw DeadlyImportError("MD5MESH: Invalid weight index"); 1.483 + 1.484 + MD5::WeightDesc& desc = meshSrc.mWeights[w]; 1.485 + if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { 1.486 + continue; 1.487 + } 1.488 + 1.489 + const float fNewWeight = desc.mWeight / fSum; 1.490 + 1.491 + // transform the local position into worldspace 1.492 + MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone]; 1.493 + const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition); 1.494 + 1.495 + // use the original weight to compute the vertex position 1.496 + // (some MD5s seem to depend on the invalid weight values ...) 1.497 + *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight); 1.498 + 1.499 + aiBone* bone = mesh->mBones[boneSrc.mMap]; 1.500 + *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight); 1.501 + } 1.502 + } 1.503 + 1.504 + // undo our nice offset tricks ... 1.505 + for (unsigned int p = 0; p < mesh->mNumBones;++p) { 1.506 + mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights; 1.507 + } 1.508 + } 1.509 + 1.510 + delete[] piCount; 1.511 + 1.512 + // now setup all faces - we can directly copy the list 1.513 + // (however, take care that the aiFace destructor doesn't delete the mIndices array) 1.514 + mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size(); 1.515 + mesh->mFaces = new aiFace[mesh->mNumFaces]; 1.516 + for (unsigned int c = 0; c < mesh->mNumFaces;++c) { 1.517 + mesh->mFaces[c].mNumIndices = 3; 1.518 + mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices; 1.519 + meshSrc.mFaces[c].mIndices = NULL; 1.520 + } 1.521 + 1.522 + // generate a material for the mesh 1.523 + aiMaterial* mat = new aiMaterial(); 1.524 + pScene->mMaterials[n] = mat; 1.525 + 1.526 + // insert the typical doom3 textures: 1.527 + // nnn_local.tga - normal map 1.528 + // nnn_h.tga - height map 1.529 + // nnn_s.tga - specular map 1.530 + // nnn_d.tga - diffuse map 1.531 + if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) { 1.532 + 1.533 + aiString temp(meshSrc.mShader); 1.534 + temp.Append("_local.tga"); 1.535 + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0)); 1.536 + 1.537 + temp = aiString(meshSrc.mShader); 1.538 + temp.Append("_s.tga"); 1.539 + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0)); 1.540 + 1.541 + temp = aiString(meshSrc.mShader); 1.542 + temp.Append("_d.tga"); 1.543 + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0)); 1.544 + 1.545 + temp = aiString(meshSrc.mShader); 1.546 + temp.Append("_h.tga"); 1.547 + mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0)); 1.548 + 1.549 + // set this also as material name 1.550 + mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME); 1.551 + } 1.552 + else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0)); 1.553 + mesh->mMaterialIndex = n++; 1.554 + } 1.555 +#endif 1.556 +} 1.557 + 1.558 +// ------------------------------------------------------------------------------------------------ 1.559 +// Load an MD5ANIM file 1.560 +void MD5Importer::LoadMD5AnimFile () 1.561 +{ 1.562 + std::string pFile = mFile + "md5anim"; 1.563 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); 1.564 + 1.565 + // Check whether we can read from the file 1.566 + if( !file.get() || !file->FileSize()) { 1.567 + DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile); 1.568 + return; 1.569 + } 1.570 + LoadFileIntoMemory(file.get()); 1.571 + 1.572 + // parse the basic file structure 1.573 + MD5::MD5Parser parser(mBuffer,fileSize); 1.574 + 1.575 + // load the animation information from the parse tree 1.576 + MD5::MD5AnimParser animParser(parser.mSections); 1.577 + 1.578 + // generate and fill the output animation 1.579 + if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() || 1.580 + animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) { 1.581 + 1.582 + DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded"); 1.583 + } 1.584 + else { 1.585 + bHadMD5Anim = true; 1.586 + 1.587 + pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1]; 1.588 + aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation(); 1.589 + anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size(); 1.590 + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; 1.591 + for (unsigned int i = 0; i < anim->mNumChannels;++i) { 1.592 + aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim(); 1.593 + node->mNodeName = aiString( animParser.mAnimatedBones[i].mName ); 1.594 + 1.595 + // allocate storage for the keyframes 1.596 + node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()]; 1.597 + node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()]; 1.598 + } 1.599 + 1.600 + // 1 tick == 1 frame 1.601 + anim->mTicksPerSecond = animParser.fFrameRate; 1.602 + 1.603 + for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){ 1.604 + double dTime = (double)(*iter).iIndex; 1.605 + aiNodeAnim** pcAnimNode = anim->mChannels; 1.606 + if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */ 1.607 + { 1.608 + // now process all values in there ... read all joints 1.609 + MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0]; 1.610 + for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2, 1.611 + ++pcAnimNode,++pcBaseFrame) 1.612 + { 1.613 + if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) { 1.614 + 1.615 + // Allow for empty frames 1.616 + if ((*iter2).iFlags != 0) { 1.617 + throw DeadlyImportError("MD5: Keyframe index is out of range"); 1.618 + 1.619 + } 1.620 + continue; 1.621 + } 1.622 + const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex]; 1.623 + aiNodeAnim* pcCurAnimBone = *pcAnimNode; 1.624 + 1.625 + aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++]; 1.626 + aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++]; 1.627 + aiVector3D vTemp; 1.628 + 1.629 + // translational component 1.630 + for (unsigned int i = 0; i < 3; ++i) { 1.631 + if ((*iter2).iFlags & (1u << i)) { 1.632 + vKey->mValue[i] = *fpCur++; 1.633 + } 1.634 + else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i]; 1.635 + } 1.636 + 1.637 + // orientation component 1.638 + for (unsigned int i = 0; i < 3; ++i) { 1.639 + if ((*iter2).iFlags & (8u << i)) { 1.640 + vTemp[i] = *fpCur++; 1.641 + } 1.642 + else vTemp[i] = pcBaseFrame->vRotationQuat[i]; 1.643 + } 1.644 + 1.645 + MD5::ConvertQuaternion(vTemp, qKey->mValue); 1.646 + qKey->mTime = vKey->mTime = dTime; 1.647 + 1.648 + // we need this to get to Assimp quaternion conventions 1.649 + qKey->mValue.w *= -1.f; 1.650 + } 1.651 + } 1.652 + 1.653 + // compute the duration of the animation 1.654 + anim->mDuration = std::max(dTime,anim->mDuration); 1.655 + } 1.656 + 1.657 + // If we didn't build the hierarchy yet (== we didn't load a MD5MESH), 1.658 + // construct it now from the data given in the MD5ANIM. 1.659 + if (!pScene->mRootNode) { 1.660 + pScene->mRootNode = new aiNode(); 1.661 + pScene->mRootNode->mName.Set("<MD5_Hierarchy>"); 1.662 + 1.663 + AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels); 1.664 + 1.665 + // Call SkeletonMeshBuilder to construct a mesh to represent the shape 1.666 + if (pScene->mRootNode->mNumChildren) { 1.667 + SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]); 1.668 + } 1.669 + } 1.670 + } 1.671 +} 1.672 + 1.673 +// ------------------------------------------------------------------------------------------------ 1.674 +// Load an MD5CAMERA file 1.675 +void MD5Importer::LoadMD5CameraFile () 1.676 +{ 1.677 + std::string pFile = mFile + "md5camera"; 1.678 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); 1.679 + 1.680 + // Check whether we can read from the file 1.681 + if( !file.get() || !file->FileSize()) { 1.682 + throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile); 1.683 + } 1.684 + bHadMD5Camera = true; 1.685 + LoadFileIntoMemory(file.get()); 1.686 + 1.687 + // parse the basic file structure 1.688 + MD5::MD5Parser parser(mBuffer,fileSize); 1.689 + 1.690 + // load the camera animation data from the parse tree 1.691 + MD5::MD5CameraParser cameraParser(parser.mSections); 1.692 + 1.693 + if (cameraParser.frames.empty()) { 1.694 + throw DeadlyImportError("MD5CAMERA: No frames parsed"); 1.695 + } 1.696 + 1.697 + std::vector<unsigned int>& cuts = cameraParser.cuts; 1.698 + std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames; 1.699 + 1.700 + // Construct output graph - a simple root with a dummy child. 1.701 + // The root node performs the coordinate system conversion 1.702 + aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>"); 1.703 + root->mChildren = new aiNode*[root->mNumChildren = 1]; 1.704 + root->mChildren[0] = new aiNode("<MD5Camera>"); 1.705 + root->mChildren[0]->mParent = root; 1.706 + 1.707 + // ... but with one camera assigned to it 1.708 + pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1]; 1.709 + aiCamera* cam = pScene->mCameras[0] = new aiCamera(); 1.710 + cam->mName = "<MD5Camera>"; 1.711 + 1.712 + // FIXME: Fov is currently set to the first frame's value 1.713 + cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV ); 1.714 + 1.715 + // every cut is written to a separate aiAnimation 1.716 + if (!cuts.size()) { 1.717 + cuts.push_back(0); 1.718 + cuts.push_back(frames.size()-1); 1.719 + } 1.720 + else { 1.721 + cuts.insert(cuts.begin(),0); 1.722 + 1.723 + if (cuts.back() < frames.size()-1) 1.724 + cuts.push_back(frames.size()-1); 1.725 + } 1.726 + 1.727 + pScene->mNumAnimations = cuts.size()-1; 1.728 + aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations]; 1.729 + for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) { 1.730 + 1.731 + aiAnimation* anim = *tmp++ = new aiAnimation(); 1.732 + anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1)); 1.733 + 1.734 + anim->mTicksPerSecond = cameraParser.fFrameRate; 1.735 + anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1]; 1.736 + aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim(); 1.737 + nd->mNodeName.Set("<MD5Camera>"); 1.738 + 1.739 + nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it); 1.740 + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; 1.741 + nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys]; 1.742 + for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) { 1.743 + 1.744 + nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ; 1.745 + MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue); 1.746 + nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i; 1.747 + } 1.748 + } 1.749 +} 1.750 + 1.751 +#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER