- 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-9d2fd5bffc1fpull/1/head
parent
a34378934d
commit
8271164d74
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
#define AI_BVHLOADER_H_INC
|
||||
|
||||
#include "BaseImporter.h"
|
||||
#include <boost/multi_array.hpp>
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
|
@ -57,6 +56,28 @@ class BVHLoader : public BaseImporter
|
|||
{
|
||||
friend class Importer;
|
||||
|
||||
/** Possible animation channels for which the motion data holds the values */
|
||||
enum ChannelType
|
||||
{
|
||||
Channel_PositionX,
|
||||
Channel_PositionY,
|
||||
Channel_PositionZ,
|
||||
Channel_RotationX,
|
||||
Channel_RotationY,
|
||||
Channel_RotationZ
|
||||
};
|
||||
|
||||
/** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
|
||||
struct Node
|
||||
{
|
||||
const aiNode* mNode;
|
||||
std::vector<ChannelType> mChannels;
|
||||
std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
|
||||
|
||||
Node() { }
|
||||
Node( const aiNode* pNode) : mNode( pNode) { }
|
||||
};
|
||||
|
||||
protected:
|
||||
/** Constructor to be privately used by Importer */
|
||||
BVHLoader();
|
||||
|
@ -93,11 +114,14 @@ protected:
|
|||
/** Reads a node and recursively its childs and returns the created node. */
|
||||
aiNode* ReadNode();
|
||||
|
||||
/** Reads an end node and returns the created node. */
|
||||
aiNode* ReadEndSite( const std::string& pParentName);
|
||||
|
||||
/** 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 animation channels into the given node */
|
||||
void ReadNodeChannels( BVHLoader::Node& pNode);
|
||||
|
||||
/** Reads the motion data */
|
||||
void ReadMotion( aiScene* pScene);
|
||||
|
@ -111,6 +135,9 @@ protected:
|
|||
/** Aborts the file reading with an exception */
|
||||
void ThrowException( const std::string& pError);
|
||||
|
||||
/** Constructs an animation for the motion data and stores it in the given scene */
|
||||
void CreateAnimation( aiScene* pScene);
|
||||
|
||||
protected:
|
||||
/** Filename, for a verbose error message */
|
||||
std::string mFileName;
|
||||
|
@ -124,8 +151,14 @@ protected:
|
|||
/** Current line, for error messages */
|
||||
unsigned int mLine;
|
||||
|
||||
/** motion values per frame */
|
||||
boost::multi_array<float, 2> mMotionValues;
|
||||
/** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index.
|
||||
* Also contain the motion data for the node's channels
|
||||
*/
|
||||
std::vector<Node> mNodes;
|
||||
|
||||
/** basic Animation parameters */
|
||||
float mAnimTickDuration;
|
||||
unsigned int mAnimNumFrames;
|
||||
};
|
||||
|
||||
} // end of namespace Assimp
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/** Implementation of a little class to construct a dummy mesh for a skeleton */
|
||||
|
||||
/*
|
||||
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 "../include/aiScene.h"
|
||||
#include "SkeletonMeshBuilder.h"
|
||||
|
||||
using namespace Assimp;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// The constructor processes the given scene and adds a mesh there.
|
||||
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene)
|
||||
{
|
||||
// nothing to do if there's mesh data already present at the scene
|
||||
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
|
||||
return;
|
||||
|
||||
// build some faces around each node
|
||||
CreateGeometry( pScene->mRootNode);
|
||||
|
||||
// create a mesh to hold all the generated faces
|
||||
pScene->mNumMeshes = 1;
|
||||
pScene->mMeshes = new aiMesh*[1];
|
||||
pScene->mMeshes[0] = CreateMesh();
|
||||
// and install it at the root node
|
||||
pScene->mRootNode->mNumMeshes = 1;
|
||||
pScene->mRootNode->mMeshes = new unsigned int[1];
|
||||
pScene->mRootNode->mMeshes[0] = 0;
|
||||
|
||||
// create a dummy material for the mesh
|
||||
pScene->mNumMaterials = 1;
|
||||
pScene->mMaterials = new aiMaterial*[1];
|
||||
pScene->mMaterials[0] = CreateMaterial();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Recursively builds a simple mesh representation for the given node
|
||||
void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
|
||||
{
|
||||
// add a joint entry for the node.
|
||||
const unsigned int boneIndex = mBones.size();
|
||||
const unsigned int vertexStartIndex = mVertices.size();
|
||||
|
||||
// now build the geometry.
|
||||
if( pNode->mNumChildren > 0)
|
||||
{
|
||||
// If the node has childs, we build little pointers to each of them
|
||||
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
|
||||
{
|
||||
// find a suitable coordinate system
|
||||
const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
|
||||
aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
|
||||
float distanceToChild = childpos.Length();
|
||||
aiVector3D up = aiVector3D( childpos).Normalize();
|
||||
|
||||
aiVector3D orth( 1.0f, 0.0f, 0.0f);
|
||||
if( abs( orth * up) > 0.99f)
|
||||
orth.Set( 0.0f, 1.0f, 0.0f);
|
||||
|
||||
aiVector3D front = (up ^ orth).Normalize();
|
||||
aiVector3D side = (front ^ up).Normalize();
|
||||
|
||||
mVertices.push_back( -front * distanceToChild * 0.1f);
|
||||
mVertices.push_back( childpos);
|
||||
mVertices.push_back( -side * distanceToChild * 0.1f);
|
||||
mVertices.push_back( -side * distanceToChild * 0.1f);
|
||||
mVertices.push_back( childpos);
|
||||
mVertices.push_back( front * distanceToChild * 0.1f);
|
||||
mVertices.push_back( front * distanceToChild * 0.1f);
|
||||
mVertices.push_back( childpos);
|
||||
mVertices.push_back( side * distanceToChild * 0.1f);
|
||||
mVertices.push_back( side * distanceToChild * 0.1f);
|
||||
mVertices.push_back( childpos);
|
||||
mVertices.push_back( -front * distanceToChild * 0.1f);
|
||||
|
||||
unsigned localVertexStart = vertexStartIndex + a * 12;
|
||||
mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
|
||||
mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
|
||||
mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
|
||||
mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
|
||||
}
|
||||
} else
|
||||
{
|
||||
// if the node has no children, it's an end node. Put a little knob there instead
|
||||
aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
|
||||
float sizeEstimate = ownpos.Length() * 0.2f;
|
||||
|
||||
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
|
||||
|
||||
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
|
||||
mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
|
||||
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
|
||||
|
||||
mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
|
||||
mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
|
||||
mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
|
||||
mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
|
||||
mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
|
||||
mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
|
||||
mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
|
||||
mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
|
||||
}
|
||||
|
||||
// create a bone affecting all the newly created vertices
|
||||
aiBone* bone = new aiBone;
|
||||
mBones.push_back( bone);
|
||||
bone->mName = pNode->mName;
|
||||
|
||||
// calculate the bone offset matrix by concatenating the inverse transformations of all parents
|
||||
bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
|
||||
for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
|
||||
bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
|
||||
|
||||
// add all the vertices to the bone's influences
|
||||
unsigned int numVertices = mVertices.size() - vertexStartIndex;
|
||||
bone->mNumWeights = numVertices;
|
||||
bone->mWeights = new aiVertexWeight[numVertices];
|
||||
for( unsigned int a = 0; a < numVertices; a++)
|
||||
bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0f);
|
||||
|
||||
// HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
|
||||
// them to the array, but I'm tired now and I'm annoyed.
|
||||
aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
|
||||
for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
|
||||
mVertices[a] = boneToMeshTransform * mVertices[a];
|
||||
|
||||
// and finally recurse into the children list
|
||||
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
|
||||
CreateGeometry( pNode->mChildren[a]);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Creates the mesh from the internally accumulated stuff and returns it.
|
||||
aiMesh* SkeletonMeshBuilder::CreateMesh()
|
||||
{
|
||||
aiMesh* mesh = new aiMesh();
|
||||
|
||||
// add points
|
||||
mesh->mNumVertices = mVertices.size();
|
||||
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
|
||||
std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
|
||||
|
||||
// add faces
|
||||
mesh->mNumFaces = mFaces.size();
|
||||
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
||||
for( unsigned int a = 0; a < mesh->mNumFaces; a++)
|
||||
{
|
||||
const Face& inface = mFaces[a];
|
||||
aiFace& outface = mesh->mFaces[a];
|
||||
outface.mNumIndices = 3;
|
||||
outface.mIndices = new unsigned int[3];
|
||||
outface.mIndices[0] = inface.mIndices[0];
|
||||
outface.mIndices[1] = inface.mIndices[1];
|
||||
outface.mIndices[2] = inface.mIndices[2];
|
||||
}
|
||||
|
||||
// add the bones
|
||||
mesh->mNumBones = mBones.size();
|
||||
mesh->mBones = new aiBone*[mesh->mNumBones];
|
||||
std::copy( mBones.begin(), mBones.end(), mesh->mBones);
|
||||
|
||||
// default
|
||||
mesh->mMaterialIndex = 0;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Creates a dummy material and returns it.
|
||||
aiMaterial* SkeletonMeshBuilder::CreateMaterial()
|
||||
{
|
||||
Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper;
|
||||
|
||||
// Name
|
||||
aiString matName( std::string( "Material"));
|
||||
matHelper->AddProperty( &matName, AI_MATKEY_NAME);
|
||||
|
||||
return matHelper;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/** Helper class to construct a dummy mesh for file formats containing only motion data */
|
||||
|
||||
/*
|
||||
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_SKELETONMESHBUILDER_H_INC
|
||||
#define AI_SKELETONMESHBUILDER_H_INC
|
||||
|
||||
#include <vector>
|
||||
#include "../include/aiMesh.h"
|
||||
|
||||
struct aiScene;
|
||||
struct aiNode;
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
|
||||
/**
|
||||
* This little helper class constructs a dummy mesh for a given scene
|
||||
* the resembles the node hierarchy. This is useful for file formats
|
||||
* that don't carry any mesh data but only animation data.
|
||||
*/
|
||||
class SkeletonMeshBuilder
|
||||
{
|
||||
public:
|
||||
/** The constructor processes the given scene and adds a mesh there. Does nothing
|
||||
* if the scene already has mesh data.
|
||||
* @param pScene The scene for which a skeleton mesh should be constructed.
|
||||
*/
|
||||
SkeletonMeshBuilder( aiScene* pScene);
|
||||
|
||||
protected:
|
||||
/** Recursively builds a simple mesh representation for the given node and also creates
|
||||
* a joint for the node that affects this part of the mesh.
|
||||
* @param pNode The node to build geometry for.
|
||||
*/
|
||||
void CreateGeometry( const aiNode* pNode);
|
||||
|
||||
/** Creates the mesh from the internally accumulated stuff and returns it. */
|
||||
aiMesh* CreateMesh();
|
||||
|
||||
/** Creates a dummy material and returns it. */
|
||||
aiMaterial* CreateMaterial();
|
||||
|
||||
protected:
|
||||
/** space to assemble the mesh data: points */
|
||||
std::vector<aiVector3D> mVertices;
|
||||
|
||||
/** faces */
|
||||
struct Face
|
||||
{
|
||||
unsigned int mIndices[3];
|
||||
Face();
|
||||
Face( unsigned int p0, unsigned int p1, unsigned int p2)
|
||||
{ mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; }
|
||||
};
|
||||
std::vector<Face> mFaces;
|
||||
|
||||
/** bones */
|
||||
std::vector<aiBone*> mBones;
|
||||
};
|
||||
|
||||
} // end of namespace Assimp
|
||||
|
||||
#endif // AI_SKELETONMESHBUILDER_H_INC
|
|
@ -128,7 +128,6 @@ struct aiMatrix4x4
|
|||
*/
|
||||
static aiMatrix4x4& RotationX(float a, aiMatrix4x4& out);
|
||||
|
||||
|
||||
/** \brief Returns a rotation matrix for a rotation around the y axis
|
||||
* \param a Rotation angle, in radians
|
||||
* \param out Receives the output matrix
|
||||
|
@ -136,7 +135,6 @@ struct aiMatrix4x4
|
|||
*/
|
||||
static aiMatrix4x4& RotationY(float a, aiMatrix4x4& out);
|
||||
|
||||
|
||||
/** \brief Returns a rotation matrix for a rotation around the z axis
|
||||
* \param a Rotation angle, in radians
|
||||
* \param out Receives the output matrix
|
||||
|
@ -144,13 +142,12 @@ struct aiMatrix4x4
|
|||
*/
|
||||
static aiMatrix4x4& RotationZ(float a, aiMatrix4x4& out);
|
||||
|
||||
|
||||
/** \brief Returns a translation matrix
|
||||
* \param v Translation vector
|
||||
* \param out Receives the output matrix
|
||||
* \return Reference to the output matrix
|
||||
*/
|
||||
static aiMatrix4x4& Translation(aiVector3D v, aiMatrix4x4& out);
|
||||
static aiMatrix4x4& Translation( const aiVector3D& v, aiMatrix4x4& out);
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
|
|
@ -252,6 +252,7 @@ inline aiMatrix4x4& aiMatrix4x4::RotationX(float a, aiMatrix4x4& out)
|
|||
out.b3 = -(out.c2 = sin(a));
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
inline aiMatrix4x4& aiMatrix4x4::RotationY(float a, aiMatrix4x4& out)
|
||||
{
|
||||
|
@ -281,7 +282,7 @@ inline aiMatrix4x4& aiMatrix4x4::RotationZ(float a, aiMatrix4x4& out)
|
|||
return out;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
inline aiMatrix4x4& aiMatrix4x4::Translation(aiVector3D v, aiMatrix4x4& out)
|
||||
inline aiMatrix4x4& aiMatrix4x4::Translation( const aiVector3D& v, aiMatrix4x4& out)
|
||||
{
|
||||
out = aiMatrix4x4();
|
||||
out.a4 = v.x;
|
||||
|
@ -290,6 +291,5 @@ inline aiMatrix4x4& aiMatrix4x4::Translation(aiVector3D v, aiMatrix4x4& out)
|
|||
return out;
|
||||
}
|
||||
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // AI_MATRIX4x4_INL_INC
|
||||
|
|
|
@ -1511,6 +1511,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
PrecompiledHeaderThrough="AssimpPCH.h"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
|
@ -1519,6 +1520,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"
|
||||
PrecompiledHeaderThrough="AssimpPCH.h"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
|
@ -1534,7 +1536,8 @@
|
|||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
UsePrecompiledHeader="1"
|
||||
PrecompiledHeaderThrough="AssimpPCH.h"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
|
@ -1542,7 +1545,8 @@
|
|||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
UsePrecompiledHeader="1"
|
||||
PrecompiledHeaderThrough="AssimpPCH.h"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
|
|
Loading…
Reference in New Issue