vrshoot
view 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 source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file MD5Loader.cpp
43 * @brief Implementation of the MD5 importer class
44 */
46 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
49 // internal headers
50 #include "RemoveComments.h"
51 #include "MD5Loader.h"
52 #include "StringComparison.h"
53 #include "fast_atof.h"
54 #include "SkeletonMeshBuilder.h"
56 using namespace Assimp;
58 // Minimum weight value. Weights inside [-n ... n] are ignored
59 #define AI_MD5_WEIGHT_EPSILON 1e-5f
62 static const aiImporterDesc desc = {
63 "Doom 3 / MD5 Mesh Importer",
64 "",
65 "",
66 "",
67 aiImporterFlags_SupportBinaryFlavour,
68 0,
69 0,
70 0,
71 0,
72 "md5mesh md5camera md5anim"
73 };
75 // ------------------------------------------------------------------------------------------------
76 // Constructor to be privately used by Importer
77 MD5Importer::MD5Importer()
78 : mBuffer()
79 , configNoAutoLoad (false)
80 {}
82 // ------------------------------------------------------------------------------------------------
83 // Destructor, private as well
84 MD5Importer::~MD5Importer()
85 {}
87 // ------------------------------------------------------------------------------------------------
88 // Returns whether the class can handle the format of the given file.
89 bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
90 {
91 const std::string extension = GetExtension(pFile);
93 if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
94 return true;
95 else if (!extension.length() || checkSig) {
96 if (!pIOHandler) {
97 return true;
98 }
99 const char* tokens[] = {"MD5Version"};
100 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
101 }
102 return false;
103 }
105 // ------------------------------------------------------------------------------------------------
106 // Get list of all supported extensions
107 const aiImporterDesc* MD5Importer::GetInfo () const
108 {
109 return &desc;
110 }
112 // ------------------------------------------------------------------------------------------------
113 // Setup import properties
114 void MD5Importer::SetupProperties(const Importer* pImp)
115 {
116 // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
117 configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
118 }
120 // ------------------------------------------------------------------------------------------------
121 // Imports the given file into the given scene structure.
122 void MD5Importer::InternReadFile( const std::string& pFile,
123 aiScene* _pScene, IOSystem* _pIOHandler)
124 {
125 pIOHandler = _pIOHandler;
126 pScene = _pScene;
127 bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
129 // remove the file extension
130 const std::string::size_type pos = pFile.find_last_of('.');
131 mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
133 const std::string extension = GetExtension(pFile);
134 try {
135 if (extension == "md5camera") {
136 LoadMD5CameraFile();
137 }
138 else if (configNoAutoLoad || extension == "md5anim") {
139 // determine file extension and process just *one* file
140 if (extension.length() == 0) {
141 throw DeadlyImportError("Failure, need file extension to determine MD5 part type");
142 }
143 if (extension == "md5anim") {
144 LoadMD5AnimFile();
145 }
146 else if (extension == "md5mesh") {
147 LoadMD5MeshFile();
148 }
149 }
150 else {
151 LoadMD5MeshFile();
152 LoadMD5AnimFile();
153 }
154 }
155 catch ( ... ) { // std::exception, Assimp::DeadlyImportError
156 UnloadFileFromMemory();
157 throw;
158 }
160 // make sure we have at least one file
161 if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) {
162 throw DeadlyImportError("Failed to read valid contents out of this MD5* file");
163 }
165 // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system
166 pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
167 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
169 // the output scene wouldn't pass the validation without this flag
170 if (!bHadMD5Mesh) {
171 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
172 }
174 // clean the instance -- the BaseImporter instance may be reused later.
175 UnloadFileFromMemory();
176 }
178 // ------------------------------------------------------------------------------------------------
179 // Load a file into a memory buffer
180 void MD5Importer::LoadFileIntoMemory (IOStream* file)
181 {
182 // unload the previous buffer, if any
183 UnloadFileFromMemory();
185 ai_assert(NULL != file);
186 fileSize = (unsigned int)file->FileSize();
187 ai_assert(fileSize);
189 // allocate storage and copy the contents of the file to a memory buffer
190 mBuffer = new char[fileSize+1];
191 file->Read( (void*)mBuffer, 1, fileSize);
192 iLineNumber = 1;
194 // append a terminal 0
195 mBuffer[fileSize] = '\0';
197 // now remove all line comments from the file
198 CommentRemover::RemoveLineComments("//",mBuffer,' ');
199 }
201 // ------------------------------------------------------------------------------------------------
202 // Unload the current memory buffer
203 void MD5Importer::UnloadFileFromMemory ()
204 {
205 // delete the file buffer
206 delete[] mBuffer;
207 mBuffer = NULL;
208 fileSize = 0;
209 }
211 // ------------------------------------------------------------------------------------------------
212 // Build unique vertices
213 void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
214 {
215 std::vector<bool> abHad(meshSrc.mVertices.size(),false);
217 // allocate enough storage to keep the output structures
218 const unsigned int iNewNum = meshSrc.mFaces.size()*3;
219 unsigned int iNewIndex = meshSrc.mVertices.size();
220 meshSrc.mVertices.resize(iNewNum);
222 // try to guess how much storage we'll need for new weights
223 const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
224 const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum);
225 meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
227 for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){
228 const aiFace& face = *iter;
229 for (unsigned int i = 0; i < 3;++i) {
230 if (face.mIndices[0] >= meshSrc.mVertices.size()) {
231 throw DeadlyImportError("MD5MESH: Invalid vertex index");
232 }
234 if (abHad[face.mIndices[i]]) {
235 // generate a new vertex
236 meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
237 face.mIndices[i] = iNewIndex++;
238 }
239 else abHad[face.mIndices[i]] = true;
240 }
241 // swap face order
242 std::swap(face.mIndices[0],face.mIndices[2]);
243 }
244 }
246 // ------------------------------------------------------------------------------------------------
247 // Recursive node graph construction from a MD5MESH
248 void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
249 {
250 ai_assert(NULL != piParent && !piParent->mNumChildren);
252 // First find out how many children we'll have
253 for (int i = 0; i < (int)bones.size();++i) {
254 if (iParentID != i && bones[i].mParentIndex == iParentID) {
255 ++piParent->mNumChildren;
256 }
257 }
258 if (piParent->mNumChildren) {
259 piParent->mChildren = new aiNode*[piParent->mNumChildren];
260 for (int i = 0; i < (int)bones.size();++i) {
261 // (avoid infinite recursion)
262 if (iParentID != i && bones[i].mParentIndex == iParentID) {
263 aiNode* pc;
264 // setup a new node
265 *piParent->mChildren++ = pc = new aiNode();
266 pc->mName = aiString(bones[i].mName);
267 pc->mParent = piParent;
269 // get the transformation matrix from rotation and translational components
270 aiQuaternion quat;
271 MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
273 // FIX to get to Assimp's quaternion conventions
274 quat.w *= -1.f;
276 bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
277 bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
278 bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
279 bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
281 // store it for later use
282 pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
283 bones[i].mInvTransform.Inverse();
285 // the transformations for each bone are absolute, so we need to multiply them
286 // with the inverse of the absolute matrix of the parent joint
287 if (-1 != iParentID) {
288 pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
289 }
291 // add children to this node, too
292 AttachChilds_Mesh( i, pc, bones);
293 }
294 }
295 // undo offset computations
296 piParent->mChildren -= piParent->mNumChildren;
297 }
298 }
300 // ------------------------------------------------------------------------------------------------
301 // Recursive node graph construction from a MD5ANIM
302 void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
303 {
304 ai_assert(NULL != piParent && !piParent->mNumChildren);
306 // First find out how many children we'll have
307 for (int i = 0; i < (int)bones.size();++i) {
308 if (iParentID != i && bones[i].mParentIndex == iParentID) {
309 ++piParent->mNumChildren;
310 }
311 }
312 if (piParent->mNumChildren) {
313 piParent->mChildren = new aiNode*[piParent->mNumChildren];
314 for (int i = 0; i < (int)bones.size();++i) {
315 // (avoid infinite recursion)
316 if (iParentID != i && bones[i].mParentIndex == iParentID)
317 {
318 aiNode* pc;
319 // setup a new node
320 *piParent->mChildren++ = pc = new aiNode();
321 pc->mName = aiString(bones[i].mName);
322 pc->mParent = piParent;
324 // get the corresponding animation channel and its first frame
325 const aiNodeAnim** cur = node_anims;
326 while ((**cur).mNodeName != pc->mName)++cur;
328 aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
329 pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
331 // add children to this node, too
332 AttachChilds_Anim( i, pc, bones,node_anims);
333 }
334 }
335 // undo offset computations
336 piParent->mChildren -= piParent->mNumChildren;
337 }
338 }
340 // ------------------------------------------------------------------------------------------------
341 // Load a MD5MESH file
342 void MD5Importer::LoadMD5MeshFile ()
343 {
344 std::string pFile = mFile + "md5mesh";
345 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
347 // Check whether we can read from the file
348 if( file.get() == NULL || !file->FileSize()) {
349 DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile);
350 return;
351 }
352 bHadMD5Mesh = true;
353 LoadFileIntoMemory(file.get());
355 // now construct a parser and parse the file
356 MD5::MD5Parser parser(mBuffer,fileSize);
358 // load the mesh information from it
359 MD5::MD5MeshParser meshParser(parser.mSections);
361 // create the bone hierarchy - first the root node and dummy nodes for all meshes
362 pScene->mRootNode = new aiNode("<MD5_Root>");
363 pScene->mRootNode->mNumChildren = 2;
364 pScene->mRootNode->mChildren = new aiNode*[2];
366 // build the hierarchy from the MD5MESH file
367 aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
368 pcNode->mName.Set("<MD5_Hierarchy>");
369 pcNode->mParent = pScene->mRootNode;
370 AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
372 pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
373 pcNode->mName.Set("<MD5_Mesh>");
374 pcNode->mParent = pScene->mRootNode;
376 #if 0
377 if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
378 SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
379 #else
381 // FIX: MD5 files exported from Blender can have empty meshes
382 for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
383 if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
384 ++pScene->mNumMaterials;
385 }
387 // generate all meshes
388 pScene->mNumMeshes = pScene->mNumMaterials;
389 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
390 pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
392 // storage for node mesh indices
393 pcNode->mNumMeshes = pScene->mNumMeshes;
394 pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
395 for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
396 pcNode->mMeshes[m] = m;
398 unsigned int n = 0;
399 for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
400 MD5::MeshDesc& meshSrc = *it;
401 if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
402 continue;
404 aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
405 mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
407 // generate unique vertices in our internal verbose format
408 MakeDataUnique(meshSrc);
410 mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
411 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
412 mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
413 mesh->mNumUVComponents[0] = 2;
415 // copy texture coordinates
416 aiVector3D* pv = mesh->mTextureCoords[0];
417 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
418 pv->x = (*iter).mUV.x;
419 pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
420 pv->z = 0.0f;
421 }
423 // sort all bone weights - per bone
424 unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
425 ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
427 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
428 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
429 {
430 MD5::WeightDesc& desc = meshSrc.mWeights[w];
431 /* FIX for some invalid exporters */
432 if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
433 ++piCount[desc.mBone];
434 }
435 }
437 // check how many we will need
438 for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
439 if (piCount[p])mesh->mNumBones++;
441 if (mesh->mNumBones) // just for safety
442 {
443 mesh->mBones = new aiBone*[mesh->mNumBones];
444 for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q)
445 {
446 if (!piCount[q])continue;
447 aiBone* p = mesh->mBones[h] = new aiBone();
448 p->mNumWeights = piCount[q];
449 p->mWeights = new aiVertexWeight[p->mNumWeights];
450 p->mName = aiString(meshParser.mJoints[q].mName);
451 p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
453 // store the index for later use
454 MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
455 boneSrc.mMap = h++;
457 // compute w-component of quaternion
458 MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
459 }
461 //unsigned int g = 0;
462 pv = mesh->mVertices;
463 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
464 // compute the final vertex position from all single weights
465 *pv = aiVector3D();
467 // there are models which have weights which don't sum to 1 ...
468 float fSum = 0.0f;
469 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
470 fSum += meshSrc.mWeights[w].mWeight;
471 if (!fSum) {
472 DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
473 continue;
474 }
476 // process bone weights
477 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) {
478 if (w >= meshSrc.mWeights.size())
479 throw DeadlyImportError("MD5MESH: Invalid weight index");
481 MD5::WeightDesc& desc = meshSrc.mWeights[w];
482 if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
483 continue;
484 }
486 const float fNewWeight = desc.mWeight / fSum;
488 // transform the local position into worldspace
489 MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
490 const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
492 // use the original weight to compute the vertex position
493 // (some MD5s seem to depend on the invalid weight values ...)
494 *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
496 aiBone* bone = mesh->mBones[boneSrc.mMap];
497 *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
498 }
499 }
501 // undo our nice offset tricks ...
502 for (unsigned int p = 0; p < mesh->mNumBones;++p) {
503 mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
504 }
505 }
507 delete[] piCount;
509 // now setup all faces - we can directly copy the list
510 // (however, take care that the aiFace destructor doesn't delete the mIndices array)
511 mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
512 mesh->mFaces = new aiFace[mesh->mNumFaces];
513 for (unsigned int c = 0; c < mesh->mNumFaces;++c) {
514 mesh->mFaces[c].mNumIndices = 3;
515 mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
516 meshSrc.mFaces[c].mIndices = NULL;
517 }
519 // generate a material for the mesh
520 aiMaterial* mat = new aiMaterial();
521 pScene->mMaterials[n] = mat;
523 // insert the typical doom3 textures:
524 // nnn_local.tga - normal map
525 // nnn_h.tga - height map
526 // nnn_s.tga - specular map
527 // nnn_d.tga - diffuse map
528 if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
530 aiString temp(meshSrc.mShader);
531 temp.Append("_local.tga");
532 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
534 temp = aiString(meshSrc.mShader);
535 temp.Append("_s.tga");
536 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
538 temp = aiString(meshSrc.mShader);
539 temp.Append("_d.tga");
540 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
542 temp = aiString(meshSrc.mShader);
543 temp.Append("_h.tga");
544 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
546 // set this also as material name
547 mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
548 }
549 else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
550 mesh->mMaterialIndex = n++;
551 }
552 #endif
553 }
555 // ------------------------------------------------------------------------------------------------
556 // Load an MD5ANIM file
557 void MD5Importer::LoadMD5AnimFile ()
558 {
559 std::string pFile = mFile + "md5anim";
560 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
562 // Check whether we can read from the file
563 if( !file.get() || !file->FileSize()) {
564 DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
565 return;
566 }
567 LoadFileIntoMemory(file.get());
569 // parse the basic file structure
570 MD5::MD5Parser parser(mBuffer,fileSize);
572 // load the animation information from the parse tree
573 MD5::MD5AnimParser animParser(parser.mSections);
575 // generate and fill the output animation
576 if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() ||
577 animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) {
579 DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded");
580 }
581 else {
582 bHadMD5Anim = true;
584 pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
585 aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
586 anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
587 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
588 for (unsigned int i = 0; i < anim->mNumChannels;++i) {
589 aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
590 node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
592 // allocate storage for the keyframes
593 node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
594 node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
595 }
597 // 1 tick == 1 frame
598 anim->mTicksPerSecond = animParser.fFrameRate;
600 for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
601 double dTime = (double)(*iter).iIndex;
602 aiNodeAnim** pcAnimNode = anim->mChannels;
603 if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
604 {
605 // now process all values in there ... read all joints
606 MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
607 for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
608 ++pcAnimNode,++pcBaseFrame)
609 {
610 if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
612 // Allow for empty frames
613 if ((*iter2).iFlags != 0) {
614 throw DeadlyImportError("MD5: Keyframe index is out of range");
616 }
617 continue;
618 }
619 const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
620 aiNodeAnim* pcCurAnimBone = *pcAnimNode;
622 aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
623 aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++];
624 aiVector3D vTemp;
626 // translational component
627 for (unsigned int i = 0; i < 3; ++i) {
628 if ((*iter2).iFlags & (1u << i)) {
629 vKey->mValue[i] = *fpCur++;
630 }
631 else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
632 }
634 // orientation component
635 for (unsigned int i = 0; i < 3; ++i) {
636 if ((*iter2).iFlags & (8u << i)) {
637 vTemp[i] = *fpCur++;
638 }
639 else vTemp[i] = pcBaseFrame->vRotationQuat[i];
640 }
642 MD5::ConvertQuaternion(vTemp, qKey->mValue);
643 qKey->mTime = vKey->mTime = dTime;
645 // we need this to get to Assimp quaternion conventions
646 qKey->mValue.w *= -1.f;
647 }
648 }
650 // compute the duration of the animation
651 anim->mDuration = std::max(dTime,anim->mDuration);
652 }
654 // If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
655 // construct it now from the data given in the MD5ANIM.
656 if (!pScene->mRootNode) {
657 pScene->mRootNode = new aiNode();
658 pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
660 AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
662 // Call SkeletonMeshBuilder to construct a mesh to represent the shape
663 if (pScene->mRootNode->mNumChildren) {
664 SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
665 }
666 }
667 }
668 }
670 // ------------------------------------------------------------------------------------------------
671 // Load an MD5CAMERA file
672 void MD5Importer::LoadMD5CameraFile ()
673 {
674 std::string pFile = mFile + "md5camera";
675 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
677 // Check whether we can read from the file
678 if( !file.get() || !file->FileSize()) {
679 throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
680 }
681 bHadMD5Camera = true;
682 LoadFileIntoMemory(file.get());
684 // parse the basic file structure
685 MD5::MD5Parser parser(mBuffer,fileSize);
687 // load the camera animation data from the parse tree
688 MD5::MD5CameraParser cameraParser(parser.mSections);
690 if (cameraParser.frames.empty()) {
691 throw DeadlyImportError("MD5CAMERA: No frames parsed");
692 }
694 std::vector<unsigned int>& cuts = cameraParser.cuts;
695 std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames;
697 // Construct output graph - a simple root with a dummy child.
698 // The root node performs the coordinate system conversion
699 aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>");
700 root->mChildren = new aiNode*[root->mNumChildren = 1];
701 root->mChildren[0] = new aiNode("<MD5Camera>");
702 root->mChildren[0]->mParent = root;
704 // ... but with one camera assigned to it
705 pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1];
706 aiCamera* cam = pScene->mCameras[0] = new aiCamera();
707 cam->mName = "<MD5Camera>";
709 // FIXME: Fov is currently set to the first frame's value
710 cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV );
712 // every cut is written to a separate aiAnimation
713 if (!cuts.size()) {
714 cuts.push_back(0);
715 cuts.push_back(frames.size()-1);
716 }
717 else {
718 cuts.insert(cuts.begin(),0);
720 if (cuts.back() < frames.size()-1)
721 cuts.push_back(frames.size()-1);
722 }
724 pScene->mNumAnimations = cuts.size()-1;
725 aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations];
726 for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) {
728 aiAnimation* anim = *tmp++ = new aiAnimation();
729 anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1));
731 anim->mTicksPerSecond = cameraParser.fFrameRate;
732 anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1];
733 aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim();
734 nd->mNodeName.Set("<MD5Camera>");
736 nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it);
737 nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
738 nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys];
739 for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
741 nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ;
742 MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue);
743 nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i;
744 }
745 }
746 }
748 #endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER