1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2021, 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 static const char * const 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
219 auto file = pIOHandler->Open(pFile, "rb");
220 if (!file)
221 throw DeadlyImportError("MS3D: Could not open ", pFile);
222
223 StreamReaderLE stream(file);
224
225 // CanRead() should have done this already
226 char head[10];
227 int32_t version;
228
229 mScene = pScene;
230
231
232 // 1 ------------ read into temporary data structures mirroring the original file
233
234 stream.CopyAndAdvance(head,10);
235 stream >> version;
236 if (strncmp(head,"MS3D000000",10)) {
237 throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: ", pFile);
238 }
239
240 if (version != 4) {
241 throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
242 }
243
244 uint16_t verts;
245 stream >> verts;
246
247 std::vector<TempVertex> vertices(verts);
248 for (unsigned int i = 0; i < verts; ++i) {
249 TempVertex& v = vertices[i];
250
251 stream.IncPtr(1);
252 ReadVector(stream,v.pos);
253 v.bone_id[0] = stream.GetI1();
254 v.ref_cnt = stream.GetI1();
255
256 v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = UINT_MAX;
257 v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
258 v.weights[0] = 1.f;
259 }
260
261 uint16_t tris;
262 stream >> tris;
263
264 std::vector<TempTriangle> triangles(tris);
265 for (unsigned int i = 0;i < tris; ++i) {
266 TempTriangle& t = triangles[i];
267
268 stream.IncPtr(2);
269 for (unsigned int j = 0; j < 3; ++j) {
270 t.indices[j] = stream.GetI2();
271 }
272
273 for (unsigned int j = 0; j < 3; ++j) {
274 ReadVector(stream,t.normals[j]);
275 }
276
277 for (unsigned int j = 0; j < 3; ++j) {
278 stream >> (float&)(t.uv[j].x); // see note in ReadColor()
279 }
280 for (unsigned int j = 0; j < 3; ++j) {
281 stream >> (float&)(t.uv[j].y);
282 }
283
284 t.sg = stream.GetI1();
285 t.group = stream.GetI1();
286 }
287
288 uint16_t grp;
289 stream >> grp;
290
291 bool need_default = false;
292 std::vector<TempGroup> groups(grp);
293 for (unsigned int i = 0;i < grp; ++i) {
294 TempGroup& t = groups[i];
295
296 stream.IncPtr(1);
297 stream.CopyAndAdvance(t.name,32);
298
299 t.name[32] = '\0';
300 uint16_t num;
301 stream >> num;
302
303 t.triangles.resize(num);
304 for (unsigned int j = 0; j < num; ++j) {
305 t.triangles[j] = stream.GetI2();
306 }
307 t.mat = stream.GetI1();
308 if (t.mat == UINT_MAX) {
309 need_default = true;
310 }
311 }
312
313 uint16_t mat;
314 stream >> mat;
315
316 std::vector<TempMaterial> materials(mat);
317 for (unsigned int j = 0;j < mat; ++j) {
318 TempMaterial& t = materials[j];
319
320 stream.CopyAndAdvance(t.name,32);
321 t.name[32] = '\0';
322
323 ReadColor(stream,t.ambient);
324 ReadColor(stream,t.diffuse);
325 ReadColor(stream,t.specular);
326 ReadColor(stream,t.emissive);
327 stream >> t.shininess >> t.transparency;
328
329 stream.IncPtr(1);
330
331 stream.CopyAndAdvance(t.texture,128);
332 t.texture[128] = '\0';
333
334 stream.CopyAndAdvance(t.alphamap,128);
335 t.alphamap[128] = '\0';
336 }
337
338 float animfps, currenttime;
339 uint32_t totalframes;
340 stream >> animfps >> currenttime >> totalframes;
341
342 uint16_t joint;
343 stream >> joint;
344
345 std::vector<TempJoint> joints(joint);
346 for(unsigned int ii = 0; ii < joint; ++ii) {
347 TempJoint& j = joints[ii];
348
349 stream.IncPtr(1);
350 stream.CopyAndAdvance(j.name,32);
351 j.name[32] = '\0';
352
353 stream.CopyAndAdvance(j.parentName,32);
354 j.parentName[32] = '\0';
355
356 ReadVector(stream,j.rotation);
357 ReadVector(stream,j.position);
358
359 j.rotFrames.resize(stream.GetI2());
360 j.posFrames.resize(stream.GetI2());
361
362 for(unsigned int a = 0; a < j.rotFrames.size(); ++a) {
363 TempKeyFrame& kf = j.rotFrames[a];
364 stream >> kf.time;
365 ReadVector(stream,kf.value);
366 }
367 for(unsigned int a = 0; a < j.posFrames.size(); ++a) {
368 TempKeyFrame& kf = j.posFrames[a];
369 stream >> kf.time;
370 ReadVector(stream,kf.value);
371 }
372 }
373
374 if(stream.GetRemainingSize() > 4) {
375 uint32_t subversion;
376 stream >> subversion;
377 if (subversion == 1) {
378 ReadComments<TempGroup>(stream,groups);
379 ReadComments<TempMaterial>(stream,materials);
380 ReadComments<TempJoint>(stream,joints);
381
382 // model comment - print it for we have such a nice log.
383 if (stream.GetI4()) {
384 const size_t len = static_cast<size_t>(stream.GetI4());
385 if (len > stream.GetRemainingSize()) {
386 throw DeadlyImportError("MS3D: Model comment is too long");
387 }
388
389 const std::string& s = std::string(reinterpret_cast<char*>(stream.GetPtr()),len);
390 ASSIMP_LOG_DEBUG("MS3D: Model comment: ", s);
391 }
392
393 if(stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
394 for(unsigned int i = 0; i < verts; ++i) {
395 TempVertex& v = vertices[i];
396 v.weights[3]=1.f;
397 for(unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
398 v.bone_id[n+1] = stream.GetI1();
399 v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
400 }
401 stream.IncPtr((subversion-1)<<2u);
402 }
403
404 // even further extra data is not of interest for us, at least now now.
405 }
406 }
407 }
408
409 // 2 ------------ convert to proper aiXX data structures -----------------------------------
410
411 if (need_default && materials.size()) {
412 ASSIMP_LOG_WARN("MS3D: Found group with no material assigned, spawning default material");
413 // if one of the groups has no material assigned, but there are other
414 // groups with materials, a default material needs to be added (
415 // scenepreprocessor adds a default material only if nummat==0).
416 materials.push_back(TempMaterial());
417 TempMaterial& m = materials.back();
418
419 strcpy(m.name,"<MS3D_DefaultMat>");
420 m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
421 m.transparency = 1.f;
422 m.shininess = 0.f;
423
424 // this is because these TempXXX struct's have no c'tors.
425 m.texture[0] = m.alphamap[0] = '\0';
426
427 for (unsigned int i = 0; i < groups.size(); ++i) {
428 TempGroup& g = groups[i];
429 if (g.mat == UINT_MAX) {
430 g.mat = static_cast<unsigned int>(materials.size()-1);
431 }
432 }
433 }
434
435 // convert materials to our generic key-value dict-alike
436 if (materials.size()) {
437 pScene->mMaterials = new aiMaterial*[materials.size()];
438 for (size_t i = 0; i < materials.size(); ++i) {
439
440 aiMaterial* mo = new aiMaterial();
441 pScene->mMaterials[pScene->mNumMaterials++] = mo;
442
443 const TempMaterial& mi = materials[i];
444
445 aiString tmp;
446 if (0[mi.alphamap]) {
447 tmp = aiString(mi.alphamap);
448 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
449 }
450 if (0[mi.texture]) {
451 tmp = aiString(mi.texture);
452 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
453 }
454 if (0[mi.name]) {
455 tmp = aiString(mi.name);
456 mo->AddProperty(&tmp,AI_MATKEY_NAME);
457 }
458
459 mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
460 mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
461 mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
462 mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
463
464 mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
465 mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
466
467 const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
468 mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
469 }
470 }
471
472 // convert groups to meshes
473 if (groups.empty()) {
474 throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
475 }
476
477 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
478 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
479
480 aiMesh* m = pScene->mMeshes[i] = new aiMesh();
481 const TempGroup& g = groups[i];
482
483 if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
484 throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
485 } // no error if no materials at all - scenepreprocessor adds one then
486
487 m->mMaterialIndex = g.mat;
488 m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
489
490 m->mFaces = new aiFace[m->mNumFaces = static_cast<unsigned int>(g.triangles.size())];
491 m->mNumVertices = m->mNumFaces*3;
492
493 // storage for vertices - verbose format, as requested by the postprocessing pipeline
494 m->mVertices = new aiVector3D[m->mNumVertices];
495 m->mNormals = new aiVector3D[m->mNumVertices];
496 m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
497 m->mNumUVComponents[0] = 2;
498
499 typedef std::map<unsigned int,unsigned int> BoneSet;
500 BoneSet mybones;
501
502 for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
503 aiFace& f = m->mFaces[j];
504 if (g.triangles[j]>triangles.size()) {
505 throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
506 }
507
508 TempTriangle& t = triangles[g.triangles[j]];
509 f.mIndices = new unsigned int[f.mNumIndices=3];
510
511 for (unsigned int k = 0; k < 3; ++k,++n) {
512 if (t.indices[k]>vertices.size()) {
513 throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
514 }
515
516 const TempVertex& v = vertices[t.indices[k]];
517 for(unsigned int a = 0; a < 4; ++a) {
518 if (v.bone_id[a] != UINT_MAX) {
519 if (v.bone_id[a] >= joints.size()) {
520 throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
521 }
522 if (mybones.find(v.bone_id[a]) == mybones.end()) {
523 mybones[v.bone_id[a]] = 1;
524 }
525 else ++mybones[v.bone_id[a]];
526 }
527 }
528
529 // collect vertex components
530 m->mVertices[n] = v.pos;
531
532 m->mNormals[n] = t.normals[k];
533 m->mTextureCoords[0][n] = aiVector3D(t.uv[k].x,1.f-t.uv[k].y,0.0);
534 f.mIndices[k] = n;
535 }
536 }
537
538 // allocate storage for bones
539 if(!mybones.empty()) {
540 std::vector<unsigned int> bmap(joints.size());
541 m->mBones = new aiBone*[mybones.size()]();
542 for(BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
543 aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
544 const TempJoint& jnt = joints[(*it).first];
545
546 bn->mName.Set(jnt.name);
547 bn->mWeights = new aiVertexWeight[(*it).second];
548
549 bmap[(*it).first] = m->mNumBones++;
550 }
551
552 // .. and collect bone weights
553 for (unsigned int j = 0,n = 0; j < m->mNumFaces; ++j) {
554 TempTriangle& t = triangles[g.triangles[j]];
555
556 for (unsigned int k = 0; k < 3; ++k,++n) {
557 const TempVertex& v = vertices[t.indices[k]];
558 for(unsigned int a = 0; a < 4; ++a) {
559 const unsigned int bone = v.bone_id[a];
560 if(bone==UINT_MAX){
561 continue;
562 }
563
564 aiBone* const outbone = m->mBones[bmap[bone]];
565 aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
566
567 outwght.mVertexId = n;
568 outwght.mWeight = v.weights[a];
569 }
570 }
571 }
572 }
573 }
574
575 // ... add dummy nodes under a single root, each holding a reference to one
576 // mesh. If we didn't do this, we'd lose the group name.
577 aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
578
579 #ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
580 rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
581
582 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
583 aiNode* nd = rt->mChildren[i] = new aiNode();
584
585 const TempGroup& g = groups[i];
586
587 // we need to generate an unique name for all mesh nodes.
588 // since we want to keep the group name, a prefix is
589 // prepended.
590 nd->mName = aiString("<MS3DMesh>_");
591 nd->mName.Append(g.name);
592 nd->mParent = rt;
593
594 nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
595 nd->mMeshes[0] = i;
596 }
597 #else
598 rt->mMeshes = new unsigned int[pScene->mNumMeshes];
599 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
600 rt->mMeshes[rt->mNumMeshes++] = i;
601 }
602 #endif
603
604 // convert animations as well
605 if(joints.size()) {
606 #ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
607 rt->mChildren = new aiNode*[1]();
608 rt->mNumChildren = 1;
609
610 aiNode* jt = rt->mChildren[0] = new aiNode();
611 #else
612 aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
613 #endif
614 jt->mParent = rt;
615 CollectChildJoints(joints,jt);
616 jt->mName.Set("<MS3DJointRoot>");
617
618 pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
619 aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
620
621 anim->mName.Set("<MS3DMasterAnim>");
622
623 // carry the fps info to the user by scaling all times with it
624 anim->mTicksPerSecond = animfps;
625
626 // leave duration at its default, so ScenePreprocessor will fill an appropriate
627 // value (the values taken from some MS3D files seem to be too unreliable
628 // to pass the validation)
629 // anim->mDuration = totalframes/animfps;
630
631 anim->mChannels = new aiNodeAnim*[joints.size()]();
632 for(std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
633 if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
634 continue;
635 }
636
637 aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
638 nd->mNodeName.Set((*it).name);
639
640 if ((*it).rotFrames.size()) {
641 nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
642 for(std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
643 aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
644
645 q.mTime = (*rot).time*animfps;
646 q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)*
647 aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)));
648 }
649 }
650
651 if ((*it).posFrames.size()) {
652 nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
653
654 aiQuatKey* qu = nd->mRotationKeys;
655 for(std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
656 aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
657
658 v.mTime = (*pos).time*animfps;
659 v.mValue = (*it).position + (*pos).value;
660 }
661 }
662 }
663 // fixup to pass the validation if not a single animation channel is non-trivial
664 if (!anim->mNumChannels) {
665 anim->mChannels = nullptr;
666 }
667 }
668 }
669
670 #endif
671