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 MS3DLoader.cpp
44 * @brief Implementation of the Ms3D importer class.
45 * Written against http://chumbalum.swissquake.ch/ms3d/ms3dspec.txt
46 */
47
48
49#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
50
51// internal headers
52#include "MS3DLoader.h"
53#include "StreamReader.h"
54#include <assimp/DefaultLogger.hpp>
55#include <assimp/scene.h>
56#include <assimp/IOSystem.hpp>
57#include <assimp/importerdesc.h>
58#include <map>
59
60using namespace Assimp;
61
62static const aiImporterDesc desc = {
63 "Milkshape 3D Importer",
64 "",
65 "",
66 "http://chumbalum.swissquake.ch/",
67 aiImporterFlags_SupportBinaryFlavour,
68 0,
69 0,
70 0,
71 0,
72 "ms3d"
73};
74
75// ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
76// (enable old code path, which generates extra nodes per mesh while
77// the newer code uses aiMesh::mName to express the name of the
78// meshes (a.k.a. groups in MS3D))
79
80// ------------------------------------------------------------------------------------------------
81// Constructor to be privately used by Importer
82MS3DImporter::MS3DImporter()
83 : mScene()
84{}
85
86// ------------------------------------------------------------------------------------------------
87// Destructor, private as well
88MS3DImporter::~MS3DImporter()
89{}
90
91// ------------------------------------------------------------------------------------------------
92// Returns whether the class can handle the format of the given file.
93bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
94{
95 // first call - simple extension check
96 const std::string extension = GetExtension(pFile);
97 if (extension == "ms3d") {
98 return true;
99 }
100
101 // second call - check for magic identifiers
102 else if (!extension.length() || checkSig) {
103 if (!pIOHandler) {
104 return true;
105 }
106 const char* tokens[] = {"MS3D000000"};
107 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
108 }
109 return false;
110}
111
112// ------------------------------------------------------------------------------------------------
113const aiImporterDesc* MS3DImporter::GetInfo () const
114{
115 return &desc;
116}
117
118// ------------------------------------------------------------------------------------------------
119void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
120{
121 // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
122 stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
123}
124
125// ------------------------------------------------------------------------------------------------
126void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
127{
128 // See note in ReadColor()
129 stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
130}
131
132// ------------------------------------------------------------------------------------------------
133template<typename T>
134void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
135{
136 uint16_t cnt;
137 stream >> cnt;
138
139 for(unsigned int i = 0; i < cnt; ++i) {
140 uint32_t index, clength;
141 stream >> index >> clength;
142
143 if(index >= outp.size()) {
144 DefaultLogger::get()->warn("MS3D: Invalid index in comment section");
145 }
146 else if (clength > stream.GetRemainingSize()) {
147 throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
148 }
149 else {
150 outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
151 }
152 stream.IncPtr(clength);
153 }
154}
155
156// ------------------------------------------------------------------------------------------------
157template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
158{
159 return in > lower && in <= higher;
160}
161
162// ------------------------------------------------------------------------------------------------
163void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints,
164 std::vector<bool>& hadit,
165 aiNode* nd,
166 const aiMatrix4x4& absTrafo)
167{
168 unsigned int cnt = 0;
169 for(size_t i = 0; i < joints.size(); ++i) {
170 if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
171 ++cnt;
172 }
173 }
174
175 nd->mChildren = new aiNode*[nd->mNumChildren = cnt];
176 cnt = 0;
177 for(size_t i = 0; i < joints.size(); ++i) {
178 if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
179 aiNode* ch = nd->mChildren[cnt++] = new aiNode(joints[i].name);
180 ch->mParent = nd;
181
182 ch->mTransformation = aiMatrix4x4::Translation(joints[i].position,aiMatrix4x4()=aiMatrix4x4())*
183 // XXX actually, I don't *know* why we need the inverse here. Probably column vs. row order?
184 aiMatrix4x4().FromEulerAnglesXYZ(joints[i].rotation).Transpose();
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// ------------------------------------------------------------------------------------------------
205void 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.
215void 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 // DefaultLogger::get()->debug(j.name);
352 // DefaultLogger::get()->debug(j.parentName);
353
354 ReadVector(stream,j.rotation);
355 ReadVector(stream,j.position);
356
357 j.rotFrames.resize(stream.GetI2());
358 j.posFrames.resize(stream.GetI2());
359
360 for(unsigned int a = 0; a < j.rotFrames.size(); ++a) {
361 TempKeyFrame& kf = j.rotFrames[a];
362 stream >> kf.time;
363 ReadVector(stream,kf.value);
364 }
365 for(unsigned int a = 0; a < j.posFrames.size(); ++a) {
366 TempKeyFrame& kf = j.posFrames[a];
367 stream >> kf.time;
368 ReadVector(stream,kf.value);
369 }
370 }
371
372 if(stream.GetRemainingSize() > 4) {
373 uint32_t subversion;
374 stream >> subversion;
375 if (subversion == 1) {
376 ReadComments<TempGroup>(stream,groups);
377 ReadComments<TempMaterial>(stream,materials);
378 ReadComments<TempJoint>(stream,joints);
379
380 // model comment - print it for we have such a nice log.
381 if (stream.GetI4()) {
382 const size_t len = static_cast<size_t>(stream.GetI4());
383 if (len > stream.GetRemainingSize()) {
384 throw DeadlyImportError("MS3D: Model comment is too long");
385 }
386
387 const std::string& s = std::string(reinterpret_cast<char*>(stream.GetPtr()),len);
388 DefaultLogger::get()->debug("MS3D: Model comment: " + s);
389 }
390
391 if(stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
392 for(unsigned int i = 0; i < verts; ++i) {
393 TempVertex& v = vertices[i];
394 v.weights[3]=1.f;
395 for(unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
396 v.bone_id[n+1] = stream.GetI1();
397 v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
398 }
399 stream.IncPtr((subversion-1)<<2u);
400 }
401
402 // even further extra data is not of interest for us, at least now now.
403 }
404 }
405 }
406
407 // 2 ------------ convert to proper aiXX data structures -----------------------------------
408
409 if (need_default && materials.size()) {
410 DefaultLogger::get()->warn("MS3D: Found group with no material assigned, spawning default material");
411 // if one of the groups has no material assigned, but there are other
412 // groups with materials, a default material needs to be added (
413 // scenepreprocessor adds a default material only if nummat==0).
414 materials.push_back(TempMaterial());
415 TempMaterial& m = materials.back();
416
417 strcpy(m.name,"<MS3D_DefaultMat>");
418 m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
419 m.transparency = 1.f;
420 m.shininess = 0.f;
421
422 // this is because these TempXXX struct's have no c'tors.
423 m.texture[0] = m.alphamap[0] = '\0';
424
425 for (unsigned int i = 0; i < groups.size(); ++i) {
426 TempGroup& g = groups[i];
427 if (g.mat == UINT_MAX) {
428 g.mat = static_cast<unsigned int>(materials.size()-1);
429 }
430 }
431 }
432
433 // convert materials to our generic key-value dict-alike
434 if (materials.size()) {
435 pScene->mMaterials = new aiMaterial*[materials.size()];
436 for (size_t i = 0; i < materials.size(); ++i) {
437
438 aiMaterial* mo = new aiMaterial();
439 pScene->mMaterials[pScene->mNumMaterials++] = mo;
440
441 const TempMaterial& mi = materials[i];
442
443 aiString tmp;
444 if (0[mi.alphamap]) {
445 tmp = aiString(mi.alphamap);
446 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
447 }
448 if (0[mi.texture]) {
449 tmp = aiString(mi.texture);
450 mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
451 }
452 if (0[mi.name]) {
453 tmp = aiString(mi.name);
454 mo->AddProperty(&tmp,AI_MATKEY_NAME);
455 }
456
457 mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
458 mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
459 mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
460 mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
461
462 mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
463 mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
464
465 const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
466 mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
467 }
468 }
469
470 // convert groups to meshes
471 if (groups.empty()) {
472 throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
473 }
474
475 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
476 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
477
478 aiMesh* m = pScene->mMeshes[i] = new aiMesh();
479 const TempGroup& g = groups[i];
480
481 if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
482 throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
483 } // no error if no materials at all - scenepreprocessor adds one then
484
485 m->mMaterialIndex = g.mat;
486 m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
487
488 m->mFaces = new aiFace[m->mNumFaces = static_cast<unsigned int>(g.triangles.size())];
489 m->mNumVertices = m->mNumFaces*3;
490
491 // storage for vertices - verbose format, as requested by the postprocessing pipeline
492 m->mVertices = new aiVector3D[m->mNumVertices];
493 m->mNormals = new aiVector3D[m->mNumVertices];
494 m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
495 m->mNumUVComponents[0] = 2;
496
497 typedef std::map<unsigned int,unsigned int> BoneSet;
498 BoneSet mybones;
499
500 for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
501 aiFace& f = m->mFaces[i];
502 if (g.triangles[i]>triangles.size()) {
503 throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
504 }
505
506 TempTriangle& t = triangles[g.triangles[i]];
507 f.mIndices = new unsigned int[f.mNumIndices=3];
508
509 for (unsigned int i = 0; i < 3; ++i,++n) {
510 if (t.indices[i]>vertices.size()) {
511 throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
512 }
513
514 const TempVertex& v = vertices[t.indices[i]];
515 for(unsigned int a = 0; a < 4; ++a) {
516 if (v.bone_id[a] != UINT_MAX) {
517 if (v.bone_id[a] >= joints.size()) {
518 throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
519 }
520 if (mybones.find(v.bone_id[a]) == mybones.end()) {
521 mybones[v.bone_id[a]] = 1;
522 }
523 else ++mybones[v.bone_id[a]];
524 }
525 }
526
527 // collect vertex components
528 m->mVertices[n] = v.pos;
529
530 m->mNormals[n] = t.normals[i];
531 m->mTextureCoords[0][n] = aiVector3D(t.uv[i].x,1.f-t.uv[i].y,0.0);
532 f.mIndices[i] = n;
533 }
534 }
535
536 // allocate storage for bones
537 if(!mybones.empty()) {
538 std::vector<unsigned int> bmap(joints.size());
539 m->mBones = new aiBone*[mybones.size()]();
540 for(BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
541 aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
542 const TempJoint& jnt = joints[(*it).first];
543
544 bn->mName.Set(jnt.name);
545 bn->mWeights = new aiVertexWeight[(*it).second];
546
547 bmap[(*it).first] = m->mNumBones++;
548 }
549
550 // .. and collect bone weights
551 for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
552 TempTriangle& t = triangles[g.triangles[i]];
553
554 for (unsigned int i = 0; i < 3; ++i,++n) {
555 const TempVertex& v = vertices[t.indices[i]];
556 for(unsigned int a = 0; a < 4; ++a) {
557 const unsigned int bone = v.bone_id[a];
558 if(bone==UINT_MAX){
559 continue;
560 }
561
562 aiBone* const outbone = m->mBones[bmap[bone]];
563 aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
564
565 outwght.mVertexId = n;
566 outwght.mWeight = v.weights[a];
567 }
568 }
569 }
570 }
571 }
572
573 // ... add dummy nodes under a single root, each holding a reference to one
574 // mesh. If we didn't do this, we'd lose the group name.
575 aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
576
577#ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
578 rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
579
580 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
581 aiNode* nd = rt->mChildren[i] = new aiNode();
582
583 const TempGroup& g = groups[i];
584
585 // we need to generate an unique name for all mesh nodes.
586 // since we want to keep the group name, a prefix is
587 // prepended.
588 nd->mName = aiString("<MS3DMesh>_");
589 nd->mName.Append(g.name);
590 nd->mParent = rt;
591
592 nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
593 nd->mMeshes[0] = i;
594 }
595#else
596 rt->mMeshes = new unsigned int[pScene->mNumMeshes];
597 for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
598 rt->mMeshes[rt->mNumMeshes++] = i;
599 }
600#endif
601
602 // convert animations as well
603 if(joints.size()) {
604#ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
605 rt->mChildren = new aiNode*[1]();
606 rt->mNumChildren = 1;
607
608 aiNode* jt = rt->mChildren[0] = new aiNode();
609#else
610 aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
611#endif
612 jt->mParent = rt;
613 CollectChildJoints(joints,jt);
614 jt->mName.Set("<MS3DJointRoot>");
615
616 pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
617 aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
618
619 anim->mName.Set("<MS3DMasterAnim>");
620
621 // carry the fps info to the user by scaling all times with it
622 anim->mTicksPerSecond = animfps;
623
624 // leave duration at its default, so ScenePreprocessor will fill an appropriate
625 // value (the values taken from some MS3D files seem to be too unreliable
626 // to pass the validation)
627 // anim->mDuration = totalframes/animfps;
628
629 anim->mChannels = new aiNodeAnim*[joints.size()]();
630 for(std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
631 if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
632 continue;
633 }
634
635 aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
636 nd->mNodeName.Set((*it).name);
637
638 if ((*it).rotFrames.size()) {
639 nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
640 for(std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
641 aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
642
643 q.mTime = (*rot).time*animfps;
644
645 // XXX it seems our matrix&quaternion code has faults in its conversion routines --
646 // aiQuaternion(x,y,z) seems to besomething different as quat(matrix.fromeuler(x,y,z)).
647 q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)*
648 aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)).Transpose());
649 }
650 }
651
652 if ((*it).posFrames.size()) {
653 nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
654
655 aiQuatKey* qu = nd->mRotationKeys;
656 for(std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
657 aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
658
659 v.mTime = (*pos).time*animfps;
660 v.mValue = (*it).position + (*pos).value;
661 }
662 }
663 }
664 // fixup to pass the validation if not a single animation channel is non-trivial
665 if (!anim->mNumChannels) {
666 anim->mChannels = NULL;
667 }
668 }
669}
670
671#endif
672