vrshoot
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/SMDLoader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,1141 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file SMDLoader.cpp 1.46 + * @brief Implementation of the SMD importer class 1.47 + */ 1.48 + 1.49 +#include "AssimpPCH.h" 1.50 +#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER 1.51 + 1.52 +// internal headers 1.53 +#include "SMDLoader.h" 1.54 +#include "fast_atof.h" 1.55 +#include "SkeletonMeshBuilder.h" 1.56 + 1.57 +using namespace Assimp; 1.58 + 1.59 +static const aiImporterDesc desc = { 1.60 + "Valve SMD Importer", 1.61 + "", 1.62 + "", 1.63 + "", 1.64 + aiImporterFlags_SupportTextFlavour, 1.65 + 0, 1.66 + 0, 1.67 + 0, 1.68 + 0, 1.69 + "smd vta" 1.70 +}; 1.71 + 1.72 +// ------------------------------------------------------------------------------------------------ 1.73 +// Constructor to be privately used by Importer 1.74 +SMDImporter::SMDImporter() 1.75 +{} 1.76 + 1.77 +// ------------------------------------------------------------------------------------------------ 1.78 +// Destructor, private as well 1.79 +SMDImporter::~SMDImporter() 1.80 +{} 1.81 + 1.82 +// ------------------------------------------------------------------------------------------------ 1.83 +// Returns whether the class can handle the format of the given file. 1.84 +bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const 1.85 +{ 1.86 + // fixme: auto format detection 1.87 + return SimpleExtensionCheck(pFile,"smd","vta"); 1.88 +} 1.89 + 1.90 +// ------------------------------------------------------------------------------------------------ 1.91 +// Get a list of all supported file extensions 1.92 +const aiImporterDesc* SMDImporter::GetInfo () const 1.93 +{ 1.94 + return &desc; 1.95 +} 1.96 + 1.97 +// ------------------------------------------------------------------------------------------------ 1.98 +// Setup configuration properties 1.99 +void SMDImporter::SetupProperties(const Importer* pImp) 1.100 +{ 1.101 + // The 1.102 + // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the 1.103 + // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. 1.104 + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,-1); 1.105 + if(static_cast<unsigned int>(-1) == configFrameID) { 1.106 + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); 1.107 + } 1.108 +} 1.109 + 1.110 +// ------------------------------------------------------------------------------------------------ 1.111 +// Imports the given file into the given scene structure. 1.112 +void SMDImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) 1.113 +{ 1.114 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); 1.115 + 1.116 + // Check whether we can read from the file 1.117 + if( file.get() == NULL) { 1.118 + throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + "."); 1.119 + } 1.120 + 1.121 + iFileSize = (unsigned int)file->FileSize(); 1.122 + 1.123 + // Allocate storage and copy the contents of the file to a memory buffer 1.124 + this->pScene = pScene; 1.125 + 1.126 + std::vector<char> buff(iFileSize+1); 1.127 + TextFileToBuffer(file.get(),buff); 1.128 + mBuffer = &buff[0]; 1.129 + 1.130 + iSmallestFrame = (1 << 31); 1.131 + bHasUVs = true; 1.132 + iLineNumber = 1; 1.133 + 1.134 + // Reserve enough space for ... hm ... 10 textures 1.135 + aszTextures.reserve(10); 1.136 + 1.137 + // Reserve enough space for ... hm ... 1000 triangles 1.138 + asTriangles.reserve(1000); 1.139 + 1.140 + // Reserve enough space for ... hm ... 20 bones 1.141 + asBones.reserve(20); 1.142 + 1.143 + 1.144 + // parse the file ... 1.145 + ParseFile(); 1.146 + 1.147 + // If there are no triangles it seems to be an animation SMD, 1.148 + // containing only the animation skeleton. 1.149 + if (asTriangles.empty()) 1.150 + { 1.151 + if (asBones.empty()) 1.152 + { 1.153 + throw DeadlyImportError("SMD: No triangles and no bones have " 1.154 + "been found in the file. This file seems to be invalid."); 1.155 + } 1.156 + 1.157 + // Set the flag in the scene structure which indicates 1.158 + // that there is nothing than an animation skeleton 1.159 + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; 1.160 + } 1.161 + 1.162 + if (!asBones.empty()) 1.163 + { 1.164 + // Check whether all bones have been initialized 1.165 + for (std::vector<SMD::Bone>::const_iterator 1.166 + i = asBones.begin(); 1.167 + i != asBones.end();++i) 1.168 + { 1.169 + if (!(*i).mName.length()) 1.170 + { 1.171 + DefaultLogger::get()->warn("SMD: Not all bones have been initialized"); 1.172 + break; 1.173 + } 1.174 + } 1.175 + 1.176 + // now fix invalid time values and make sure the animation starts at frame 0 1.177 + FixTimeValues(); 1.178 + 1.179 + // compute absolute bone transformation matrices 1.180 + // ComputeAbsoluteBoneTransformations(); 1.181 + } 1.182 + 1.183 + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) 1.184 + { 1.185 + // create output meshes 1.186 + CreateOutputMeshes(); 1.187 + 1.188 + // build an output material list 1.189 + CreateOutputMaterials(); 1.190 + } 1.191 + 1.192 + // build the output animation 1.193 + CreateOutputAnimations(); 1.194 + 1.195 + // build output nodes (bones are added as empty dummy nodes) 1.196 + CreateOutputNodes(); 1.197 + 1.198 + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) 1.199 + { 1.200 + SkeletonMeshBuilder skeleton(pScene); 1.201 + } 1.202 +} 1.203 +// ------------------------------------------------------------------------------------------------ 1.204 +// Write an error message with line number to the log file 1.205 +void SMDImporter::LogErrorNoThrow(const char* msg) 1.206 +{ 1.207 + char szTemp[1024]; 1.208 + sprintf(szTemp,"Line %i: %s",iLineNumber,msg); 1.209 + DefaultLogger::get()->error(szTemp); 1.210 +} 1.211 + 1.212 +// ------------------------------------------------------------------------------------------------ 1.213 +// Write a warning with line number to the log file 1.214 +void SMDImporter::LogWarning(const char* msg) 1.215 +{ 1.216 + char szTemp[1024]; 1.217 + ai_assert(strlen(msg) < 1000); 1.218 + sprintf(szTemp,"Line %i: %s",iLineNumber,msg); 1.219 + DefaultLogger::get()->warn(szTemp); 1.220 +} 1.221 + 1.222 +// ------------------------------------------------------------------------------------------------ 1.223 +// Fix invalid time values in the file 1.224 +void SMDImporter::FixTimeValues() 1.225 +{ 1.226 + double dDelta = (double)iSmallestFrame; 1.227 + double dMax = 0.0f; 1.228 + for (std::vector<SMD::Bone>::iterator 1.229 + iBone = asBones.begin(); 1.230 + iBone != asBones.end();++iBone) 1.231 + { 1.232 + for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator 1.233 + iKey = (*iBone).sAnim.asKeys.begin(); 1.234 + iKey != (*iBone).sAnim.asKeys.end();++iKey) 1.235 + { 1.236 + (*iKey).dTime -= dDelta; 1.237 + dMax = std::max(dMax, (*iKey).dTime); 1.238 + } 1.239 + } 1.240 + dLengthOfAnim = dMax; 1.241 +} 1.242 + 1.243 +// ------------------------------------------------------------------------------------------------ 1.244 +// create output meshes 1.245 +void SMDImporter::CreateOutputMeshes() 1.246 +{ 1.247 + if (aszTextures.empty()) 1.248 + aszTextures.push_back(std::string()); 1.249 + 1.250 + // we need to sort all faces by their material index 1.251 + // in opposition to other loaders we can be sure that each 1.252 + // material is at least used once. 1.253 + pScene->mNumMeshes = (unsigned int) aszTextures.size(); 1.254 + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; 1.255 + 1.256 + typedef std::vector<unsigned int> FaceList; 1.257 + FaceList* aaiFaces = new FaceList[pScene->mNumMeshes]; 1.258 + 1.259 + // approximate the space that will be required 1.260 + unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes; 1.261 + iNum += iNum >> 1; 1.262 + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) 1.263 + aaiFaces[i].reserve(iNum); 1.264 + 1.265 + 1.266 + // collect all faces 1.267 + iNum = 0; 1.268 + for (std::vector<SMD::Face>::const_iterator 1.269 + iFace = asTriangles.begin(); 1.270 + iFace != asTriangles.end();++iFace,++iNum) 1.271 + { 1.272 + if (UINT_MAX == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 ); 1.273 + else if ((*iFace).iTexture >= aszTextures.size()) 1.274 + { 1.275 + DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face"); 1.276 + aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1); 1.277 + } 1.278 + else aaiFaces[(*iFace).iTexture].push_back(iNum); 1.279 + } 1.280 + 1.281 + // now create the output meshes 1.282 + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) 1.283 + { 1.284 + aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh(); 1.285 + ai_assert(!aaiFaces[i].empty()); // should not be empty ... 1.286 + 1.287 + pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; 1.288 + pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3; 1.289 + pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size(); 1.290 + pcMesh->mMaterialIndex = i; 1.291 + 1.292 + // storage for bones 1.293 + typedef std::pair<unsigned int,float> TempWeightListEntry; 1.294 + typedef std::vector< TempWeightListEntry > TempBoneWeightList; 1.295 + 1.296 + TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()](); 1.297 + 1.298 + // try to reserve enough memory without wasting too much 1.299 + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) 1.300 + { 1.301 + aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size()); 1.302 + } 1.303 + 1.304 + // allocate storage 1.305 + pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; 1.306 + aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; 1.307 + aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; 1.308 + 1.309 + aiVector3D* pcUVs = NULL; 1.310 + if (bHasUVs) 1.311 + { 1.312 + pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; 1.313 + pcMesh->mNumUVComponents[0] = 2; 1.314 + } 1.315 + 1.316 + iNum = 0; 1.317 + for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) 1.318 + { 1.319 + pcMesh->mFaces[iFace].mIndices = new unsigned int[3]; 1.320 + pcMesh->mFaces[iFace].mNumIndices = 3; 1.321 + 1.322 + // fill the vertices 1.323 + unsigned int iSrcFace = aaiFaces[i][iFace]; 1.324 + SMD::Face& face = asTriangles[iSrcFace]; 1.325 + 1.326 + *pcVerts++ = face.avVertices[0].pos; 1.327 + *pcVerts++ = face.avVertices[1].pos; 1.328 + *pcVerts++ = face.avVertices[2].pos; 1.329 + 1.330 + // fill the normals 1.331 + *pcNormals++ = face.avVertices[0].nor; 1.332 + *pcNormals++ = face.avVertices[1].nor; 1.333 + *pcNormals++ = face.avVertices[2].nor; 1.334 + 1.335 + // fill the texture coordinates 1.336 + if (pcUVs) 1.337 + { 1.338 + *pcUVs++ = face.avVertices[0].uv; 1.339 + *pcUVs++ = face.avVertices[1].uv; 1.340 + *pcUVs++ = face.avVertices[2].uv; 1.341 + } 1.342 + 1.343 + for (unsigned int iVert = 0; iVert < 3;++iVert) 1.344 + { 1.345 + float fSum = 0.0f; 1.346 + for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) 1.347 + { 1.348 + TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; 1.349 + 1.350 + // FIX: The second check is here just to make sure we won't 1.351 + // assign more than one weight to a single vertex index 1.352 + if (pairval.first >= asBones.size() || 1.353 + pairval.first == face.avVertices[iVert].iParentNode) 1.354 + { 1.355 + DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " 1.356 + "The bone index will be ignored, the weight will be assigned " 1.357 + "to the vertex' parent node"); 1.358 + continue; 1.359 + } 1.360 + aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second)); 1.361 + fSum += pairval.second; 1.362 + } 1.363 + // ****************************************************************** 1.364 + // If the sum of all vertex weights is not 1.0 we must assign 1.365 + // the rest to the vertex' parent node. Well, at least the doc says 1.366 + // we should ... 1.367 + // FIX: We use 0.975 as limit, floating-point inaccuracies seem to 1.368 + // be very strong in some SMD exporters. Furthermore it is possible 1.369 + // that the parent of a vertex is 0xffffffff (if the corresponding 1.370 + // entry in the file was unreadable) 1.371 + // ****************************************************************** 1.372 + if (fSum < 0.975f && face.avVertices[iVert].iParentNode != UINT_MAX) 1.373 + { 1.374 + if (face.avVertices[iVert].iParentNode >= asBones.size()) 1.375 + { 1.376 + DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " 1.377 + "The index of the vertex parent bone is invalid. " 1.378 + "The remaining weights will be normalized to 1.0"); 1.379 + 1.380 + if (fSum) 1.381 + { 1.382 + fSum = 1 / fSum; 1.383 + for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) 1.384 + { 1.385 + TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; 1.386 + if (pairval.first >= asBones.size())continue; 1.387 + aaiBones[pairval.first].back().second *= fSum; 1.388 + } 1.389 + } 1.390 + } 1.391 + else 1.392 + { 1.393 + aaiBones[face.avVertices[iVert].iParentNode].push_back( 1.394 + TempWeightListEntry(iNum,1.0f-fSum)); 1.395 + } 1.396 + } 1.397 + pcMesh->mFaces[iFace].mIndices[iVert] = iNum++; 1.398 + } 1.399 + } 1.400 + 1.401 + // now build all bones of the mesh 1.402 + iNum = 0; 1.403 + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) 1.404 + if (!aaiBones[iBone].empty())++iNum; 1.405 + 1.406 + if (false && iNum) 1.407 + { 1.408 + pcMesh->mNumBones = iNum; 1.409 + pcMesh->mBones = new aiBone*[pcMesh->mNumBones]; 1.410 + iNum = 0; 1.411 + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) 1.412 + { 1.413 + if (aaiBones[iBone].empty())continue; 1.414 + aiBone*& bone = pcMesh->mBones[iNum] = new aiBone(); 1.415 + 1.416 + bone->mNumWeights = (unsigned int)aaiBones[iBone].size(); 1.417 + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; 1.418 + bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix; 1.419 + bone->mName.Set( asBones[iBone].mName ); 1.420 + 1.421 + asBones[iBone].bIsUsed = true; 1.422 + 1.423 + for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight) 1.424 + { 1.425 + bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first; 1.426 + bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second; 1.427 + } 1.428 + ++iNum; 1.429 + } 1.430 + } 1.431 + delete[] aaiBones; 1.432 + } 1.433 + delete[] aaiFaces; 1.434 +} 1.435 + 1.436 +// ------------------------------------------------------------------------------------------------ 1.437 +// add bone child nodes 1.438 +void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) 1.439 +{ 1.440 + ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren); 1.441 + 1.442 + // first count ... 1.443 + for (unsigned int i = 0; i < asBones.size();++i) 1.444 + { 1.445 + SMD::Bone& bone = asBones[i]; 1.446 + if (bone.iParent == iParent)++pcNode->mNumChildren; 1.447 + } 1.448 + 1.449 + // now allocate the output array 1.450 + pcNode->mChildren = new aiNode*[pcNode->mNumChildren]; 1.451 + 1.452 + // and fill all subnodes 1.453 + unsigned int qq = 0; 1.454 + for (unsigned int i = 0; i < asBones.size();++i) 1.455 + { 1.456 + SMD::Bone& bone = asBones[i]; 1.457 + if (bone.iParent != iParent)continue; 1.458 + 1.459 + aiNode* pc = pcNode->mChildren[qq++] = new aiNode(); 1.460 + pc->mName.Set(bone.mName); 1.461 + 1.462 + // store the local transformation matrix of the bind pose 1.463 + pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix; 1.464 + pc->mParent = pcNode; 1.465 + 1.466 + // add children to this node, too 1.467 + AddBoneChildren(pc,i); 1.468 + } 1.469 +} 1.470 + 1.471 +// ------------------------------------------------------------------------------------------------ 1.472 +// create output nodes 1.473 +void SMDImporter::CreateOutputNodes() 1.474 +{ 1.475 + pScene->mRootNode = new aiNode(); 1.476 + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) 1.477 + { 1.478 + // create one root node that renders all meshes 1.479 + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; 1.480 + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; 1.481 + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) 1.482 + pScene->mRootNode->mMeshes[i] = i; 1.483 + } 1.484 + 1.485 + // now add all bones as dummy sub nodes to the graph 1.486 + // AddBoneChildren(pScene->mRootNode,(uint32_t)-1); 1.487 + 1.488 + // if we have only one bone we can even remove the root node 1.489 + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE && 1.490 + 1 == pScene->mRootNode->mNumChildren) 1.491 + { 1.492 + aiNode* pcOldRoot = pScene->mRootNode; 1.493 + pScene->mRootNode = pcOldRoot->mChildren[0]; 1.494 + pcOldRoot->mChildren[0] = NULL; 1.495 + delete pcOldRoot; 1.496 + 1.497 + pScene->mRootNode->mParent = NULL; 1.498 + } 1.499 + else 1.500 + { 1.501 + ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>"); 1.502 + pScene->mRootNode->mName.length = 10; 1.503 + } 1.504 +} 1.505 + 1.506 +// ------------------------------------------------------------------------------------------------ 1.507 +// create output animations 1.508 +void SMDImporter::CreateOutputAnimations() 1.509 +{ 1.510 + unsigned int iNumBones = 0; 1.511 + for (std::vector<SMD::Bone>::const_iterator 1.512 + i = asBones.begin(); 1.513 + i != asBones.end();++i) 1.514 + { 1.515 + if ((*i).bIsUsed)++iNumBones; 1.516 + } 1.517 + if (!iNumBones) 1.518 + { 1.519 + // just make sure this case doesn't occur ... (it could occur 1.520 + // if the file was invalid) 1.521 + return; 1.522 + } 1.523 + 1.524 + pScene->mNumAnimations = 1; 1.525 + pScene->mAnimations = new aiAnimation*[1]; 1.526 + aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation(); 1.527 + 1.528 + anim->mDuration = dLengthOfAnim; 1.529 + anim->mNumChannels = iNumBones; 1.530 + anim->mTicksPerSecond = 25.0; // FIXME: is this correct? 1.531 + 1.532 + aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; 1.533 + 1.534 + // now build valid keys 1.535 + unsigned int a = 0; 1.536 + for (std::vector<SMD::Bone>::const_iterator 1.537 + i = asBones.begin(); 1.538 + i != asBones.end();++i) 1.539 + { 1.540 + if (!(*i).bIsUsed)continue; 1.541 + 1.542 + aiNodeAnim* p = pp[a] = new aiNodeAnim(); 1.543 + 1.544 + // copy the name of the bone 1.545 + p->mNodeName.Set( i->mName); 1.546 + 1.547 + p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size(); 1.548 + if (p->mNumRotationKeys) 1.549 + { 1.550 + p->mNumPositionKeys = p->mNumRotationKeys; 1.551 + aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys]; 1.552 + aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys]; 1.553 + 1.554 + for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator 1.555 + qq = (*i).sAnim.asKeys.begin(); 1.556 + qq != (*i).sAnim.asKeys.end(); ++qq) 1.557 + { 1.558 + pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime; 1.559 + 1.560 + // compute the rotation quaternion from the euler angles 1.561 + pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z ); 1.562 + pVecKeys->mValue = (*qq).vPos; 1.563 + 1.564 + ++pVecKeys; ++pRotKeys; 1.565 + } 1.566 + } 1.567 + ++a; 1.568 + 1.569 + // there are no scaling keys ... 1.570 + } 1.571 +} 1.572 + 1.573 +// ------------------------------------------------------------------------------------------------ 1.574 +void SMDImporter::ComputeAbsoluteBoneTransformations() 1.575 +{ 1.576 + // For each bone: determine the key with the lowest time value 1.577 + // theoretically the SMD format should have all keyframes 1.578 + // in order. However, I've seen a file where this wasn't true. 1.579 + for (unsigned int i = 0; i < asBones.size();++i) 1.580 + { 1.581 + SMD::Bone& bone = asBones[i]; 1.582 + 1.583 + uint32_t iIndex = 0; 1.584 + double dMin = 10e10; 1.585 + for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i) 1.586 + { 1.587 + double d = std::min(bone.sAnim.asKeys[i].dTime,dMin); 1.588 + if (d < dMin) 1.589 + { 1.590 + dMin = d; 1.591 + iIndex = i; 1.592 + } 1.593 + } 1.594 + bone.sAnim.iFirstTimeKey = iIndex; 1.595 + } 1.596 + 1.597 + unsigned int iParent = 0; 1.598 + while (iParent < asBones.size()) 1.599 + { 1.600 + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) 1.601 + { 1.602 + SMD::Bone& bone = asBones[iBone]; 1.603 + 1.604 + if (iParent == bone.iParent) 1.605 + { 1.606 + SMD::Bone& parentBone = asBones[iParent]; 1.607 + 1.608 + 1.609 + uint32_t iIndex = bone.sAnim.iFirstTimeKey; 1.610 + const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix; 1.611 + aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute; 1.612 + 1.613 + // The same for the parent bone ... 1.614 + iIndex = parentBone.sAnim.iFirstTimeKey; 1.615 + const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute; 1.616 + 1.617 + // Compute the absolute transformation matrix 1.618 + matOut = mat * mat2; 1.619 + } 1.620 + } 1.621 + ++iParent; 1.622 + } 1.623 + 1.624 + // Store the inverse of the absolute transformation matrix 1.625 + // of the first key as bone offset matrix 1.626 + for (iParent = 0; iParent < asBones.size();++iParent) 1.627 + { 1.628 + SMD::Bone& bone = asBones[iParent]; 1.629 + bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute; 1.630 + bone.mOffsetMatrix.Inverse(); 1.631 + } 1.632 +} 1.633 + 1.634 +// ------------------------------------------------------------------------------------------------ 1.635 +// create output materials 1.636 +void SMDImporter::CreateOutputMaterials() 1.637 +{ 1.638 + pScene->mNumMaterials = (unsigned int)aszTextures.size(); 1.639 + pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)]; 1.640 + 1.641 + for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat) 1.642 + { 1.643 + aiMaterial* pcMat = new aiMaterial(); 1.644 + pScene->mMaterials[iMat] = pcMat; 1.645 + 1.646 + aiString szName; 1.647 + szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat); 1.648 + pcMat->AddProperty(&szName,AI_MATKEY_NAME); 1.649 + 1.650 + if (aszTextures[iMat].length()) 1.651 + { 1.652 + ::strcpy(szName.data, aszTextures[iMat].c_str() ); 1.653 + szName.length = aszTextures[iMat].length(); 1.654 + pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); 1.655 + } 1.656 + } 1.657 + 1.658 + // create a default material if necessary 1.659 + if (0 == pScene->mNumMaterials) 1.660 + { 1.661 + pScene->mNumMaterials = 1; 1.662 + 1.663 + aiMaterial* pcHelper = new aiMaterial(); 1.664 + pScene->mMaterials[0] = pcHelper; 1.665 + 1.666 + int iMode = (int)aiShadingMode_Gouraud; 1.667 + pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); 1.668 + 1.669 + aiColor3D clr; 1.670 + clr.b = clr.g = clr.r = 0.7f; 1.671 + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); 1.672 + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); 1.673 + 1.674 + clr.b = clr.g = clr.r = 0.05f; 1.675 + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); 1.676 + 1.677 + aiString szName; 1.678 + szName.Set(AI_DEFAULT_MATERIAL_NAME); 1.679 + pcHelper->AddProperty(&szName,AI_MATKEY_NAME); 1.680 + } 1.681 +} 1.682 + 1.683 +// ------------------------------------------------------------------------------------------------ 1.684 +// Parse the file 1.685 +void SMDImporter::ParseFile() 1.686 +{ 1.687 + const char* szCurrent = mBuffer; 1.688 + 1.689 + // read line per line ... 1.690 + for ( ;; ) 1.691 + { 1.692 + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; 1.693 + 1.694 + // "version <n> \n", <n> should be 1 for hl and hlČ SMD files 1.695 + if (TokenMatch(szCurrent,"version",7)) 1.696 + { 1.697 + if(!SkipSpaces(szCurrent,&szCurrent)) break; 1.698 + if (1 != strtoul10(szCurrent,&szCurrent)) 1.699 + { 1.700 + DefaultLogger::get()->warn("SMD.version is not 1. This " 1.701 + "file format is not known. Continuing happily ..."); 1.702 + } 1.703 + continue; 1.704 + } 1.705 + // "nodes\n" - Starts the node section 1.706 + if (TokenMatch(szCurrent,"nodes",5)) 1.707 + { 1.708 + ParseNodesSection(szCurrent,&szCurrent); 1.709 + continue; 1.710 + } 1.711 + // "triangles\n" - Starts the triangle section 1.712 + if (TokenMatch(szCurrent,"triangles",9)) 1.713 + { 1.714 + ParseTrianglesSection(szCurrent,&szCurrent); 1.715 + continue; 1.716 + } 1.717 + // "vertexanimation\n" - Starts the vertex animation section 1.718 + if (TokenMatch(szCurrent,"vertexanimation",15)) 1.719 + { 1.720 + bHasUVs = false; 1.721 + ParseVASection(szCurrent,&szCurrent); 1.722 + continue; 1.723 + } 1.724 + // "skeleton\n" - Starts the skeleton section 1.725 + if (TokenMatch(szCurrent,"skeleton",8)) 1.726 + { 1.727 + ParseSkeletonSection(szCurrent,&szCurrent); 1.728 + continue; 1.729 + } 1.730 + SkipLine(szCurrent,&szCurrent); 1.731 + } 1.732 + return; 1.733 +} 1.734 + 1.735 +// ------------------------------------------------------------------------------------------------ 1.736 +unsigned int SMDImporter::GetTextureIndex(const std::string& filename) 1.737 +{ 1.738 + unsigned int iIndex = 0; 1.739 + for (std::vector<std::string>::const_iterator 1.740 + i = aszTextures.begin(); 1.741 + i != aszTextures.end();++i,++iIndex) 1.742 + { 1.743 + // case-insensitive ... it's a path 1.744 + if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex; 1.745 + } 1.746 + iIndex = (unsigned int)aszTextures.size(); 1.747 + aszTextures.push_back(filename); 1.748 + return iIndex; 1.749 +} 1.750 + 1.751 +// ------------------------------------------------------------------------------------------------ 1.752 +// Parse the nodes section of the file 1.753 +void SMDImporter::ParseNodesSection(const char* szCurrent, 1.754 + const char** szCurrentOut) 1.755 +{ 1.756 + for ( ;; ) 1.757 + { 1.758 + // "end\n" - Ends the nodes section 1.759 + if (0 == ASSIMP_strincmp(szCurrent,"end",3) && 1.760 + IsSpaceOrNewLine(*(szCurrent+3))) 1.761 + { 1.762 + szCurrent += 4; 1.763 + break; 1.764 + } 1.765 + ParseNodeInfo(szCurrent,&szCurrent); 1.766 + } 1.767 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.768 + *szCurrentOut = szCurrent; 1.769 +} 1.770 + 1.771 +// ------------------------------------------------------------------------------------------------ 1.772 +// Parse the triangles section of the file 1.773 +void SMDImporter::ParseTrianglesSection(const char* szCurrent, 1.774 + const char** szCurrentOut) 1.775 +{ 1.776 + // Parse a triangle, parse another triangle, parse the next triangle ... 1.777 + // and so on until we reach a token that looks quite similar to "end" 1.778 + for ( ;; ) 1.779 + { 1.780 + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; 1.781 + 1.782 + // "end\n" - Ends the triangles section 1.783 + if (TokenMatch(szCurrent,"end",3)) 1.784 + break; 1.785 + ParseTriangle(szCurrent,&szCurrent); 1.786 + } 1.787 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.788 + *szCurrentOut = szCurrent; 1.789 +} 1.790 +// ------------------------------------------------------------------------------------------------ 1.791 +// Parse the vertex animation section of the file 1.792 +void SMDImporter::ParseVASection(const char* szCurrent, 1.793 + const char** szCurrentOut) 1.794 +{ 1.795 + unsigned int iCurIndex = 0; 1.796 + for ( ;; ) 1.797 + { 1.798 + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; 1.799 + 1.800 + // "end\n" - Ends the "vertexanimation" section 1.801 + if (TokenMatch(szCurrent,"end",3)) 1.802 + break; 1.803 + 1.804 + // "time <n>\n" 1.805 + if (TokenMatch(szCurrent,"time",4)) 1.806 + { 1.807 + // NOTE: The doc says that time values COULD be negative ... 1.808 + // NOTE2: this is the shape key -> valve docs 1.809 + int iTime = 0; 1.810 + if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break; 1.811 + SkipLine(szCurrent,&szCurrent); 1.812 + } 1.813 + else 1.814 + { 1.815 + if(0 == iCurIndex) 1.816 + { 1.817 + asTriangles.push_back(SMD::Face()); 1.818 + } 1.819 + if (++iCurIndex == 3)iCurIndex = 0; 1.820 + ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true); 1.821 + } 1.822 + } 1.823 + 1.824 + if (iCurIndex != 2 && !asTriangles.empty()) 1.825 + { 1.826 + // we want to no degenerates, so throw this triangle away 1.827 + asTriangles.pop_back(); 1.828 + } 1.829 + 1.830 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.831 + *szCurrentOut = szCurrent; 1.832 +} 1.833 +// ------------------------------------------------------------------------------------------------ 1.834 +// Parse the skeleton section of the file 1.835 +void SMDImporter::ParseSkeletonSection(const char* szCurrent, 1.836 + const char** szCurrentOut) 1.837 +{ 1.838 + int iTime = 0; 1.839 + for ( ;; ) 1.840 + { 1.841 + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; 1.842 + 1.843 + // "end\n" - Ends the skeleton section 1.844 + if (TokenMatch(szCurrent,"end",3)) 1.845 + break; 1.846 + 1.847 + // "time <n>\n" - Specifies the current animation frame 1.848 + else if (TokenMatch(szCurrent,"time",4)) 1.849 + { 1.850 + // NOTE: The doc says that time values COULD be negative ... 1.851 + if(!ParseSignedInt(szCurrent,&szCurrent,iTime))break; 1.852 + 1.853 + iSmallestFrame = std::min(iSmallestFrame,iTime); 1.854 + SkipLine(szCurrent,&szCurrent); 1.855 + } 1.856 + else ParseSkeletonElement(szCurrent,&szCurrent,iTime); 1.857 + } 1.858 + *szCurrentOut = szCurrent; 1.859 +} 1.860 + 1.861 +// ------------------------------------------------------------------------------------------------ 1.862 +#define SMDI_PARSE_RETURN { \ 1.863 + SkipLine(szCurrent,&szCurrent); \ 1.864 + *szCurrentOut = szCurrent; \ 1.865 + return; \ 1.866 +} 1.867 +// ------------------------------------------------------------------------------------------------ 1.868 +// Parse a node line 1.869 +void SMDImporter::ParseNodeInfo(const char* szCurrent, 1.870 + const char** szCurrentOut) 1.871 +{ 1.872 + unsigned int iBone = 0; 1.873 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.874 + if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) 1.875 + { 1.876 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index"); 1.877 + SMDI_PARSE_RETURN; 1.878 + } 1.879 + // add our bone to the list 1.880 + if (iBone >= asBones.size())asBones.resize(iBone+1); 1.881 + SMD::Bone& bone = asBones[iBone]; 1.882 + 1.883 + bool bQuota = true; 1.884 + if ('\"' != *szCurrent) 1.885 + { 1.886 + LogWarning("Bone name is expcted to be enclosed in " 1.887 + "double quotation marks. "); 1.888 + bQuota = false; 1.889 + } 1.890 + else ++szCurrent; 1.891 + 1.892 + const char* szEnd = szCurrent; 1.893 + for ( ;; ) 1.894 + { 1.895 + if (bQuota && '\"' == *szEnd) 1.896 + { 1.897 + iBone = (unsigned int)(szEnd - szCurrent); 1.898 + ++szEnd; 1.899 + break; 1.900 + } 1.901 + else if (IsSpaceOrNewLine(*szEnd)) 1.902 + { 1.903 + iBone = (unsigned int)(szEnd - szCurrent); 1.904 + break; 1.905 + } 1.906 + else if (!(*szEnd)) 1.907 + { 1.908 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name"); 1.909 + SMDI_PARSE_RETURN; 1.910 + } 1.911 + ++szEnd; 1.912 + } 1.913 + bone.mName = std::string(szCurrent,iBone); 1.914 + szCurrent = szEnd; 1.915 + 1.916 + // the only negative bone parent index that could occur is -1 AFAIK 1.917 + if(!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent)) 1.918 + { 1.919 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1"); 1.920 + SMDI_PARSE_RETURN; 1.921 + } 1.922 + 1.923 + // go to the beginning of the next line 1.924 + SMDI_PARSE_RETURN; 1.925 +} 1.926 + 1.927 +// ------------------------------------------------------------------------------------------------ 1.928 +// Parse a skeleton element 1.929 +void SMDImporter::ParseSkeletonElement(const char* szCurrent, 1.930 + const char** szCurrentOut,int iTime) 1.931 +{ 1.932 + aiVector3D vPos; 1.933 + aiVector3D vRot; 1.934 + 1.935 + unsigned int iBone = 0; 1.936 + if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone)) 1.937 + { 1.938 + DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index"); 1.939 + SMDI_PARSE_RETURN; 1.940 + } 1.941 + if (iBone >= asBones.size()) 1.942 + { 1.943 + LogErrorNoThrow("Bone index in skeleton section is out of range"); 1.944 + SMDI_PARSE_RETURN; 1.945 + } 1.946 + SMD::Bone& bone = asBones[iBone]; 1.947 + 1.948 + bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey()); 1.949 + SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back(); 1.950 + 1.951 + key.dTime = (double)iTime; 1.952 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x)) 1.953 + { 1.954 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x"); 1.955 + SMDI_PARSE_RETURN; 1.956 + } 1.957 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y)) 1.958 + { 1.959 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y"); 1.960 + SMDI_PARSE_RETURN; 1.961 + } 1.962 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z)) 1.963 + { 1.964 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z"); 1.965 + SMDI_PARSE_RETURN; 1.966 + } 1.967 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x)) 1.968 + { 1.969 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x"); 1.970 + SMDI_PARSE_RETURN; 1.971 + } 1.972 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y)) 1.973 + { 1.974 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y"); 1.975 + SMDI_PARSE_RETURN; 1.976 + } 1.977 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z)) 1.978 + { 1.979 + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z"); 1.980 + SMDI_PARSE_RETURN; 1.981 + } 1.982 + // build the transformation matrix of the key 1.983 + key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z); 1.984 + { 1.985 + aiMatrix4x4 mTemp; 1.986 + mTemp.a4 = vPos.x; 1.987 + mTemp.b4 = vPos.y; 1.988 + mTemp.c4 = vPos.z; 1.989 + key.matrix = key.matrix * mTemp; 1.990 + } 1.991 + 1.992 + // go to the beginning of the next line 1.993 + SMDI_PARSE_RETURN; 1.994 +} 1.995 + 1.996 +// ------------------------------------------------------------------------------------------------ 1.997 +// Parse a triangle 1.998 +void SMDImporter::ParseTriangle(const char* szCurrent, 1.999 + const char** szCurrentOut) 1.1000 +{ 1.1001 + asTriangles.push_back(SMD::Face()); 1.1002 + SMD::Face& face = asTriangles.back(); 1.1003 + 1.1004 + if(!SkipSpaces(szCurrent,&szCurrent)) 1.1005 + { 1.1006 + LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle"); 1.1007 + return; 1.1008 + } 1.1009 + 1.1010 + // read the texture file name 1.1011 + const char* szLast = szCurrent; 1.1012 + while (!IsSpaceOrNewLine(*szCurrent++)); 1.1013 + 1.1014 + // ... and get the index that belongs to this file name 1.1015 + face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast)); 1.1016 + 1.1017 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.1018 + 1.1019 + // load three vertices 1.1020 + for (unsigned int iVert = 0; iVert < 3;++iVert) 1.1021 + { 1.1022 + ParseVertex(szCurrent,&szCurrent, 1.1023 + face.avVertices[iVert]); 1.1024 + } 1.1025 + *szCurrentOut = szCurrent; 1.1026 +} 1.1027 + 1.1028 +// ------------------------------------------------------------------------------------------------ 1.1029 +// Parse a float 1.1030 +bool SMDImporter::ParseFloat(const char* szCurrent, 1.1031 + const char** szCurrentOut, float& out) 1.1032 +{ 1.1033 + if(!SkipSpaces(&szCurrent)) 1.1034 + return false; 1.1035 + 1.1036 + *szCurrentOut = fast_atoreal_move<float>(szCurrent,out); 1.1037 + return true; 1.1038 +} 1.1039 + 1.1040 +// ------------------------------------------------------------------------------------------------ 1.1041 +// Parse an unsigned int 1.1042 +bool SMDImporter::ParseUnsignedInt(const char* szCurrent, 1.1043 + const char** szCurrentOut, unsigned int& out) 1.1044 +{ 1.1045 + if(!SkipSpaces(&szCurrent)) 1.1046 + return false; 1.1047 + 1.1048 + out = strtoul10(szCurrent,szCurrentOut); 1.1049 + return true; 1.1050 +} 1.1051 + 1.1052 +// ------------------------------------------------------------------------------------------------ 1.1053 +// Parse a signed int 1.1054 +bool SMDImporter::ParseSignedInt(const char* szCurrent, 1.1055 + const char** szCurrentOut, int& out) 1.1056 +{ 1.1057 + if(!SkipSpaces(&szCurrent)) 1.1058 + return false; 1.1059 + 1.1060 + out = strtol10(szCurrent,szCurrentOut); 1.1061 + return true; 1.1062 +} 1.1063 + 1.1064 +// ------------------------------------------------------------------------------------------------ 1.1065 +// Parse a vertex 1.1066 +void SMDImporter::ParseVertex(const char* szCurrent, 1.1067 + const char** szCurrentOut, SMD::Vertex& vertex, 1.1068 + bool bVASection /*= false*/) 1.1069 +{ 1.1070 + if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent)) 1.1071 + { 1.1072 + SkipSpacesAndLineEnd(szCurrent,&szCurrent); 1.1073 + return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection); 1.1074 + } 1.1075 + if(!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode)) 1.1076 + { 1.1077 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent"); 1.1078 + SMDI_PARSE_RETURN; 1.1079 + } 1.1080 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x)) 1.1081 + { 1.1082 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x"); 1.1083 + SMDI_PARSE_RETURN; 1.1084 + } 1.1085 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y)) 1.1086 + { 1.1087 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y"); 1.1088 + SMDI_PARSE_RETURN; 1.1089 + } 1.1090 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z)) 1.1091 + { 1.1092 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z"); 1.1093 + SMDI_PARSE_RETURN; 1.1094 + } 1.1095 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x)) 1.1096 + { 1.1097 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x"); 1.1098 + SMDI_PARSE_RETURN; 1.1099 + } 1.1100 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y)) 1.1101 + { 1.1102 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y"); 1.1103 + SMDI_PARSE_RETURN; 1.1104 + } 1.1105 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z)) 1.1106 + { 1.1107 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z"); 1.1108 + SMDI_PARSE_RETURN; 1.1109 + } 1.1110 + 1.1111 + if (bVASection)SMDI_PARSE_RETURN; 1.1112 + 1.1113 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x)) 1.1114 + { 1.1115 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x"); 1.1116 + SMDI_PARSE_RETURN; 1.1117 + } 1.1118 + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y)) 1.1119 + { 1.1120 + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y"); 1.1121 + SMDI_PARSE_RETURN; 1.1122 + } 1.1123 + 1.1124 + // now read the number of bones affecting this vertex 1.1125 + // all elements from now are fully optional, we don't need them 1.1126 + unsigned int iSize = 0; 1.1127 + if(!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN; 1.1128 + vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f)); 1.1129 + 1.1130 + for (std::vector<std::pair<unsigned int, float> >::iterator 1.1131 + i = vertex.aiBoneLinks.begin(); 1.1132 + i != vertex.aiBoneLinks.end();++i) 1.1133 + { 1.1134 + if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first)) 1.1135 + SMDI_PARSE_RETURN; 1.1136 + if(!ParseFloat(szCurrent,&szCurrent,(*i).second)) 1.1137 + SMDI_PARSE_RETURN; 1.1138 + } 1.1139 + 1.1140 + // go to the beginning of the next line 1.1141 + SMDI_PARSE_RETURN; 1.1142 +} 1.1143 + 1.1144 +#endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER