1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, assimp team
7
8
9
10 All rights reserved.
11
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15
16 * Redistributions of source code must retain the above
17 copyright notice, this list of conditions and the
18 following disclaimer.
19
20 * Redistributions in binary form must reproduce the above
21 copyright notice, this list of conditions and the
22 following disclaimer in the documentation and/or other
23 materials provided with the distribution.
24
25 * Neither the name of the assimp team, nor the names of its
26 contributors may be used to endorse or promote products
27 derived from this software without specific prior
28 written permission of the assimp team.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43
44 /** @file MS3DLoader.cpp
45 * @brief Implementation of the Ms3D importer class.
46 * Written against http://chumbalum.swissquake.ch/ms3d/ms3dspec.txt
47 */
48
49
50 #ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
51
52 // internal headers
53 #include "MS3DLoader.h"
54 #include <assimp/StreamReader.h>
55 #include <assimp/DefaultLogger.hpp>
56 #include <assimp/scene.h>
57 #include <assimp/IOSystem.hpp>
58 #include <assimp/importerdesc.h>
59 #include <map>
60
61 using namespace Assimp;
62
63 static const aiImporterDesc desc = {
64 "Milkshape 3D Importer",
65 "",
66 "",
67 "http://chumbalum.swissquake.ch/",
68 aiImporterFlags_SupportBinaryFlavour,
69 0,
70 0,
71 0,
72 0,
73 "ms3d"
74 };
75
76 // ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
77 // (enable old code path, which generates extra nodes per mesh while
78 // the newer code uses aiMesh::mName to express the name of the
79 // meshes (a.k.a. groups in MS3D))
80
81 // ------------------------------------------------------------------------------------------------
82 // Constructor to be privately used by Importer
MS3DImporter()83 MS3DImporter::MS3DImporter()
84 : mScene()
85 {}
86
87 // ------------------------------------------------------------------------------------------------
88 // Destructor, private as well
~MS3DImporter()89 MS3DImporter::~MS3DImporter()
90 {}
91
92 // ------------------------------------------------------------------------------------------------
93 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const94 bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
95 {
96 // first call - simple extension check
97 const std::string extension = GetExtension(pFile);
98 if (extension == "ms3d") {
99 return true;
100 }
101
102 // second call - check for magic identifiers
103 else if (!extension.length() || checkSig) {
104 if (!pIOHandler) {
105 return true;
106 }
107 const char* tokens[] = {"MS3D000000"};
108 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
109 }
110 return false;
111 }
112
113 // ------------------------------------------------------------------------------------------------
GetInfo() const114 const aiImporterDesc* MS3DImporter::GetInfo () const
115 {
116 return &desc;
117 }
118
119 // ------------------------------------------------------------------------------------------------
ReadColor(StreamReaderLE & stream,aiColor4D & ambient)120 void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
121 {
122 // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
123 stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
124 }
125
126 // ------------------------------------------------------------------------------------------------
ReadVector(StreamReaderLE & stream,aiVector3D & pos)127 void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
128 {
129 // See note in ReadColor()
130 stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
131 }
132
133 // ------------------------------------------------------------------------------------------------
134 template<typename T>
ReadComments(StreamReaderLE & stream,std::vector<T> & outp)135 void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
136 {
137 uint16_t cnt;
138 stream >> cnt;
139
140 for(unsigned int i = 0; i < cnt; ++i) {
141 uint32_t index, clength;
142 stream >> index >> clength;
143
144 if(index >= outp.size()) {
145 ASSIMP_LOG_WARN("MS3D: Invalid index in comment section");
146 }
147 else if (clength > stream.GetRemainingSize()) {
148 throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
149 }
150 else {
151 outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
152 }
153 stream.IncPtr(clength);
154 }
155 }
156
157 // ------------------------------------------------------------------------------------------------
inrange(const T & in,const T2 & lower,const T3 & higher)158 template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
159 {
160 return in > lower && in <= higher;
161 }
162
163 // ------------------------------------------------------------------------------------------------
CollectChildJoints(const std::vector<TempJoint> & joints,std::vector<bool> & hadit,aiNode * nd,const aiMatrix4x4 & absTrafo)164 void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints,
165 std::vector<bool>& hadit,
166 aiNode* nd,
167 const aiMatrix4x4& absTrafo)
168 {
169 unsigned int cnt = 0;
170 for(size_t i = 0; i < joints.size(); ++i) {
171 if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
172 ++cnt;
173 }
174 }
175
176 nd->mChildren = new aiNode*[nd->mNumChildren = cnt];
177 cnt = 0;
178 for(size_t i = 0; i < joints.size(); ++i) {
179 if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
180 aiNode* ch = nd->mChildren[cnt++] = new aiNode(joints[i].name);
181 ch->mParent = nd;
182
183 ch->mTransformation = aiMatrix4x4::Translation(joints[i].position,aiMatrix4x4()=aiMatrix4x4())*
184 aiMatrix4x4().FromEulerAnglesXYZ(joints[i].rotation);
185
186 const aiMatrix4x4 abs = absTrafo*ch->mTransformation;
187 for(unsigned int a = 0; a < mScene->mNumMeshes; ++a) {
188 aiMesh* const msh = mScene->mMeshes[a];
189 for(unsigned int n = 0; n < msh->mNumBones; ++n) {
190 aiBone* const bone = msh->mBones[n];
191
192 if(bone->mName == ch->mName) {
193 bone->mOffsetMatrix = aiMatrix4x4(abs).Inverse();
194 }
195 }
196 }
197
198 hadit[i] = true;
199 CollectChildJoints(joints,hadit,ch,abs);
200 }
201 }
202 }
203
204 // ------------------------------------------------------------------------------------------------
CollectChildJoints(const std::vector<TempJoint> & joints,aiNode * nd)205 void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd)
206 {
207 std::vector<bool> hadit(joints.size(),false);
208 aiMatrix4x4 trafo;
209
210 CollectChildJoints(joints,hadit,nd,trafo);
211 }
212
213 // ------------------------------------------------------------------------------------------------
214 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)215 void MS3DImporter::InternReadFile( const std::string& pFile,
216 aiScene* pScene, IOSystem* pIOHandler)
217 {
218 StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
219
220 // CanRead() should have done this already
221 char head[10];
222 int32_t version;
223
224 mScene = pScene;
225
226
227 // 1 ------------ read into temporary data structures mirroring the original file
228
229 stream.CopyAndAdvance(head,10);
230 stream >> version;
231 if (strncmp(head,"MS3D000000",10)) {
232 throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: "+pFile);
233 }
234
235 if (version != 4) {
236 throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
237 }
238
239 uint16_t verts;
240 stream >> verts;
241
242 std::vector<TempVertex> vertices(verts);
243 for (unsigned int i = 0; i < verts; ++i) {
244 TempVertex& v = vertices[i];
245
246 stream.IncPtr(1);
247 ReadVector(stream,v.pos);
248 v.bone_id[0] = stream.GetI1();
249 v.ref_cnt = stream.GetI1();
250
251 v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = UINT_MAX;
252 v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
253 v.weights[0] = 1.f;
254 }
255
256 uint16_t tris;
257 stream >> tris;
258
259 std::vector<TempTriangle> triangles(tris);
260 for (unsigned int i = 0;i < tris; ++i) {
261 TempTriangle& t = triangles[i];
262
263 stream.IncPtr(2);
264 for (unsigned int i = 0; i < 3; ++i) {
265 t.indices[i] = stream.GetI2();
266 }
267
268 for (unsigned int i = 0; i < 3; ++i) {
269 ReadVector(stream,t.normals[i]);
270 }
271
272 for (unsigned int i = 0; i < 3; ++i) {
273 stream >> (float&)(t.uv[i].x); // see note in ReadColor()
274 }
275 for (unsigned int i = 0; i < 3; ++i) {
276 stream >> (float&)(t.uv[i].y);
277 }
278
279 t.sg = stream.GetI1();
280 t.group = stream.GetI1();
281 }
282
283 uint16_t grp;
284 stream >> grp;
285
286 bool need_default = false;
287 std::vector<TempGroup> groups(grp);
288 for (unsigned int i = 0;i < grp; ++i) {
289 TempGroup& t = groups[i];
290
291 stream.IncPtr(1);
292 stream.CopyAndAdvance(t.name,32);
293
294 t.name[32] = '\0';
295 uint16_t num;
296 stream >> num;
297
298 t.triangles.resize(num);
299 for (unsigned int i = 0; i < num; ++i) {
300 t.triangles[i] = stream.GetI2();
301 }
302 t.mat = stream.GetI1();
303 if (t.mat == UINT_MAX) {
304 need_default = true;
305 }
306 }
307
308 uint16_t mat;
309 stream >> mat;
310
311 std::vector<TempMaterial> materials(mat);
312 for (unsigned int i = 0;i < mat; ++i) {
313 TempMaterial& t = materials[i];
314
315 stream.CopyAndAdvance(t.name,32);
316 t.name[32] = '\0';
317
318 ReadColor(stream,t.ambient);
319 ReadColor(stream,t.diffuse);
320 ReadColor(stream,t.specular);
321 ReadColor(stream,t.emissive);
322 stream >> t.shininess >> t.transparency;
323
324 stream.IncPtr(1);
325
326 stream.CopyAndAdvance(t.texture,128);
327 t.texture[128] = '\0';
328
329 stream.CopyAndAdvance(t.alphamap,128);
330 t.alphamap[128] = '\0';
331 }
332
333 float animfps, currenttime;
334 uint32_t totalframes;
335 stream >> animfps >> currenttime >> totalframes;
336
337 uint16_t joint;
338 stream >> joint;
339
340 std::vector<TempJoint> joints(joint);
341 for(unsigned int i = 0; i < joint; ++i) {
342 TempJoint& j = joints[i];
343
344 stream.IncPtr(1);
345 stream.CopyAndAdvance(j.name,32);
346 j.name[32] = '\0';
347
348 stream.CopyAndAdvance(j.parentName,32);
349 j.parentName[32] = '\0';
350
351 ReadVector(stream,j.rotation);
352 ReadVector(stream,j.position);
353
354 j.rotFrames.resize(stream.GetI2());
355 j.posFrames.resize(stream.GetI2());
356
357 for(unsigned int a = 0; a < j.rotFrames.size(); ++a) {
358 TempKeyFrame& kf = j.rotFrames[a];
359 stream >> kf.time;
360 ReadVector(stream,kf.value);
361 }
362 for(unsigned int a = 0; a < j.posFrames.size(); ++a) {
363 TempKeyFrame& kf = j.posFrames[a];
364 stream >> kf.time;
365 ReadVector(stream,kf.value);
366 }
367 }
368
369 if(stream.GetRemainingSize() > 4) {
370 uint32_t subversion;
371 stream >> subversion;
372 if (subversion == 1) {
373 ReadComments<TempGroup>(stream,groups);
374 ReadComments<TempMaterial>(stream,materials);
375 ReadComments<TempJoint>(stream,joints);
376
377 // model comment - print it for we have such a nice log.
378 if (stream.GetI4()) {
379 const size_t len = static_cast<size_t>(stream.GetI4());
380 if (len > stream.GetRemainingSize()) {
381 throw DeadlyImportError("MS3D: Model comment is too long");
382 }
383
384 const std::string& s = std::string(reinterpret_cast<char*>(stream.GetPtr()),len);
385 ASSIMP_LOG_DEBUG_F("MS3D: Model comment: ", s);
386 }
387
388 if(stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
389 for(unsigned int i = 0; i < verts; ++i) {
390 TempVertex& v = vertices[i];
391 v.weights[3]=1.f;
392 for(unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
393 v.bone_id[n+1] = stream.GetI1();
394 v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
395 }
396 stream.IncPtr((subversion-1)<<2u);
397 }
398
399 // even further extra data is not of interest for us, at least now now.
400 }
401 }
402 }
403
404 // 2 ------------ convert to proper aiXX data structures -----------------------------------
405
406 if (need_default && materials.size()) {
407 ASSIMP_LOG_WARN("MS3D: Found group with no material assigned, spawning default material");
408 // if one of the groups has no material assigned, but there are other
409 // groups with materials, a default material needs to be added (
410 // scenepreprocessor adds a default material only if nummat==0).
411 materials.push_back(TempMaterial());
412 TempMaterial& m = materials.back();
413
414 strcpy(m.name,"<MS3D_DefaultMat>");
415 m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
416 m.transparency = 1.f;
417 m.shininess = 0.f;
418
419 // this is because these TempXXX struct's have no c'tors.
420 m.texture[0] = m.alphamap[0] = '\0';
421
422 for (unsigned int i = 0; i < groups.size(); ++i) {
423 TempGroup& g = groups[i];
424 if (g.mat == UINT_MAX) {
425 g.mat = static_cast<unsigned int>(materials.size()-1);
426 }
427 }
428 }
429
430 // convert materials to our generic key-value dict-alike
431 if (materials.size()) {
432 pScene->mMaterials = new aiMaterial*[materials.size()];
433 for (size_t i = 0; i < materials.size(); ++i) {
434
435 aiMaterial* mo = new aiMaterial();
436 pScene->mMaterials[pScene->mNumMaterials++] = mo;
437
438 const TempMaterial& mi = materials[i];
439
440 aiString tmp;
441 if (0[mi.alphamap]) {
442 tmp = aiString(mi.alphamap);
443 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
444 }
445 if (0[mi.texture]) {
446 tmp = aiString(mi.texture);
447 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
448 }
449 if (0[mi.name]) {
450 tmp = aiString(mi.name);
451 mo->AddProperty(&tmp,AI_MATKEY_NAME);
452 }
453
454 mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
455 mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
456 mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
457 mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
458
459 mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
460 mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
461
462 const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
463 mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
464 }
465 }
466
467 // convert groups to meshes
468 if (groups.empty()) {
469 throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
470 }
471
472 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
473 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
474
475 aiMesh* m = pScene->mMeshes[i] = new aiMesh();
476 const TempGroup& g = groups[i];
477
478 if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
479 throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
480 } // no error if no materials at all - scenepreprocessor adds one then
481
482 m->mMaterialIndex = g.mat;
483 m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
484
485 m->mFaces = new aiFace[m->mNumFaces = static_cast<unsigned int>(g.triangles.size())];
486 m->mNumVertices = m->mNumFaces*3;
487
488 // storage for vertices - verbose format, as requested by the postprocessing pipeline
489 m->mVertices = new aiVector3D[m->mNumVertices];
490 m->mNormals = new aiVector3D[m->mNumVertices];
491 m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
492 m->mNumUVComponents[0] = 2;
493
494 typedef std::map<unsigned int,unsigned int> BoneSet;
495 BoneSet mybones;
496
497 for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
498 aiFace& f = m->mFaces[i];
499 if (g.triangles[i]>triangles.size()) {
500 throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
501 }
502
503 TempTriangle& t = triangles[g.triangles[i]];
504 f.mIndices = new unsigned int[f.mNumIndices=3];
505
506 for (unsigned int i = 0; i < 3; ++i,++n) {
507 if (t.indices[i]>vertices.size()) {
508 throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
509 }
510
511 const TempVertex& v = vertices[t.indices[i]];
512 for(unsigned int a = 0; a < 4; ++a) {
513 if (v.bone_id[a] != UINT_MAX) {
514 if (v.bone_id[a] >= joints.size()) {
515 throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
516 }
517 if (mybones.find(v.bone_id[a]) == mybones.end()) {
518 mybones[v.bone_id[a]] = 1;
519 }
520 else ++mybones[v.bone_id[a]];
521 }
522 }
523
524 // collect vertex components
525 m->mVertices[n] = v.pos;
526
527 m->mNormals[n] = t.normals[i];
528 m->mTextureCoords[0][n] = aiVector3D(t.uv[i].x,1.f-t.uv[i].y,0.0);
529 f.mIndices[i] = n;
530 }
531 }
532
533 // allocate storage for bones
534 if(!mybones.empty()) {
535 std::vector<unsigned int> bmap(joints.size());
536 m->mBones = new aiBone*[mybones.size()]();
537 for(BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
538 aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
539 const TempJoint& jnt = joints[(*it).first];
540
541 bn->mName.Set(jnt.name);
542 bn->mWeights = new aiVertexWeight[(*it).second];
543
544 bmap[(*it).first] = m->mNumBones++;
545 }
546
547 // .. and collect bone weights
548 for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
549 TempTriangle& t = triangles[g.triangles[i]];
550
551 for (unsigned int i = 0; i < 3; ++i,++n) {
552 const TempVertex& v = vertices[t.indices[i]];
553 for(unsigned int a = 0; a < 4; ++a) {
554 const unsigned int bone = v.bone_id[a];
555 if(bone==UINT_MAX){
556 continue;
557 }
558
559 aiBone* const outbone = m->mBones[bmap[bone]];
560 aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
561
562 outwght.mVertexId = n;
563 outwght.mWeight = v.weights[a];
564 }
565 }
566 }
567 }
568 }
569
570 // ... add dummy nodes under a single root, each holding a reference to one
571 // mesh. If we didn't do this, we'd lose the group name.
572 aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
573
574 #ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
575 rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
576
577 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
578 aiNode* nd = rt->mChildren[i] = new aiNode();
579
580 const TempGroup& g = groups[i];
581
582 // we need to generate an unique name for all mesh nodes.
583 // since we want to keep the group name, a prefix is
584 // prepended.
585 nd->mName = aiString("<MS3DMesh>_");
586 nd->mName.Append(g.name);
587 nd->mParent = rt;
588
589 nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
590 nd->mMeshes[0] = i;
591 }
592 #else
593 rt->mMeshes = new unsigned int[pScene->mNumMeshes];
594 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
595 rt->mMeshes[rt->mNumMeshes++] = i;
596 }
597 #endif
598
599 // convert animations as well
600 if(joints.size()) {
601 #ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
602 rt->mChildren = new aiNode*[1]();
603 rt->mNumChildren = 1;
604
605 aiNode* jt = rt->mChildren[0] = new aiNode();
606 #else
607 aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
608 #endif
609 jt->mParent = rt;
610 CollectChildJoints(joints,jt);
611 jt->mName.Set("<MS3DJointRoot>");
612
613 pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
614 aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
615
616 anim->mName.Set("<MS3DMasterAnim>");
617
618 // carry the fps info to the user by scaling all times with it
619 anim->mTicksPerSecond = animfps;
620
621 // leave duration at its default, so ScenePreprocessor will fill an appropriate
622 // value (the values taken from some MS3D files seem to be too unreliable
623 // to pass the validation)
624 // anim->mDuration = totalframes/animfps;
625
626 anim->mChannels = new aiNodeAnim*[joints.size()]();
627 for(std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
628 if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
629 continue;
630 }
631
632 aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
633 nd->mNodeName.Set((*it).name);
634
635 if ((*it).rotFrames.size()) {
636 nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
637 for(std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
638 aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
639
640 q.mTime = (*rot).time*animfps;
641 q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)*
642 aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)));
643 }
644 }
645
646 if ((*it).posFrames.size()) {
647 nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
648
649 aiQuatKey* qu = nd->mRotationKeys;
650 for(std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
651 aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
652
653 v.mTime = (*pos).time*animfps;
654 v.mValue = (*it).position + (*pos).value;
655 }
656 }
657 }
658 // fixup to pass the validation if not a single animation channel is non-trivial
659 if (!anim->mNumChannels) {
660 anim->mChannels = NULL;
661 }
662 }
663 }
664
665 #endif
666