diff options
Diffstat (limited to 'src/3rdparty/assimp/code/SMDLoader.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/SMDLoader.cpp | 1875 |
1 files changed, 944 insertions, 931 deletions
diff --git a/src/3rdparty/assimp/code/SMDLoader.cpp b/src/3rdparty/assimp/code/SMDLoader.cpp index 4717e5f15..7aef7bc56 100644 --- a/src/3rdparty/assimp/code/SMDLoader.cpp +++ b/src/3rdparty/assimp/code/SMDLoader.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2016, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -25,1117 +25,1130 @@ conditions are met: derived from this software without specific prior written permission of the assimp team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file SMDLoader.cpp - * @brief Implementation of the SMD importer class +/** @file SMDLoader.cpp + * @brief Implementation of the SMD importer class */ -#include "AssimpPCH.h" + #ifndef ASSIMP_BUILD_NO_SMD_IMPORTER // internal headers #include "SMDLoader.h" #include "fast_atof.h" #include "SkeletonMeshBuilder.h" +#include <assimp/Importer.hpp> +#include <assimp/IOSystem.hpp> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <memory> using namespace Assimp; static const aiImporterDesc desc = { - "Valve SMD Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "smd vta" + "Valve SMD Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "smd vta" }; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer SMDImporter::SMDImporter() + : configFrameID(), + mBuffer(), + pScene(), + iFileSize(), + iSmallestFrame(), + dLengthOfAnim(), + bHasUVs(), + iLineNumber() {} // ------------------------------------------------------------------------------------------------ -// Destructor, private as well +// Destructor, private as well SMDImporter::~SMDImporter() {} // ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. +// Returns whether the class can handle the format of the given file. bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const { - // fixme: auto format detection - return SimpleExtensionCheck(pFile,"smd","vta"); + // fixme: auto format detection + return SimpleExtensionCheck(pFile,"smd","vta"); } // ------------------------------------------------------------------------------------------------ // Get a list of all supported file extensions const aiImporterDesc* SMDImporter::GetInfo () const { - return &desc; + return &desc; } // ------------------------------------------------------------------------------------------------ // Setup configuration properties void SMDImporter::SetupProperties(const Importer* pImp) { - // The - // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the - // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,-1); - if(static_cast<unsigned int>(-1) == configFrameID) { - configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); - } + // The + // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the + // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,-1); + if(static_cast<unsigned int>(-1) == configFrameID) { + configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + } } // ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. +// Imports the given file into the given scene structure. void SMDImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { - boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( file.get() == NULL) { - throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + "."); - } - - iFileSize = (unsigned int)file->FileSize(); - - // Allocate storage and copy the contents of the file to a memory buffer - this->pScene = pScene; - - std::vector<char> buff(iFileSize+1); - TextFileToBuffer(file.get(),buff); - mBuffer = &buff[0]; - - iSmallestFrame = (1 << 31); - bHasUVs = true; - iLineNumber = 1; - - // Reserve enough space for ... hm ... 10 textures - aszTextures.reserve(10); - - // Reserve enough space for ... hm ... 1000 triangles - asTriangles.reserve(1000); - - // Reserve enough space for ... hm ... 20 bones - asBones.reserve(20); - - - // parse the file ... - ParseFile(); - - // If there are no triangles it seems to be an animation SMD, - // containing only the animation skeleton. - if (asTriangles.empty()) - { - if (asBones.empty()) - { - throw DeadlyImportError("SMD: No triangles and no bones have " - "been found in the file. This file seems to be invalid."); - } - - // Set the flag in the scene structure which indicates - // that there is nothing than an animation skeleton - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } - - if (!asBones.empty()) - { - // Check whether all bones have been initialized - for (std::vector<SMD::Bone>::const_iterator - i = asBones.begin(); - i != asBones.end();++i) - { - if (!(*i).mName.length()) - { - DefaultLogger::get()->warn("SMD: Not all bones have been initialized"); - break; - } - } - - // now fix invalid time values and make sure the animation starts at frame 0 - FixTimeValues(); - - // compute absolute bone transformation matrices - // ComputeAbsoluteBoneTransformations(); - } - - if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) - { - // create output meshes - CreateOutputMeshes(); - - // build an output material list - CreateOutputMaterials(); - } - - // build the output animation - CreateOutputAnimations(); - - // build output nodes (bones are added as empty dummy nodes) - CreateOutputNodes(); - - if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) - { - SkeletonMeshBuilder skeleton(pScene); - } + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) { + throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + "."); + } + + iFileSize = (unsigned int)file->FileSize(); + + // Allocate storage and copy the contents of the file to a memory buffer + this->pScene = pScene; + + std::vector<char> buff(iFileSize+1); + TextFileToBuffer(file.get(),buff); + mBuffer = &buff[0]; + + iSmallestFrame = (1 << 31); + bHasUVs = true; + iLineNumber = 1; + + // Reserve enough space for ... hm ... 10 textures + aszTextures.reserve(10); + + // Reserve enough space for ... hm ... 1000 triangles + asTriangles.reserve(1000); + + // Reserve enough space for ... hm ... 20 bones + asBones.reserve(20); + + + // parse the file ... + ParseFile(); + + // If there are no triangles it seems to be an animation SMD, + // containing only the animation skeleton. + if (asTriangles.empty()) + { + if (asBones.empty()) + { + throw DeadlyImportError("SMD: No triangles and no bones have " + "been found in the file. This file seems to be invalid."); + } + + // Set the flag in the scene structure which indicates + // that there is nothing than an animation skeleton + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + } + + if (!asBones.empty()) + { + // Check whether all bones have been initialized + for (std::vector<SMD::Bone>::const_iterator + i = asBones.begin(); + i != asBones.end();++i) + { + if (!(*i).mName.length()) + { + DefaultLogger::get()->warn("SMD: Not all bones have been initialized"); + break; + } + } + + // now fix invalid time values and make sure the animation starts at frame 0 + FixTimeValues(); + + // compute absolute bone transformation matrices + // ComputeAbsoluteBoneTransformations(); + } + + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) + { + // create output meshes + CreateOutputMeshes(); + + // build an output material list + CreateOutputMaterials(); + } + + // build the output animation + CreateOutputAnimations(); + + // build output nodes (bones are added as empty dummy nodes) + CreateOutputNodes(); + + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) + { + SkeletonMeshBuilder skeleton(pScene); + } } // ------------------------------------------------------------------------------------------------ // Write an error message with line number to the log file void SMDImporter::LogErrorNoThrow(const char* msg) { - char szTemp[1024]; - sprintf(szTemp,"Line %i: %s",iLineNumber,msg); - DefaultLogger::get()->error(szTemp); + char szTemp[1024]; + ai_snprintf(szTemp,1024,"Line %u: %s",iLineNumber,msg); + DefaultLogger::get()->error(szTemp); } // ------------------------------------------------------------------------------------------------ // Write a warning with line number to the log file void SMDImporter::LogWarning(const char* msg) { - char szTemp[1024]; - ai_assert(strlen(msg) < 1000); - sprintf(szTemp,"Line %i: %s",iLineNumber,msg); - DefaultLogger::get()->warn(szTemp); + char szTemp[1024]; + ai_assert(strlen(msg) < 1000); + ai_snprintf(szTemp,1024,"Line %u: %s",iLineNumber,msg); + DefaultLogger::get()->warn(szTemp); } // ------------------------------------------------------------------------------------------------ // Fix invalid time values in the file void SMDImporter::FixTimeValues() { - double dDelta = (double)iSmallestFrame; - double dMax = 0.0f; - for (std::vector<SMD::Bone>::iterator - iBone = asBones.begin(); - iBone != asBones.end();++iBone) - { - for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator - iKey = (*iBone).sAnim.asKeys.begin(); - iKey != (*iBone).sAnim.asKeys.end();++iKey) - { - (*iKey).dTime -= dDelta; - dMax = std::max(dMax, (*iKey).dTime); - } - } - dLengthOfAnim = dMax; + double dDelta = (double)iSmallestFrame; + double dMax = 0.0f; + for (std::vector<SMD::Bone>::iterator + iBone = asBones.begin(); + iBone != asBones.end();++iBone) + { + for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator + iKey = (*iBone).sAnim.asKeys.begin(); + iKey != (*iBone).sAnim.asKeys.end();++iKey) + { + (*iKey).dTime -= dDelta; + dMax = std::max(dMax, (*iKey).dTime); + } + } + dLengthOfAnim = dMax; } // ------------------------------------------------------------------------------------------------ // create output meshes void SMDImporter::CreateOutputMeshes() { - if (aszTextures.empty()) - aszTextures.push_back(std::string()); - - // we need to sort all faces by their material index - // in opposition to other loaders we can be sure that each - // material is at least used once. - pScene->mNumMeshes = (unsigned int) aszTextures.size(); - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - - typedef std::vector<unsigned int> FaceList; - FaceList* aaiFaces = new FaceList[pScene->mNumMeshes]; - - // approximate the space that will be required - unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes; - iNum += iNum >> 1; - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - aaiFaces[i].reserve(iNum); - - - // collect all faces - iNum = 0; - for (std::vector<SMD::Face>::const_iterator - iFace = asTriangles.begin(); - iFace != asTriangles.end();++iFace,++iNum) - { - if (UINT_MAX == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 ); - else if ((*iFace).iTexture >= aszTextures.size()) - { - DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face"); - aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1); - } - else aaiFaces[(*iFace).iTexture].push_back(iNum); - } - - // now create the output meshes - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh(); - ai_assert(!aaiFaces[i].empty()); // should not be empty ... - - pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3; - pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size(); - pcMesh->mMaterialIndex = i; - - // storage for bones - typedef std::pair<unsigned int,float> TempWeightListEntry; - typedef std::vector< TempWeightListEntry > TempBoneWeightList; - - TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()](); - - // try to reserve enough memory without wasting too much - for (unsigned int iBone = 0; iBone < asBones.size();++iBone) - { - aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size()); - } - - // allocate storage - pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; - aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; - aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; - - aiVector3D* pcUVs = NULL; - if (bHasUVs) - { - pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNumUVComponents[0] = 2; - } - - iNum = 0; - for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) - { - pcMesh->mFaces[iFace].mIndices = new unsigned int[3]; - pcMesh->mFaces[iFace].mNumIndices = 3; - - // fill the vertices - unsigned int iSrcFace = aaiFaces[i][iFace]; - SMD::Face& face = asTriangles[iSrcFace]; - - *pcVerts++ = face.avVertices[0].pos; - *pcVerts++ = face.avVertices[1].pos; - *pcVerts++ = face.avVertices[2].pos; - - // fill the normals - *pcNormals++ = face.avVertices[0].nor; - *pcNormals++ = face.avVertices[1].nor; - *pcNormals++ = face.avVertices[2].nor; - - // fill the texture coordinates - if (pcUVs) - { - *pcUVs++ = face.avVertices[0].uv; - *pcUVs++ = face.avVertices[1].uv; - *pcUVs++ = face.avVertices[2].uv; - } - - for (unsigned int iVert = 0; iVert < 3;++iVert) - { - float fSum = 0.0f; - for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) - { - TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; - - // FIX: The second check is here just to make sure we won't - // assign more than one weight to a single vertex index - if (pairval.first >= asBones.size() || - pairval.first == face.avVertices[iVert].iParentNode) - { - DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " - "The bone index will be ignored, the weight will be assigned " - "to the vertex' parent node"); - continue; - } - aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second)); - fSum += pairval.second; - } - // ****************************************************************** - // If the sum of all vertex weights is not 1.0 we must assign - // the rest to the vertex' parent node. Well, at least the doc says - // we should ... - // FIX: We use 0.975 as limit, floating-point inaccuracies seem to - // be very strong in some SMD exporters. Furthermore it is possible - // that the parent of a vertex is 0xffffffff (if the corresponding - // entry in the file was unreadable) - // ****************************************************************** - if (fSum < 0.975f && face.avVertices[iVert].iParentNode != UINT_MAX) - { - if (face.avVertices[iVert].iParentNode >= asBones.size()) - { - DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " - "The index of the vertex parent bone is invalid. " - "The remaining weights will be normalized to 1.0"); - - if (fSum) - { - fSum = 1 / fSum; - for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) - { - TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; - if (pairval.first >= asBones.size())continue; - aaiBones[pairval.first].back().second *= fSum; - } - } - } - else - { - aaiBones[face.avVertices[iVert].iParentNode].push_back( - TempWeightListEntry(iNum,1.0f-fSum)); - } - } - pcMesh->mFaces[iFace].mIndices[iVert] = iNum++; - } - } - - // now build all bones of the mesh - iNum = 0; - for (unsigned int iBone = 0; iBone < asBones.size();++iBone) - if (!aaiBones[iBone].empty())++iNum; - - if (false && iNum) - { - pcMesh->mNumBones = iNum; - pcMesh->mBones = new aiBone*[pcMesh->mNumBones]; - iNum = 0; - for (unsigned int iBone = 0; iBone < asBones.size();++iBone) - { - if (aaiBones[iBone].empty())continue; - aiBone*& bone = pcMesh->mBones[iNum] = new aiBone(); - - bone->mNumWeights = (unsigned int)aaiBones[iBone].size(); - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix; - bone->mName.Set( asBones[iBone].mName ); - - asBones[iBone].bIsUsed = true; - - for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight) - { - bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first; - bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second; - } - ++iNum; - } - } - delete[] aaiBones; - } - delete[] aaiFaces; + if (aszTextures.empty()) + aszTextures.push_back(std::string()); + + // we need to sort all faces by their material index + // in opposition to other loaders we can be sure that each + // material is at least used once. + pScene->mNumMeshes = (unsigned int) aszTextures.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + + typedef std::vector<unsigned int> FaceList; + FaceList* aaiFaces = new FaceList[pScene->mNumMeshes]; + + // approximate the space that will be required + unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes; + iNum += iNum >> 1; + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + aaiFaces[i].reserve(iNum); + + + // collect all faces + iNum = 0; + for (std::vector<SMD::Face>::const_iterator + iFace = asTriangles.begin(); + iFace != asTriangles.end();++iFace,++iNum) + { + if (UINT_MAX == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 ); + else if ((*iFace).iTexture >= aszTextures.size()) + { + DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face"); + aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1); + } + else aaiFaces[(*iFace).iTexture].push_back(iNum); + } + + // now create the output meshes + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh(); + ai_assert(!aaiFaces[i].empty()); // should not be empty ... + + pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3; + pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size(); + pcMesh->mMaterialIndex = i; + + // storage for bones + typedef std::pair<unsigned int,float> TempWeightListEntry; + typedef std::vector< TempWeightListEntry > TempBoneWeightList; + + TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()](); + + // try to reserve enough memory without wasting too much + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) + { + aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size()); + } + + // allocate storage + pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; + aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; + aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; + + aiVector3D* pcUVs = NULL; + if (bHasUVs) + { + pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mNumUVComponents[0] = 2; + } + + iNum = 0; + for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) + { + pcMesh->mFaces[iFace].mIndices = new unsigned int[3]; + pcMesh->mFaces[iFace].mNumIndices = 3; + + // fill the vertices + unsigned int iSrcFace = aaiFaces[i][iFace]; + SMD::Face& face = asTriangles[iSrcFace]; + + *pcVerts++ = face.avVertices[0].pos; + *pcVerts++ = face.avVertices[1].pos; + *pcVerts++ = face.avVertices[2].pos; + + // fill the normals + *pcNormals++ = face.avVertices[0].nor; + *pcNormals++ = face.avVertices[1].nor; + *pcNormals++ = face.avVertices[2].nor; + + // fill the texture coordinates + if (pcUVs) + { + *pcUVs++ = face.avVertices[0].uv; + *pcUVs++ = face.avVertices[1].uv; + *pcUVs++ = face.avVertices[2].uv; + } + + for (unsigned int iVert = 0; iVert < 3;++iVert) + { + float fSum = 0.0f; + for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) + { + TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; + + // FIX: The second check is here just to make sure we won't + // assign more than one weight to a single vertex index + if (pairval.first >= asBones.size() || + pairval.first == face.avVertices[iVert].iParentNode) + { + DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " + "The bone index will be ignored, the weight will be assigned " + "to the vertex' parent node"); + continue; + } + aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second)); + fSum += pairval.second; + } + // ****************************************************************** + // If the sum of all vertex weights is not 1.0 we must assign + // the rest to the vertex' parent node. Well, at least the doc says + // we should ... + // FIX: We use 0.975 as limit, floating-point inaccuracies seem to + // be very strong in some SMD exporters. Furthermore it is possible + // that the parent of a vertex is 0xffffffff (if the corresponding + // entry in the file was unreadable) + // ****************************************************************** + if (fSum < 0.975f && face.avVertices[iVert].iParentNode != UINT_MAX) + { + if (face.avVertices[iVert].iParentNode >= asBones.size()) + { + DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. " + "The index of the vertex parent bone is invalid. " + "The remaining weights will be normalized to 1.0"); + + if (fSum) + { + fSum = 1 / fSum; + for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone) + { + TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone]; + if (pairval.first >= asBones.size())continue; + aaiBones[pairval.first].back().second *= fSum; + } + } + } + else + { + aaiBones[face.avVertices[iVert].iParentNode].push_back( + TempWeightListEntry(iNum,1.0f-fSum)); + } + } + pcMesh->mFaces[iFace].mIndices[iVert] = iNum++; + } + } + + // now build all bones of the mesh + iNum = 0; + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) + if (!aaiBones[iBone].empty())++iNum; + + if (false && iNum) + { + pcMesh->mNumBones = iNum; + pcMesh->mBones = new aiBone*[pcMesh->mNumBones]; + iNum = 0; + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) + { + if (aaiBones[iBone].empty())continue; + aiBone*& bone = pcMesh->mBones[iNum] = new aiBone(); + + bone->mNumWeights = (unsigned int)aaiBones[iBone].size(); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix; + bone->mName.Set( asBones[iBone].mName ); + + asBones[iBone].bIsUsed = true; + + for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight) + { + bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first; + bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second; + } + ++iNum; + } + } + delete[] aaiBones; + } + delete[] aaiFaces; } // ------------------------------------------------------------------------------------------------ // add bone child nodes void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) { - ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren); - - // first count ... - for (unsigned int i = 0; i < asBones.size();++i) - { - SMD::Bone& bone = asBones[i]; - if (bone.iParent == iParent)++pcNode->mNumChildren; - } - - // now allocate the output array - pcNode->mChildren = new aiNode*[pcNode->mNumChildren]; - - // and fill all subnodes - unsigned int qq = 0; - for (unsigned int i = 0; i < asBones.size();++i) - { - SMD::Bone& bone = asBones[i]; - if (bone.iParent != iParent)continue; - - aiNode* pc = pcNode->mChildren[qq++] = new aiNode(); - pc->mName.Set(bone.mName); - - // store the local transformation matrix of the bind pose - pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix; - pc->mParent = pcNode; - - // add children to this node, too - AddBoneChildren(pc,i); - } + ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren); + + // first count ... + for (unsigned int i = 0; i < asBones.size();++i) + { + SMD::Bone& bone = asBones[i]; + if (bone.iParent == iParent)++pcNode->mNumChildren; + } + + // now allocate the output array + pcNode->mChildren = new aiNode*[pcNode->mNumChildren]; + + // and fill all subnodes + unsigned int qq = 0; + for (unsigned int i = 0; i < asBones.size();++i) + { + SMD::Bone& bone = asBones[i]; + if (bone.iParent != iParent)continue; + + aiNode* pc = pcNode->mChildren[qq++] = new aiNode(); + pc->mName.Set(bone.mName); + + // store the local transformation matrix of the bind pose + pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix; + pc->mParent = pcNode; + + // add children to this node, too + AddBoneChildren(pc,i); + } } // ------------------------------------------------------------------------------------------------ // create output nodes void SMDImporter::CreateOutputNodes() { - pScene->mRootNode = new aiNode(); - if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) - { - // create one root node that renders all meshes - pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; - pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - pScene->mRootNode->mMeshes[i] = i; - } - - // now add all bones as dummy sub nodes to the graph - // AddBoneChildren(pScene->mRootNode,(uint32_t)-1); - - // if we have only one bone we can even remove the root node - if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE && - 1 == pScene->mRootNode->mNumChildren) - { - aiNode* pcOldRoot = pScene->mRootNode; - pScene->mRootNode = pcOldRoot->mChildren[0]; - pcOldRoot->mChildren[0] = NULL; - delete pcOldRoot; - - pScene->mRootNode->mParent = NULL; - } - else - { - ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>"); - pScene->mRootNode->mName.length = 10; - } + pScene->mRootNode = new aiNode(); + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) + { + // create one root node that renders all meshes + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + pScene->mRootNode->mMeshes[i] = i; + } + + // now add all bones as dummy sub nodes to the graph + // AddBoneChildren(pScene->mRootNode,(uint32_t)-1); + + // if we have only one bone we can even remove the root node + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE && + 1 == pScene->mRootNode->mNumChildren) + { + aiNode* pcOldRoot = pScene->mRootNode; + pScene->mRootNode = pcOldRoot->mChildren[0]; + pcOldRoot->mChildren[0] = NULL; + delete pcOldRoot; + + pScene->mRootNode->mParent = NULL; + } + else + { + ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>"); + pScene->mRootNode->mName.length = 10; + } } // ------------------------------------------------------------------------------------------------ // create output animations void SMDImporter::CreateOutputAnimations() { - unsigned int iNumBones = 0; - for (std::vector<SMD::Bone>::const_iterator - i = asBones.begin(); - i != asBones.end();++i) - { - if ((*i).bIsUsed)++iNumBones; - } - if (!iNumBones) - { - // just make sure this case doesn't occur ... (it could occur - // if the file was invalid) - return; - } - - pScene->mNumAnimations = 1; - pScene->mAnimations = new aiAnimation*[1]; - aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation(); - - anim->mDuration = dLengthOfAnim; - anim->mNumChannels = iNumBones; - anim->mTicksPerSecond = 25.0; // FIXME: is this correct? - - aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; - - // now build valid keys - unsigned int a = 0; - for (std::vector<SMD::Bone>::const_iterator - i = asBones.begin(); - i != asBones.end();++i) - { - if (!(*i).bIsUsed)continue; - - aiNodeAnim* p = pp[a] = new aiNodeAnim(); - - // copy the name of the bone - p->mNodeName.Set( i->mName); - - p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size(); - if (p->mNumRotationKeys) - { - p->mNumPositionKeys = p->mNumRotationKeys; - aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys]; - aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys]; - - for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator - qq = (*i).sAnim.asKeys.begin(); - qq != (*i).sAnim.asKeys.end(); ++qq) - { - pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime; - - // compute the rotation quaternion from the euler angles - pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z ); - pVecKeys->mValue = (*qq).vPos; - - ++pVecKeys; ++pRotKeys; - } - } - ++a; - - // there are no scaling keys ... - } + unsigned int iNumBones = 0; + for (std::vector<SMD::Bone>::const_iterator + i = asBones.begin(); + i != asBones.end();++i) + { + if ((*i).bIsUsed)++iNumBones; + } + if (!iNumBones) + { + // just make sure this case doesn't occur ... (it could occur + // if the file was invalid) + return; + } + + pScene->mNumAnimations = 1; + pScene->mAnimations = new aiAnimation*[1]; + aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation(); + + anim->mDuration = dLengthOfAnim; + anim->mNumChannels = iNumBones; + anim->mTicksPerSecond = 25.0; // FIXME: is this correct? + + aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + + // now build valid keys + unsigned int a = 0; + for (std::vector<SMD::Bone>::const_iterator + i = asBones.begin(); + i != asBones.end();++i) + { + if (!(*i).bIsUsed)continue; + + aiNodeAnim* p = pp[a] = new aiNodeAnim(); + + // copy the name of the bone + p->mNodeName.Set( i->mName); + + p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size(); + if (p->mNumRotationKeys) + { + p->mNumPositionKeys = p->mNumRotationKeys; + aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys]; + aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys]; + + for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator + qq = (*i).sAnim.asKeys.begin(); + qq != (*i).sAnim.asKeys.end(); ++qq) + { + pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime; + + // compute the rotation quaternion from the euler angles + pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z ); + pVecKeys->mValue = (*qq).vPos; + + ++pVecKeys; ++pRotKeys; + } + } + ++a; + + // there are no scaling keys ... + } } // ------------------------------------------------------------------------------------------------ void SMDImporter::ComputeAbsoluteBoneTransformations() { - // For each bone: determine the key with the lowest time value - // theoretically the SMD format should have all keyframes - // in order. However, I've seen a file where this wasn't true. - for (unsigned int i = 0; i < asBones.size();++i) - { - SMD::Bone& bone = asBones[i]; - - uint32_t iIndex = 0; - double dMin = 10e10; - for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i) - { - double d = std::min(bone.sAnim.asKeys[i].dTime,dMin); - if (d < dMin) - { - dMin = d; - iIndex = i; - } - } - bone.sAnim.iFirstTimeKey = iIndex; - } - - unsigned int iParent = 0; - while (iParent < asBones.size()) - { - for (unsigned int iBone = 0; iBone < asBones.size();++iBone) - { - SMD::Bone& bone = asBones[iBone]; - - if (iParent == bone.iParent) - { - SMD::Bone& parentBone = asBones[iParent]; - - - uint32_t iIndex = bone.sAnim.iFirstTimeKey; - const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix; - aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute; - - // The same for the parent bone ... - iIndex = parentBone.sAnim.iFirstTimeKey; - const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute; - - // Compute the absolute transformation matrix - matOut = mat * mat2; - } - } - ++iParent; - } - - // Store the inverse of the absolute transformation matrix - // of the first key as bone offset matrix - for (iParent = 0; iParent < asBones.size();++iParent) - { - SMD::Bone& bone = asBones[iParent]; - bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute; - bone.mOffsetMatrix.Inverse(); - } + // For each bone: determine the key with the lowest time value + // theoretically the SMD format should have all keyframes + // in order. However, I've seen a file where this wasn't true. + for (unsigned int i = 0; i < asBones.size();++i) + { + SMD::Bone& bone = asBones[i]; + + uint32_t iIndex = 0; + double dMin = 10e10; + for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i) + { + double d = std::min(bone.sAnim.asKeys[i].dTime,dMin); + if (d < dMin) + { + dMin = d; + iIndex = i; + } + } + bone.sAnim.iFirstTimeKey = iIndex; + } + + unsigned int iParent = 0; + while (iParent < asBones.size()) + { + for (unsigned int iBone = 0; iBone < asBones.size();++iBone) + { + SMD::Bone& bone = asBones[iBone]; + + if (iParent == bone.iParent) + { + SMD::Bone& parentBone = asBones[iParent]; + + + uint32_t iIndex = bone.sAnim.iFirstTimeKey; + const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix; + aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute; + + // The same for the parent bone ... + iIndex = parentBone.sAnim.iFirstTimeKey; + const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute; + + // Compute the absolute transformation matrix + matOut = mat * mat2; + } + } + ++iParent; + } + + // Store the inverse of the absolute transformation matrix + // of the first key as bone offset matrix + for (iParent = 0; iParent < asBones.size();++iParent) + { + SMD::Bone& bone = asBones[iParent]; + bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute; + bone.mOffsetMatrix.Inverse(); + } } // ------------------------------------------------------------------------------------------------ // create output materials void SMDImporter::CreateOutputMaterials() { - pScene->mNumMaterials = (unsigned int)aszTextures.size(); - pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)]; - - for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat) - { - aiMaterial* pcMat = new aiMaterial(); - pScene->mMaterials[iMat] = pcMat; - - aiString szName; - szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat); - pcMat->AddProperty(&szName,AI_MATKEY_NAME); - - if (aszTextures[iMat].length()) - { - ::strcpy(szName.data, aszTextures[iMat].c_str() ); - szName.length = aszTextures[iMat].length(); - pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); - } - } - - // create a default material if necessary - if (0 == pScene->mNumMaterials) - { - pScene->mNumMaterials = 1; - - aiMaterial* pcHelper = new aiMaterial(); - pScene->mMaterials[0] = pcHelper; - - int iMode = (int)aiShadingMode_Gouraud; - pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - aiColor3D clr; - clr.b = clr.g = clr.r = 0.7f; - pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); - - aiString szName; - szName.Set(AI_DEFAULT_MATERIAL_NAME); - pcHelper->AddProperty(&szName,AI_MATKEY_NAME); - } + pScene->mNumMaterials = (unsigned int)aszTextures.size(); + pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)]; + + for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat) + { + aiMaterial* pcMat = new aiMaterial(); + pScene->mMaterials[iMat] = pcMat; + + aiString szName; + szName.length = (size_t)ai_snprintf(szName.data,MAXLEN,"Texture_%u",iMat); + pcMat->AddProperty(&szName,AI_MATKEY_NAME); + + if (aszTextures[iMat].length()) + { + ::strcpy(szName.data, aszTextures[iMat].c_str() ); + szName.length = aszTextures[iMat].length(); + pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } + } + + // create a default material if necessary + if (0 == pScene->mNumMaterials) + { + pScene->mNumMaterials = 1; + + aiMaterial* pcHelper = new aiMaterial(); + pScene->mMaterials[0] = pcHelper; + + int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 0.7f; + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT); + + aiString szName; + szName.Set(AI_DEFAULT_MATERIAL_NAME); + pcHelper->AddProperty(&szName,AI_MATKEY_NAME); + } } // ------------------------------------------------------------------------------------------------ // Parse the file void SMDImporter::ParseFile() { - const char* szCurrent = mBuffer; - - // read line per line ... - for ( ;; ) - { - if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; - - // "version <n> \n", <n> should be 1 for hl and hl² SMD files - if (TokenMatch(szCurrent,"version",7)) - { - if(!SkipSpaces(szCurrent,&szCurrent)) break; - if (1 != strtoul10(szCurrent,&szCurrent)) - { - DefaultLogger::get()->warn("SMD.version is not 1. This " - "file format is not known. Continuing happily ..."); - } - continue; - } - // "nodes\n" - Starts the node section - if (TokenMatch(szCurrent,"nodes",5)) - { - ParseNodesSection(szCurrent,&szCurrent); - continue; - } - // "triangles\n" - Starts the triangle section - if (TokenMatch(szCurrent,"triangles",9)) - { - ParseTrianglesSection(szCurrent,&szCurrent); - continue; - } - // "vertexanimation\n" - Starts the vertex animation section - if (TokenMatch(szCurrent,"vertexanimation",15)) - { - bHasUVs = false; - ParseVASection(szCurrent,&szCurrent); - continue; - } - // "skeleton\n" - Starts the skeleton section - if (TokenMatch(szCurrent,"skeleton",8)) - { - ParseSkeletonSection(szCurrent,&szCurrent); - continue; - } - SkipLine(szCurrent,&szCurrent); - } - return; + const char* szCurrent = mBuffer; + + // read line per line ... + for ( ;; ) + { + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; + + // "version <n> \n", <n> should be 1 for hl and hl� SMD files + if (TokenMatch(szCurrent,"version",7)) + { + if(!SkipSpaces(szCurrent,&szCurrent)) break; + if (1 != strtoul10(szCurrent,&szCurrent)) + { + DefaultLogger::get()->warn("SMD.version is not 1. This " + "file format is not known. Continuing happily ..."); + } + continue; + } + // "nodes\n" - Starts the node section + if (TokenMatch(szCurrent,"nodes",5)) + { + ParseNodesSection(szCurrent,&szCurrent); + continue; + } + // "triangles\n" - Starts the triangle section + if (TokenMatch(szCurrent,"triangles",9)) + { + ParseTrianglesSection(szCurrent,&szCurrent); + continue; + } + // "vertexanimation\n" - Starts the vertex animation section + if (TokenMatch(szCurrent,"vertexanimation",15)) + { + bHasUVs = false; + ParseVASection(szCurrent,&szCurrent); + continue; + } + // "skeleton\n" - Starts the skeleton section + if (TokenMatch(szCurrent,"skeleton",8)) + { + ParseSkeletonSection(szCurrent,&szCurrent); + continue; + } + SkipLine(szCurrent,&szCurrent); + } + return; } // ------------------------------------------------------------------------------------------------ unsigned int SMDImporter::GetTextureIndex(const std::string& filename) { - unsigned int iIndex = 0; - for (std::vector<std::string>::const_iterator - i = aszTextures.begin(); - i != aszTextures.end();++i,++iIndex) - { - // case-insensitive ... it's a path - if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex; - } - iIndex = (unsigned int)aszTextures.size(); - aszTextures.push_back(filename); - return iIndex; + unsigned int iIndex = 0; + for (std::vector<std::string>::const_iterator + i = aszTextures.begin(); + i != aszTextures.end();++i,++iIndex) + { + // case-insensitive ... it's a path + if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex; + } + iIndex = (unsigned int)aszTextures.size(); + aszTextures.push_back(filename); + return iIndex; } // ------------------------------------------------------------------------------------------------ // Parse the nodes section of the file void SMDImporter::ParseNodesSection(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - for ( ;; ) - { - // "end\n" - Ends the nodes section - if (0 == ASSIMP_strincmp(szCurrent,"end",3) && - IsSpaceOrNewLine(*(szCurrent+3))) - { - szCurrent += 4; - break; - } - ParseNodeInfo(szCurrent,&szCurrent); - } - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - *szCurrentOut = szCurrent; + for ( ;; ) + { + // "end\n" - Ends the nodes section + if (0 == ASSIMP_strincmp(szCurrent,"end",3) && + IsSpaceOrNewLine(*(szCurrent+3))) + { + szCurrent += 4; + break; + } + ParseNodeInfo(szCurrent,&szCurrent); + } + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + *szCurrentOut = szCurrent; } // ------------------------------------------------------------------------------------------------ // Parse the triangles section of the file void SMDImporter::ParseTrianglesSection(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - // Parse a triangle, parse another triangle, parse the next triangle ... - // and so on until we reach a token that looks quite similar to "end" - for ( ;; ) - { - if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; - - // "end\n" - Ends the triangles section - if (TokenMatch(szCurrent,"end",3)) - break; - ParseTriangle(szCurrent,&szCurrent); - } - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - *szCurrentOut = szCurrent; + // Parse a triangle, parse another triangle, parse the next triangle ... + // and so on until we reach a token that looks quite similar to "end" + for ( ;; ) + { + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; + + // "end\n" - Ends the triangles section + if (TokenMatch(szCurrent,"end",3)) + break; + ParseTriangle(szCurrent,&szCurrent); + } + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + *szCurrentOut = szCurrent; } // ------------------------------------------------------------------------------------------------ // Parse the vertex animation section of the file void SMDImporter::ParseVASection(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - unsigned int iCurIndex = 0; - for ( ;; ) - { - if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; - - // "end\n" - Ends the "vertexanimation" section - if (TokenMatch(szCurrent,"end",3)) - break; - - // "time <n>\n" - if (TokenMatch(szCurrent,"time",4)) - { - // NOTE: The doc says that time values COULD be negative ... - // NOTE2: this is the shape key -> valve docs - int iTime = 0; - if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break; - SkipLine(szCurrent,&szCurrent); - } - else - { - if(0 == iCurIndex) - { - asTriangles.push_back(SMD::Face()); - } - if (++iCurIndex == 3)iCurIndex = 0; - ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true); - } - } - - if (iCurIndex != 2 && !asTriangles.empty()) - { - // we want to no degenerates, so throw this triangle away - asTriangles.pop_back(); - } - - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - *szCurrentOut = szCurrent; + unsigned int iCurIndex = 0; + for ( ;; ) + { + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; + + // "end\n" - Ends the "vertexanimation" section + if (TokenMatch(szCurrent,"end",3)) + break; + + // "time <n>\n" + if (TokenMatch(szCurrent,"time",4)) + { + // NOTE: The doc says that time values COULD be negative ... + // NOTE2: this is the shape key -> valve docs + int iTime = 0; + if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break; + SkipLine(szCurrent,&szCurrent); + } + else + { + if(0 == iCurIndex) + { + asTriangles.push_back(SMD::Face()); + } + if (++iCurIndex == 3)iCurIndex = 0; + ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true); + } + } + + if (iCurIndex != 2 && !asTriangles.empty()) + { + // we want to no degenerates, so throw this triangle away + asTriangles.pop_back(); + } + + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + *szCurrentOut = szCurrent; } // ------------------------------------------------------------------------------------------------ // Parse the skeleton section of the file void SMDImporter::ParseSkeletonSection(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - int iTime = 0; - for ( ;; ) - { - if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; - - // "end\n" - Ends the skeleton section - if (TokenMatch(szCurrent,"end",3)) - break; - - // "time <n>\n" - Specifies the current animation frame - else if (TokenMatch(szCurrent,"time",4)) - { - // NOTE: The doc says that time values COULD be negative ... - if(!ParseSignedInt(szCurrent,&szCurrent,iTime))break; - - iSmallestFrame = std::min(iSmallestFrame,iTime); - SkipLine(szCurrent,&szCurrent); - } - else ParseSkeletonElement(szCurrent,&szCurrent,iTime); - } - *szCurrentOut = szCurrent; + int iTime = 0; + for ( ;; ) + { + if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; + + // "end\n" - Ends the skeleton section + if (TokenMatch(szCurrent,"end",3)) + break; + + // "time <n>\n" - Specifies the current animation frame + else if (TokenMatch(szCurrent,"time",4)) + { + // NOTE: The doc says that time values COULD be negative ... + if(!ParseSignedInt(szCurrent,&szCurrent,iTime))break; + + iSmallestFrame = std::min(iSmallestFrame,iTime); + SkipLine(szCurrent,&szCurrent); + } + else ParseSkeletonElement(szCurrent,&szCurrent,iTime); + } + *szCurrentOut = szCurrent; } // ------------------------------------------------------------------------------------------------ #define SMDI_PARSE_RETURN { \ - SkipLine(szCurrent,&szCurrent); \ - *szCurrentOut = szCurrent; \ - return; \ + SkipLine(szCurrent,&szCurrent); \ + *szCurrentOut = szCurrent; \ + return; \ } // ------------------------------------------------------------------------------------------------ // Parse a node line void SMDImporter::ParseNodeInfo(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - unsigned int iBone = 0; - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index"); - SMDI_PARSE_RETURN; - } - // add our bone to the list - if (iBone >= asBones.size())asBones.resize(iBone+1); - SMD::Bone& bone = asBones[iBone]; - - bool bQuota = true; - if ('\"' != *szCurrent) - { - LogWarning("Bone name is expcted to be enclosed in " - "double quotation marks. "); - bQuota = false; - } - else ++szCurrent; - - const char* szEnd = szCurrent; - for ( ;; ) - { - if (bQuota && '\"' == *szEnd) - { - iBone = (unsigned int)(szEnd - szCurrent); - ++szEnd; - break; - } - else if (IsSpaceOrNewLine(*szEnd)) - { - iBone = (unsigned int)(szEnd - szCurrent); - break; - } - else if (!(*szEnd)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name"); - SMDI_PARSE_RETURN; - } - ++szEnd; - } - bone.mName = std::string(szCurrent,iBone); - szCurrent = szEnd; - - // the only negative bone parent index that could occur is -1 AFAIK - if(!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1"); - SMDI_PARSE_RETURN; - } - - // go to the beginning of the next line - SMDI_PARSE_RETURN; + unsigned int iBone = 0; + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index"); + SMDI_PARSE_RETURN; + } + // add our bone to the list + if (iBone >= asBones.size())asBones.resize(iBone+1); + SMD::Bone& bone = asBones[iBone]; + + bool bQuota = true; + if ('\"' != *szCurrent) + { + LogWarning("Bone name is expcted to be enclosed in " + "double quotation marks. "); + bQuota = false; + } + else ++szCurrent; + + const char* szEnd = szCurrent; + for ( ;; ) + { + if (bQuota && '\"' == *szEnd) + { + iBone = (unsigned int)(szEnd - szCurrent); + ++szEnd; + break; + } + else if (IsSpaceOrNewLine(*szEnd)) + { + iBone = (unsigned int)(szEnd - szCurrent); + break; + } + else if (!(*szEnd)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name"); + SMDI_PARSE_RETURN; + } + ++szEnd; + } + bone.mName = std::string(szCurrent,iBone); + szCurrent = szEnd; + + // the only negative bone parent index that could occur is -1 AFAIK + if(!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1"); + SMDI_PARSE_RETURN; + } + + // go to the beginning of the next line + SMDI_PARSE_RETURN; } // ------------------------------------------------------------------------------------------------ // Parse a skeleton element void SMDImporter::ParseSkeletonElement(const char* szCurrent, - const char** szCurrentOut,int iTime) + const char** szCurrentOut,int iTime) { - aiVector3D vPos; - aiVector3D vRot; - - unsigned int iBone = 0; - if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone)) - { - DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index"); - SMDI_PARSE_RETURN; - } - if (iBone >= asBones.size()) - { - LogErrorNoThrow("Bone index in skeleton section is out of range"); - SMDI_PARSE_RETURN; - } - SMD::Bone& bone = asBones[iBone]; - - bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey()); - SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back(); - - key.dTime = (double)iTime; - if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z"); - SMDI_PARSE_RETURN; - } - // build the transformation matrix of the key - key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z); - { - aiMatrix4x4 mTemp; - mTemp.a4 = vPos.x; - mTemp.b4 = vPos.y; - mTemp.c4 = vPos.z; - key.matrix = key.matrix * mTemp; - } - - // go to the beginning of the next line - SMDI_PARSE_RETURN; + aiVector3D vPos; + aiVector3D vRot; + + unsigned int iBone = 0; + if(!ParseUnsignedInt(szCurrent,&szCurrent,iBone)) + { + DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index"); + SMDI_PARSE_RETURN; + } + if (iBone >= asBones.size()) + { + LogErrorNoThrow("Bone index in skeleton section is out of range"); + SMDI_PARSE_RETURN; + } + SMD::Bone& bone = asBones[iBone]; + + bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey()); + SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back(); + + key.dTime = (double)iTime; + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z"); + SMDI_PARSE_RETURN; + } + // build the transformation matrix of the key + key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z); + { + aiMatrix4x4 mTemp; + mTemp.a4 = vPos.x; + mTemp.b4 = vPos.y; + mTemp.c4 = vPos.z; + key.matrix = key.matrix * mTemp; + } + + // go to the beginning of the next line + SMDI_PARSE_RETURN; } // ------------------------------------------------------------------------------------------------ // Parse a triangle void SMDImporter::ParseTriangle(const char* szCurrent, - const char** szCurrentOut) + const char** szCurrentOut) { - asTriangles.push_back(SMD::Face()); - SMD::Face& face = asTriangles.back(); - - if(!SkipSpaces(szCurrent,&szCurrent)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle"); - return; - } - - // read the texture file name - const char* szLast = szCurrent; - while (!IsSpaceOrNewLine(*szCurrent++)); - - // ... and get the index that belongs to this file name - face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast)); - - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - - // load three vertices - for (unsigned int iVert = 0; iVert < 3;++iVert) - { - ParseVertex(szCurrent,&szCurrent, - face.avVertices[iVert]); - } - *szCurrentOut = szCurrent; + asTriangles.push_back(SMD::Face()); + SMD::Face& face = asTriangles.back(); + + if(!SkipSpaces(szCurrent,&szCurrent)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle"); + return; + } + + // read the texture file name + const char* szLast = szCurrent; + while (!IsSpaceOrNewLine(*szCurrent++)); + + // ... and get the index that belongs to this file name + face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast)); + + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + + // load three vertices + for (unsigned int iVert = 0; iVert < 3;++iVert) + { + ParseVertex(szCurrent,&szCurrent, + face.avVertices[iVert]); + } + *szCurrentOut = szCurrent; } // ------------------------------------------------------------------------------------------------ // Parse a float bool SMDImporter::ParseFloat(const char* szCurrent, - const char** szCurrentOut, float& out) + const char** szCurrentOut, float& out) { - if(!SkipSpaces(&szCurrent)) - return false; + if(!SkipSpaces(&szCurrent)) + return false; - *szCurrentOut = fast_atoreal_move<float>(szCurrent,out); - return true; + *szCurrentOut = fast_atoreal_move<float>(szCurrent,out); + return true; } // ------------------------------------------------------------------------------------------------ // Parse an unsigned int bool SMDImporter::ParseUnsignedInt(const char* szCurrent, - const char** szCurrentOut, unsigned int& out) + const char** szCurrentOut, unsigned int& out) { - if(!SkipSpaces(&szCurrent)) - return false; + if(!SkipSpaces(&szCurrent)) + return false; - out = strtoul10(szCurrent,szCurrentOut); - return true; + out = strtoul10(szCurrent,szCurrentOut); + return true; } // ------------------------------------------------------------------------------------------------ // Parse a signed int bool SMDImporter::ParseSignedInt(const char* szCurrent, - const char** szCurrentOut, int& out) + const char** szCurrentOut, int& out) { - if(!SkipSpaces(&szCurrent)) - return false; + if(!SkipSpaces(&szCurrent)) + return false; - out = strtol10(szCurrent,szCurrentOut); - return true; + out = strtol10(szCurrent,szCurrentOut); + return true; } // ------------------------------------------------------------------------------------------------ // Parse a vertex void SMDImporter::ParseVertex(const char* szCurrent, - const char** szCurrentOut, SMD::Vertex& vertex, - bool bVASection /*= false*/) + const char** szCurrentOut, SMD::Vertex& vertex, + bool bVASection /*= false*/) { - if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent)) - { - SkipSpacesAndLineEnd(szCurrent,&szCurrent); - return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection); - } - if(!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z"); - SMDI_PARSE_RETURN; - } - - if (bVASection)SMDI_PARSE_RETURN; - - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x"); - SMDI_PARSE_RETURN; - } - if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y)) - { - LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y"); - SMDI_PARSE_RETURN; - } - - // now read the number of bones affecting this vertex - // all elements from now are fully optional, we don't need them - unsigned int iSize = 0; - if(!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN; - vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f)); - - for (std::vector<std::pair<unsigned int, float> >::iterator - i = vertex.aiBoneLinks.begin(); - i != vertex.aiBoneLinks.end();++i) - { - if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first)) - SMDI_PARSE_RETURN; - if(!ParseFloat(szCurrent,&szCurrent,(*i).second)) - SMDI_PARSE_RETURN; - } - - // go to the beginning of the next line - SMDI_PARSE_RETURN; + if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent)) + { + SkipSpacesAndLineEnd(szCurrent,&szCurrent); + return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection); + } + if(!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z"); + SMDI_PARSE_RETURN; + } + + if (bVASection)SMDI_PARSE_RETURN; + + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x"); + SMDI_PARSE_RETURN; + } + if(!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y)) + { + LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y"); + SMDI_PARSE_RETURN; + } + + // now read the number of bones affecting this vertex + // all elements from now are fully optional, we don't need them + unsigned int iSize = 0; + if(!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN; + vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f)); + + for (std::vector<std::pair<unsigned int, float> >::iterator + i = vertex.aiBoneLinks.begin(); + i != vertex.aiBoneLinks.end();++i) + { + if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first)) + SMDI_PARSE_RETURN; + if(!ParseFloat(szCurrent,&szCurrent,(*i).second)) + SMDI_PARSE_RETURN; + } + + // go to the beginning of the next line + SMDI_PARSE_RETURN; } #endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER |