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