vrshoot
view libs/assimp/3DSConverter.cpp @ 1:e7ca128b8713
looks nice :)
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Feb 2014 00:35:22 +0200 |
parents | |
children |
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file Implementation of the 3ds importer class */
44 #include "AssimpPCH.h"
45 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
47 // internal headers
48 #include "3DSLoader.h"
49 #include "TargetAnimation.h"
51 using namespace Assimp;
53 // ------------------------------------------------------------------------------------------------
54 // Setup final material indices, generae a default material if necessary
55 void Discreet3DSImporter::ReplaceDefaultMaterial()
56 {
58 // Try to find an existing material that matches the
59 // typical default material setting:
60 // - no textures
61 // - diffuse color (in grey!)
62 // NOTE: This is here to workaround the fact that some
63 // exporters are writing a default material, too.
64 unsigned int idx = 0xcdcdcdcd;
65 for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
66 {
67 std::string s = mScene->mMaterials[i].mName;
68 for (std::string::iterator it = s.begin(); it != s.end(); ++it)
69 *it = ::tolower(*it);
71 if (std::string::npos == s.find("default"))continue;
73 if (mScene->mMaterials[i].mDiffuse.r !=
74 mScene->mMaterials[i].mDiffuse.g ||
75 mScene->mMaterials[i].mDiffuse.r !=
76 mScene->mMaterials[i].mDiffuse.b)continue;
78 if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
79 mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
80 mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
81 mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
82 mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
83 mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
84 {
85 continue;
86 }
87 idx = i;
88 }
89 if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size();
91 // now iterate through all meshes and through all faces and
92 // find all faces that are using the default material
93 unsigned int cnt = 0;
94 for (std::vector<D3DS::Mesh>::iterator
95 i = mScene->mMeshes.begin();
96 i != mScene->mMeshes.end();++i)
97 {
98 for (std::vector<unsigned int>::iterator
99 a = (*i).mFaceMaterials.begin();
100 a != (*i).mFaceMaterials.end();++a)
101 {
102 // NOTE: The additional check seems to be necessary,
103 // some exporters seem to generate invalid data here
104 if (0xcdcdcdcd == (*a))
105 {
106 (*a) = idx;
107 ++cnt;
108 }
109 else if ( (*a) >= mScene->mMaterials.size())
110 {
111 (*a) = idx;
112 DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material");
113 ++cnt;
114 }
115 }
116 }
117 if (cnt && idx == mScene->mMaterials.size())
118 {
119 // We need to create our own default material
120 D3DS::Material sMat;
121 sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
122 sMat.mName = "%%%DEFAULT";
123 mScene->mMaterials.push_back(sMat);
125 DefaultLogger::get()->info("3DS: Generating default material");
126 }
127 }
129 // ------------------------------------------------------------------------------------------------
130 // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
131 void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
132 {
133 for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
134 {
135 // check whether all indices are in range
136 for (unsigned int a = 0; a < 3;++a)
137 {
138 if ((*i).mIndices[a] >= sMesh.mPositions.size())
139 {
140 DefaultLogger::get()->warn("3DS: Vertex index overflow)");
141 (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
142 }
143 if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
144 {
145 DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)");
146 (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
147 }
148 }
149 }
150 }
152 // ------------------------------------------------------------------------------------------------
153 // Generate out unique verbose format representation
154 void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
155 {
156 // TODO: really necessary? I don't think. Just a waste of memory and time
157 // to do it now in a separate buffer.
159 // Allocate output storage
160 std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
161 std::vector<aiVector3D> vNew2;
162 if (sMesh.mTexCoords.size())
163 vNew2.resize(sMesh.mFaces.size() * 3);
165 for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
166 {
167 D3DS::Face& face = sMesh.mFaces[i];
169 // Positions
170 for (unsigned int a = 0; a < 3;++a,++base)
171 {
172 vNew[base] = sMesh.mPositions[face.mIndices[a]];
173 if (sMesh.mTexCoords.size())
174 vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
176 face.mIndices[a] = base;
177 }
178 }
179 sMesh.mPositions = vNew;
180 sMesh.mTexCoords = vNew2;
181 }
183 // ------------------------------------------------------------------------------------------------
184 // Convert a 3DS texture to texture keys in an aiMaterial
185 void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
186 {
187 // Setup the texture name
188 aiString tex;
189 tex.Set( texture.mMapName);
190 mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
192 // Setup the texture blend factor
193 if (is_not_qnan(texture.mTextureBlend))
194 mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
196 // Setup the texture mapping mode
197 mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
198 mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
200 // Mirroring - double the scaling values
201 // FIXME: this is not really correct ...
202 if (texture.mMapMode == aiTextureMapMode_Mirror)
203 {
204 texture.mScaleU *= 2.f;
205 texture.mScaleV *= 2.f;
206 texture.mOffsetU /= 2.f;
207 texture.mOffsetV /= 2.f;
208 }
210 // Setup texture UV transformations
211 mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
212 }
214 // ------------------------------------------------------------------------------------------------
215 // Convert a 3DS material to an aiMaterial
216 void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
217 aiMaterial& mat)
218 {
219 // NOTE: Pass the background image to the viewer by bypassing the
220 // material system. This is an evil hack, never do it again!
221 if (0 != mBackgroundImage.length() && bHasBG)
222 {
223 aiString tex;
224 tex.Set( mBackgroundImage);
225 mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
227 // Be sure this is only done for the first material
228 mBackgroundImage = std::string("");
229 }
231 // At first add the base ambient color of the scene to the material
232 oldMat.mAmbient.r += mClrAmbient.r;
233 oldMat.mAmbient.g += mClrAmbient.g;
234 oldMat.mAmbient.b += mClrAmbient.b;
236 aiString name;
237 name.Set( oldMat.mName);
238 mat.AddProperty( &name, AI_MATKEY_NAME);
240 // Material colors
241 mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
242 mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
243 mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
244 mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
246 // Phong shininess and shininess strength
247 if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
248 D3DS::Discreet3DS::Metal == oldMat.mShading)
249 {
250 if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
251 {
252 oldMat.mShading = D3DS::Discreet3DS::Gouraud;
253 }
254 else
255 {
256 mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
257 mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
258 }
259 }
261 // Opacity
262 mat.AddProperty<float>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
264 // Bump height scaling
265 mat.AddProperty<float>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
267 // Two sided rendering?
268 if (oldMat.mTwoSided)
269 {
270 int i = 1;
271 mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
272 }
274 // Shading mode
275 aiShadingMode eShading = aiShadingMode_NoShading;
276 switch (oldMat.mShading)
277 {
278 case D3DS::Discreet3DS::Flat:
279 eShading = aiShadingMode_Flat; break;
281 // I don't know what "Wire" shading should be,
282 // assume it is simple lambertian diffuse shading
283 case D3DS::Discreet3DS::Wire:
284 {
285 // Set the wireframe flag
286 unsigned int iWire = 1;
287 mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
288 }
290 case D3DS::Discreet3DS::Gouraud:
291 eShading = aiShadingMode_Gouraud; break;
293 // assume cook-torrance shading for metals.
294 case D3DS::Discreet3DS::Phong :
295 eShading = aiShadingMode_Phong; break;
297 case D3DS::Discreet3DS::Metal :
298 eShading = aiShadingMode_CookTorrance; break;
300 // FIX to workaround a warning with GCC 4 who complained
301 // about a missing case Blinn: here - Blinn isn't a valid
302 // value in the 3DS Loader, it is just needed for ASE
303 case D3DS::Discreet3DS::Blinn :
304 eShading = aiShadingMode_Blinn; break;
305 }
306 mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
308 // DIFFUSE texture
309 if( oldMat.sTexDiffuse.mMapName.length() > 0)
310 CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
312 // SPECULAR texture
313 if( oldMat.sTexSpecular.mMapName.length() > 0)
314 CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
316 // OPACITY texture
317 if( oldMat.sTexOpacity.mMapName.length() > 0)
318 CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
320 // EMISSIVE texture
321 if( oldMat.sTexEmissive.mMapName.length() > 0)
322 CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
324 // BUMP texture
325 if( oldMat.sTexBump.mMapName.length() > 0)
326 CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
328 // SHININESS texture
329 if( oldMat.sTexShininess.mMapName.length() > 0)
330 CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
332 // REFLECTION texture
333 if( oldMat.sTexReflective.mMapName.length() > 0)
334 CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
336 // Store the name of the material itself, too
337 if( oldMat.mName.length()) {
338 aiString tex;
339 tex.Set( oldMat.mName);
340 mat.AddProperty( &tex, AI_MATKEY_NAME);
341 }
342 }
344 // ------------------------------------------------------------------------------------------------
345 // Split meshes by their materials and generate output aiMesh'es
346 void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
347 {
348 std::vector<aiMesh*> avOutMeshes;
349 avOutMeshes.reserve(mScene->mMeshes.size() * 2);
351 unsigned int iFaceCnt = 0,num = 0;
352 aiString name;
354 // we need to split all meshes by their materials
355 for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) {
356 boost::scoped_array< std::vector<unsigned int> > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
358 name.length = ASSIMP_itoa10(name.data,num++);
360 unsigned int iNum = 0;
361 for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
362 a != (*i).mFaceMaterials.end();++a,++iNum)
363 {
364 aiSplit[*a].push_back(iNum);
365 }
366 // now generate submeshes
367 for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
368 {
369 if (aiSplit[p].empty()) {
370 continue;
371 }
372 aiMesh* meshOut = new aiMesh();
373 meshOut->mName = name;
374 meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
376 // be sure to setup the correct material index
377 meshOut->mMaterialIndex = p;
379 // use the color data as temporary storage
380 meshOut->mColors[0] = (aiColor4D*)(&*i);
381 avOutMeshes.push_back(meshOut);
383 // convert vertices
384 meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
385 meshOut->mNumVertices = meshOut->mNumFaces*3;
387 // allocate enough storage for faces
388 meshOut->mFaces = new aiFace[meshOut->mNumFaces];
389 iFaceCnt += meshOut->mNumFaces;
391 meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
392 meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
393 if ((*i).mTexCoords.size())
394 {
395 meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
396 }
397 for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
398 {
399 register unsigned int index = aiSplit[p][q];
400 aiFace& face = meshOut->mFaces[q];
402 face.mIndices = new unsigned int[3];
403 face.mNumIndices = 3;
405 for (unsigned int a = 0; a < 3;++a,++base)
406 {
407 unsigned int idx = (*i).mFaces[index].mIndices[a];
408 meshOut->mVertices[base] = (*i).mPositions[idx];
409 meshOut->mNormals [base] = (*i).mNormals[idx];
411 if ((*i).mTexCoords.size())
412 meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
414 face.mIndices[a] = base;
415 }
416 }
417 }
418 }
420 // Copy them to the output array
421 pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
422 pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
423 for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
424 pcOut->mMeshes[a] = avOutMeshes[a];
425 }
427 // We should have at least one face here
428 if (!iFaceCnt) {
429 throw DeadlyImportError("No faces loaded. The mesh is empty");
430 }
431 }
433 // ------------------------------------------------------------------------------------------------
434 // Add a node to the scenegraph and setup its final transformation
435 void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
436 D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/)
437 {
438 std::vector<unsigned int> iArray;
439 iArray.reserve(3);
441 aiMatrix4x4 abs;
443 // Find all meshes with the same name as the node
444 for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
445 {
446 const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
447 ai_assert(NULL != pcMesh);
449 if (pcIn->mName == pcMesh->mName)
450 iArray.push_back(a);
451 }
452 if (!iArray.empty())
453 {
454 // The matrix should be identical for all meshes with the
455 // same name. It HAS to be identical for all meshes .....
456 D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
458 // Compute the inverse of the transformation matrix to move the
459 // vertices back to their relative and local space
460 aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
461 mInv.Inverse();mInvTransposed.Transpose();
462 aiVector3D pivot = pcIn->vPivot;
464 pcOut->mNumMeshes = (unsigned int)iArray.size();
465 pcOut->mMeshes = new unsigned int[iArray.size()];
466 for (unsigned int i = 0;i < iArray.size();++i) {
467 const unsigned int iIndex = iArray[i];
468 aiMesh* const mesh = pcSOut->mMeshes[iIndex];
470 // Transform the vertices back into their local space
471 // fixme: consider computing normals after this, so we don't need to transform them
472 const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
473 aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
475 for (;pvCurrent != pvEnd;++pvCurrent,++t2) {
476 *pvCurrent = mInv * (*pvCurrent);
477 *t2 = mInvTransposed * (*t2);
478 }
480 // Handle negative transformation matrix determinant -> invert vertex x
481 if (imesh->mMat.Determinant() < 0.0f)
482 {
483 /* we *must* have normals */
484 for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) {
485 pvCurrent->x *= -1.f;
486 t2->x *= -1.f;
487 }
488 DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
489 }
491 // Handle pivot point
492 if(pivot.x || pivot.y || pivot.z)
493 {
494 for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
495 *pvCurrent -= pivot;
496 }
497 }
499 // Setup the mesh index
500 pcOut->mMeshes[i] = iIndex;
501 }
502 }
504 // Setup the name of the node
505 pcOut->mName.Set(pcIn->mName);
507 // Now build the transformation matrix of the node
508 // ROTATION
509 if (pcIn->aRotationKeys.size()){
511 // FIX to get to Assimp's quaternion conventions
512 for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
513 (*it).mValue.w *= -1.f;
514 }
516 pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
517 }
518 else if (pcIn->aCameraRollKeys.size())
519 {
520 aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
521 pcOut->mTransformation);
522 }
524 // SCALING
525 aiMatrix4x4& m = pcOut->mTransformation;
526 if (pcIn->aScalingKeys.size())
527 {
528 const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
529 m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
530 m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
531 m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
532 }
534 // TRANSLATION
535 if (pcIn->aPositionKeys.size())
536 {
537 const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
538 m.a4 += v.x;
539 m.b4 += v.y;
540 m.c4 += v.z;
541 }
543 // Generate animation channels for the node
544 if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
545 pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
546 pcIn->aTargetPositionKeys.size() > 1)
547 {
548 aiAnimation* anim = pcSOut->mAnimations[0];
549 ai_assert(NULL != anim);
551 if (pcIn->aCameraRollKeys.size() > 1)
552 {
553 DefaultLogger::get()->debug("3DS: Converting camera roll track ...");
555 // Camera roll keys - in fact they're just rotations
556 // around the camera's z axis. The angles are given
557 // in degrees (and they're clockwise).
558 pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
559 for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
560 {
561 aiQuatKey& q = pcIn->aRotationKeys[i];
562 aiFloatKey& f = pcIn->aCameraRollKeys[i];
564 q.mTime = f.mTime;
566 // FIX to get to Assimp quaternion conventions
567 q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
568 }
569 }
570 #if 0
571 if (pcIn->aTargetPositionKeys.size() > 1)
572 {
573 DefaultLogger::get()->debug("3DS: Converting target track ...");
575 // Camera or spot light - need to convert the separate
576 // target position channel to our representation
577 TargetAnimationHelper helper;
579 if (pcIn->aPositionKeys.empty())
580 {
581 // We can just pass zero here ...
582 helper.SetFixedMainAnimationChannel(aiVector3D());
583 }
584 else helper.SetMainAnimationChannel(&pcIn->aPositionKeys);
585 helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys);
587 // Do the conversion
588 std::vector<aiVectorKey> distanceTrack;
589 helper.Process(&distanceTrack);
591 // Now add a new node as child, name it <ourName>.Target
592 // and assign the distance track to it. This is that the
593 // information where the target is and how it moves is
594 // not lost
595 D3DS::Node* nd = new D3DS::Node();
596 pcIn->push_back(nd);
598 nd->mName = pcIn->mName + ".Target";
600 aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
601 nda->mNodeName.Set(nd->mName);
603 nda->mNumPositionKeys = (unsigned int)distanceTrack.size();
604 nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
605 ::memcpy(nda->mPositionKeys,&distanceTrack[0],
606 sizeof(aiVectorKey)*nda->mNumPositionKeys);
607 }
608 #endif
610 // Cameras or lights define their transformation in their parent node and in the
611 // corresponding light or camera chunks. However, we read and process the latter
612 // to to be able to return valid cameras/lights even if no scenegraph is given.
613 for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) {
614 if (pcSOut->mCameras[n]->mName == pcOut->mName) {
615 pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
616 }
617 }
618 for (unsigned int n = 0; n < pcSOut->mNumLights;++n) {
619 if (pcSOut->mLights[n]->mName == pcOut->mName) {
620 pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
621 }
622 }
624 // Allocate a new node anim and setup its name
625 aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
626 nda->mNodeName.Set(pcIn->mName);
628 // POSITION keys
629 if (pcIn->aPositionKeys.size() > 0)
630 {
631 nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
632 nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
633 ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
634 sizeof(aiVectorKey)*nda->mNumPositionKeys);
635 }
637 // ROTATION keys
638 if (pcIn->aRotationKeys.size() > 0)
639 {
640 nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
641 nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
643 // Rotations are quaternion offsets
644 aiQuaternion abs;
645 for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
646 {
647 const aiQuatKey& q = pcIn->aRotationKeys[n];
649 abs = (n ? abs * q.mValue : q.mValue);
650 nda->mRotationKeys[n].mTime = q.mTime;
651 nda->mRotationKeys[n].mValue = abs.Normalize();
652 }
653 }
655 // SCALING keys
656 if (pcIn->aScalingKeys.size() > 0)
657 {
658 nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
659 nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
660 ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
661 sizeof(aiVectorKey)*nda->mNumScalingKeys);
662 }
663 }
665 // Allocate storage for children
666 pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
667 pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
669 // Recursively process all children
670 const unsigned int size = pcIn->mChildren.size();
671 for (unsigned int i = 0; i < size;++i)
672 {
673 pcOut->mChildren[i] = new aiNode();
674 pcOut->mChildren[i]->mParent = pcOut;
675 AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
676 }
677 }
679 // ------------------------------------------------------------------------------------------------
680 // Find out how many node animation channels we'll have finally
681 void CountTracks(D3DS::Node* node, unsigned int& cnt)
682 {
683 //////////////////////////////////////////////////////////////////////////////
684 // We will never generate more than one channel for a node, so
685 // this is rather easy here.
687 if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
688 node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
689 node->aTargetPositionKeys.size() > 1)
690 {
691 ++cnt;
693 // account for the additional channel for the camera/spotlight target position
694 if (node->aTargetPositionKeys.size() > 1)++cnt;
695 }
697 // Recursively process all children
698 for (unsigned int i = 0; i < node->mChildren.size();++i)
699 CountTracks(node->mChildren[i],cnt);
700 }
702 // ------------------------------------------------------------------------------------------------
703 // Generate the output node graph
704 void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
705 {
706 pcOut->mRootNode = new aiNode();
707 if (0 == mRootNode->mChildren.size())
708 {
709 //////////////////////////////////////////////////////////////////////////////
710 // It seems the file is so messed up that it has not even a hierarchy.
711 // generate a flat hiearachy which looks like this:
712 //
713 // ROOT_NODE
714 // |
715 // ----------------------------------------
716 // | | | | |
717 // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 ....
718 //
719 DefaultLogger::get()->warn("No hierarchy information has been found in the file. ");
721 pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
722 mScene->mCameras.size() + mScene->mLights.size();
724 pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
725 pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
727 // Build dummy nodes for all meshes
728 unsigned int a = 0;
729 for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
730 {
731 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
732 pcNode->mParent = pcOut->mRootNode;
733 pcNode->mMeshes = new unsigned int[1];
734 pcNode->mMeshes[0] = i;
735 pcNode->mNumMeshes = 1;
737 // Build a name for the node
738 pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i);
739 }
741 // Build dummy nodes for all cameras
742 for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
743 {
744 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
745 pcNode->mParent = pcOut->mRootNode;
747 // Build a name for the node
748 pcNode->mName = mScene->mCameras[i]->mName;
749 }
751 // Build dummy nodes for all lights
752 for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
753 {
754 aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
755 pcNode->mParent = pcOut->mRootNode;
757 // Build a name for the node
758 pcNode->mName = mScene->mLights[i]->mName;
759 }
760 }
761 else
762 {
763 // First of all: find out how many scaling, rotation and translation
764 // animation tracks we'll have afterwards
765 unsigned int numChannel = 0;
766 CountTracks(mRootNode,numChannel);
768 if (numChannel)
769 {
770 // Allocate a primary animation channel
771 pcOut->mNumAnimations = 1;
772 pcOut->mAnimations = new aiAnimation*[1];
773 aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
775 anim->mName.Set("3DSMasterAnim");
777 // Allocate enough storage for all node animation channels,
778 // but don't set the mNumChannels member - we'll use it to
779 // index into the array
780 anim->mChannels = new aiNodeAnim*[numChannel];
781 }
783 aiMatrix4x4 m;
784 AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m);
785 }
787 // We used the first vertex color set to store some emporary values so we need to cleanup here
788 for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
789 pcOut->mMeshes[a]->mColors[0] = NULL;
791 pcOut->mRootNode->mTransformation = aiMatrix4x4(
792 1.f,0.f,0.f,0.f,
793 0.f,0.f,1.f,0.f,
794 0.f,-1.f,0.f,0.f,
795 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
797 // If the root node is unnamed name it "<3DSRoot>"
798 if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
799 (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
800 {
801 pcOut->mRootNode->mName.Set("<3DSRoot>");
802 }
803 }
805 // ------------------------------------------------------------------------------------------------
806 // Convert all meshes in the scene and generate the final output scene.
807 void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
808 {
809 // Allocate enough storage for all output materials
810 pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
811 pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
813 // ... and convert the 3DS materials to aiMaterial's
814 for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
815 {
816 aiMaterial* pcNew = new aiMaterial();
817 ConvertMaterial(mScene->mMaterials[i],*pcNew);
818 pcOut->mMaterials[i] = pcNew;
819 }
821 // Generate the output mesh list
822 ConvertMeshes(pcOut);
824 // Now copy all light sources to the output scene
825 pcOut->mNumLights = (unsigned int)mScene->mLights.size();
826 if (pcOut->mNumLights)
827 {
828 pcOut->mLights = new aiLight*[pcOut->mNumLights];
829 ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
830 }
832 // Now copy all cameras to the output scene
833 pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
834 if (pcOut->mNumCameras)
835 {
836 pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
837 ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
838 }
839 }
841 #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER