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 CSMLoader.cpp nuclear@0: * Implementation of the CSM importer class. nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_CSM_IMPORTER nuclear@0: nuclear@0: #include "CSMLoader.h" nuclear@0: #include "SkeletonMeshBuilder.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "CharacterStudio Motion Importer (MoCap)", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "csm" nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: CSMImporter::CSMImporter() nuclear@0: : noSkeletonMesh() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: CSMImporter::~CSMImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: // check file extension nuclear@0: const std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: if( extension == "csm") nuclear@0: return true; nuclear@0: nuclear@0: if ((checkSig || !extension.length()) && pIOHandler) { nuclear@0: const char* tokens[] = {"$Filename"}; nuclear@0: return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Build a string of all file extensions supported nuclear@0: const aiImporterDesc* CSMImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Setup configuration properties for the loader nuclear@0: void CSMImporter::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void CSMImporter::InternReadFile( const std::string& pFile, nuclear@0: 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 CSM file " + pFile + "."); nuclear@0: } nuclear@0: nuclear@0: // allocate storage and copy the contents of the file to a memory buffer nuclear@0: std::vector mBuffer2; nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: const char* buffer = &mBuffer2[0]; nuclear@0: nuclear@0: aiAnimation* anim = new aiAnimation(); nuclear@0: int first = 0, last = 0x00ffffff; nuclear@0: nuclear@0: // now process the file and look out for '$' sections nuclear@0: while (1) { nuclear@0: SkipSpaces(&buffer); nuclear@0: if ('\0' == *buffer) nuclear@0: break; nuclear@0: nuclear@0: if ('$' == *buffer) { nuclear@0: ++buffer; nuclear@0: if (TokenMatchI(buffer,"firstframe",10)) { nuclear@0: SkipSpaces(&buffer); nuclear@0: first = strtol10(buffer,&buffer); nuclear@0: } nuclear@0: else if (TokenMatchI(buffer,"lastframe",9)) { nuclear@0: SkipSpaces(&buffer); nuclear@0: last = strtol10(buffer,&buffer); nuclear@0: } nuclear@0: else if (TokenMatchI(buffer,"rate",4)) { nuclear@0: SkipSpaces(&buffer); nuclear@0: float d; nuclear@0: buffer = fast_atoreal_move(buffer,d); nuclear@0: anim->mTicksPerSecond = d; nuclear@0: } nuclear@0: else if (TokenMatchI(buffer,"order",5)) { nuclear@0: std::vector< aiNodeAnim* > anims_temp; nuclear@0: anims_temp.reserve(30); nuclear@0: while (1) { nuclear@0: SkipSpaces(&buffer); nuclear@0: if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$') nuclear@0: break; // next section nuclear@0: nuclear@0: // Construct a new node animation channel and setup its name nuclear@0: anims_temp.push_back(new aiNodeAnim()); nuclear@0: aiNodeAnim* nda = anims_temp.back(); nuclear@0: nuclear@0: char* ot = nda->mNodeName.data; nuclear@0: while (!IsSpaceOrNewLine(*buffer)) nuclear@0: *ot++ = *buffer++; nuclear@0: nuclear@0: *ot = '\0'; nuclear@0: nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data); nuclear@0: } nuclear@0: nuclear@0: anim->mNumChannels = anims_temp.size(); nuclear@0: if (!anim->mNumChannels) nuclear@0: throw DeadlyImportError("CSM: Empty $order section"); nuclear@0: nuclear@0: // copy over to the output animation nuclear@0: anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; nuclear@0: ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels); nuclear@0: } nuclear@0: else if (TokenMatchI(buffer,"points",6)) { nuclear@0: if (!anim->mNumChannels) nuclear@0: throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'"); nuclear@0: nuclear@0: // If we know how many frames we'll read, we can preallocate some storage nuclear@0: unsigned int alloc = 100; nuclear@0: if (last != 0x00ffffff) nuclear@0: { nuclear@0: alloc = last-first; nuclear@0: alloc += alloc>>2u; // + 25% nuclear@0: for (unsigned int i = 0; i < anim->mNumChannels;++i) nuclear@0: anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc]; nuclear@0: } nuclear@0: nuclear@0: unsigned int filled = 0; nuclear@0: nuclear@0: // Now read all point data. nuclear@0: while (1) { nuclear@0: SkipSpaces(&buffer); nuclear@0: if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$')) { nuclear@0: break; // next section nuclear@0: } nuclear@0: nuclear@0: // read frame nuclear@0: const int frame = ::strtoul10(buffer,&buffer); nuclear@0: last = std::max(frame,last); nuclear@0: first = std::min(frame,last); nuclear@0: for (unsigned int i = 0; i < anim->mNumChannels;++i) { nuclear@0: nuclear@0: aiNodeAnim* s = anim->mChannels[i]; nuclear@0: if (s->mNumPositionKeys == alloc) { /* need to reallocate? */ nuclear@0: nuclear@0: aiVectorKey* old = s->mPositionKeys; nuclear@0: s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2]; nuclear@0: ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc); nuclear@0: delete[] old; nuclear@0: } nuclear@0: nuclear@0: // read x,y,z nuclear@0: if(!SkipSpacesAndLineEnd(&buffer)) nuclear@0: throw DeadlyImportError("CSM: Unexpected EOF occured reading sample x coord"); nuclear@0: nuclear@0: if (TokenMatchI(buffer, "DROPOUT", 7)) { nuclear@0: // seems this is invalid marker data; at least the doc says it's possible nuclear@0: DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)"); nuclear@0: } nuclear@0: else { nuclear@0: aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys; nuclear@0: sub->mTime = (double)frame; nuclear@0: buffer = fast_atoreal_move(buffer, (float&)sub->mValue.x); nuclear@0: nuclear@0: if(!SkipSpacesAndLineEnd(&buffer)) nuclear@0: throw DeadlyImportError("CSM: Unexpected EOF occured reading sample y coord"); nuclear@0: buffer = fast_atoreal_move(buffer, (float&)sub->mValue.y); nuclear@0: nuclear@0: if(!SkipSpacesAndLineEnd(&buffer)) nuclear@0: throw DeadlyImportError("CSM: Unexpected EOF occured reading sample z coord"); nuclear@0: buffer = fast_atoreal_move(buffer, (float&)sub->mValue.z); nuclear@0: nuclear@0: ++s->mNumPositionKeys; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // update allocation granularity nuclear@0: if (filled == alloc) nuclear@0: alloc *= 2; nuclear@0: nuclear@0: ++filled; nuclear@0: } nuclear@0: // all channels must be complete in order to continue safely. nuclear@0: for (unsigned int i = 0; i < anim->mNumChannels;++i) { nuclear@0: nuclear@0: if (!anim->mChannels[i]->mNumPositionKeys) nuclear@0: throw DeadlyImportError("CSM: Invalid marker track"); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: // advance to the next line nuclear@0: SkipLine(&buffer); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Setup a proper animation duration nuclear@0: anim->mDuration = last - std::min( first, 0 ); nuclear@0: nuclear@0: // build a dummy root node with the tiny markers as children nuclear@0: pScene->mRootNode = new aiNode(); nuclear@0: pScene->mRootNode->mName.Set("$CSM_DummyRoot"); nuclear@0: nuclear@0: pScene->mRootNode->mNumChildren = anim->mNumChannels; nuclear@0: pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < anim->mNumChannels;++i) { nuclear@0: aiNodeAnim* na = anim->mChannels[i]; nuclear@0: nuclear@0: aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode(); nuclear@0: nd->mName = anim->mChannels[i]->mNodeName; nuclear@0: nd->mParent = pScene->mRootNode; nuclear@0: nuclear@0: aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation); nuclear@0: } nuclear@0: nuclear@0: // Store the one and only animation in the scene nuclear@0: pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1]; nuclear@0: pScene->mAnimations[0] = anim; nuclear@0: anim->mName.Set("$CSM_MasterAnim"); nuclear@0: nuclear@0: // mark the scene as incomplete and run SkeletonMeshBuilder on it nuclear@0: pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; nuclear@0: nuclear@0: if (!noSkeletonMesh) { nuclear@0: SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_CSM_IMPORTER