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