diff --git a/code/BVHLoader.cpp b/code/BVHLoader.cpp new file mode 100644 index 000000000..461a5ae11 --- /dev/null +++ b/code/BVHLoader.cpp @@ -0,0 +1,314 @@ +/** Implementation of the BVH loader */ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (ASSIMP) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2008, ASSIMP Development 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 Development 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. +--------------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" +#include "BVHLoader.h" +#include "fast_atof.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BVHLoader::BVHLoader() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BVHLoader::~BVHLoader() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler) const +{ + // check file extension + std::string::size_type pos = pFile.find_last_of('.'); + // no file extension - can't read + if( pos == std::string::npos) + return false; + std::string extension = pFile.substr( pos); + for( std::string::iterator it = extension.begin(); it != extension.end(); ++it) + *it = tolower( *it); + + if( extension == ".bvh") + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + mFileName = pFile; + + // read file into memory + boost::scoped_ptr file( pIOHandler->Open( pFile)); + if( file.get() == NULL) + throw new ImportErrorException( "Failed to open file " + pFile + "."); + + size_t fileSize = file->FileSize(); + if( fileSize == 0) + throw new ImportErrorException( "File is too small."); + + mBuffer.resize( fileSize); + file->Read( &mBuffer.front(), 1, fileSize); + + // start reading + mReader = mBuffer.begin(); + mLine = 1; + ReadStructure( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the file +void BVHLoader::ReadStructure( aiScene* pScene) +{ + // first comes hierarchy + std::string header = GetNextToken(); + if( header != "HIERARCHY") + ThrowException( "Expected header string \"HIERARCHY\"."); + ReadHierarchy( pScene); + + // then comes the motion data + std::string motion = GetNextToken(); + if( motion != "MOTION") + ThrowException( "Expected beginning of motion data \"MOTION\"."); + ReadMotion( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the hierarchy +void BVHLoader::ReadHierarchy( aiScene* pScene) +{ + std::string root = GetNextToken(); + if( root != "ROOT") + ThrowException( "Expected root node \"ROOT\"."); + + // Go read the hierarchy from here + pScene->mRootNode = ReadNode(); +} + +// ------------------------------------------------------------------------------------------------ +// Reads a node and recursively its childs and returns the created node; +aiNode* BVHLoader::ReadNode() +{ + // first token is name + std::string nodeName = GetNextToken(); + if( nodeName.empty() || nodeName == "{") + ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); + + // HACK: (thom) end nodes are called "End Site". If the name of the node is "Site", we know it's going to be an end node + if( nodeName == "Site") + nodeName = "End Site"; + + // then an opening brace should follow + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); + + // Create a node + aiNode* node = new aiNode( nodeName); + std::vector childNodes; + + // now read the node's contents + while( 1) + { + std::string token = GetNextToken(); + + // node offset to parent node + if( token == "OFFSET") + ReadNodeOffset( node); + else if( token == "CHANNELS") + ReadNodeChannels( node); + else if( token == "JOINT") + { + // child node follows + aiNode* child = ReadNode(); + childNodes.push_back( child); + } else + if( token == "End") + { + // HACK: (thom) end child node follows. Full token is "End Site", then no name, then a node. + // But I don't want to write another function for this, so I simply leave the "Site" for ReadNode() as a node name + aiNode* child = ReadNode(); + childNodes.push_back( child); + } else + if( token == "}") + { + // we're done with that part of the hierarchy + break; + } else + { + // everything else is a parse error + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); + } + } + + // add the child nodes if there are any + if( childNodes.size() > 0) + { + node->mNumChildren = childNodes.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + std::copy( childNodes.begin(), childNodes.end(), node->mChildren); + } + + // and return the sub-hierarchy we built here + return node; +} + +// ------------------------------------------------------------------------------------------------ +// Reads a node offset for the given node +void BVHLoader::ReadNodeOffset( aiNode* pNode) +{ + // Offset consists of three floats to read + aiVector3D offset; + offset.x = GetNextTokenAsFloat(); + offset.y = GetNextTokenAsFloat(); + offset.z = GetNextTokenAsFloat(); + + // build a transformation matrix from it + pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, + 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the animation channels for the given node +void BVHLoader::ReadNodeChannels( aiNode* pNode) +{ + // number of channels. Use the float reader because we're lazy + float numChannelsFloat = GetNextTokenAsFloat(); + unsigned int numChannels = (unsigned int) numChannelsFloat; + + // TODO: (thom) proper channel parsing. For the moment I just skip the number of tokens + for( unsigned int a = 0; a < numChannels; a++) + GetNextToken(); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the motion data +void BVHLoader::ReadMotion( aiScene* pScene) +{ + // Read number of frames + std::string tokenFrames = GetNextToken(); + if( tokenFrames != "Frames:") + ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); + + float numFramesFloat = GetNextTokenAsFloat(); + unsigned int numFrames = (unsigned int) numFramesFloat; + + // Read frame duration + std::string tokenDuration1 = GetNextToken(); + std::string tokenDuration2 = GetNextToken(); + if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") + ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); + + float frameDuration = GetNextTokenAsFloat(); + + // resize value array accordingly + // ************* Continue here ******** + //mMotionValues.resize( boost::extents[numFrames][numChannels]); +} + +// ------------------------------------------------------------------------------------------------ +// Retrieves the next token +std::string BVHLoader::GetNextToken() +{ + // skip any preceeding whitespace + while( mReader != mBuffer.end()) + { + if( !isspace( *mReader)) + break; + + // count lines + if( *mReader == '\n') + mLine++; + + ++mReader; + } + + // collect all chars till the next whitespace. BVH is easy in respect to that. + std::string token; + while( mReader != mBuffer.end()) + { + if( isspace( *mReader)) + break; + + token.push_back( *mReader); + ++mReader; + + // little extra logic to make sure braces are counted correctly + if( token == "{" || token == "}") + break; + } + + // empty token means end of file, which is just fine + return token; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the next token as a float +float BVHLoader::GetNextTokenAsFloat() +{ + std::string token = GetNextToken(); + if( token.empty()) + ThrowException( "Unexpected end of file while trying to read a float"); + + // check if the float is valid by testing if the atof() function consumed every char of the token + const char* ctoken = token.c_str(); + float result = 0.0f; + ctoken = fast_atof_move( ctoken, result); + + if( ctoken != token.c_str() + token.length()) + ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +// Aborts the file reading with an exception +void BVHLoader::ThrowException( const std::string& pError) +{ + throw new ImportErrorException( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); +} diff --git a/code/BVHLoader.h b/code/BVHLoader.h new file mode 100644 index 000000000..9c098e882 --- /dev/null +++ b/code/BVHLoader.h @@ -0,0 +1,133 @@ +/** Defines the BHV motion capturing loader class */ + +/* +Open Asset Import Library (ASSIMP) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, ASSIMP Development 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 Development 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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_BVHLOADER_H_INC +#define AI_BVHLOADER_H_INC + +#include "BaseImporter.h" +#include + +namespace Assimp +{ + +/** Loader class to read Motion Capturing data from a .bvh file. This format only contains a +* hierarchy of joints and a series of keyframes for the hierarchy. It contains no actual mesh data, +* but we generate a dummy mesh inside the loader just to be able to see something. +*/ +class BVHLoader : public BaseImporter +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + BVHLoader(); + + /** Destructor, private as well */ + ~BVHLoader(); + +public: + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const; + +protected: + /** Called by Importer::GetExtensionList() for each loaded importer. + * See BaseImporter::GetExtensionList() for details + */ + void GetExtensionList( std::string& append) + { + append.append("*.bvh"); + } + + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + +protected: + /** Reads the file */ + void ReadStructure( aiScene* pScene); + + /** Reads the hierarchy */ + void ReadHierarchy( aiScene* pScene); + + /** Reads a node and recursively its childs and returns the created node. */ + aiNode* ReadNode(); + + /** Reads a node offset for the given node */ + void ReadNodeOffset( aiNode* pNode); + + /** Reads the animation channels for the given node */ + void ReadNodeChannels( aiNode* pNode); + + /** Reads the motion data */ + void ReadMotion( aiScene* pScene); + + /** Retrieves the next token */ + std::string GetNextToken(); + + /** Reads the next token as a float */ + float GetNextTokenAsFloat(); + + /** Aborts the file reading with an exception */ + void ThrowException( const std::string& pError); + +protected: + /** Filename, for a verbose error message */ + std::string mFileName; + + /** Buffer to hold the loaded file */ + std::vector mBuffer; + + /** Next char to read from the buffer */ + std::vector::const_iterator mReader; + + /** Current line, for error messages */ + unsigned int mLine; + + /** motion values per frame */ + boost::multi_array mMotionValues; +}; + +} // end of namespace Assimp + +#endif // AI_BVHLOADER_H_INC \ No newline at end of file diff --git a/code/Importer.cpp b/code/Importer.cpp index 9a1cdc7f0..f2a09e038 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -113,6 +113,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_BUILD_NO_AC_IMPORTER # include "ACLoader.h" #endif +#ifndef AI_BUILD_NO_BVH_IMPORTER +# include "BVHLoader.h" +#endif // PostProcess-Steps @@ -254,6 +257,9 @@ Importer::Importer() : #if (!defined AI_BUILD_NO_AC_IMPORTER) mImporter.push_back( new AC3DImporter()); #endif +#if (!defined AI_BUILD_NO_BVH_IMPORTER) + mImporter.push_back( new BVHLoader()); +#endif // add an instance of each post processing step here in the order // of sequence it is executed. steps that are added here are not validated - diff --git a/code/XFileImporter.cpp b/code/XFileImporter.cpp index 98ad29cd6..3b02938a6 100644 --- a/code/XFileImporter.cpp +++ b/code/XFileImporter.cpp @@ -12,18 +12,18 @@ 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. +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. +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 Development Team. +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the ASSIMP Development Team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -103,9 +103,9 @@ void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, I // and create the proper return structures out of it CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); - // if nothing came from it, report it as error - if( !pScene->mRootNode) - throw new ImportErrorException( "XFile is ill-formatted - no content imported."); + // if nothing came from it, report it as error + if( !pScene->mRootNode) + throw new ImportErrorException( "XFile is ill-formatted - no content imported."); } // ------------------------------------------------------------------------------------------------ @@ -139,8 +139,8 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const X } // convert the root node's transformation to OGL coords - if( pScene->mRootNode) - ConvertToLHProcess::ConvertToOGL( pScene->mRootNode->mTransformation); + if( pScene->mRootNode) + ConvertToLHProcess::ConvertToOGL( pScene->mRootNode->mTransformation); // finally: create a dummy material if not material was imported if( pScene->mNumMaterials == 0) @@ -180,7 +180,7 @@ aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFil memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length()); node->mName.data[node->mName.length] = 0; node->mTransformation = pNode->mTrafoMatrix; - + // convert meshes from the source node CreateMeshes( pScene, node, pNode->mMeshes); @@ -323,7 +323,7 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++) if( mesh->HasVertexColors( e)) mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]]; - + newIndex++; } } @@ -423,7 +423,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData const XFile::AnimBone* bone = anim->mAnims[b]; aiNodeAnim* nbone = new aiNodeAnim; nbone->mNodeName.Set( bone->mBoneName); - nanim->mChannels[b] = nbone; + nanim->mChannels[b] = nbone; // apply the LH->RH conversion if the animation affects the root bone bool isRootAnim = (bone->mBoneName == pScene->mRootNode->mName.data); @@ -650,7 +650,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector @@ -455,6 +459,8 @@ SmallerTypeCheck="true" RuntimeLibrary="3" EnableFunctionLevelLinking="true" + UsePrecompiledHeader="2" + PrecompiledHeaderThrough="AssimpPCH.h" WarningLevel="3" Detect64BitPortabilityProblems="false" DebugInformationFormat="4" @@ -1324,6 +1330,18 @@ > + + + + + +