vrshoot

diff libs/assimp/OgreSkeleton.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/OgreSkeleton.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,451 @@
     1.4 +/*
     1.5 +Open Asset Import Library (assimp)
     1.6 +----------------------------------------------------------------------
     1.7 +
     1.8 +Copyright (c) 2006-2012, assimp team
     1.9 +All rights reserved.
    1.10 +
    1.11 +Redistribution and use of this software in source and binary forms, 
    1.12 +with or without modification, are permitted provided that the 
    1.13 +following conditions are met:
    1.14 +
    1.15 +* Redistributions of source code must retain the above
    1.16 +  copyright notice, this list of conditions and the
    1.17 +  following disclaimer.
    1.18 +
    1.19 +* Redistributions in binary form must reproduce the above
    1.20 +  copyright notice, this list of conditions and the
    1.21 +  following disclaimer in the documentation and/or other
    1.22 +  materials provided with the distribution.
    1.23 +
    1.24 +* Neither the name of the assimp team, nor the names of its
    1.25 +  contributors may be used to endorse or promote products
    1.26 +  derived from this software without specific prior
    1.27 +  written permission of the assimp team.
    1.28 +
    1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.40 +
    1.41 +----------------------------------------------------------------------
    1.42 +*/
    1.43 +
    1.44 +#include "AssimpPCH.h"
    1.45 +
    1.46 +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
    1.47 +
    1.48 +#include "OgreImporter.hpp"
    1.49 +#include "TinyFormatter.h"
    1.50 +
    1.51 +using namespace std;
    1.52 +
    1.53 +namespace Assimp
    1.54 +{
    1.55 +namespace Ogre
    1.56 +{
    1.57 +
    1.58 +
    1.59 +
    1.60 +void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
    1.61 +{
    1.62 +	const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
    1.63 +	(void)m_CurrentScene;
    1.64 +
    1.65 +
    1.66 +	//most likely the skeleton file will only end with .skeleton
    1.67 +	//But this is a xml reader, so we need: .skeleton.xml
    1.68 +	FileName+=".xml";
    1.69 +
    1.70 +	DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
    1.71 +
    1.72 +	//Open the File:
    1.73 +	boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
    1.74 +	if(NULL==File.get())
    1.75 +		throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
    1.76 +
    1.77 +	//Read the Mesh File:
    1.78 +	boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
    1.79 +	XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
    1.80 +	if(!SkeletonFile)
    1.81 +		throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
    1.82 +
    1.83 +	XmlRead(SkeletonFile);
    1.84 +	if(string("skeleton")!=SkeletonFile->getNodeName())
    1.85 +		throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
    1.86 +
    1.87 +
    1.88 +
    1.89 +	//------------------------------------load bones-----------------------------------------
    1.90 +	XmlRead(SkeletonFile);
    1.91 +	if(string("bones")!=SkeletonFile->getNodeName())
    1.92 +		throw DeadlyImportError("No bones node in skeleton "+FileName);
    1.93 +
    1.94 +	XmlRead(SkeletonFile);
    1.95 +
    1.96 +	while(string("bone")==SkeletonFile->getNodeName())
    1.97 +	{
    1.98 +		//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
    1.99 +
   1.100 +		//read a new bone:
   1.101 +		Bone NewBone;
   1.102 +		NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
   1.103 +		NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
   1.104 +
   1.105 +		//load the position:
   1.106 +		XmlRead(SkeletonFile);
   1.107 +		if(string("position")!=SkeletonFile->getNodeName())
   1.108 +			throw DeadlyImportError("Position is not first node in Bone!");
   1.109 +		NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
   1.110 +		NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
   1.111 +		NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
   1.112 +
   1.113 +		//Rotation:
   1.114 +		XmlRead(SkeletonFile);
   1.115 +		if(string("rotation")!=SkeletonFile->getNodeName())
   1.116 +			throw DeadlyImportError("Rotation is not the second node in Bone!");
   1.117 +		NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
   1.118 +		XmlRead(SkeletonFile);
   1.119 +		if(string("axis")!=SkeletonFile->getNodeName())
   1.120 +			throw DeadlyImportError("No axis specified for bone rotation!");
   1.121 +		NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
   1.122 +		NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
   1.123 +		NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
   1.124 +
   1.125 +		//append the newly loaded bone to the bone list
   1.126 +		Bones.push_back(NewBone);
   1.127 +
   1.128 +		//Proceed to the next bone:
   1.129 +		XmlRead(SkeletonFile);
   1.130 +	}
   1.131 +	//The bones in the file a not neccesarly ordered by there id's so we do it now:
   1.132 +	std::sort(Bones.begin(), Bones.end());
   1.133 +
   1.134 +	//now the id of each bone should be equal to its position in the vector:
   1.135 +	//so we do a simple check:
   1.136 +	{
   1.137 +		bool IdsOk=true;
   1.138 +		for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
   1.139 +		{
   1.140 +			if(Bones[i].Id!=i)
   1.141 +				IdsOk=false;
   1.142 +		}
   1.143 +		if(!IdsOk)
   1.144 +			throw DeadlyImportError("Bone Ids are not valid!"+FileName);
   1.145 +	}
   1.146 +	DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
   1.147 +	//________________________________________________________________________________
   1.148 +
   1.149 +
   1.150 +
   1.151 +
   1.152 +
   1.153 +
   1.154 +	//----------------------------load bonehierarchy--------------------------------
   1.155 +	if(string("bonehierarchy")!=SkeletonFile->getNodeName())
   1.156 +		throw DeadlyImportError("no bonehierarchy node in "+FileName);
   1.157 +
   1.158 +	DefaultLogger::get()->debug("loading bonehierarchy...");
   1.159 +	XmlRead(SkeletonFile);
   1.160 +	while(string("boneparent")==SkeletonFile->getNodeName())
   1.161 +	{
   1.162 +		string Child, Parent;
   1.163 +		Child=GetAttribute<string>(SkeletonFile, "bone");
   1.164 +		Parent=GetAttribute<string>(SkeletonFile, "parent");
   1.165 +
   1.166 +		unsigned int ChildId, ParentId;
   1.167 +		ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
   1.168 +		ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
   1.169 +
   1.170 +		Bones[ChildId].ParentId=ParentId;
   1.171 +		Bones[ParentId].Children.push_back(ChildId);
   1.172 +
   1.173 +		XmlRead(SkeletonFile);
   1.174 +	}
   1.175 +	//_____________________________________________________________________________
   1.176 +
   1.177 +
   1.178 +	//--------- Calculate the WorldToBoneSpace Matrix recursively for all bones: ------------------
   1.179 +	BOOST_FOREACH(Bone &theBone, Bones)
   1.180 +	{
   1.181 +		if(-1==theBone.ParentId) //the bone is a root bone
   1.182 +		{
   1.183 +			theBone.CalculateBoneToWorldSpaceMatrix(Bones);
   1.184 +		}
   1.185 +	}
   1.186 +	//_______________________________________________________________________
   1.187 +	
   1.188 +
   1.189 +	//---------------------------load animations-----------------------------
   1.190 +	if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
   1.191 +	{
   1.192 +		DefaultLogger::get()->debug("Loading Animations");
   1.193 +		XmlRead(SkeletonFile);
   1.194 +		while(string("animation")==SkeletonFile->getNodeName())
   1.195 +		{
   1.196 +			Animation NewAnimation;
   1.197 +			NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
   1.198 +			NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
   1.199 +			
   1.200 +			//Load all Tracks
   1.201 +			XmlRead(SkeletonFile);
   1.202 +			if(string("tracks")!=SkeletonFile->getNodeName())
   1.203 +				throw DeadlyImportError("no tracks node in animation");
   1.204 +			XmlRead(SkeletonFile);
   1.205 +			while(string("track")==SkeletonFile->getNodeName())
   1.206 +			{
   1.207 +				Track NewTrack;
   1.208 +				NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
   1.209 +
   1.210 +				//Load all keyframes;
   1.211 +				XmlRead(SkeletonFile);
   1.212 +				if(string("keyframes")!=SkeletonFile->getNodeName())
   1.213 +					throw DeadlyImportError("no keyframes node!");
   1.214 +				XmlRead(SkeletonFile);
   1.215 +				while(string("keyframe")==SkeletonFile->getNodeName())
   1.216 +				{
   1.217 +					Keyframe NewKeyframe;
   1.218 +					NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
   1.219 +
   1.220 +					//loop over the attributes:
   1.221 +					
   1.222 +					while(true) //will quit, if a Node is not a animationkey
   1.223 +					{
   1.224 +						XmlRead(SkeletonFile);
   1.225 +
   1.226 +						//If any property doesn't show up, it will keep its initialization value
   1.227 +
   1.228 +						//Position:
   1.229 +						if(string("translate")==SkeletonFile->getNodeName())
   1.230 +						{
   1.231 +							NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
   1.232 +							NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
   1.233 +							NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
   1.234 +						}
   1.235 +
   1.236 +						//Rotation:
   1.237 +						else if(string("rotate")==SkeletonFile->getNodeName())
   1.238 +						{
   1.239 +							float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
   1.240 +							aiVector3D RotationAxis;
   1.241 +							XmlRead(SkeletonFile);
   1.242 +							if(string("axis")!=SkeletonFile->getNodeName())
   1.243 +								throw DeadlyImportError("No axis for keyframe rotation!");
   1.244 +							RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
   1.245 +							RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
   1.246 +							RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
   1.247 +
   1.248 +							if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis
   1.249 +							{
   1.250 +								RotationAxis.x=1.0f;
   1.251 +								if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
   1.252 +								{
   1.253 +									DefaultLogger::get()->warn("Invalid Rotation Axis in Keyframe!");
   1.254 +								}
   1.255 +							}
   1.256 +							NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
   1.257 +						}
   1.258 +
   1.259 +						//Scaling:
   1.260 +						else if(string("scale")==SkeletonFile->getNodeName())
   1.261 +						{
   1.262 +							NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
   1.263 +							NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
   1.264 +							NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
   1.265 +						}
   1.266 +
   1.267 +						//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
   1.268 +						else
   1.269 +							break;
   1.270 +					}
   1.271 +
   1.272 +					NewTrack.Keyframes.push_back(NewKeyframe);
   1.273 +				}
   1.274 +
   1.275 +				NewAnimation.Tracks.push_back(NewTrack);
   1.276 +			}
   1.277 +
   1.278 +			Animations.push_back(NewAnimation);
   1.279 +		}
   1.280 +	}
   1.281 +	//_____________________________________________________________________________
   1.282 +
   1.283 +}
   1.284 +
   1.285 +
   1.286 +void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
   1.287 +{
   1.288 +	if(!m_CurrentScene->mRootNode)
   1.289 +		throw DeadlyImportError("No root node exists!!");
   1.290 +	if(0!=m_CurrentScene->mRootNode->mNumChildren)
   1.291 +		throw DeadlyImportError("Root Node already has childnodes!");
   1.292 +
   1.293 +
   1.294 +	//Createt the assimp bone hierarchy
   1.295 +	vector<aiNode*> RootBoneNodes;
   1.296 +	BOOST_FOREACH(const Bone &theBone, Bones)
   1.297 +	{
   1.298 +		if(-1==theBone.ParentId) //the bone is a root bone
   1.299 +		{
   1.300 +			//which will recursily add all other nodes
   1.301 +			RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));
   1.302 +		}
   1.303 +	}
   1.304 +	
   1.305 +	if(RootBoneNodes.size() > 0)
   1.306 +	{
   1.307 +		m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();	
   1.308 +		m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
   1.309 +		memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
   1.310 +	}
   1.311 +}
   1.312 +
   1.313 +
   1.314 +void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
   1.315 +{
   1.316 +	//-----------------Create the Assimp Animations --------------------
   1.317 +	if(Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called
   1.318 +	{
   1.319 +		m_CurrentScene->mNumAnimations=Animations.size();
   1.320 +		m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
   1.321 +		for(unsigned int i=0; i<Animations.size(); ++i)//create all animations
   1.322 +		{
   1.323 +			aiAnimation* NewAnimation=new aiAnimation();
   1.324 +			NewAnimation->mName=Animations[i].Name;
   1.325 +			NewAnimation->mDuration=Animations[i].Length;
   1.326 +			NewAnimation->mTicksPerSecond=1.0f;
   1.327 +
   1.328 +			//Create all tracks in this animation
   1.329 +			NewAnimation->mNumChannels=Animations[i].Tracks.size();
   1.330 +			NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
   1.331 +			for(unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
   1.332 +			{
   1.333 +				aiNodeAnim* NewNodeAnim=new aiNodeAnim();
   1.334 +				NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
   1.335 +
   1.336 +				//we need this, to acces the bones default pose, which we need to make keys absolute to the default bone pose
   1.337 +				vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
   1.338 +				aiMatrix4x4 t0, t1;
   1.339 +				aiMatrix4x4 DefBonePose=aiMatrix4x4::Translation(CurBone->Position, t1)
   1.340 +									 *	aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0);
   1.341 +				
   1.342 +
   1.343 +				//Create the keyframe arrays...
   1.344 +				unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
   1.345 +				NewNodeAnim->mNumPositionKeys=KeyframeCount;
   1.346 +				NewNodeAnim->mNumRotationKeys=KeyframeCount;
   1.347 +				NewNodeAnim->mNumScalingKeys =KeyframeCount;
   1.348 +				NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
   1.349 +				NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
   1.350 +				NewNodeAnim->mScalingKeys =new aiVectorKey[KeyframeCount];
   1.351 +				
   1.352 +				//...and fill them
   1.353 +				for(unsigned int k=0; k<KeyframeCount; ++k)
   1.354 +				{
   1.355 +					aiMatrix4x4 t2, t3;
   1.356 +
   1.357 +					//Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
   1.358 +					aiMatrix4x4 PoseToKey=
   1.359 +									  aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3)	//pos
   1.360 +									* aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix())		//rot
   1.361 +									* aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2);		//scale
   1.362 +									
   1.363 +
   1.364 +					//calculate the complete transformation from world space to bone space
   1.365 +					aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
   1.366 +					
   1.367 +					aiVector3D Pos;
   1.368 +					aiQuaternion Rot;
   1.369 +					aiVector3D Scale;
   1.370 +
   1.371 +					CompleteTransform.Decompose(Scale, Rot, Pos);
   1.372 +
   1.373 +					double Time=Animations[i].Tracks[j].Keyframes[k].Time;
   1.374 +
   1.375 +					NewNodeAnim->mPositionKeys[k].mTime=Time;
   1.376 +					NewNodeAnim->mPositionKeys[k].mValue=Pos;
   1.377 +					
   1.378 +					NewNodeAnim->mRotationKeys[k].mTime=Time;
   1.379 +					NewNodeAnim->mRotationKeys[k].mValue=Rot;
   1.380 +
   1.381 +					NewNodeAnim->mScalingKeys[k].mTime=Time;
   1.382 +					NewNodeAnim->mScalingKeys[k].mValue=Scale;
   1.383 +				}
   1.384 +				
   1.385 +				NewAnimation->mChannels[j]=NewNodeAnim;
   1.386 +			}
   1.387 +
   1.388 +			m_CurrentScene->mAnimations[i]=NewAnimation;
   1.389 +		}
   1.390 +	}
   1.391 +//TODO: Auf nicht vorhandene Animationskeys achten!
   1.392 +//#pragma warning (s.o.)
   1.393 +	//__________________________________________________________________
   1.394 +}
   1.395 +
   1.396 +
   1.397 +aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode)
   1.398 +{
   1.399 +	//----Create the node for this bone and set its values-----
   1.400 +	aiNode* NewNode=new aiNode(Bones[BoneId].Name);
   1.401 +	NewNode->mParent=ParentNode;
   1.402 +
   1.403 +	aiMatrix4x4 t0,t1;
   1.404 +	NewNode->mTransformation=
   1.405 +		aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
   1.406 +		*aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
   1.407 +	;
   1.408 +	//__________________________________________________________
   1.409 +
   1.410 +
   1.411 +	//---------- recursivly create all children Nodes: ----------
   1.412 +	NewNode->mNumChildren=Bones[BoneId].Children.size();
   1.413 +	NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
   1.414 +	for(unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
   1.415 +	{
   1.416 +		NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
   1.417 +	}
   1.418 +	//____________________________________________________
   1.419 +
   1.420 +
   1.421 +	return NewNode;
   1.422 +}
   1.423 +
   1.424 +
   1.425 +void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
   1.426 +{
   1.427 +	//Calculate the matrix for this bone:
   1.428 +
   1.429 +	aiMatrix4x4 t0,t1;
   1.430 +	aiMatrix4x4 Transf=	aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
   1.431 +					*	aiMatrix4x4::Translation(-Position, t0);
   1.432 +
   1.433 +	if(-1==ParentId)
   1.434 +	{
   1.435 +		BoneToWorldSpace=Transf;
   1.436 +	}
   1.437 +	else
   1.438 +	{
   1.439 +		BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
   1.440 +	}
   1.441 +	
   1.442 +
   1.443 +	//and recursivly for all children:
   1.444 +	BOOST_FOREACH(int theChildren, Children)
   1.445 +	{
   1.446 +		Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
   1.447 +	}
   1.448 +}
   1.449 +
   1.450 +
   1.451 +}//namespace Ogre
   1.452 +}//namespace Assimp
   1.453 +
   1.454 +#endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER