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
16copyright notice, this list of conditions and the
17following disclaimer.
18
19* Redistributions in binary form must reproduce the above
20copyright notice, this list of conditions and the
21following disclaimer in the documentation and/or other
22materials provided with the distribution.
23
24* Neither the name of the assimp team, nor the names of its
25contributors may be used to endorse or promote products
26derived from this software without specific prior
27written 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/** @file XFileImporter.cpp
43 * @brief Implementation of the XFile importer class
44 */
45
46
47#ifndef ASSIMP_BUILD_NO_X_IMPORTER
48
49#include "XFileImporter.h"
50#include "XFileParser.h"
51#include "TinyFormatter.h"
52#include "ConvertToLHProcess.h"
53#include <assimp/Defines.h>
54#include <assimp/IOSystem.hpp>
55#include <assimp/scene.h>
56#include <assimp/DefaultLogger.hpp>
57#include <assimp/importerdesc.h>
58
59#include <cctype>
60#include <memory>
61
62using namespace Assimp;
63using namespace Assimp::Formatter;
64
65static const aiImporterDesc desc = {
66 "Direct3D XFile Importer",
67 "",
68 "",
69 "",
70 aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour,
71 1,
72 3,
73 1,
74 5,
75 "x"
76};
77
78// ------------------------------------------------------------------------------------------------
79// Constructor to be privately used by Importer
80XFileImporter::XFileImporter()
81{}
82
83// ------------------------------------------------------------------------------------------------
84// Destructor, private as well
85XFileImporter::~XFileImporter()
86{}
87
88// ------------------------------------------------------------------------------------------------
89// Returns whether the class can handle the format of the given file.
90bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
91{
92 std::string extension = GetExtension(pFile);
93 if(extension == "x") {
94 return true;
95 }
96 if (!extension.length() || checkSig) {
97 uint32_t token[1];
98 token[0] = AI_MAKE_MAGIC("xof ");
99 return CheckMagicToken(pIOHandler,pFile,token,1,0);
100 }
101 return false;
102}
103
104// ------------------------------------------------------------------------------------------------
105// Get file extension list
106const aiImporterDesc* XFileImporter::GetInfo () const
107{
108 return &desc;
109}
110
111// ------------------------------------------------------------------------------------------------
112// Imports the given file into the given scene structure.
113void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
114{
115 // read file into memory
116 std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
117 if( file.get() == NULL)
118 throw DeadlyImportError( "Failed to open file " + pFile + ".");
119
120 size_t fileSize = file->FileSize();
121 if( fileSize < 16)
122 throw DeadlyImportError( "XFile is too small.");
123
124 // in the hope that binary files will never start with a BOM ...
125 mBuffer.resize( fileSize + 1);
126 file->Read( &mBuffer.front(), 1, fileSize);
127 ConvertToUTF8(mBuffer);
128
129 // parse the file into a temporary representation
130 XFileParser parser( mBuffer);
131
132 // and create the proper return structures out of it
133 CreateDataRepresentationFromImport( pScene, parser.GetImportedData());
134
135 // if nothing came from it, report it as error
136 if( !pScene->mRootNode)
137 throw DeadlyImportError( "XFile is ill-formatted - no content imported.");
138}
139
140// ------------------------------------------------------------------------------------------------
141// Constructs the return data structure out of the imported data.
142void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile::Scene* pData)
143{
144 // Read the global materials first so that meshes referring to them can find them later
145 ConvertMaterials( pScene, pData->mGlobalMaterials);
146
147 // copy nodes, extracting meshes and materials on the way
148 pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode);
149
150 // extract animations
151 CreateAnimations( pScene, pData);
152
153 // read the global meshes that were stored outside of any node
154 if( pData->mGlobalMeshes.size() > 0)
155 {
156 // create a root node to hold them if there isn't any, yet
157 if( pScene->mRootNode == NULL)
158 {
159 pScene->mRootNode = new aiNode;
160 pScene->mRootNode->mName.Set( "$dummy_node");
161 }
162
163 // convert all global meshes and store them in the root node.
164 // If there was one before, the global meshes now suddenly have its transformation matrix...
165 // Don't know what to do there, I don't want to insert another node under the present root node
166 // just to avoid this.
167 CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes);
168 }
169
170 if (!pScene->mRootNode) {
171 throw DeadlyImportError( "No root node" );
172 }
173
174 // Convert everything to OpenGL space... it's the same operation as the conversion back, so we can reuse the step directly
175 MakeLeftHandedProcess convertProcess;
176 convertProcess.Execute( pScene);
177
178 FlipWindingOrderProcess flipper;
179 flipper.Execute(pScene);
180
181 // finally: create a dummy material if not material was imported
182 if( pScene->mNumMaterials == 0)
183 {
184 pScene->mNumMaterials = 1;
185 // create the Material
186 aiMaterial* mat = new aiMaterial;
187 int shadeMode = (int) aiShadingMode_Gouraud;
188 mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
189 // material colours
190 int specExp = 1;
191
192 aiColor3D clr = aiColor3D( 0, 0, 0);
193 mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_EMISSIVE);
194 mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_SPECULAR);
195
196 clr = aiColor3D( 0.5f, 0.5f, 0.5f);
197 mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_DIFFUSE);
198 mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
199
200 pScene->mMaterials = new aiMaterial*[1];
201 pScene->mMaterials[0] = mat;
202 }
203}
204
205// ------------------------------------------------------------------------------------------------
206// Recursively creates scene nodes from the imported hierarchy.
207aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode)
208{
209 if( !pNode)
210 return NULL;
211
212 // create node
213 aiNode* node = new aiNode;
214 node->mName.length = pNode->mName.length();
215 node->mParent = pParent;
216 memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length());
217 node->mName.data[node->mName.length] = 0;
218 node->mTransformation = pNode->mTrafoMatrix;
219
220 // convert meshes from the source node
221 CreateMeshes( pScene, node, pNode->mMeshes);
222
223 // handle childs
224 if( pNode->mChildren.size() > 0)
225 {
226 node->mNumChildren = (unsigned int)pNode->mChildren.size();
227 node->mChildren = new aiNode* [node->mNumChildren];
228
229 for( unsigned int a = 0; a < pNode->mChildren.size(); a++)
230 node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]);
231 }
232
233 return node;
234}
235
236// ------------------------------------------------------------------------------------------------
237// Creates the meshes for the given node.
238void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes)
239{
240 if (pMeshes.empty()) {
241 return;
242 }
243
244 // create a mesh for each mesh-material combination in the source node
245 std::vector<aiMesh*> meshes;
246 for( unsigned int a = 0; a < pMeshes.size(); a++)
247 {
248 XFile::Mesh* sourceMesh = pMeshes[a];
249 // first convert its materials so that we can find them with their index afterwards
250 ConvertMaterials( pScene, sourceMesh->mMaterials);
251
252 unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u);
253 for( unsigned int b = 0; b < numMaterials; b++)
254 {
255 // collect the faces belonging to this material
256 std::vector<unsigned int> faces;
257 unsigned int numVertices = 0;
258 if( sourceMesh->mFaceMaterials.size() > 0)
259 {
260 // if there is a per-face material defined, select the faces with the corresponding material
261 for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++)
262 {
263 if( sourceMesh->mFaceMaterials[c] == b)
264 {
265 faces.push_back( c);
266 numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
267 }
268 }
269 } else
270 {
271 // if there is no per-face material, place everything into one mesh
272 for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++)
273 {
274 faces.push_back( c);
275 numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
276 }
277 }
278
279 // no faces/vertices using this material? strange...
280 if( numVertices == 0)
281 continue;
282
283 // create a submesh using this material
284 aiMesh* mesh = new aiMesh;
285 meshes.push_back( mesh);
286
287 // find the material in the scene's material list. Either own material
288 // or referenced material, it should already have a valid index
289 if( sourceMesh->mFaceMaterials.size() > 0)
290 {
291 mesh->mMaterialIndex = static_cast<unsigned int>(sourceMesh->mMaterials[b].sceneIndex);
292 } else
293 {
294 mesh->mMaterialIndex = 0;
295 }
296
297 // Create properly sized data arrays in the mesh. We store unique vertices per face,
298 // as specified
299 mesh->mNumVertices = numVertices;
300 mesh->mVertices = new aiVector3D[numVertices];
301 mesh->mNumFaces = (unsigned int)faces.size();
302 mesh->mFaces = new aiFace[mesh->mNumFaces];
303
304 // name
305 mesh->mName.Set(sourceMesh->mName);
306
307 // normals?
308 if( sourceMesh->mNormals.size() > 0)
309 mesh->mNormals = new aiVector3D[numVertices];
310 // texture coords
311 for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++)
312 {
313 if( sourceMesh->mTexCoords[c].size() > 0)
314 mesh->mTextureCoords[c] = new aiVector3D[numVertices];
315 }
316 // vertex colors
317 for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++)
318 {
319 if( sourceMesh->mColors[c].size() > 0)
320 mesh->mColors[c] = new aiColor4D[numVertices];
321 }
322
323 // now collect the vertex data of all data streams present in the imported mesh
324 unsigned int newIndex = 0;
325 std::vector<unsigned int> orgPoints; // from which original point each new vertex stems
326 orgPoints.resize( numVertices, 0);
327
328 for( unsigned int c = 0; c < faces.size(); c++)
329 {
330 unsigned int f = faces[c]; // index of the source face
331 const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face
332
333 // create face. either triangle or triangle fan depending on the index count
334 aiFace& df = mesh->mFaces[c]; // destination face
335 df.mNumIndices = (unsigned int)pf.mIndices.size();
336 df.mIndices = new unsigned int[ df.mNumIndices];
337
338 // collect vertex data for indices of this face
339 for( unsigned int d = 0; d < df.mNumIndices; d++)
340 {
341 df.mIndices[d] = newIndex;
342 orgPoints[newIndex] = pf.mIndices[d];
343
344 // Position
345 mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]];
346 // Normal, if present
347 if( mesh->HasNormals())
348 mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]];
349
350 // texture coord sets
351 for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++)
352 {
353 if( mesh->HasTextureCoords( e))
354 {
355 aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]];
356 mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f);
357 }
358 }
359 // vertex color sets
360 for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++)
361 if( mesh->HasVertexColors( e))
362 mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]];
363
364 newIndex++;
365 }
366 }
367
368 // there should be as much new vertices as we calculated before
369 ai_assert( newIndex == numVertices);
370
371 // convert all bones of the source mesh which influence vertices in this newly created mesh
372 const std::vector<XFile::Bone>& bones = sourceMesh->mBones;
373 std::vector<aiBone*> newBones;
374 for( unsigned int c = 0; c < bones.size(); c++)
375 {
376 const XFile::Bone& obone = bones[c];
377 // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex
378 std::vector<ai_real> oldWeights( sourceMesh->mPositions.size(), 0.0);
379 for( unsigned int d = 0; d < obone.mWeights.size(); d++)
380 oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight;
381
382 // collect all vertex weights that influence a vertex in the new mesh
383 std::vector<aiVertexWeight> newWeights;
384 newWeights.reserve( numVertices);
385 for( unsigned int d = 0; d < orgPoints.size(); d++)
386 {
387 // does the new vertex stem from an old vertex which was influenced by this bone?
388 ai_real w = oldWeights[orgPoints[d]];
389 if( w > 0.0)
390 newWeights.push_back( aiVertexWeight( d, w));
391 }
392
393 // if the bone has no weights in the newly created mesh, ignore it
394 if( newWeights.size() == 0)
395 continue;
396
397 // create
398 aiBone* nbone = new aiBone;
399 newBones.push_back( nbone);
400 // copy name and matrix
401 nbone->mName.Set( obone.mName);
402 nbone->mOffsetMatrix = obone.mOffsetMatrix;
403 nbone->mNumWeights = (unsigned int)newWeights.size();
404 nbone->mWeights = new aiVertexWeight[nbone->mNumWeights];
405 for( unsigned int d = 0; d < newWeights.size(); d++)
406 nbone->mWeights[d] = newWeights[d];
407 }
408
409 // store the bones in the mesh
410 mesh->mNumBones = (unsigned int)newBones.size();
411 if( newBones.size() > 0)
412 {
413 mesh->mBones = new aiBone*[mesh->mNumBones];
414 std::copy( newBones.begin(), newBones.end(), mesh->mBones);
415 }
416 }
417 }
418
419 // reallocate scene mesh array to be large enough
420 aiMesh** prevArray = pScene->mMeshes;
421 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()];
422 if( prevArray)
423 {
424 memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*));
425 delete [] prevArray;
426 }
427
428 // allocate mesh index array in the node
429 pNode->mNumMeshes = (unsigned int)meshes.size();
430 pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
431
432 // store all meshes in the mesh library of the scene and store their indices in the node
433 for( unsigned int a = 0; a < meshes.size(); a++)
434 {
435 pScene->mMeshes[pScene->mNumMeshes] = meshes[a];
436 pNode->mMeshes[a] = pScene->mNumMeshes;
437 pScene->mNumMeshes++;
438 }
439}
440
441// ------------------------------------------------------------------------------------------------
442// Converts the animations from the given imported data and creates them in the scene.
443void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData)
444{
445 std::vector<aiAnimation*> newAnims;
446
447 for( unsigned int a = 0; a < pData->mAnims.size(); a++)
448 {
449 const XFile::Animation* anim = pData->mAnims[a];
450 // some exporters mock me with empty animation tags.
451 if( anim->mAnims.size() == 0)
452 continue;
453
454 // create a new animation to hold the data
455 aiAnimation* nanim = new aiAnimation;
456 newAnims.push_back( nanim);
457 nanim->mName.Set( anim->mName);
458 // duration will be determined by the maximum length
459 nanim->mDuration = 0;
460 nanim->mTicksPerSecond = pData->mAnimTicksPerSecond;
461 nanim->mNumChannels = (unsigned int)anim->mAnims.size();
462 nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels];
463
464 for( unsigned int b = 0; b < anim->mAnims.size(); b++)
465 {
466 const XFile::AnimBone* bone = anim->mAnims[b];
467 aiNodeAnim* nbone = new aiNodeAnim;
468 nbone->mNodeName.Set( bone->mBoneName);
469 nanim->mChannels[b] = nbone;
470
471 // keyframes are given as combined transformation matrix keys
472 if( bone->mTrafoKeys.size() > 0)
473 {
474 nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size();
475 nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
476 nbone->mNumRotationKeys = (unsigned int)bone->mTrafoKeys.size();
477 nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
478 nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size();
479 nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
480
481 for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++)
482 {
483 // deconstruct each matrix into separate position, rotation and scaling
484 double time = bone->mTrafoKeys[c].mTime;
485 aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix;
486
487 // extract position
488 aiVector3D pos( trafo.a4, trafo.b4, trafo.c4);
489
490 nbone->mPositionKeys[c].mTime = time;
491 nbone->mPositionKeys[c].mValue = pos;
492
493 // extract scaling
494 aiVector3D scale;
495 scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length();
496 scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length();
497 scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length();
498 nbone->mScalingKeys[c].mTime = time;
499 nbone->mScalingKeys[c].mValue = scale;
500
501 // reconstruct rotation matrix without scaling
502 aiMatrix3x3 rotmat(
503 trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z,
504 trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z,
505 trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z);
506
507 // and convert it into a quaternion
508 nbone->mRotationKeys[c].mTime = time;
509 nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
510 }
511
512 // longest lasting key sequence determines duration
513 nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime);
514 } else
515 {
516 // separate key sequences for position, rotation, scaling
517 nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size();
518 nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
519 for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++)
520 {
521 aiVector3D pos = bone->mPosKeys[c].mValue;
522
523 nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime;
524 nbone->mPositionKeys[c].mValue = pos;
525 }
526
527 // rotation
528 nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size();
529 nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
530 for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++)
531 {
532 aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix();
533
534 nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime;
535 nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
536 nbone->mRotationKeys[c].mValue.w *= -1.0f; // needs quat inversion
537 }
538
539 // scaling
540 nbone->mNumScalingKeys = (unsigned int)bone->mScaleKeys.size();
541 nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
542 for( unsigned int c = 0; c < nbone->mNumScalingKeys; c++)
543 nbone->mScalingKeys[c] = bone->mScaleKeys[c];
544
545 // longest lasting key sequence determines duration
546 if( bone->mPosKeys.size() > 0)
547 nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime);
548 if( bone->mRotKeys.size() > 0)
549 nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime);
550 if( bone->mScaleKeys.size() > 0)
551 nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime);
552 }
553 }
554 }
555
556 // store all converted animations in the scene
557 if( newAnims.size() > 0)
558 {
559 pScene->mNumAnimations = (unsigned int)newAnims.size();
560 pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations];
561 for( unsigned int a = 0; a < newAnims.size(); a++)
562 pScene->mAnimations[a] = newAnims[a];
563 }
564}
565
566// ------------------------------------------------------------------------------------------------
567// Converts all materials in the given array and stores them in the scene's material list.
568void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector<XFile::Material>& pMaterials)
569{
570 // count the non-referrer materials in the array
571 unsigned int numNewMaterials = 0;
572 for( unsigned int a = 0; a < pMaterials.size(); a++)
573 if( !pMaterials[a].mIsReference)
574 numNewMaterials++;
575
576 // resize the scene's material list to offer enough space for the new materials
577 if( numNewMaterials > 0 )
578 {
579 aiMaterial** prevMats = pScene->mMaterials;
580 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials];
581 if( prevMats)
582 {
583 memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*));
584 delete [] prevMats;
585 }
586 }
587
588 // convert all the materials given in the array
589 for( unsigned int a = 0; a < pMaterials.size(); a++)
590 {
591 XFile::Material& oldMat = pMaterials[a];
592 if( oldMat.mIsReference)
593 {
594 // find the material it refers to by name, and store its index
595 for( size_t a = 0; a < pScene->mNumMaterials; ++a )
596 {
597 aiString name;
598 pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name);
599 if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 )
600 {
601 oldMat.sceneIndex = a;
602 break;
603 }
604 }
605
606 if( oldMat.sceneIndex == SIZE_MAX )
607 {
608 DefaultLogger::get()->warn( format() << "Could not resolve global material reference \"" << oldMat.mName << "\"" );
609 oldMat.sceneIndex = 0;
610 }
611
612 continue;
613 }
614
615 aiMaterial* mat = new aiMaterial;
616 aiString name;
617 name.Set( oldMat.mName);
618 mat->AddProperty( &name, AI_MATKEY_NAME);
619
620 // Shading model: hardcoded to PHONG, there is no such information in an XFile
621 // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix
622 // for some models in the SDK (e.g. good old tiny.x)
623 int shadeMode = (int)oldMat.mSpecularExponent == 0.0f
624 ? aiShadingMode_Gouraud : aiShadingMode_Phong;
625
626 mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
627 // material colours
628 // Unclear: there's no ambient colour, but emissive. What to put for ambient?
629 // Probably nothing at all, let the user select a suitable default.
630 mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
631 mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
632 mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
633 mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
634
635
636 // texture, if there is one
637 if (1 == oldMat.mTextures.size())
638 {
639 const XFile::TexEntry& otex = oldMat.mTextures.back();
640 if (otex.mName.length())
641 {
642 // if there is only one texture assume it contains the diffuse color
643 aiString tex( otex.mName);
644 if( otex.mIsNormalMap)
645 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0));
646 else
647 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
648 }
649 }
650 else
651 {
652 // Otherwise ... try to search for typical strings in the
653 // texture's file name like 'bump' or 'diffuse'
654 unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
655 for( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
656 {
657 const XFile::TexEntry& otex = oldMat.mTextures[b];
658 std::string sz = otex.mName;
659 if (!sz.length())continue;
660
661
662 // find the file name
663 //const size_t iLen = sz.length();
664 std::string::size_type s = sz.find_last_of("\\/");
665 if (std::string::npos == s)
666 s = 0;
667
668 // cut off the file extension
669 std::string::size_type sExt = sz.find_last_of('.');
670 if (std::string::npos != sExt){
671 sz[sExt] = '\0';
672 }
673
674 // convert to lower case for easier comparison
675 for( unsigned int c = 0; c < sz.length(); c++)
676 if( isalpha( sz[c]))
677 sz[c] = tolower( sz[c]);
678
679
680 // Place texture filename property under the corresponding name
681 aiString tex( oldMat.mTextures[b].mName);
682
683 // bump map
684 if (std::string::npos != sz.find("bump", s) || std::string::npos != sz.find("height", s))
685 {
686 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++));
687 } else
688 if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s))
689 {
690 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++));
691 } else
692 if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s))
693 {
694 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++));
695 } else
696 if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s))
697 {
698 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++));
699 } else
700 if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s))
701 {
702 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++));
703 } else
704 {
705 // Assume it is a diffuse texture
706 mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++));
707 }
708 }
709 }
710
711 pScene->mMaterials[pScene->mNumMaterials] = mat;
712 oldMat.sceneIndex = pScene->mNumMaterials;
713 pScene->mNumMaterials++;
714 }
715}
716
717#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
718