vrshoot

diff libs/assimp/BVHLoader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/BVHLoader.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,534 @@
     1.4 +/** Implementation of the BVH loader */
     1.5 +/*
     1.6 +---------------------------------------------------------------------------
     1.7 +Open Asset Import Library (assimp)
     1.8 +---------------------------------------------------------------------------
     1.9 +
    1.10 +Copyright (c) 2006-2012, assimp team
    1.11 +
    1.12 +All rights reserved.
    1.13 +
    1.14 +Redistribution and use of this software in source and binary forms, 
    1.15 +with or without modification, are permitted provided that the following 
    1.16 +conditions are met:
    1.17 +
    1.18 +* Redistributions of source code must retain the above
    1.19 +copyright notice, this list of conditions and the
    1.20 +following disclaimer.
    1.21 +
    1.22 +* Redistributions in binary form must reproduce the above
    1.23 +copyright notice, this list of conditions and the
    1.24 +following disclaimer in the documentation and/or other
    1.25 +materials provided with the distribution.
    1.26 +
    1.27 +* Neither the name of the assimp team, nor the names of its
    1.28 +contributors may be used to endorse or promote products
    1.29 +derived from this software without specific prior
    1.30 +written permission of the assimp team.
    1.31 +
    1.32 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.33 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.34 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.35 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.36 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.37 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.38 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.39 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.40 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.41 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.42 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.43 +---------------------------------------------------------------------------
    1.44 +*/
    1.45 +
    1.46 +#include "AssimpPCH.h"
    1.47 +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
    1.48 +
    1.49 +#include "BVHLoader.h"
    1.50 +#include "fast_atof.h"
    1.51 +#include "SkeletonMeshBuilder.h"
    1.52 +
    1.53 +using namespace Assimp;
    1.54 +
    1.55 +static const aiImporterDesc desc = {
    1.56 +	"BVH Importer (MoCap)",
    1.57 +	"",
    1.58 +	"",
    1.59 +	"",
    1.60 +	aiImporterFlags_SupportTextFlavour,
    1.61 +	0,
    1.62 +	0,
    1.63 +	0,
    1.64 +	0,
    1.65 +	"bvh"
    1.66 +};
    1.67 +
    1.68 +// ------------------------------------------------------------------------------------------------
    1.69 +// Constructor to be privately used by Importer
    1.70 +BVHLoader::BVHLoader()
    1.71 +: noSkeletonMesh()
    1.72 +{}
    1.73 +
    1.74 +// ------------------------------------------------------------------------------------------------
    1.75 +// Destructor, private as well
    1.76 +BVHLoader::~BVHLoader()
    1.77 +{}
    1.78 +
    1.79 +// ------------------------------------------------------------------------------------------------
    1.80 +// Returns whether the class can handle the format of the given file. 
    1.81 +bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
    1.82 +{
    1.83 +	// check file extension 
    1.84 +	const std::string extension = GetExtension(pFile);
    1.85 +	
    1.86 +	if( extension == "bvh")
    1.87 +		return true;
    1.88 +
    1.89 +	if ((!extension.length() || cs) && pIOHandler) {
    1.90 +		const char* tokens[] = {"HIERARCHY"};
    1.91 +		return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
    1.92 +	}
    1.93 +	return false;
    1.94 +}
    1.95 +
    1.96 +// ------------------------------------------------------------------------------------------------
    1.97 +void BVHLoader::SetupProperties(const Importer* pImp)
    1.98 +{
    1.99 +	noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
   1.100 +}
   1.101 +
   1.102 +// ------------------------------------------------------------------------------------------------
   1.103 +// Loader meta information
   1.104 +const aiImporterDesc* BVHLoader::GetInfo () const
   1.105 +{
   1.106 +	return &desc;
   1.107 +}
   1.108 +
   1.109 +// ------------------------------------------------------------------------------------------------
   1.110 +// Imports the given file into the given scene structure. 
   1.111 +void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
   1.112 +{
   1.113 +	mFileName = pFile;
   1.114 +
   1.115 +	// read file into memory
   1.116 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
   1.117 +	if( file.get() == NULL)
   1.118 +		throw DeadlyImportError( "Failed to open file " + pFile + ".");
   1.119 +
   1.120 +	size_t fileSize = file->FileSize();
   1.121 +	if( fileSize == 0)
   1.122 +		throw DeadlyImportError( "File is too small.");
   1.123 +
   1.124 +	mBuffer.resize( fileSize);
   1.125 +	file->Read( &mBuffer.front(), 1, fileSize);
   1.126 +
   1.127 +	// start reading
   1.128 +	mReader = mBuffer.begin();
   1.129 +	mLine = 1;
   1.130 +	ReadStructure( pScene);
   1.131 +
   1.132 +	if (!noSkeletonMesh) {
   1.133 +		// build a dummy mesh for the skeleton so that we see something at least
   1.134 +		SkeletonMeshBuilder meshBuilder( pScene);
   1.135 +	}
   1.136 +
   1.137 +	// construct an animation from all the motion data we read
   1.138 +	CreateAnimation( pScene);
   1.139 +}
   1.140 +
   1.141 +// ------------------------------------------------------------------------------------------------
   1.142 +// Reads the file
   1.143 +void BVHLoader::ReadStructure( aiScene* pScene)
   1.144 +{
   1.145 +	// first comes hierarchy
   1.146 +	std::string header = GetNextToken();
   1.147 +	if( header != "HIERARCHY")
   1.148 +		ThrowException( "Expected header string \"HIERARCHY\".");
   1.149 +	ReadHierarchy( pScene);
   1.150 +
   1.151 +	// then comes the motion data
   1.152 +	std::string motion = GetNextToken();
   1.153 +	if( motion != "MOTION")
   1.154 +		ThrowException( "Expected beginning of motion data \"MOTION\".");
   1.155 +	ReadMotion( pScene);
   1.156 +}
   1.157 +
   1.158 +// ------------------------------------------------------------------------------------------------
   1.159 +// Reads the hierarchy
   1.160 +void BVHLoader::ReadHierarchy( aiScene* pScene)
   1.161 +{
   1.162 +	std::string root = GetNextToken();
   1.163 +	if( root != "ROOT")
   1.164 +		ThrowException( "Expected root node \"ROOT\".");
   1.165 +
   1.166 +	// Go read the hierarchy from here
   1.167 +	pScene->mRootNode = ReadNode();
   1.168 +}
   1.169 +
   1.170 +// ------------------------------------------------------------------------------------------------
   1.171 +// Reads a node and recursively its childs and returns the created node;
   1.172 +aiNode* BVHLoader::ReadNode()
   1.173 +{
   1.174 +	// first token is name
   1.175 +	std::string nodeName = GetNextToken();
   1.176 +	if( nodeName.empty() || nodeName == "{")
   1.177 +		ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName));
   1.178 +
   1.179 +	// then an opening brace should follow
   1.180 +	std::string openBrace = GetNextToken();
   1.181 +	if( openBrace != "{")
   1.182 +		ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
   1.183 +
   1.184 +	// Create a node
   1.185 +	aiNode* node = new aiNode( nodeName);
   1.186 +	std::vector<aiNode*> childNodes;
   1.187 +
   1.188 +	// and create an bone entry for it
   1.189 +	mNodes.push_back( Node( node));
   1.190 +	Node& internNode = mNodes.back();
   1.191 +
   1.192 +	// now read the node's contents
   1.193 +	while( 1)
   1.194 +	{
   1.195 +		std::string token = GetNextToken();
   1.196 +
   1.197 +		// node offset to parent node
   1.198 +		if( token == "OFFSET")
   1.199 +			ReadNodeOffset( node);
   1.200 +		else if( token == "CHANNELS")
   1.201 +			ReadNodeChannels( internNode);
   1.202 +		else if( token == "JOINT")
   1.203 +		{
   1.204 +			// child node follows
   1.205 +			aiNode* child = ReadNode();
   1.206 +			child->mParent = node;
   1.207 +			childNodes.push_back( child);
   1.208 +		} 
   1.209 +		else if( token == "End")
   1.210 +		{
   1.211 +			// The real symbol is "End Site". Second part comes in a separate token
   1.212 +			std::string siteToken = GetNextToken();
   1.213 +			if( siteToken != "Site")
   1.214 +				ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken));
   1.215 +
   1.216 +			aiNode* child = ReadEndSite( nodeName);
   1.217 +			child->mParent = node;
   1.218 +			childNodes.push_back( child);
   1.219 +		} 
   1.220 +		else if( token == "}")
   1.221 +		{
   1.222 +			// we're done with that part of the hierarchy
   1.223 +			break;
   1.224 +		} else
   1.225 +		{
   1.226 +			// everything else is a parse error
   1.227 +			ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
   1.228 +		}
   1.229 +	}
   1.230 +
   1.231 +	// add the child nodes if there are any
   1.232 +	if( childNodes.size() > 0)
   1.233 +	{
   1.234 +		node->mNumChildren = childNodes.size();
   1.235 +		node->mChildren = new aiNode*[node->mNumChildren];
   1.236 +		std::copy( childNodes.begin(), childNodes.end(), node->mChildren);
   1.237 +	}
   1.238 +
   1.239 +	// and return the sub-hierarchy we built here
   1.240 +	return node;
   1.241 +}
   1.242 +
   1.243 +// ------------------------------------------------------------------------------------------------
   1.244 +// Reads an end node and returns the created node.
   1.245 +aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
   1.246 +{
   1.247 +	// check opening brace
   1.248 +	std::string openBrace = GetNextToken();
   1.249 +	if( openBrace != "{")
   1.250 +		ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
   1.251 +
   1.252 +	// Create a node
   1.253 +	aiNode* node = new aiNode( "EndSite_" + pParentName);
   1.254 +
   1.255 +	// now read the node's contents. Only possible entry is "OFFSET"
   1.256 +	while( 1)
   1.257 +	{
   1.258 +		std::string token = GetNextToken();
   1.259 +
   1.260 +		// end node's offset
   1.261 +		if( token == "OFFSET")
   1.262 +		{
   1.263 +			ReadNodeOffset( node);
   1.264 +		} 
   1.265 +		else if( token == "}")
   1.266 +		{
   1.267 +			// we're done with the end node
   1.268 +			break;
   1.269 +		} else
   1.270 +		{
   1.271 +			// everything else is a parse error
   1.272 +			ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
   1.273 +		}
   1.274 +	}
   1.275 +
   1.276 +	// and return the sub-hierarchy we built here
   1.277 +	return node;
   1.278 +}
   1.279 +// ------------------------------------------------------------------------------------------------
   1.280 +// Reads a node offset for the given node
   1.281 +void BVHLoader::ReadNodeOffset( aiNode* pNode)
   1.282 +{
   1.283 +	// Offset consists of three floats to read
   1.284 +	aiVector3D offset;
   1.285 +	offset.x = GetNextTokenAsFloat();
   1.286 +	offset.y = GetNextTokenAsFloat();
   1.287 +	offset.z = GetNextTokenAsFloat();
   1.288 +
   1.289 +	// build a transformation matrix from it
   1.290 +	pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y,
   1.291 +		0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f);
   1.292 +}
   1.293 +
   1.294 +// ------------------------------------------------------------------------------------------------
   1.295 +// Reads the animation channels for the given node
   1.296 +void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode)
   1.297 +{
   1.298 +	// number of channels. Use the float reader because we're lazy
   1.299 +	float numChannelsFloat = GetNextTokenAsFloat();
   1.300 +	unsigned int numChannels = (unsigned int) numChannelsFloat;
   1.301 +
   1.302 +	for( unsigned int a = 0; a < numChannels; a++)
   1.303 +	{
   1.304 +		std::string channelToken = GetNextToken();
   1.305 +
   1.306 +		if( channelToken == "Xposition")
   1.307 +			pNode.mChannels.push_back( Channel_PositionX);
   1.308 +		else if( channelToken == "Yposition")
   1.309 +			pNode.mChannels.push_back( Channel_PositionY);
   1.310 +		else if( channelToken == "Zposition")
   1.311 +			pNode.mChannels.push_back( Channel_PositionZ);
   1.312 +		else if( channelToken == "Xrotation")
   1.313 +			pNode.mChannels.push_back( Channel_RotationX);
   1.314 +		else if( channelToken == "Yrotation")
   1.315 +			pNode.mChannels.push_back( Channel_RotationY);
   1.316 +		else if( channelToken == "Zrotation")
   1.317 +			pNode.mChannels.push_back( Channel_RotationZ);
   1.318 +		else
   1.319 +			ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken));
   1.320 +	}
   1.321 +}
   1.322 +
   1.323 +// ------------------------------------------------------------------------------------------------
   1.324 +// Reads the motion data
   1.325 +void BVHLoader::ReadMotion( aiScene* /*pScene*/)
   1.326 +{
   1.327 +	// Read number of frames
   1.328 +	std::string tokenFrames = GetNextToken();
   1.329 +	if( tokenFrames != "Frames:")
   1.330 +		ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames));
   1.331 +
   1.332 +	float numFramesFloat = GetNextTokenAsFloat();
   1.333 +	mAnimNumFrames = (unsigned int) numFramesFloat;
   1.334 +
   1.335 +	// Read frame duration
   1.336 +	std::string tokenDuration1 = GetNextToken();
   1.337 +	std::string tokenDuration2 = GetNextToken();
   1.338 +	if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
   1.339 +		ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2));
   1.340 +
   1.341 +	mAnimTickDuration = GetNextTokenAsFloat();
   1.342 +
   1.343 +	// resize value vectors for each node
   1.344 +	for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
   1.345 +		it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
   1.346 +
   1.347 +	// now read all the data and store it in the corresponding node's value vector
   1.348 +	for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame)
   1.349 +	{
   1.350 +		// on each line read the values for all nodes
   1.351 +		for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
   1.352 +		{
   1.353 +			// get as many values as the node has channels
   1.354 +			for( unsigned int c = 0; c < it->mChannels.size(); ++c)
   1.355 +				it->mChannelValues.push_back( GetNextTokenAsFloat());
   1.356 +		}
   1.357 +
   1.358 +		// after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
   1.359 +	}
   1.360 +}
   1.361 +
   1.362 +// ------------------------------------------------------------------------------------------------
   1.363 +// Retrieves the next token
   1.364 +std::string BVHLoader::GetNextToken()
   1.365 +{
   1.366 +	// skip any preceeding whitespace
   1.367 +	while( mReader != mBuffer.end())
   1.368 +	{
   1.369 +		if( !isspace( *mReader))
   1.370 +			break;
   1.371 +
   1.372 +		// count lines
   1.373 +		if( *mReader == '\n')
   1.374 +			mLine++;
   1.375 +
   1.376 +		++mReader;
   1.377 +	}
   1.378 +
   1.379 +	// collect all chars till the next whitespace. BVH is easy in respect to that.
   1.380 +	std::string token;
   1.381 +	while( mReader != mBuffer.end())
   1.382 +	{
   1.383 +		if( isspace( *mReader))
   1.384 +			break;
   1.385 +
   1.386 +		token.push_back( *mReader);
   1.387 +		++mReader;
   1.388 +
   1.389 +		// little extra logic to make sure braces are counted correctly
   1.390 +		if( token == "{" || token == "}")
   1.391 +			break;
   1.392 +	}
   1.393 +
   1.394 +	// empty token means end of file, which is just fine
   1.395 +	return token;
   1.396 +}
   1.397 +
   1.398 +// ------------------------------------------------------------------------------------------------
   1.399 +// Reads the next token as a float
   1.400 +float BVHLoader::GetNextTokenAsFloat()
   1.401 +{
   1.402 +	std::string token = GetNextToken();
   1.403 +	if( token.empty())
   1.404 +		ThrowException( "Unexpected end of file while trying to read a float");
   1.405 +
   1.406 +	// check if the float is valid by testing if the atof() function consumed every char of the token
   1.407 +	const char* ctoken = token.c_str();
   1.408 +	float result = 0.0f;
   1.409 +	ctoken = fast_atoreal_move<float>( ctoken, result);
   1.410 +
   1.411 +	if( ctoken != token.c_str() + token.length())
   1.412 +		ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token));
   1.413 +
   1.414 +	return result;
   1.415 +}
   1.416 +
   1.417 +// ------------------------------------------------------------------------------------------------
   1.418 +// Aborts the file reading with an exception
   1.419 +void BVHLoader::ThrowException( const std::string& pError)
   1.420 +{
   1.421 +	throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError));
   1.422 +}
   1.423 +
   1.424 +// ------------------------------------------------------------------------------------------------
   1.425 +// Constructs an animation for the motion data and stores it in the given scene
   1.426 +void BVHLoader::CreateAnimation( aiScene* pScene)
   1.427 +{
   1.428 +	// create the animation
   1.429 +	pScene->mNumAnimations = 1;
   1.430 +	pScene->mAnimations = new aiAnimation*[1];
   1.431 +	aiAnimation* anim = new aiAnimation;
   1.432 +	pScene->mAnimations[0] = anim;
   1.433 +
   1.434 +	// put down the basic parameters
   1.435 +	anim->mName.Set( "Motion");
   1.436 +	anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration);
   1.437 +	anim->mDuration = double( mAnimNumFrames - 1);
   1.438 +
   1.439 +	// now generate the tracks for all nodes
   1.440 +	anim->mNumChannels = mNodes.size();
   1.441 +	anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
   1.442 +
   1.443 +	// FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
   1.444 +	for (unsigned int i = 0; i < anim->mNumChannels;++i)
   1.445 +		anim->mChannels[i] = NULL;
   1.446 +
   1.447 +	for( unsigned int a = 0; a < anim->mNumChannels; a++)
   1.448 +	{
   1.449 +		const Node& node = mNodes[a];
   1.450 +		const std::string nodeName = std::string( node.mNode->mName.data );
   1.451 +		aiNodeAnim* nodeAnim = new aiNodeAnim;
   1.452 +		anim->mChannels[a] = nodeAnim;
   1.453 +		nodeAnim->mNodeName.Set( nodeName);
   1.454 +
   1.455 +		// translational part, if given
   1.456 +		if( node.mChannels.size() == 6)
   1.457 +		{
   1.458 +			nodeAnim->mNumPositionKeys = mAnimNumFrames;
   1.459 +			nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
   1.460 +			aiVectorKey* poskey = nodeAnim->mPositionKeys;
   1.461 +			for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
   1.462 +			{
   1.463 +				poskey->mTime = double( fr);
   1.464 +
   1.465 +				// Now compute all translations in the right order
   1.466 +				for( unsigned int channel = 0; channel < 3; ++channel)
   1.467 +				{
   1.468 +					switch( node.mChannels[channel])
   1.469 +					{	
   1.470 +					case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
   1.471 +					case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
   1.472 +					case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
   1.473 +					default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
   1.474 +					}
   1.475 +				}
   1.476 +				++poskey;
   1.477 +			}
   1.478 +		} else
   1.479 +		{
   1.480 +			// if no translation part is given, put a default sequence
   1.481 +			aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
   1.482 +			nodeAnim->mNumPositionKeys = 1;
   1.483 +			nodeAnim->mPositionKeys = new aiVectorKey[1];
   1.484 +			nodeAnim->mPositionKeys[0].mTime = 0.0;
   1.485 +			nodeAnim->mPositionKeys[0].mValue = nodePos;
   1.486 +		}
   1.487 +
   1.488 +		// rotation part. Always present. First find value offsets
   1.489 +		{
   1.490 +			unsigned int rotOffset  = 0;
   1.491 +			if( node.mChannels.size() == 6)
   1.492 +			{
   1.493 +				// Offset all further calculations
   1.494 +				rotOffset = 3;
   1.495 +			} 
   1.496 +
   1.497 +			// Then create the number of rotation keys
   1.498 +			nodeAnim->mNumRotationKeys = mAnimNumFrames;
   1.499 +			nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
   1.500 +			aiQuatKey* rotkey = nodeAnim->mRotationKeys;
   1.501 +			for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
   1.502 +			{
   1.503 +				aiMatrix4x4 temp;
   1.504 +				aiMatrix3x3 rotMatrix;
   1.505 +
   1.506 +				for( unsigned int channel = 0; channel < 3; ++channel)
   1.507 +				{
   1.508 +					// translate ZXY euler angels into a quaternion
   1.509 +					const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
   1.510 +
   1.511 +					// Compute rotation transformations in the right order
   1.512 +					switch (node.mChannels[rotOffset+channel]) 
   1.513 +					{
   1.514 +					case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
   1.515 +					case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp);	break;
   1.516 +					case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
   1.517 +					default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
   1.518 +					}
   1.519 +				}
   1.520 +
   1.521 +				rotkey->mTime = double( fr);
   1.522 +				rotkey->mValue = aiQuaternion( rotMatrix);
   1.523 +				++rotkey;
   1.524 +			}
   1.525 +		}
   1.526 +
   1.527 +		// scaling part. Always just a default track
   1.528 +		{
   1.529 +			nodeAnim->mNumScalingKeys = 1;
   1.530 +			nodeAnim->mScalingKeys = new aiVectorKey[1];
   1.531 +			nodeAnim->mScalingKeys[0].mTime = 0.0;
   1.532 +			nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
   1.533 +		}
   1.534 +	}
   1.535 +}
   1.536 +
   1.537 +#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER