vrshoot

view libs/assimp/SMDLoader.cpp @ 0:b2f14e535253

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