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 Implementation of the 3ds importer class */
44
45
46#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
47
48// internal headers
49#include "3DSLoader.h"
50#include "TargetAnimation.h"
51#include <assimp/scene.h>
52#include <assimp/DefaultLogger.hpp>
53#include "StringComparison.h"
54#include <memory>
55#include <cctype>
56
57using namespace Assimp;
58
59static const unsigned int NotSet = 0xcdcdcdcd;
60
61// ------------------------------------------------------------------------------------------------
62// Setup final material indices, generae a default material if necessary
63void Discreet3DSImporter::ReplaceDefaultMaterial()
64{
65 // Try to find an existing material that matches the
66 // typical default material setting:
67 // - no textures
68 // - diffuse color (in grey!)
69 // NOTE: This is here to workaround the fact that some
70 // exporters are writing a default material, too.
71 unsigned int idx( NotSet );
72 for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
73 {
74 std::string s = mScene->mMaterials[i].mName;
75 for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
76 *it = static_cast< char >( ::tolower( *it ) );
77 }
78
79 if (std::string::npos == s.find("default"))continue;
80
81 if (mScene->mMaterials[i].mDiffuse.r !=
82 mScene->mMaterials[i].mDiffuse.g ||
83 mScene->mMaterials[i].mDiffuse.r !=
84 mScene->mMaterials[i].mDiffuse.b)continue;
85
86 if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
87 mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
88 mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
89 mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
90 mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
91 mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
92 {
93 continue;
94 }
95 idx = i;
96 }
97 if ( NotSet == idx ) {
98 idx = ( unsigned int )mScene->mMaterials.size();
99 }
100
101 // now iterate through all meshes and through all faces and
102 // find all faces that are using the default material
103 unsigned int cnt = 0;
104 for (std::vector<D3DS::Mesh>::iterator
105 i = mScene->mMeshes.begin();
106 i != mScene->mMeshes.end();++i)
107 {
108 for (std::vector<unsigned int>::iterator
109 a = (*i).mFaceMaterials.begin();
110 a != (*i).mFaceMaterials.end();++a)
111 {
112 // NOTE: The additional check seems to be necessary,
113 // some exporters seem to generate invalid data here
114 if (0xcdcdcdcd == (*a))
115 {
116 (*a) = idx;
117 ++cnt;
118 }
119 else if ( (*a) >= mScene->mMaterials.size())
120 {
121 (*a) = idx;
122 DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material");
123 ++cnt;
124 }
125 }
126 }
127 if (cnt && idx == mScene->mMaterials.size())
128 {
129 // We need to create our own default material
130 D3DS::Material sMat;
131 sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
132 sMat.mName = "%%%DEFAULT";
133 mScene->mMaterials.push_back(sMat);
134
135 DefaultLogger::get()->info("3DS: Generating default material");
136 }
137}
138
139// ------------------------------------------------------------------------------------------------
140// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
141void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
142{
143 for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
144 {
145 // check whether all indices are in range
146 for (unsigned int a = 0; a < 3;++a)
147 {
148 if ((*i).mIndices[a] >= sMesh.mPositions.size())
149 {
150 DefaultLogger::get()->warn("3DS: Vertex index overflow)");
151 (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
152 }
153 if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
154 {
155 DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)");
156 (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
157 }
158 }
159 }
160}
161
162// ------------------------------------------------------------------------------------------------
163// Generate out unique verbose format representation
164void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
165{
166 // TODO: really necessary? I don't think. Just a waste of memory and time
167 // to do it now in a separate buffer.
168
169 // Allocate output storage
170 std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
171 std::vector<aiVector3D> vNew2;
172 if (sMesh.mTexCoords.size())
173 vNew2.resize(sMesh.mFaces.size() * 3);
174
175 for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
176 {
177 D3DS::Face& face = sMesh.mFaces[i];
178
179 // Positions
180 for (unsigned int a = 0; a < 3;++a,++base)
181 {
182 vNew[base] = sMesh.mPositions[face.mIndices[a]];
183 if (sMesh.mTexCoords.size())
184 vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
185
186 face.mIndices[a] = base;
187 }
188 }
189 sMesh.mPositions = vNew;
190 sMesh.mTexCoords = vNew2;
191}
192
193// ------------------------------------------------------------------------------------------------
194// Convert a 3DS texture to texture keys in an aiMaterial
195void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
196{
197 // Setup the texture name
198 aiString tex;
199 tex.Set( texture.mMapName);
200 mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
201
202 // Setup the texture blend factor
203 if (is_not_qnan(texture.mTextureBlend))
204 mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
205
206 // Setup the texture mapping mode
207 mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
208 mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
209
210 // Mirroring - double the scaling values
211 // FIXME: this is not really correct ...
212 if (texture.mMapMode == aiTextureMapMode_Mirror)
213 {
214 texture.mScaleU *= 2.0;
215 texture.mScaleV *= 2.0;
216 texture.mOffsetU /= 2.0;
217 texture.mOffsetV /= 2.0;
218 }
219
220 // Setup texture UV transformations
221 mat.AddProperty<ai_real>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
222}
223
224// ------------------------------------------------------------------------------------------------
225// Convert a 3DS material to an aiMaterial
226void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
227 aiMaterial& mat)
228{
229 // NOTE: Pass the background image to the viewer by bypassing the
230 // material system. This is an evil hack, never do it again!
231 if (0 != mBackgroundImage.length() && bHasBG)
232 {
233 aiString tex;
234 tex.Set( mBackgroundImage);
235 mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
236
237 // Be sure this is only done for the first material
238 mBackgroundImage = std::string("");
239 }
240
241 // At first add the base ambient color of the scene to the material
242 oldMat.mAmbient.r += mClrAmbient.r;
243 oldMat.mAmbient.g += mClrAmbient.g;
244 oldMat.mAmbient.b += mClrAmbient.b;
245
246 aiString name;
247 name.Set( oldMat.mName);
248 mat.AddProperty( &name, AI_MATKEY_NAME);
249
250 // Material colors
251 mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
252 mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
253 mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
254 mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
255
256 // Phong shininess and shininess strength
257 if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
258 D3DS::Discreet3DS::Metal == oldMat.mShading)
259 {
260 if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
261 {
262 oldMat.mShading = D3DS::Discreet3DS::Gouraud;
263 }
264 else
265 {
266 mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
267 mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
268 }
269 }
270
271 // Opacity
272 mat.AddProperty<ai_real>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
273
274 // Bump height scaling
275 mat.AddProperty<ai_real>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
276
277 // Two sided rendering?
278 if (oldMat.mTwoSided)
279 {
280 int i = 1;
281 mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
282 }
283
284 // Shading mode
285 aiShadingMode eShading = aiShadingMode_NoShading;
286 switch (oldMat.mShading)
287 {
288 case D3DS::Discreet3DS::Flat:
289 eShading = aiShadingMode_Flat; break;
290
291 // I don't know what "Wire" shading should be,
292 // assume it is simple lambertian diffuse shading
293 case D3DS::Discreet3DS::Wire:
294 {
295 // Set the wireframe flag
296 unsigned int iWire = 1;
297 mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
298 }
299
300 case D3DS::Discreet3DS::Gouraud:
301 eShading = aiShadingMode_Gouraud; break;
302
303 // assume cook-torrance shading for metals.
304 case D3DS::Discreet3DS::Phong :
305 eShading = aiShadingMode_Phong; break;
306
307 case D3DS::Discreet3DS::Metal :
308 eShading = aiShadingMode_CookTorrance; break;
309
310 // FIX to workaround a warning with GCC 4 who complained
311 // about a missing case Blinn: here - Blinn isn't a valid
312 // value in the 3DS Loader, it is just needed for ASE
313 case D3DS::Discreet3DS::Blinn :
314 eShading = aiShadingMode_Blinn; break;
315 }
316 mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
317
318 // DIFFUSE texture
319 if( oldMat.sTexDiffuse.mMapName.length() > 0)
320 CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
321
322 // SPECULAR texture
323 if( oldMat.sTexSpecular.mMapName.length() > 0)
324 CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
325
326 // OPACITY texture
327 if( oldMat.sTexOpacity.mMapName.length() > 0)
328 CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
329
330 // EMISSIVE texture
331 if( oldMat.sTexEmissive.mMapName.length() > 0)
332 CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
333
334 // BUMP texture
335 if( oldMat.sTexBump.mMapName.length() > 0)
336 CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
337
338 // SHININESS texture
339 if( oldMat.sTexShininess.mMapName.length() > 0)
340 CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
341
342 // REFLECTION texture
343 if( oldMat.sTexReflective.mMapName.length() > 0)
344 CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
345
346 // Store the name of the material itself, too
347 if( oldMat.mName.length()) {
348 aiString tex;
349 tex.Set( oldMat.mName);
350 mat.AddProperty( &tex, AI_MATKEY_NAME);
351 }
352}
353
354// ------------------------------------------------------------------------------------------------
355// Split meshes by their materials and generate output aiMesh'es
356void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
357{
358 std::vector<aiMesh*> avOutMeshes;
359 avOutMeshes.reserve(mScene->mMeshes.size() * 2);
360
361 unsigned int iFaceCnt = 0,num = 0;
362 aiString name;
363
364 // we need to split all meshes by their materials
365 for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) {
366 std::unique_ptr< std::vector<unsigned int>[] > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
367
368 name.length = ASSIMP_itoa10(name.data,num++);
369
370 unsigned int iNum = 0;
371 for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
372 a != (*i).mFaceMaterials.end();++a,++iNum)
373 {
374 aiSplit[*a].push_back(iNum);
375 }
376 // now generate submeshes
377 for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
378 {
379 if (aiSplit[p].empty()) {
380 continue;
381 }
382 aiMesh* meshOut = new aiMesh();
383 meshOut->mName = name;
384 meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
385
386 // be sure to setup the correct material index
387 meshOut->mMaterialIndex = p;
388
389 // use the color data as temporary storage
390 meshOut->mColors[0] = (aiColor4D*)(&*i);
391 avOutMeshes.push_back(meshOut);
392
393 // convert vertices
394 meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
395 meshOut->mNumVertices = meshOut->mNumFaces*3;
396
397 // allocate enough storage for faces
398 meshOut->mFaces = new aiFace[meshOut->mNumFaces];
399 iFaceCnt += meshOut->mNumFaces;
400
401 meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
402 meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
403 if ((*i).mTexCoords.size())
404 {
405 meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
406 }
407 for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
408 {
409 unsigned int index = aiSplit[p][q];
410 aiFace& face = meshOut->mFaces[q];
411
412 face.mIndices = new unsigned int[3];
413 face.mNumIndices = 3;
414
415 for (unsigned int a = 0; a < 3;++a,++base)
416 {
417 unsigned int idx = (*i).mFaces[index].mIndices[a];
418 meshOut->mVertices[base] = (*i).mPositions[idx];
419 meshOut->mNormals [base] = (*i).mNormals[idx];
420
421 if ((*i).mTexCoords.size())
422 meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
423
424 face.mIndices[a] = base;
425 }
426 }
427 }
428 }
429
430 // Copy them to the output array
431 pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
432 pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
433 for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
434 pcOut->mMeshes[a] = avOutMeshes[a];
435 }
436
437 // We should have at least one face here
438 if (!iFaceCnt) {
439 throw DeadlyImportError("No faces loaded. The mesh is empty");
440 }
441}
442
443// ------------------------------------------------------------------------------------------------
444// Add a node to the scenegraph and setup its final transformation
445void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
446 D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/)
447{
448 std::vector<unsigned int> iArray;
449 iArray.reserve(3);
450
451 aiMatrix4x4 abs;
452
453 // Find all meshes with the same name as the node
454 for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
455 {
456 const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
457 ai_assert(NULL != pcMesh);
458
459 if (pcIn->mName == pcMesh->mName)
460 iArray.push_back(a);
461 }
462 if (!iArray.empty())
463 {
464 // The matrix should be identical for all meshes with the
465 // same name. It HAS to be identical for all meshes .....
466 D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
467
468 // Compute the inverse of the transformation matrix to move the
469 // vertices back to their relative and local space
470 aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
471 mInv.Inverse();mInvTransposed.Transpose();
472 aiVector3D pivot = pcIn->vPivot;
473
474 pcOut->mNumMeshes = (unsigned int)iArray.size();
475 pcOut->mMeshes = new unsigned int[iArray.size()];
476 for (unsigned int i = 0;i < iArray.size();++i) {
477 const unsigned int iIndex = iArray[i];
478 aiMesh* const mesh = pcSOut->mMeshes[iIndex];
479
480 if (mesh->mColors[1] == NULL)
481 {
482 // Transform the vertices back into their local space
483 // fixme: consider computing normals after this, so we don't need to transform them
484 const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices;
485 aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
486
487 for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
488 *pvCurrent = mInv * (*pvCurrent);
489 *t2 = mInvTransposed * (*t2);
490 }
491
492 // Handle negative transformation matrix determinant -> invert vertex x
493 if (imesh->mMat.Determinant() < 0.0f)
494 {
495 /* we *must* have normals */
496 for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
497 pvCurrent->x *= -1.f;
498 t2->x *= -1.f;
499 }
500 DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
501 }
502
503 // Handle pivot point
504 if (pivot.x || pivot.y || pivot.z)
505 {
506 for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
507 *pvCurrent -= pivot;
508 }
509 }
510
511 mesh->mColors[1] = (aiColor4D*)1;
512 }
513 else
514 mesh->mColors[1] = (aiColor4D*)1;
515
516 // Setup the mesh index
517 pcOut->mMeshes[i] = iIndex;
518 }
519 }
520
521 // Setup the name of the node
522 // First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
523 if (pcIn->mInstanceNumber > 1)
524 {
525 char tmp[12];
526 ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
527 std::string tempStr = pcIn->mName + "_inst_";
528 tempStr += tmp;
529 pcOut->mName.Set(tempStr);
530 }
531 else
532 pcOut->mName.Set(pcIn->mName);
533
534 // Now build the transformation matrix of the node
535 // ROTATION
536 if (pcIn->aRotationKeys.size()){
537
538 // FIX to get to Assimp's quaternion conventions
539 for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
540 (*it).mValue.w *= -1.f;
541 }
542
543 pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
544 }
545 else if (pcIn->aCameraRollKeys.size())
546 {
547 aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
548 pcOut->mTransformation);
549 }
550
551 // SCALING
552 aiMatrix4x4& m = pcOut->mTransformation;
553 if (pcIn->aScalingKeys.size())
554 {
555 const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
556 m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
557 m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
558 m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
559 }
560
561 // TRANSLATION
562 if (pcIn->aPositionKeys.size())
563 {
564 const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
565 m.a4 += v.x;
566 m.b4 += v.y;
567 m.c4 += v.z;
568 }
569
570 // Generate animation channels for the node
571 if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
572 pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
573 pcIn->aTargetPositionKeys.size() > 1)
574 {
575 aiAnimation* anim = pcSOut->mAnimations[0];
576 ai_assert(NULL != anim);
577
578 if (pcIn->aCameraRollKeys.size() > 1)
579 {
580 DefaultLogger::get()->debug("3DS: Converting camera roll track ...");
581
582 // Camera roll keys - in fact they're just rotations
583 // around the camera's z axis. The angles are given
584 // in degrees (and they're clockwise).
585 pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
586 for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
587 {
588 aiQuatKey& q = pcIn->aRotationKeys[i];
589 aiFloatKey& f = pcIn->aCameraRollKeys[i];
590
591 q.mTime = f.mTime;
592
593 // FIX to get to Assimp quaternion conventions
594 q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
595 }
596 }
597#if 0
598 if (pcIn->aTargetPositionKeys.size() > 1)
599 {
600 DefaultLogger::get()->debug("3DS: Converting target track ...");
601
602 // Camera or spot light - need to convert the separate
603 // target position channel to our representation
604 TargetAnimationHelper helper;
605
606 if (pcIn->aPositionKeys.empty())
607 {
608 // We can just pass zero here ...
609 helper.SetFixedMainAnimationChannel(aiVector3D());
610 }
611 else helper.SetMainAnimationChannel(&pcIn->aPositionKeys);
612 helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys);
613
614 // Do the conversion
615 std::vector<aiVectorKey> distanceTrack;
616 helper.Process(&distanceTrack);
617
618 // Now add a new node as child, name it <ourName>.Target
619 // and assign the distance track to it. This is that the
620 // information where the target is and how it moves is
621 // not lost
622 D3DS::Node* nd = new D3DS::Node();
623 pcIn->push_back(nd);
624
625 nd->mName = pcIn->mName + ".Target";
626
627 aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
628 nda->mNodeName.Set(nd->mName);
629
630 nda->mNumPositionKeys = (unsigned int)distanceTrack.size();
631 nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
632 ::memcpy(nda->mPositionKeys,&distanceTrack[0],
633 sizeof(aiVectorKey)*nda->mNumPositionKeys);
634 }
635#endif
636
637 // Cameras or lights define their transformation in their parent node and in the
638 // corresponding light or camera chunks. However, we read and process the latter
639 // to to be able to return valid cameras/lights even if no scenegraph is given.
640 for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) {
641 if (pcSOut->mCameras[n]->mName == pcOut->mName) {
642 pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
643 }
644 }
645 for (unsigned int n = 0; n < pcSOut->mNumLights;++n) {
646 if (pcSOut->mLights[n]->mName == pcOut->mName) {
647 pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
648 }
649 }
650
651 // Allocate a new node anim and setup its name
652 aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
653 nda->mNodeName.Set(pcIn->mName);
654
655 // POSITION keys
656 if (pcIn->aPositionKeys.size() > 0)
657 {
658 nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
659 nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
660 ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
661 sizeof(aiVectorKey)*nda->mNumPositionKeys);
662 }
663
664 // ROTATION keys
665 if (pcIn->aRotationKeys.size() > 0)
666 {
667 nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
668 nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
669
670 // Rotations are quaternion offsets
671 aiQuaternion abs1;
672 for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
673 {
674 const aiQuatKey& q = pcIn->aRotationKeys[n];
675
676 abs1 = (n ? abs1 * q.mValue : q.mValue);
677 nda->mRotationKeys[n].mTime = q.mTime;
678 nda->mRotationKeys[n].mValue = abs1.Normalize();
679 }
680 }
681
682 // SCALING keys
683 if (pcIn->aScalingKeys.size() > 0)
684 {
685 nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
686 nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
687 ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
688 sizeof(aiVectorKey)*nda->mNumScalingKeys);
689 }
690 }
691
692 // Allocate storage for children
693 pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
694 pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
695
696 // Recursively process all children
697 const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
698 for (unsigned int i = 0; i < size;++i)
699 {
700 pcOut->mChildren[i] = new aiNode();
701 pcOut->mChildren[i]->mParent = pcOut;
702 AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
703 }
704}
705
706// ------------------------------------------------------------------------------------------------
707// Find out how many node animation channels we'll have finally
708void CountTracks(D3DS::Node* node, unsigned int& cnt)
709{
710 //////////////////////////////////////////////////////////////////////////////
711 // We will never generate more than one channel for a node, so
712 // this is rather easy here.
713
714 if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
715 node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
716 node->aTargetPositionKeys.size() > 1)
717 {
718 ++cnt;
719
720 // account for the additional channel for the camera/spotlight target position
721 if (node->aTargetPositionKeys.size() > 1)++cnt;
722 }
723
724 // Recursively process all children
725 for (unsigned int i = 0; i < node->mChildren.size();++i)
726 CountTracks(node->mChildren[i],cnt);
727}
728
729// ------------------------------------------------------------------------------------------------
730// Generate the output node graph
731void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
732{
733 pcOut->mRootNode = new aiNode();
734 if (0 == mRootNode->mChildren.size())
735 {
736 //////////////////////////////////////////////////////////////////////////////
737 // It seems the file is so messed up that it has not even a hierarchy.
738 // generate a flat hiearachy which looks like this:
739 //
740 // ROOT_NODE
741 // |
742 // ----------------------------------------
743 // | | | | |
744 // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 ....
745 //
746 DefaultLogger::get()->warn("No hierarchy information has been found in the file. ");
747
748 pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
749 static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
750
751 pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
752 pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
753
754 // Build dummy nodes for all meshes
755 unsigned int a = 0;
756 for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
757 {
758 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
759 pcNode->mParent = pcOut->mRootNode;
760 pcNode->mMeshes = new unsigned int[1];
761 pcNode->mMeshes[0] = i;
762 pcNode->mNumMeshes = 1;
763
764 // Build a name for the node
765 pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u",i);
766 }
767
768 // Build dummy nodes for all cameras
769 for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
770 {
771 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
772 pcNode->mParent = pcOut->mRootNode;
773
774 // Build a name for the node
775 pcNode->mName = mScene->mCameras[i]->mName;
776 }
777
778 // Build dummy nodes for all lights
779 for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
780 {
781 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
782 pcNode->mParent = pcOut->mRootNode;
783
784 // Build a name for the node
785 pcNode->mName = mScene->mLights[i]->mName;
786 }
787 }
788 else
789 {
790 // First of all: find out how many scaling, rotation and translation
791 // animation tracks we'll have afterwards
792 unsigned int numChannel = 0;
793 CountTracks(mRootNode,numChannel);
794
795 if (numChannel)
796 {
797 // Allocate a primary animation channel
798 pcOut->mNumAnimations = 1;
799 pcOut->mAnimations = new aiAnimation*[1];
800 aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
801
802 anim->mName.Set("3DSMasterAnim");
803
804 // Allocate enough storage for all node animation channels,
805 // but don't set the mNumChannels member - we'll use it to
806 // index into the array
807 anim->mChannels = new aiNodeAnim*[numChannel];
808 }
809
810 aiMatrix4x4 m;
811 AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m);
812 }
813
814 // We used the first and second vertex color set to store some temporary values so we need to cleanup here
815 for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a)
816 {
817 pcOut->mMeshes[a]->mColors[0] = NULL;
818 pcOut->mMeshes[a]->mColors[1] = NULL;
819 }
820
821 pcOut->mRootNode->mTransformation = aiMatrix4x4(
822 1.f,0.f,0.f,0.f,
823 0.f,0.f,1.f,0.f,
824 0.f,-1.f,0.f,0.f,
825 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
826
827 // If the root node is unnamed name it "<3DSRoot>"
828 if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
829 (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
830 {
831 pcOut->mRootNode->mName.Set("<3DSRoot>");
832 }
833}
834
835// ------------------------------------------------------------------------------------------------
836// Convert all meshes in the scene and generate the final output scene.
837void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
838{
839 // Allocate enough storage for all output materials
840 pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
841 pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
842
843 // ... and convert the 3DS materials to aiMaterial's
844 for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
845 {
846 aiMaterial* pcNew = new aiMaterial();
847 ConvertMaterial(mScene->mMaterials[i],*pcNew);
848 pcOut->mMaterials[i] = pcNew;
849 }
850
851 // Generate the output mesh list
852 ConvertMeshes(pcOut);
853
854 // Now copy all light sources to the output scene
855 pcOut->mNumLights = (unsigned int)mScene->mLights.size();
856 if (pcOut->mNumLights)
857 {
858 pcOut->mLights = new aiLight*[pcOut->mNumLights];
859 ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
860 }
861
862 // Now copy all cameras to the output scene
863 pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
864 if (pcOut->mNumCameras)
865 {
866 pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
867 ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
868 }
869}
870
871#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
872