- 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
pull/1/head
ulfjorensen 2008-10-19 21:44:12 +00:00
parent a34378934d
commit 8271164d74
7 changed files with 610 additions and 42 deletions

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>