1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions 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 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF 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 | |
57 | using namespace Assimp; |
58 | |
59 | static const unsigned int NotSet = 0xcdcdcdcd; |
60 | |
61 | // ------------------------------------------------------------------------------------------------ |
62 | // Setup final material indices, generae a default material if necessary |
63 | void 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 |
141 | void 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 |
164 | void 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 |
195 | void 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 |
226 | void 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 |
356 | void 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 |
445 | void 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 |
708 | void 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 |
731 | void 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. |
837 | void 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 | |