1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16   copyright notice, this list of conditions and the
17   following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20   copyright notice, this list of conditions and the
21   following disclaimer in the documentation and/or other
22   materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25   contributors may be used to endorse or promote products
26   derived from this software without specific prior
27   written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 /** @file  SMDLoader.cpp
44  *  @brief Implementation of the SMD importer class
45  */
46 
47 
48 #ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
49 
50 // internal headers
51 #include "SMDLoader.h"
52 #include "fast_atof.h"
53 #include "SkeletonMeshBuilder.h"
54 #include <assimp/Importer.hpp>
55 #include <assimp/IOSystem.hpp>
56 #include <assimp/scene.h>
57 #include <assimp/DefaultLogger.hpp>
58 #include <assimp/importerdesc.h>
59 #include <memory>
60 
61 using namespace Assimp;
62 
63 static const aiImporterDesc desc = {
64     "Valve SMD Importer",
65     "",
66     "",
67     "",
68     aiImporterFlags_SupportTextFlavour,
69     0,
70     0,
71     0,
72     0,
73     "smd vta"
74 };
75 
76 // ------------------------------------------------------------------------------------------------
77 // Constructor to be privately used by Importer
SMDImporter()78 SMDImporter::SMDImporter()
79 : configFrameID(),
80 mBuffer(),
81 pScene( nullptr ),
82 iFileSize( 0 ),
83 iSmallestFrame( -1 ),
84 dLengthOfAnim( 0.0 ),
85 bHasUVs(false ),
86 iLineNumber(-1) {
87     // empty
88 }
89 
90 // ------------------------------------------------------------------------------------------------
91 // Destructor, private as well
~SMDImporter()92 SMDImporter::~SMDImporter() {
93     // empty
94 }
95 
96 // ------------------------------------------------------------------------------------------------
97 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem *,bool) const98 bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const
99 {
100     // fixme: auto format detection
101     return SimpleExtensionCheck(pFile,"smd","vta");
102 }
103 
104 // ------------------------------------------------------------------------------------------------
105 // Get a list of all supported file extensions
GetInfo() const106 const aiImporterDesc* SMDImporter::GetInfo () const
107 {
108     return &desc;
109 }
110 
111 // ------------------------------------------------------------------------------------------------
112 // Setup configuration properties
SetupProperties(const Importer * pImp)113 void SMDImporter::SetupProperties(const Importer* pImp)
114 {
115     // The
116     // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
117     // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
118     configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,-1);
119     if(static_cast<unsigned int>(-1) == configFrameID)  {
120         configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
121     }
122 }
123 
124 // ------------------------------------------------------------------------------------------------
125 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)126 void SMDImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
127 {
128     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
129 
130     // Check whether we can read from the file
131     if( file.get() == NULL) {
132         throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + ".");
133     }
134 
135     iFileSize = (unsigned int)file->FileSize();
136 
137     // Allocate storage and copy the contents of the file to a memory buffer
138     this->pScene = pScene;
139 
140     mBuffer.resize( iFileSize + 1 );
141     TextFileToBuffer(file.get(), mBuffer );
142 
143     iSmallestFrame = (1 << 31);
144     bHasUVs = true;
145     iLineNumber = 1;
146 
147     // Reserve enough space for ... hm ... 10 textures
148     aszTextures.reserve(10);
149 
150     // Reserve enough space for ... hm ... 1000 triangles
151     asTriangles.reserve(1000);
152 
153     // Reserve enough space for ... hm ... 20 bones
154     asBones.reserve(20);
155 
156 
157     // parse the file ...
158     ParseFile();
159 
160     // If there are no triangles it seems to be an animation SMD,
161     // containing only the animation skeleton.
162     if (asTriangles.empty())
163     {
164         if (asBones.empty())
165         {
166             throw DeadlyImportError("SMD: No triangles and no bones have "
167                 "been found in the file. This file seems to be invalid.");
168         }
169 
170         // Set the flag in the scene structure which indicates
171         // that there is nothing than an animation skeleton
172         pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
173     }
174 
175     if (!asBones.empty())
176     {
177         // Check whether all bones have been initialized
178         for (std::vector<SMD::Bone>::const_iterator
179             i =  asBones.begin();
180             i != asBones.end();++i)
181         {
182             if (!(*i).mName.length())
183             {
184                 DefaultLogger::get()->warn("SMD: Not all bones have been initialized");
185                 break;
186             }
187         }
188 
189         // now fix invalid time values and make sure the animation starts at frame 0
190         FixTimeValues();
191 
192         // compute absolute bone transformation matrices
193     //  ComputeAbsoluteBoneTransformations();
194     }
195 
196     if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
197     {
198         // create output meshes
199         CreateOutputMeshes();
200 
201         // build an output material list
202         CreateOutputMaterials();
203     }
204 
205     // build the output animation
206     CreateOutputAnimations();
207 
208     // build output nodes (bones are added as empty dummy nodes)
209     CreateOutputNodes();
210 
211     if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)
212     {
213         SkeletonMeshBuilder skeleton(pScene);
214     }
215 }
216 // ------------------------------------------------------------------------------------------------
217 // Write an error message with line number to the log file
LogErrorNoThrow(const char * msg)218 void SMDImporter::LogErrorNoThrow(const char* msg)
219 {
220     char szTemp[1024];
221     ai_snprintf(szTemp,1024,"Line %u: %s",iLineNumber,msg);
222     DefaultLogger::get()->error(szTemp);
223 }
224 
225 // ------------------------------------------------------------------------------------------------
226 // Write a warning with line number to the log file
LogWarning(const char * msg)227 void SMDImporter::LogWarning(const char* msg)
228 {
229     char szTemp[1024];
230     ai_assert(strlen(msg) < 1000);
231     ai_snprintf(szTemp,1024,"Line %u: %s",iLineNumber,msg);
232     DefaultLogger::get()->warn(szTemp);
233 }
234 
235 // ------------------------------------------------------------------------------------------------
236 // Fix invalid time values in the file
FixTimeValues()237 void SMDImporter::FixTimeValues()
238 {
239     double dDelta = (double)iSmallestFrame;
240     double dMax = 0.0f;
241     for (std::vector<SMD::Bone>::iterator
242         iBone =  asBones.begin();
243         iBone != asBones.end();++iBone)
244     {
245         for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator
246             iKey =  (*iBone).sAnim.asKeys.begin();
247             iKey != (*iBone).sAnim.asKeys.end();++iKey)
248         {
249             (*iKey).dTime -= dDelta;
250             dMax = std::max(dMax, (*iKey).dTime);
251         }
252     }
253     dLengthOfAnim = dMax;
254 }
255 
256 // ------------------------------------------------------------------------------------------------
257 // create output meshes
CreateOutputMeshes()258 void SMDImporter::CreateOutputMeshes()
259 {
260     if (aszTextures.empty())
261         aszTextures.push_back(std::string());
262 
263     // we need to sort all faces by their material index
264     // in opposition to other loaders we can be sure that each
265     // material is at least used once.
266     pScene->mNumMeshes = (unsigned int) aszTextures.size();
267     pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
268 
269     typedef std::vector<unsigned int> FaceList;
270     FaceList* aaiFaces = new FaceList[pScene->mNumMeshes];
271 
272     // approximate the space that will be required
273     unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes;
274     iNum += iNum >> 1;
275     for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
276         aaiFaces[i].reserve(iNum);
277 
278 
279     // collect all faces
280     iNum = 0;
281     for (std::vector<SMD::Face>::const_iterator
282         iFace =  asTriangles.begin();
283         iFace != asTriangles.end();++iFace,++iNum)
284     {
285         if (UINT_MAX == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 );
286         else if ((*iFace).iTexture >= aszTextures.size())
287         {
288             DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face");
289             aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1);
290         }
291         else aaiFaces[(*iFace).iTexture].push_back(iNum);
292     }
293 
294     // now create the output meshes
295     for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
296     {
297         aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh();
298         ai_assert(!aaiFaces[i].empty()); // should not be empty ...
299 
300         pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
301         pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3;
302         pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size();
303         pcMesh->mMaterialIndex = i;
304 
305         // storage for bones
306         typedef std::pair<unsigned int,float> TempWeightListEntry;
307         typedef std::vector< TempWeightListEntry > TempBoneWeightList;
308 
309         TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()]();
310 
311         // try to reserve enough memory without wasting too much
312         for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
313         {
314             aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size());
315         }
316 
317         // allocate storage
318         pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
319         aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
320         aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
321 
322         aiVector3D* pcUVs = NULL;
323         if (bHasUVs)
324         {
325             pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
326             pcMesh->mNumUVComponents[0] = 2;
327         }
328 
329         iNum = 0;
330         for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace)
331         {
332             pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
333             pcMesh->mFaces[iFace].mNumIndices = 3;
334 
335             // fill the vertices
336             unsigned int iSrcFace = aaiFaces[i][iFace];
337             SMD::Face& face = asTriangles[iSrcFace];
338 
339             *pcVerts++ = face.avVertices[0].pos;
340             *pcVerts++ = face.avVertices[1].pos;
341             *pcVerts++ = face.avVertices[2].pos;
342 
343             // fill the normals
344             *pcNormals++ = face.avVertices[0].nor;
345             *pcNormals++ = face.avVertices[1].nor;
346             *pcNormals++ = face.avVertices[2].nor;
347 
348             // fill the texture coordinates
349             if (pcUVs)
350             {
351                 *pcUVs++ = face.avVertices[0].uv;
352                 *pcUVs++ = face.avVertices[1].uv;
353                 *pcUVs++ = face.avVertices[2].uv;
354             }
355 
356             for (unsigned int iVert = 0; iVert < 3;++iVert)
357             {
358                 float fSum = 0.0f;
359                 for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
360                 {
361                     TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
362 
363                     // FIX: The second check is here just to make sure we won't
364                     // assign more than one weight to a single vertex index
365                     if (pairval.first >= asBones.size() ||
366                         pairval.first == face.avVertices[iVert].iParentNode)
367                     {
368                         DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
369                             "The bone index will be ignored, the weight will be assigned "
370                             "to the vertex' parent node");
371                         continue;
372                     }
373                     aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second));
374                     fSum += pairval.second;
375                 }
376                 // ******************************************************************
377                 // If the sum of all vertex weights is not 1.0 we must assign
378                 // the rest to the vertex' parent node. Well, at least the doc says
379                 // we should ...
380                 // FIX: We use 0.975 as limit, floating-point inaccuracies seem to
381                 // be very strong in some SMD exporters. Furthermore it is possible
382                 // that the parent of a vertex is 0xffffffff (if the corresponding
383                 // entry in the file was unreadable)
384                 // ******************************************************************
385                 if (fSum < 0.975f && face.avVertices[iVert].iParentNode != UINT_MAX)
386                 {
387                     if (face.avVertices[iVert].iParentNode >= asBones.size())
388                     {
389                         DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
390                             "The index of the vertex parent bone is invalid. "
391                             "The remaining weights will be normalized to 1.0");
392 
393                         if (fSum)
394                         {
395                             fSum = 1 / fSum;
396                             for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
397                             {
398                                 TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
399                                 if (pairval.first >= asBones.size())continue;
400                                 aaiBones[pairval.first].back().second *= fSum;
401                             }
402                         }
403                     }
404                     else
405                     {
406                         aaiBones[face.avVertices[iVert].iParentNode].push_back(
407                             TempWeightListEntry(iNum,1.0f-fSum));
408                     }
409                 }
410                 pcMesh->mFaces[iFace].mIndices[iVert] = iNum++;
411             }
412         }
413 
414         // now build all bones of the mesh
415         iNum = 0;
416         for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
417             if (!aaiBones[iBone].empty())++iNum;
418 
419         if (false && iNum)
420         {
421             pcMesh->mNumBones = iNum;
422             pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
423             iNum = 0;
424             for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
425             {
426                 if (aaiBones[iBone].empty())continue;
427                 aiBone*& bone = pcMesh->mBones[iNum] = new aiBone();
428 
429                 bone->mNumWeights = (unsigned int)aaiBones[iBone].size();
430                 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
431                 bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix;
432                 bone->mName.Set( asBones[iBone].mName );
433 
434                 asBones[iBone].bIsUsed = true;
435 
436                 for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight)
437                 {
438                     bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first;
439                     bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second;
440                 }
441                 ++iNum;
442             }
443         }
444         delete[] aaiBones;
445     }
446     delete[] aaiFaces;
447 }
448 
449 // ------------------------------------------------------------------------------------------------
450 // add bone child nodes
AddBoneChildren(aiNode * pcNode,uint32_t iParent)451 void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent)
452 {
453     ai_assert( NULL != pcNode );
454     ai_assert( 0 == pcNode->mNumChildren );
455     ai_assert( NULL == pcNode->mChildren);
456 
457     // first count ...
458     for (unsigned int i = 0; i < asBones.size();++i)
459     {
460         SMD::Bone& bone = asBones[i];
461         if (bone.iParent == iParent)++pcNode->mNumChildren;
462     }
463 
464     // now allocate the output array
465     pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
466 
467     // and fill all subnodes
468     unsigned int qq = 0;
469     for (unsigned int i = 0; i < asBones.size();++i)
470     {
471         SMD::Bone& bone = asBones[i];
472         if (bone.iParent != iParent)continue;
473 
474         aiNode* pc = pcNode->mChildren[qq++] = new aiNode();
475         pc->mName.Set(bone.mName);
476 
477         // store the local transformation matrix of the bind pose
478         pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix;
479         pc->mParent = pcNode;
480 
481         // add children to this node, too
482         AddBoneChildren(pc,i);
483     }
484 }
485 
486 // ------------------------------------------------------------------------------------------------
487 // create output nodes
CreateOutputNodes()488 void SMDImporter::CreateOutputNodes()
489 {
490     pScene->mRootNode = new aiNode();
491     if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
492     {
493         // create one root node that renders all meshes
494         pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
495         pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
496         for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
497             pScene->mRootNode->mMeshes[i] = i;
498     }
499 
500     // now add all bones as dummy sub nodes to the graph
501     // AddBoneChildren(pScene->mRootNode,(uint32_t)-1);
502 
503     // if we have only one bone we can even remove the root node
504     if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE &&
505         1 == pScene->mRootNode->mNumChildren)
506     {
507         aiNode* pcOldRoot = pScene->mRootNode;
508         pScene->mRootNode = pcOldRoot->mChildren[0];
509         pcOldRoot->mChildren[0] = NULL;
510         delete pcOldRoot;
511 
512         pScene->mRootNode->mParent = NULL;
513     }
514     else
515     {
516         ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>");
517         pScene->mRootNode->mName.length = 10;
518     }
519 }
520 
521 // ------------------------------------------------------------------------------------------------
522 // create output animations
CreateOutputAnimations()523 void SMDImporter::CreateOutputAnimations()
524 {
525     unsigned int iNumBones = 0;
526     for (std::vector<SMD::Bone>::const_iterator
527         i =  asBones.begin();
528         i != asBones.end();++i)
529     {
530         if ((*i).bIsUsed)++iNumBones;
531     }
532     if (!iNumBones)
533     {
534         // just make sure this case doesn't occur ... (it could occur
535         // if the file was invalid)
536         return;
537     }
538 
539     pScene->mNumAnimations = 1;
540     pScene->mAnimations = new aiAnimation*[1];
541     aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation();
542 
543     anim->mDuration = dLengthOfAnim;
544     anim->mNumChannels = iNumBones;
545     anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
546 
547     aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
548 
549     // now build valid keys
550     unsigned int a = 0;
551     for (std::vector<SMD::Bone>::const_iterator
552         i =  asBones.begin();
553         i != asBones.end();++i)
554     {
555         if (!(*i).bIsUsed)continue;
556 
557         aiNodeAnim* p = pp[a] = new aiNodeAnim();
558 
559         // copy the name of the bone
560         p->mNodeName.Set( i->mName);
561 
562         p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size();
563         if (p->mNumRotationKeys)
564         {
565             p->mNumPositionKeys = p->mNumRotationKeys;
566             aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys];
567             aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys];
568 
569             for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator
570                 qq =  (*i).sAnim.asKeys.begin();
571                 qq != (*i).sAnim.asKeys.end(); ++qq)
572             {
573                 pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime;
574 
575                 // compute the rotation quaternion from the euler angles
576                 pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z );
577                 pVecKeys->mValue = (*qq).vPos;
578 
579                 ++pVecKeys; ++pRotKeys;
580             }
581         }
582         ++a;
583 
584         // there are no scaling keys ...
585     }
586 }
587 
588 // ------------------------------------------------------------------------------------------------
ComputeAbsoluteBoneTransformations()589 void SMDImporter::ComputeAbsoluteBoneTransformations()
590 {
591     // For each bone: determine the key with the lowest time value
592     // theoretically the SMD format should have all keyframes
593     // in order. However, I've seen a file where this wasn't true.
594     for (unsigned int i = 0; i < asBones.size();++i)
595     {
596         SMD::Bone& bone = asBones[i];
597 
598         uint32_t iIndex = 0;
599         double dMin = 10e10;
600         for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i)
601         {
602             double d = std::min(bone.sAnim.asKeys[i].dTime,dMin);
603             if (d < dMin)
604             {
605                 dMin = d;
606                 iIndex = i;
607             }
608         }
609         bone.sAnim.iFirstTimeKey = iIndex;
610     }
611 
612     unsigned int iParent = 0;
613     while (iParent < asBones.size())
614     {
615         for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
616         {
617             SMD::Bone& bone = asBones[iBone];
618 
619             if (iParent == bone.iParent)
620             {
621                 SMD::Bone& parentBone = asBones[iParent];
622 
623 
624                 uint32_t iIndex = bone.sAnim.iFirstTimeKey;
625                 const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix;
626                 aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute;
627 
628                 // The same for the parent bone ...
629                 iIndex = parentBone.sAnim.iFirstTimeKey;
630                 const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute;
631 
632                 // Compute the absolute transformation matrix
633                 matOut = mat * mat2;
634             }
635         }
636         ++iParent;
637     }
638 
639     // Store the inverse of the absolute transformation matrix
640     // of the first key as bone offset matrix
641     for (iParent = 0; iParent < asBones.size();++iParent)
642     {
643         SMD::Bone& bone = asBones[iParent];
644         bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute;
645         bone.mOffsetMatrix.Inverse();
646     }
647 }
648 \
649 // ------------------------------------------------------------------------------------------------
650 // create output materials
CreateOutputMaterials()651 void SMDImporter::CreateOutputMaterials()
652 {
653     ai_assert( nullptr != pScene );
654 
655     pScene->mNumMaterials = (unsigned int)aszTextures.size();
656     pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)];
657 
658     for (unsigned int iMat = 0; iMat < pScene->mNumMaterials; ++iMat) {
659         aiMaterial* pcMat = new aiMaterial();
660         ai_assert( nullptr != pcMat );
661         pScene->mMaterials[iMat] = pcMat;
662 
663         aiString szName;
664         szName.length = (size_t)ai_snprintf(szName.data,MAXLEN,"Texture_%u",iMat);
665         pcMat->AddProperty(&szName,AI_MATKEY_NAME);
666 
667         if (aszTextures[iMat].length())
668         {
669             ::strncpy(szName.data, aszTextures[iMat].c_str(),MAXLEN-1);
670             szName.length = aszTextures[iMat].length();
671             pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0));
672         }
673     }
674 
675     // create a default material if necessary
676     if (0 == pScene->mNumMaterials)
677     {
678         pScene->mNumMaterials = 1;
679 
680         aiMaterial* pcHelper = new aiMaterial();
681         pScene->mMaterials[0] = pcHelper;
682 
683         int iMode = (int)aiShadingMode_Gouraud;
684         pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
685 
686         aiColor3D clr;
687         clr.b = clr.g = clr.r = 0.7f;
688         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
689         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
690 
691         clr.b = clr.g = clr.r = 0.05f;
692         pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
693 
694         aiString szName;
695         szName.Set(AI_DEFAULT_MATERIAL_NAME);
696         pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
697     }
698 }
699 
700 // ------------------------------------------------------------------------------------------------
701 // Parse the file
ParseFile()702 void SMDImporter::ParseFile()
703 {
704     const char* szCurrent = &mBuffer[0];
705 
706     // read line per line ...
707     for ( ;; )
708     {
709         if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
710 
711         // "version <n> \n", <n> should be 1 for hl and hl2 SMD files
712         if (TokenMatch(szCurrent,"version",7))
713         {
714             if(!SkipSpaces(szCurrent,&szCurrent)) break;
715             if (1 != strtoul10(szCurrent,&szCurrent))
716             {
717                 DefaultLogger::get()->warn("SMD.version is not 1. This "
718                     "file format is not known. Continuing happily ...");
719             }
720             continue;
721         }
722         // "nodes\n" - Starts the node section
723         if (TokenMatch(szCurrent,"nodes",5))
724         {
725             ParseNodesSection(szCurrent,&szCurrent);
726             continue;
727         }
728         // "triangles\n" - Starts the triangle section
729         if (TokenMatch(szCurrent,"triangles",9))
730         {
731             ParseTrianglesSection(szCurrent,&szCurrent);
732             continue;
733         }
734         // "vertexanimation\n" - Starts the vertex animation section
735         if (TokenMatch(szCurrent,"vertexanimation",15))
736         {
737             bHasUVs = false;
738             ParseVASection(szCurrent,&szCurrent);
739             continue;
740         }
741         // "skeleton\n" - Starts the skeleton section
742         if (TokenMatch(szCurrent,"skeleton",8))
743         {
744             ParseSkeletonSection(szCurrent,&szCurrent);
745             continue;
746         }
747         SkipLine(szCurrent,&szCurrent);
748     }
749     return;
750 }
751 
752 // ------------------------------------------------------------------------------------------------
GetTextureIndex(const std::string & filename)753 unsigned int SMDImporter::GetTextureIndex(const std::string& filename)
754 {
755     unsigned int iIndex = 0;
756     for (std::vector<std::string>::const_iterator
757         i =  aszTextures.begin();
758         i != aszTextures.end();++i,++iIndex)
759     {
760         // case-insensitive ... it's a path
761         if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex;
762     }
763     iIndex = (unsigned int)aszTextures.size();
764     aszTextures.push_back(filename);
765     return iIndex;
766 }
767 
768 // ------------------------------------------------------------------------------------------------
769 // Parse the nodes section of the file
ParseNodesSection(const char * szCurrent,const char ** szCurrentOut)770 void SMDImporter::ParseNodesSection(const char* szCurrent,
771     const char** szCurrentOut)
772 {
773     for ( ;; )
774     {
775         // "end\n" - Ends the nodes section
776         if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
777             IsSpaceOrNewLine(*(szCurrent+3)))
778         {
779             szCurrent += 4;
780             break;
781         }
782         ParseNodeInfo(szCurrent,&szCurrent);
783     }
784     SkipSpacesAndLineEnd(szCurrent,&szCurrent);
785     *szCurrentOut = szCurrent;
786 }
787 
788 // ------------------------------------------------------------------------------------------------
789 // Parse the triangles section of the file
ParseTrianglesSection(const char * szCurrent,const char ** szCurrentOut)790 void SMDImporter::ParseTrianglesSection(const char* szCurrent,
791     const char** szCurrentOut)
792 {
793     // Parse a triangle, parse another triangle, parse the next triangle ...
794     // and so on until we reach a token that looks quite similar to "end"
795     for ( ;; )
796     {
797         if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
798 
799         // "end\n" - Ends the triangles section
800         if (TokenMatch(szCurrent,"end",3))
801             break;
802         ParseTriangle(szCurrent,&szCurrent);
803     }
804     SkipSpacesAndLineEnd(szCurrent,&szCurrent);
805     *szCurrentOut = szCurrent;
806 }
807 // ------------------------------------------------------------------------------------------------
808 // Parse the vertex animation section of the file
ParseVASection(const char * szCurrent,const char ** szCurrentOut)809 void SMDImporter::ParseVASection(const char* szCurrent,
810     const char** szCurrentOut)
811 {
812     unsigned int iCurIndex = 0;
813     for ( ;; )
814     {
815         if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
816 
817         // "end\n" - Ends the "vertexanimation" section
818         if (TokenMatch(szCurrent,"end",3))
819             break;
820 
821         // "time <n>\n"
822         if (TokenMatch(szCurrent,"time",4))
823         {
824             // NOTE: The doc says that time values COULD be negative ...
825             // NOTE2: this is the shape key -> valve docs
826             int iTime = 0;
827             if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break;
828             SkipLine(szCurrent,&szCurrent);
829         }
830         else
831         {
832             if(0 == iCurIndex)
833             {
834                 asTriangles.push_back(SMD::Face());
835             }
836             if (++iCurIndex == 3)iCurIndex = 0;
837             ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true);
838         }
839     }
840 
841     if (iCurIndex != 2 && !asTriangles.empty())
842     {
843         // we want to no degenerates, so throw this triangle away
844         asTriangles.pop_back();
845     }
846 
847     SkipSpacesAndLineEnd(szCurrent,&szCurrent);
848     *szCurrentOut = szCurrent;
849 }
850 // ------------------------------------------------------------------------------------------------
851 // Parse the skeleton section of the file
ParseSkeletonSection(const char * szCurrent,const char ** szCurrentOut)852 void SMDImporter::ParseSkeletonSection(const char* szCurrent,
853     const char** szCurrentOut)
854 {
855     int iTime = 0;
856     for ( ;; )
857     {
858         if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
859 
860         // "end\n" - Ends the skeleton section
861         if (TokenMatch(szCurrent,"end",3))
862             break;
863 
864         // "time <n>\n" - Specifies the current animation frame
865         else if (TokenMatch(szCurrent,"time",4))
866         {
867             // NOTE: The doc says that time values COULD be negative ...
868             if(!ParseSignedInt(szCurrent,&szCurrent,iTime))break;
869 
870             iSmallestFrame = std::min(iSmallestFrame,iTime);
871             SkipLine(szCurrent,&szCurrent);
872         }
873         else ParseSkeletonElement(szCurrent,&szCurrent,iTime);
874     }
875     *szCurrentOut = szCurrent;
876 }
877 
878 // ------------------------------------------------------------------------------------------------
879 #define SMDI_PARSE_RETURN { \
880     SkipLine(szCurrent,&szCurrent); \
881     *szCurrentOut = szCurrent; \
882     return; \
883 }
884 // ------------------------------------------------------------------------------------------------
885 // Parse a node line
ParseNodeInfo(const char * szCurrent,const char ** szCurrentOut)886 void SMDImporter::ParseNodeInfo(const char* szCurrent,
887     const char** szCurrentOut)
888 {
889     unsigned int iBone  = 0;
890     SkipSpacesAndLineEnd(szCurrent,&szCurrent);
891     if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent))
892     {
893         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index");
894         SMDI_PARSE_RETURN;
895     }
896     // add our bone to the list
897     if (iBone >= asBones.size())asBones.resize(iBone+1);
898     SMD::Bone& bone = asBones[iBone];
899 
900     bool bQuota = true;
901     if ('\"' != *szCurrent)
902     {
903         LogWarning("Bone name is expcted to be enclosed in "
904             "double quotation marks. ");
905         bQuota = false;
906     }
907     else ++szCurrent;
908 
909     const char* szEnd = szCurrent;
910     for ( ;; )
911     {
912         if (bQuota && '\"' == *szEnd)
913         {
914             iBone = (unsigned int)(szEnd - szCurrent);
915             ++szEnd;
916             break;
917         }
918         else if (IsSpaceOrNewLine(*szEnd))
919         {
920             iBone = (unsigned int)(szEnd - szCurrent);
921             break;
922         }
923         else if (!(*szEnd))
924         {
925             LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name");
926             SMDI_PARSE_RETURN;
927         }
928         ++szEnd;
929     }
930     bone.mName = std::string(szCurrent,iBone);
931     szCurrent = szEnd;
932 
933     // the only negative bone parent index that could occur is -1 AFAIK
934     if(!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent))
935     {
936         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1");
937         SMDI_PARSE_RETURN;
938     }
939 
940     // go to the beginning of the next line
941     SMDI_PARSE_RETURN;
942 }
943 
944 // ------------------------------------------------------------------------------------------------
945 // Parse a skeleton element
ParseSkeletonElement(const char * szCurrent,const char ** szCurrentOut,int iTime)946 void SMDImporter::ParseSkeletonElement(const char* szCurrent,
947     const char** szCurrentOut,int iTime)
948 {
949     aiVector3D vPos;
950     aiVector3D vRot;
951 
952     unsigned int iBone  = 0;
953     if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone))
954     {
955         DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index");
956         SMDI_PARSE_RETURN;
957     }
958     if (iBone >= asBones.size())
959     {
960         LogErrorNoThrow("Bone index in skeleton section is out of range");
961         SMDI_PARSE_RETURN;
962     }
963     SMD::Bone& bone = asBones[iBone];
964 
965     bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey());
966     SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back();
967 
968     key.dTime = (double)iTime;
969     if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x))
970     {
971         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x");
972         SMDI_PARSE_RETURN;
973     }
974     if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y))
975     {
976         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y");
977         SMDI_PARSE_RETURN;
978     }
979     if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z))
980     {
981         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z");
982         SMDI_PARSE_RETURN;
983     }
984     if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x))
985     {
986         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x");
987         SMDI_PARSE_RETURN;
988     }
989     if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y))
990     {
991         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y");
992         SMDI_PARSE_RETURN;
993     }
994     if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z))
995     {
996         LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z");
997         SMDI_PARSE_RETURN;
998     }
999     // build the transformation matrix of the key
1000     key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z);
1001     {
1002         aiMatrix4x4 mTemp;
1003         mTemp.a4 = vPos.x;
1004         mTemp.b4 = vPos.y;
1005         mTemp.c4 = vPos.z;
1006         key.matrix = key.matrix * mTemp;
1007     }
1008 
1009     // go to the beginning of the next line
1010     SMDI_PARSE_RETURN;
1011 }
1012 
1013 // ------------------------------------------------------------------------------------------------
1014 // Parse a triangle
ParseTriangle(const char * szCurrent,const char ** szCurrentOut)1015 void SMDImporter::ParseTriangle(const char* szCurrent,
1016     const char** szCurrentOut)
1017 {
1018     asTriangles.push_back(SMD::Face());
1019     SMD::Face& face = asTriangles.back();
1020 
1021     if(!SkipSpaces(szCurrent,&szCurrent))
1022     {
1023         LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle");
1024         return;
1025     }
1026 
1027     // read the texture file name
1028     const char* szLast = szCurrent;
1029     while (!IsSpaceOrNewLine(*++szCurrent));
1030 
1031     // ... and get the index that belongs to this file name
1032     face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast));
1033 
1034     SkipSpacesAndLineEnd(szCurrent,&szCurrent);
1035 
1036     // load three vertices
1037     for (unsigned int iVert = 0; iVert < 3;++iVert)
1038     {
1039         ParseVertex(szCurrent,&szCurrent,
1040             face.avVertices[iVert]);
1041     }
1042     *szCurrentOut = szCurrent;
1043 }
1044 
1045 // ------------------------------------------------------------------------------------------------
1046 // Parse a float
ParseFloat(const char * szCurrent,const char ** szCurrentOut,float & out)1047 bool SMDImporter::ParseFloat(const char* szCurrent,
1048     const char** szCurrentOut, float& out)
1049 {
1050     if(!SkipSpaces(&szCurrent))
1051         return false;
1052 
1053     *szCurrentOut = fast_atoreal_move<float>(szCurrent,out);
1054     return true;
1055 }
1056 
1057 // ------------------------------------------------------------------------------------------------
1058 // Parse an unsigned int
ParseUnsignedInt(const char * szCurrent,const char ** szCurrentOut,unsigned int & out)1059 bool SMDImporter::ParseUnsignedInt(const char* szCurrent,
1060     const char** szCurrentOut, unsigned int& out)
1061 {
1062     if(!SkipSpaces(&szCurrent))
1063         return false;
1064 
1065     out = strtoul10(szCurrent,szCurrentOut);
1066     return true;
1067 }
1068 
1069 // ------------------------------------------------------------------------------------------------
1070 // Parse a signed int
ParseSignedInt(const char * szCurrent,const char ** szCurrentOut,int & out)1071 bool SMDImporter::ParseSignedInt(const char* szCurrent,
1072     const char** szCurrentOut, int& out)
1073 {
1074     if(!SkipSpaces(&szCurrent))
1075         return false;
1076 
1077     out = strtol10(szCurrent,szCurrentOut);
1078     return true;
1079 }
1080 
1081 // ------------------------------------------------------------------------------------------------
1082 // Parse a vertex
ParseVertex(const char * szCurrent,const char ** szCurrentOut,SMD::Vertex & vertex,bool bVASection)1083 void SMDImporter::ParseVertex(const char* szCurrent,
1084     const char** szCurrentOut, SMD::Vertex& vertex,
1085     bool bVASection /*= false*/)
1086 {
1087     if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent))
1088     {
1089         SkipSpacesAndLineEnd(szCurrent,&szCurrent);
1090         return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection);
1091     }
1092     if(!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode))
1093     {
1094         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent");
1095         SMDI_PARSE_RETURN;
1096     }
1097     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x))
1098     {
1099         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x");
1100         SMDI_PARSE_RETURN;
1101     }
1102     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y))
1103     {
1104         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y");
1105         SMDI_PARSE_RETURN;
1106     }
1107     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z))
1108     {
1109         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z");
1110         SMDI_PARSE_RETURN;
1111     }
1112     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x))
1113     {
1114         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x");
1115         SMDI_PARSE_RETURN;
1116     }
1117     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y))
1118     {
1119         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y");
1120         SMDI_PARSE_RETURN;
1121     }
1122     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z))
1123     {
1124         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z");
1125         SMDI_PARSE_RETURN;
1126     }
1127 
1128     if (bVASection)SMDI_PARSE_RETURN;
1129 
1130     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x))
1131     {
1132         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x");
1133         SMDI_PARSE_RETURN;
1134     }
1135     if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y))
1136     {
1137         LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y");
1138         SMDI_PARSE_RETURN;
1139     }
1140 
1141     // now read the number of bones affecting this vertex
1142     // all elements from now are fully optional, we don't need them
1143     unsigned int iSize = 0;
1144     if(!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN;
1145     vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f));
1146 
1147     for (std::vector<std::pair<unsigned int, float> >::iterator
1148         i =  vertex.aiBoneLinks.begin();
1149         i != vertex.aiBoneLinks.end();++i)
1150     {
1151         if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first))
1152             SMDI_PARSE_RETURN;
1153         if(!ParseFloat(szCurrent,&szCurrent,(*i).second))
1154             SMDI_PARSE_RETURN;
1155     }
1156 
1157     // go to the beginning of the next line
1158     SMDI_PARSE_RETURN;
1159 }
1160 
1161 #endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER
1162