diff options
Diffstat (limited to 'src/3rdparty/assimp/code/SMDLoader.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/SMDLoader.cpp | 1161 |
1 files changed, 0 insertions, 1161 deletions
diff --git a/src/3rdparty/assimp/code/SMDLoader.cpp b/src/3rdparty/assimp/code/SMDLoader.cpp deleted file mode 100644 index 60e3f63c8..000000000 --- a/src/3rdparty/assimp/code/SMDLoader.cpp +++ /dev/null @@ -1,1161 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2017, 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 -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - 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 -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -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 -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 -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file SMDLoader.cpp - * @brief Implementation of the SMD importer class - */ - - -#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 <assimp/importerdesc.h> -#include <memory> - -using namespace Assimp; - -static const aiImporterDesc desc = { - "Valve SMD Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "smd vta" -}; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -SMDImporter::SMDImporter() -: configFrameID(), -mBuffer(), -pScene( nullptr ), -iFileSize( 0 ), -iSmallestFrame( -1 ), -dLengthOfAnim( 0.0 ), -bHasUVs(false ), -iLineNumber(-1) { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -SMDImporter::~SMDImporter() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -// 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"); -} - -// ------------------------------------------------------------------------------------------------ -// Get a list of all supported file extensions -const aiImporterDesc* SMDImporter::GetInfo () const -{ - 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); - } -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void SMDImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ - 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; - - mBuffer.resize( iFileSize + 1 ); - TextFileToBuffer(file.get(), mBuffer ); - - 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]; - 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); - 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; -} - -// ------------------------------------------------------------------------------------------------ -// 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; -} - -// ------------------------------------------------------------------------------------------------ -// add bone child nodes -void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) -{ - ai_assert( NULL != pcNode ); - ai_assert( 0 == pcNode->mNumChildren ); - ai_assert( 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; - } -} - -// ------------------------------------------------------------------------------------------------ -// 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 ... - } -} - -// ------------------------------------------------------------------------------------------------ -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(); - } -} -\ -// ------------------------------------------------------------------------------------------------ -// create output materials -void SMDImporter::CreateOutputMaterials() -{ - ai_assert( nullptr != pScene ); - - 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(); - ai_assert( nullptr != pcMat ); - 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()) - { - ::strncpy(szName.data, aszTextures[iMat].c_str(),MAXLEN-1); - 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[0]; - - // read line per line ... - for ( ;; ) - { - if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; - - // "version <n> \n", <n> should be 1 for hl and hl2 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; -} - -// ------------------------------------------------------------------------------------------------ -// Parse the nodes section of the file -void SMDImporter::ParseNodesSection(const char* szCurrent, - 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; -} - -// ------------------------------------------------------------------------------------------------ -// Parse the triangles section of the file -void SMDImporter::ParseTrianglesSection(const char* szCurrent, - 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 the vertex animation section of the file -void SMDImporter::ParseVASection(const char* szCurrent, - 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; -} -// ------------------------------------------------------------------------------------------------ -// Parse the skeleton section of the file -void SMDImporter::ParseSkeletonSection(const char* szCurrent, - 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; -} - -// ------------------------------------------------------------------------------------------------ -#define SMDI_PARSE_RETURN { \ - SkipLine(szCurrent,&szCurrent); \ - *szCurrentOut = szCurrent; \ - return; \ -} -// ------------------------------------------------------------------------------------------------ -// Parse a node line -void SMDImporter::ParseNodeInfo(const char* szCurrent, - 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; -} - -// ------------------------------------------------------------------------------------------------ -// Parse a skeleton element -void SMDImporter::ParseSkeletonElement(const char* szCurrent, - 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; -} - -// ------------------------------------------------------------------------------------------------ -// Parse a triangle -void SMDImporter::ParseTriangle(const char* szCurrent, - 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; -} - -// ------------------------------------------------------------------------------------------------ -// Parse a float -bool SMDImporter::ParseFloat(const char* szCurrent, - const char** szCurrentOut, float& out) -{ - if(!SkipSpaces(&szCurrent)) - return false; - - *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) -{ - if(!SkipSpaces(&szCurrent)) - return false; - - out = strtoul10(szCurrent,szCurrentOut); - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Parse a signed int -bool SMDImporter::ParseSignedInt(const char* szCurrent, - const char** szCurrentOut, int& out) -{ - if(!SkipSpaces(&szCurrent)) - return false; - - out = strtol10(szCurrent,szCurrentOut); - return true; -} - -// ------------------------------------------------------------------------------------------------ -// Parse a vertex -void SMDImporter::ParseVertex(const char* szCurrent, - 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; -} - -#endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER |