- Further work at the BVH loader. Still work in progress.
- Added a helper class to build a mesh for a meshless node hierarchy git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@189 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
This commit is contained in:
@@ -13,18 +13,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
|
||||
@@ -41,8 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "AssimpPCH.h"
|
||||
#include "../include/aiAnim.h"
|
||||
#include "BVHLoader.h"
|
||||
#include "fast_atof.h"
|
||||
#include "SkeletonMeshBuilder.h"
|
||||
|
||||
using namespace Assimp;
|
||||
|
||||
@@ -99,6 +101,12 @@ void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSys
|
||||
mReader = mBuffer.begin();
|
||||
mLine = 1;
|
||||
ReadStructure( pScene);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -139,10 +147,6 @@ aiNode* BVHLoader::ReadNode()
|
||||
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 != "{")
|
||||
@@ -152,30 +156,39 @@ aiNode* BVHLoader::ReadNode()
|
||||
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( node);
|
||||
ReadNodeChannels( internNode);
|
||||
else if( token == "JOINT")
|
||||
{
|
||||
// child node follows
|
||||
aiNode* child = ReadNode();
|
||||
child->mParent = node;
|
||||
childNodes.push_back( child);
|
||||
} else
|
||||
if( token == "End")
|
||||
}
|
||||
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();
|
||||
// The real symbol is "End Site". Second part comes in a separate token
|
||||
std::string siteToken = GetNextToken();
|
||||
if( siteToken != "Site")
|
||||
ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken));
|
||||
|
||||
aiNode* child = ReadEndSite( nodeName);
|
||||
child->mParent = node;
|
||||
childNodes.push_back( child);
|
||||
} else
|
||||
if( token == "}")
|
||||
}
|
||||
else if( token == "}")
|
||||
{
|
||||
// we're done with that part of the hierarchy
|
||||
break;
|
||||
@@ -198,6 +211,42 @@ aiNode* BVHLoader::ReadNode()
|
||||
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( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % 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( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
|
||||
}
|
||||
}
|
||||
|
||||
// and return the sub-hierarchy we built here
|
||||
return node;
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Reads a node offset for the given node
|
||||
void BVHLoader::ReadNodeOffset( aiNode* pNode)
|
||||
@@ -215,15 +264,31 @@ void BVHLoader::ReadNodeOffset( aiNode* pNode)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Reads the animation channels for the given node
|
||||
void BVHLoader::ReadNodeChannels( aiNode* pNode)
|
||||
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;
|
||||
|
||||
// TODO: (thom) proper channel parsing. For the moment I just skip the number of tokens
|
||||
for( unsigned int a = 0; a < numChannels; a++)
|
||||
GetNextToken();
|
||||
{
|
||||
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( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken));
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -236,7 +301,7 @@ void BVHLoader::ReadMotion( aiScene* pScene)
|
||||
ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames));
|
||||
|
||||
float numFramesFloat = GetNextTokenAsFloat();
|
||||
unsigned int numFrames = (unsigned int) numFramesFloat;
|
||||
mAnimNumFrames = (unsigned int) numFramesFloat;
|
||||
|
||||
// Read frame duration
|
||||
std::string tokenDuration1 = GetNextToken();
|
||||
@@ -244,11 +309,25 @@ void BVHLoader::ReadMotion( aiScene* pScene)
|
||||
if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
|
||||
ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2));
|
||||
|
||||
float frameDuration = GetNextTokenAsFloat();
|
||||
mAnimTickDuration = GetNextTokenAsFloat();
|
||||
|
||||
// resize value array accordingly
|
||||
// ************* Continue here ********
|
||||
//mMotionValues.resize( boost::extents[numFrames][numChannels]);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -312,3 +391,118 @@ void BVHLoader::ThrowException( const std::string& pError)
|
||||
{
|
||||
throw new ImportErrorException( boost::str( boost::format( "%s:%d - %s") % 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 = mNodes.size();
|
||||
anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
|
||||
for( unsigned int a = 0; a < anim->mNumChannels; a++)
|
||||
{
|
||||
const Node& node = mNodes[a];
|
||||
const char* nodeName = node.mNode->mName.data;
|
||||
aiNodeAnim* nodeAnim = new aiNodeAnim;
|
||||
anim->mChannels[a] = nodeAnim;
|
||||
nodeAnim->mNodeName.Set( std::string( nodeName));
|
||||
|
||||
// translational part, if given
|
||||
if( node.mChannels.size() == 6)
|
||||
{
|
||||
if( node.mChannels[0] != Channel_PositionX || node.mChannels[1] != Channel_PositionY
|
||||
|| node.mChannels[2] != Channel_PositionZ)
|
||||
{
|
||||
throw new ImportErrorException( boost::str( boost::format( "Unexpected animation "
|
||||
"channel setup at node \"%s\".") % nodeName));
|
||||
}
|
||||
|
||||
nodeAnim->mNumPositionKeys = mAnimNumFrames;
|
||||
nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
|
||||
aiVectorKey* poskey = nodeAnim->mPositionKeys;
|
||||
for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
|
||||
{
|
||||
poskey->mTime = double( fr);
|
||||
poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + 0];
|
||||
poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + 1];
|
||||
poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + 2];
|
||||
++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 = 2;
|
||||
nodeAnim->mPositionKeys = new aiVectorKey[2];
|
||||
nodeAnim->mPositionKeys[0].mTime = 0.0;
|
||||
nodeAnim->mPositionKeys[0].mValue = nodePos;
|
||||
nodeAnim->mPositionKeys[1].mTime = anim->mDuration;
|
||||
nodeAnim->mPositionKeys[1].mValue = nodePos;
|
||||
}
|
||||
|
||||
// rotation part. Always present. First find value offsets
|
||||
{
|
||||
unsigned int rotOffset = 0;
|
||||
if( node.mChannels.size() == 6)
|
||||
{
|
||||
if( node.mChannels[3] != Channel_RotationZ || node.mChannels[4] != Channel_RotationX
|
||||
|| node.mChannels[5] != Channel_RotationY)
|
||||
{
|
||||
throw new ImportErrorException( boost::str( boost::format( "Unexpected animation "
|
||||
"channel setup at node \"%s\".") % nodeName));
|
||||
}
|
||||
rotOffset = 3;
|
||||
} else
|
||||
{
|
||||
if( node.mChannels[0] != Channel_RotationZ || node.mChannels[1] != Channel_RotationX
|
||||
|| node.mChannels[2] != Channel_RotationY || node.mChannels.size() != 3)
|
||||
{
|
||||
throw new ImportErrorException( boost::str( boost::format( "Unexpected animation "
|
||||
"channel setup at node \"%s\".") % nodeName));
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// translate ZXY euler angels into a quaternion
|
||||
float angleZ = node.mChannelValues[fr * node.mChannels.size() + rotOffset + 0] * float( AI_MATH_PI) / 180.0f;
|
||||
float angleX = node.mChannelValues[fr * node.mChannels.size() + rotOffset + 1] * float( AI_MATH_PI) / 180.0f;
|
||||
float angleY = node.mChannelValues[fr * node.mChannels.size() + rotOffset + 2] * float( AI_MATH_PI) / 180.0f;
|
||||
aiMatrix4x4 temp;
|
||||
aiMatrix3x3 rotMatrix;
|
||||
aiMatrix4x4::RotationX( angleX, temp); rotMatrix *= aiMatrix3x3( temp);
|
||||
aiMatrix4x4::RotationY( angleY, temp); rotMatrix *= aiMatrix3x3( temp);
|
||||
aiMatrix4x4::RotationZ( angleZ, temp); rotMatrix *= aiMatrix3x3( temp);
|
||||
|
||||
rotkey->mTime = double( fr);
|
||||
rotkey->mValue = aiQuaternion( rotMatrix);
|
||||
++rotkey;
|
||||
}
|
||||
}
|
||||
|
||||
// scaling part. Always just a default track
|
||||
{
|
||||
nodeAnim->mNumScalingKeys = 2;
|
||||
nodeAnim->mScalingKeys = new aiVectorKey[2];
|
||||
nodeAnim->mScalingKeys[0].mTime = 0.0;
|
||||
nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
|
||||
nodeAnim->mScalingKeys[1].mTime = anim->mDuration;
|
||||
nodeAnim->mScalingKeys[1].mValue.Set( 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user