diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp')
| -rw-r--r-- | NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp b/NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp new file mode 100644 index 0000000..c20cbec --- /dev/null +++ b/NvCloth/samples/external/assimp-4.1.0/code/BVHLoader.cpp @@ -0,0 +1,545 @@ +/** Implementation of the BVH loader */ +/* +--------------------------------------------------------------------------- +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. +--------------------------------------------------------------------------- +*/ + + +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER + +#include "BVHLoader.h" +#include "fast_atof.h" +#include "SkeletonMeshBuilder.h" +#include <assimp/Importer.hpp> +#include <memory> +#include "TinyFormatter.h" +#include <assimp/IOSystem.hpp> +#include <assimp/scene.h> +#include <assimp/importerdesc.h> + +using namespace Assimp; +using namespace Assimp::Formatter; + +static const aiImporterDesc desc = { + "BVH Importer (MoCap)", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "bvh" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BVHLoader::BVHLoader() + : mLine(), + mAnimTickDuration(), + mAnimNumFrames(), + noSkeletonMesh() +{} + +// ------------------------------------------------------------------------------------------------ +// 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, bool cs) const +{ + // check file extension + const std::string extension = GetExtension(pFile); + + if( extension == "bvh") + return true; + + if ((!extension.length() || cs) && pIOHandler) { + const char* tokens[] = {"HIERARCHY"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +void BVHLoader::SetupProperties(const Importer* pImp) +{ + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* BVHLoader::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// 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 + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open file " + pFile + "."); + + size_t fileSize = file->FileSize(); + if( fileSize == 0) + throw DeadlyImportError( "File is too small."); + + mBuffer.resize( fileSize); + file->Read( &mBuffer.front(), 1, fileSize); + + // start reading + mReader = mBuffer.begin(); + mLine = 1; + ReadStructure( pScene); + + if (!noSkeletonMesh) { + // build a dummy mesh for the skeleton so that we see something at least + SkeletonMeshBuilder meshBuilder( pScene); + } + + // construct an animation from all the motion data we read + CreateAnimation( 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( format() << "Expected node name, but found \"" << nodeName << "\"." ); + + // then an opening brace should follow + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( format() << "Expected opening brace \"{\", but found \"" << openBrace << "\"." ); + + // Create a node + aiNode* node = new aiNode( nodeName); + std::vector<aiNode*> childNodes; + + // and create an bone entry for it + mNodes.push_back( Node( node)); + Node& internNode = mNodes.back(); + + // 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( internNode); + else if( token == "JOINT") + { + // child node follows + aiNode* child = ReadNode(); + child->mParent = node; + childNodes.push_back( child); + } + else if( token == "End") + { + // The real symbol is "End Site". Second part comes in a separate token + std::string siteToken = GetNextToken(); + if( siteToken != "Site") + ThrowException( format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\"." ); + + aiNode* child = ReadEndSite( nodeName); + child->mParent = node; + 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( format() << "Unknown keyword \"" << token << "\"." ); + } + } + + // add the child nodes if there are any + if( childNodes.size() > 0) + { + node->mNumChildren = static_cast<unsigned int>(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 an end node and returns the created node. +aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) +{ + // check opening brace + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( format() << "Expected opening brace \"{\", but found \"" << openBrace << "\"."); + + // Create a node + aiNode* node = new aiNode( "EndSite_" + pParentName); + + // now read the node's contents. Only possible entry is "OFFSET" + while( 1) + { + std::string token = GetNextToken(); + + // end node's offset + if( token == "OFFSET") + { + ReadNodeOffset( node); + } + else if( token == "}") + { + // we're done with the end node + break; + } else + { + // everything else is a parse error + ThrowException( format() << "Unknown keyword \"" << token << "\"." ); + } + } + + // 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( BVHLoader::Node& pNode) +{ + // number of channels. Use the float reader because we're lazy + float numChannelsFloat = GetNextTokenAsFloat(); + unsigned int numChannels = (unsigned int) numChannelsFloat; + + for( unsigned int a = 0; a < numChannels; a++) + { + std::string channelToken = GetNextToken(); + + if( channelToken == "Xposition") + pNode.mChannels.push_back( Channel_PositionX); + else if( channelToken == "Yposition") + pNode.mChannels.push_back( Channel_PositionY); + else if( channelToken == "Zposition") + pNode.mChannels.push_back( Channel_PositionZ); + else if( channelToken == "Xrotation") + pNode.mChannels.push_back( Channel_RotationX); + else if( channelToken == "Yrotation") + pNode.mChannels.push_back( Channel_RotationY); + else if( channelToken == "Zrotation") + pNode.mChannels.push_back( Channel_RotationZ); + else + ThrowException( format() << "Invalid channel specifier \"" << channelToken << "\"." ); + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the motion data +void BVHLoader::ReadMotion( aiScene* /*pScene*/) +{ + // Read number of frames + std::string tokenFrames = GetNextToken(); + if( tokenFrames != "Frames:") + ThrowException( format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\"."); + + float numFramesFloat = GetNextTokenAsFloat(); + mAnimNumFrames = (unsigned int) numFramesFloat; + + // Read frame duration + std::string tokenDuration1 = GetNextToken(); + std::string tokenDuration2 = GetNextToken(); + if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") + ThrowException( format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\"." ); + + mAnimTickDuration = GetNextTokenAsFloat(); + + // resize value vectors for each node + for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); + + // now read all the data and store it in the corresponding node's value vector + for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) + { + // on each line read the values for all nodes + for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + { + // get as many values as the node has channels + for( unsigned int c = 0; c < it->mChannels.size(); ++c) + it->mChannelValues.push_back( GetNextTokenAsFloat()); + } + + // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it + } +} + +// ------------------------------------------------------------------------------------------------ +// Retrieves the next token +std::string BVHLoader::GetNextToken() +{ + // skip any preceding 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_atoreal_move<float>( ctoken, result); + + if( ctoken != token.c_str() + token.length()) + ThrowException( format() << "Expected a floating point number, but found \"" << token << "\"." ); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +// Aborts the file reading with an exception +AI_WONT_RETURN void BVHLoader::ThrowException( const std::string& pError) +{ + throw DeadlyImportError( format() << mFileName << ":" << mLine << " - " << pError); +} + +// ------------------------------------------------------------------------------------------------ +// Constructs an animation for the motion data and stores it in the given scene +void BVHLoader::CreateAnimation( aiScene* pScene) +{ + // create the animation + pScene->mNumAnimations = 1; + pScene->mAnimations = new aiAnimation*[1]; + aiAnimation* anim = new aiAnimation; + pScene->mAnimations[0] = anim; + + // put down the basic parameters + anim->mName.Set( "Motion"); + anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); + anim->mDuration = double( mAnimNumFrames - 1); + + // now generate the tracks for all nodes + anim->mNumChannels = static_cast<unsigned int>(mNodes.size()); + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + + // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown + for (unsigned int i = 0; i < anim->mNumChannels;++i) + anim->mChannels[i] = NULL; + + for( unsigned int a = 0; a < anim->mNumChannels; a++) + { + const Node& node = mNodes[a]; + const std::string nodeName = std::string( node.mNode->mName.data ); + aiNodeAnim* nodeAnim = new aiNodeAnim; + anim->mChannels[a] = nodeAnim; + nodeAnim->mNodeName.Set( nodeName); + + // translational part, if given + if( node.mChannels.size() == 6) + { + nodeAnim->mNumPositionKeys = mAnimNumFrames; + nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; + aiVectorKey* poskey = nodeAnim->mPositionKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + poskey->mTime = double( fr); + + // Now compute all translations in the right order + for( unsigned int channel = 0; channel < 3; ++channel) + { + switch( node.mChannels[channel]) + { + case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + ++poskey; + } + } else + { + // if no translation part is given, put a default sequence + aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); + nodeAnim->mNumPositionKeys = 1; + nodeAnim->mPositionKeys = new aiVectorKey[1]; + nodeAnim->mPositionKeys[0].mTime = 0.0; + nodeAnim->mPositionKeys[0].mValue = nodePos; + } + + // rotation part. Always present. First find value offsets + { + unsigned int rotOffset = 0; + if( node.mChannels.size() == 6) + { + // Offset all further calculations + rotOffset = 3; + } + + // Then create the number of rotation keys + nodeAnim->mNumRotationKeys = mAnimNumFrames; + nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; + aiQuatKey* rotkey = nodeAnim->mRotationKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + aiMatrix4x4 temp; + aiMatrix3x3 rotMatrix; + + for( unsigned int channel = 0; channel < 3; ++channel) + { + // translate ZXY euler angels into a quaternion + const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; + + // Compute rotation transformations in the right order + switch (node.mChannels[rotOffset+channel]) + { + case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + + rotkey->mTime = double( fr); + rotkey->mValue = aiQuaternion( rotMatrix); + ++rotkey; + } + } + + // scaling part. Always just a default track + { + nodeAnim->mNumScalingKeys = 1; + nodeAnim->mScalingKeys = new aiVectorKey[1]; + nodeAnim->mScalingKeys[0].mTime = 0.0; + nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); + } + } +} + +#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER |