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