Browse Source

- 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

ulfjorensen 17 years ago
parent
commit
8271164d74

+ 223 - 29
code/BVHLoader.cpp

@@ -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 vectors for each node
+	for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+		it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
 
-	// resize value array accordingly
-	// ************* Continue here ********
-	//mMotionValues.resize( boost::extents[numFrames][numChannels]);
+	// 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);
+		}
+	}
+}

+ 38 - 5
code/BVHLoader.h

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

+ 238 - 0
code/SkeletonMeshBuilder.cpp

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

+ 102 - 0
code/SkeletonMeshBuilder.h

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

+ 1 - 4
include/aiMatrix4x4.h

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

+ 2 - 2
include/aiMatrix4x4.inl

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

+ 6 - 2
workspaces/vc8/assimp.vcproj

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