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