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