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