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 |
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 |
92 | SMDImporter::~SMDImporter() { |
93 | // empty |
94 | } |
95 | |
96 | // ------------------------------------------------------------------------------------------------ |
97 | // Returns whether the class can handle the format of the given file. |
98 | 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 |
106 | const aiImporterDesc* SMDImporter::GetInfo () const |
107 | { |
108 | return &desc; |
109 | } |
110 | |
111 | // ------------------------------------------------------------------------------------------------ |
112 | // Setup configuration properties |
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. |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | // ------------------------------------------------------------------------------------------------ |
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 |
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 |
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 | // ------------------------------------------------------------------------------------------------ |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |