1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF 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 | |
63 | using namespace Assimp; |
64 | |
65 | // Minimum weight value. Weights inside [-n ... n] are ignored |
66 | #define AI_MD5_WEIGHT_EPSILON 1e-5f |
67 | |
68 | |
69 | static 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 |
84 | MD5Importer::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 |
99 | MD5Importer::~MD5Importer() |
100 | {} |
101 | |
102 | // ------------------------------------------------------------------------------------------------ |
103 | // Returns whether the class can handle the format of the given file. |
104 | bool 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 |
122 | const aiImporterDesc* MD5Importer::GetInfo () const |
123 | { |
124 | return &desc; |
125 | } |
126 | |
127 | // ------------------------------------------------------------------------------------------------ |
128 | // Setup import properties |
129 | void 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. |
137 | void 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 |
195 | void 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 |
218 | void MD5Importer::UnloadFileFromMemory () |
219 | { |
220 | // delete the file buffer |
221 | delete[] mBuffer; |
222 | mBuffer = NULL; |
223 | fileSize = 0; |
224 | } |
225 | |
226 | // ------------------------------------------------------------------------------------------------ |
227 | // Build unique vertices |
228 | void 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 |
263 | void 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 |
314 | void 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 |
354 | void 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 |
569 | void 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 |
681 | void 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 | |