Bläddra i källkod

OgreImporter: Cleanup and bugfixes to OgreSkeleton.cpp. This was actually so badly broken that it did nothing if the mesh referenced a binary skeleton. Now logs a warning for this case and tries to read from .skeleton.xml like the original author intended it to work. The assimp skeleton is still broken, I will fix that later on when I (eventually) get to that part of the code.

Jonne Nauha 11 år sedan
förälder
incheckning
f5c7b283bc
6 ändrade filer med 193 tillägg och 214 borttagningar
  1. 9 13
      code/OgreImporter.cpp
  2. 6 3
      code/OgreImporter.h
  3. 0 5
      code/OgreMaterial.cpp
  4. 3 4
      code/OgreMesh.cpp
  5. 21 5
      code/OgreParsingUtils.h
  6. 154 184
      code/OgreSkeleton.cpp

+ 9 - 13
code/OgreImporter.cpp

@@ -39,6 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #include "AssimpPCH.h"
+
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
 
 #include <vector>
@@ -71,14 +72,10 @@ namespace Ogre
 bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
 {
 	if (!checkSig)
-	{
-		string ext = "mesh.xml";
-		int len = ext.length();
-		string fileExt = ToLower(pFile.substr(pFile.length()-len, len));
-		return (ASSIMP_stricmp(fileExt, ext) == 0);
-	}
-	const char* tokens[] = {"<mesh>"};
-	return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+		return EndsWith(pFile, ".mesh.xml", false);
+
+	const char* tokens[] = { "<mesh>" };
+	return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
 }
 
 void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
@@ -101,7 +98,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
 	// Read root node
 	NextNode(reader.get());
 	if (!CurrentNodeNameEquals(reader, "mesh"))
-		throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + ">");
+		throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + "> in " + pFile);
 	
 	// Node names
 	const string nnSharedGeometry = "sharedgeometry";
@@ -242,8 +239,7 @@ void OgreImporter::SetupProperties(const Importer* pImp)
 	m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false);
 }
 
+} // Ogre
+} // Assimp
 
-}//namespace Ogre
-}//namespace Assimp
-
-#endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
+#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER

+ 6 - 3
code/OgreImporter.h

@@ -155,7 +155,7 @@ struct BoneAssignment
 /// Ogre Bone weight
 struct BoneWeight
 {
-	/// Bone ID
+	/// Bone Id
 	unsigned int Id;
 	/// BoneWeight
 	float Value;
@@ -187,8 +187,11 @@ struct Bone
 	{
 	}
 
-	/// This operator is needed to sort the bones after Id's
-	bool operator<(const Bone &other) const { return Id < other.Id; }
+	/// Returns if this bone is parented.
+	bool IsParented() const { return (ParentId != -1); }
+
+	/// This operator is needed to sort the bones by Id in a vector<Bone>.
+	bool operator<(const Bone &other) const { return (Id < other.Id); }
 
 	/// This operator is needed to find a bone by its name in a vector<Bone>
 	bool operator==(const std::string& other) const { return Name == other; }

+ 0 - 5
code/OgreMaterial.cpp

@@ -38,11 +38,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/**
-This file contains material related code. This is
-spilitted up from the main file OgreImporter.cpp
-to make it shorter easier to maintain.
-*/
 #include "AssimpPCH.h"
 
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER

+ 3 - 4
code/OgreMesh.cpp

@@ -548,8 +548,7 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(aiScene *pScene, const SubMesh& submes
 	return NewAiMesh;
 }
 
+} // Ogre
+} // Assimp
 
-}//namespace Ogre
-}//namespace Assimp
-
-#endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
+#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER

+ 21 - 5
code/OgreParsingUtils.h

@@ -118,10 +118,25 @@ static inline std::string ToLower(std::string s)
 	return s;
 }
 
-// ------------------------------------------------------------------------------------------------
-// From http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
+/// Returns if @c s ends with @c suffix. If @c caseSensitive is false, both strings will be lower cased before matching.
+static inline bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true)
+{
+	if (s.empty() || suffix.empty())
+		return false;
+	else if (s.length() < suffix.length())
+		return false;
+
+	if (!caseSensitive)
+		return EndsWith(ToLower(s), ToLower(suffix), true);
 
-// trim from start
+	size_t len = suffix.length();
+	std::string sSuffix = s.substr(s.length()-len, len);
+	return (ASSIMP_stricmp(sSuffix, suffix) == 0);
+}
+
+// Below trim functions adapted from http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
+
+/// Trim from start
 static inline std::string &TrimLeft(std::string &s, bool newlines = true)
 {
 	if (!newlines)
@@ -131,7 +146,7 @@ static inline std::string &TrimLeft(std::string &s, bool newlines = true)
 	return s;
 }
 
-// trim from end
+/// Trim from end
 static inline std::string &TrimRight(std::string &s, bool newlines = true)
 {
 	if (!newlines)
@@ -140,7 +155,8 @@ static inline std::string &TrimRight(std::string &s, bool newlines = true)
 		s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine<char>))));
 	return s;
 }
-// trim from both ends
+
+/// Trim from both ends
 static inline std::string &Trim(std::string &s, bool newlines = true)
 {
 	return TrimLeft(TrimRight(s, newlines), newlines);

+ 154 - 184
code/OgreSkeleton.cpp

@@ -55,226 +55,205 @@ namespace Ogre
 void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
 							    const std::string &skeletonFile, vector<Bone> &Bones, vector<Animation> &Animations) const
 {
-	//most likely the skeleton file will only end with .skeleton
-	//But this is a xml reader, so we need: .skeleton.xml
-	string skeletonPath = skeletonFile + ".xml";
-
-	DefaultLogger::get()->debug(string("Loading Skeleton: ")+skeletonFile);
-
-	//Open the File:
-	boost::scoped_ptr<IOStream> File(pIOHandler->Open(skeletonFile));
-	if(NULL==File.get())
-		throw DeadlyImportError("Failed to open skeleton file "+skeletonFile+".");
+	string filename = skeletonFile;
+	if (EndsWith(filename, ".skeleton"))
+	{
+		DefaultLogger::get()->warn("Mesh is referencing a Ogre binary skeleton. Parsing binary Ogre assets is not supported at the moment. Trying to find .skeleton.xml file instead.");
+		filename += ".xml";
+	}
 
-	//Read the Mesh File:
-	boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
-	XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
-	if(!SkeletonFile)
-		throw DeadlyImportError(string("Failed to create XML Reader for ")+skeletonFile);
+	if (!pIOHandler->Exists(filename))
+	{
+		DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "', skeleton will be missing.");
+		return;
+	}
 
-	NextNode(SkeletonFile);
-	if(string("skeleton")!=SkeletonFile->getNodeName())
-		throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+skeletonFile);
+	boost::scoped_ptr<IOStream> file(pIOHandler->Open(filename));
+	if (!file.get())
+		throw DeadlyImportError("Failed to open skeleton file " + filename);
 
+	boost::scoped_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
+	XmlReader* reader = irr::io::createIrrXMLReader(stream.get());
+	if (!reader)
+		throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
 
+	DefaultLogger::get()->debug("Reading skeleton '" + filename + "'");
 
-	//------------------------------------load bones-----------------------------------------
-	NextNode(SkeletonFile);
-	if(string("bones")!=SkeletonFile->getNodeName())
-		throw DeadlyImportError("No bones node in skeleton "+skeletonFile);
+	// Root
+	NextNode(reader);
+	if (!CurrentNodeNameEquals(reader, "skeleton"))
+		throw DeadlyImportError("Root node is not <skeleton> but <" + string(reader->getNodeName()) + "> in " + filename);
 
-	NextNode(SkeletonFile);
+	// Bones
+	NextNode(reader);
+	if (!CurrentNodeNameEquals(reader, "bones"))
+		throw DeadlyImportError("No <bones> node in skeleton " + skeletonFile);
 
-	while(string("bone")==SkeletonFile->getNodeName())
+	NextNode(reader);
+	while(CurrentNodeNameEquals(reader, "bone"))
 	{
 		//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
+		/// @todo What does the above mean?
 
-		//read a new bone:
-		Bone NewBone;
-		NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
-		NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
+		Bone bone;
+		bone.Id = GetAttribute<int>(reader, "id");
+		bone.Name = GetAttribute<string>(reader, "name");
 
-		//load the position:
-		NextNode(SkeletonFile);
-		if(string("position")!=SkeletonFile->getNodeName())
+		NextNode(reader);
+		if (!CurrentNodeNameEquals(reader, "position"))
 			throw DeadlyImportError("Position is not first node in Bone!");
-		NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
-		NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
-		NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
 
-		//Rotation:
-		NextNode(SkeletonFile);
-		if(string("rotation")!=SkeletonFile->getNodeName())
+		bone.Position.x = GetAttribute<float>(reader, "x");
+		bone.Position.y = GetAttribute<float>(reader, "y");
+		bone.Position.z = GetAttribute<float>(reader, "z");
+
+		NextNode(reader);
+		if (!CurrentNodeNameEquals(reader, "rotation"))
 			throw DeadlyImportError("Rotation is not the second node in Bone!");
-		NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
-		NextNode(SkeletonFile);
-		if(string("axis")!=SkeletonFile->getNodeName())
+		
+		bone.RotationAngle = GetAttribute<float>(reader, "angle");
+		
+		NextNode(reader);
+		if (!CurrentNodeNameEquals(reader, "axis"))
 			throw DeadlyImportError("No axis specified for bone rotation!");
-		NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
-		NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
-		NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
-
-		//append the newly loaded bone to the bone list
-		Bones.push_back(NewBone);
+			
+		bone.RotationAxis.x = GetAttribute<float>(reader, "x");
+		bone.RotationAxis.y = GetAttribute<float>(reader, "y");
+		bone.RotationAxis.z = GetAttribute<float>(reader, "z");
 
-		//Proceed to the next bone:
-		NextNode(SkeletonFile);
-	}
-	//The bones in the file a not neccesarly ordered by there id's so we do it now:
-	std::sort(Bones.begin(), Bones.end());
+		Bones.push_back(bone);
 
-	//now the id of each bone should be equal to its position in the vector:
-	//so we do a simple check:
-	{
-		bool IdsOk=true;
-		for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
-		{
-			if(Bones[i].Id!=i)
-				IdsOk=false;
-		}
-		if(!IdsOk)
-			throw DeadlyImportError("Bone Ids are not valid!"+skeletonFile);
+		NextNode(reader);
 	}
-	DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
-	//________________________________________________________________________________
-
-
 
+	// Order bones by Id
+	std::sort(Bones.begin(), Bones.end());
 
+	// Validate that bone indexes are not skipped.
+	/** @note Left this from original authors code, but not sure if this is strictly necessary
+		as per the Ogre skeleton spec. It might be more that other (later) code in this imported does not break. */
+	for (size_t i=0, len=Bones.size(); i<len; ++i)
+		if (static_cast<int>(Bones[i].Id) != static_cast<int>(i))
+			throw DeadlyImportError("Bone Ids are not in sequence in " + skeletonFile);
 
+	DefaultLogger::get()->debug(Formatter::format() << "  - Bones " << Bones.size());
 
-	//----------------------------load bonehierarchy--------------------------------
-	if(string("bonehierarchy")!=SkeletonFile->getNodeName())
-		throw DeadlyImportError("no bonehierarchy node in "+skeletonFile);
+	// Bone hierarchy
+	if (!CurrentNodeNameEquals(reader, "bonehierarchy"))
+		throw DeadlyImportError("No <bonehierarchy> node found after <bones> in " + skeletonFile);
 
-	DefaultLogger::get()->debug("loading bonehierarchy...");
-	NextNode(SkeletonFile);
-	while(string("boneparent")==SkeletonFile->getNodeName())
+	NextNode(reader);
+	while(CurrentNodeNameEquals(reader, "boneparent"))
 	{
-		string Child, Parent;
-		Child=GetAttribute<string>(SkeletonFile, "bone");
-		Parent=GetAttribute<string>(SkeletonFile, "parent");
+		string childName = GetAttribute<string>(reader, "bone");
+		string parentName = GetAttribute<string>(reader, "parent");
 
-		unsigned int ChildId, ParentId;
-		ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
-		ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
-
-		Bones[ChildId].ParentId=ParentId;
-		Bones[ParentId].Children.push_back(ChildId);
+		vector<Bone>::iterator iterChild = find(Bones.begin(), Bones.end(), childName);
+		vector<Bone>::iterator iterParent = find(Bones.begin(), Bones.end(), parentName);
+		
+		if (iterChild != Bones.end() && iterParent != Bones.end())
+		{
+			iterChild->ParentId = iterParent->Id;
+			iterParent->Children.push_back(iterChild->Id);
+		}
+		else
+			DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + childName + " Parent " + parentName);
 
-		NextNode(SkeletonFile);
+		NextNode(reader);
 	}
-	//_____________________________________________________________________________
 
-
-	//--------- Calculate the WorldToBoneSpace Matrix recursively for all bones: ------------------
+	// Calculate bone matrices for root bones. Recursively does their children.
 	BOOST_FOREACH(Bone &theBone, Bones)
 	{
-		if(-1==theBone.ParentId) //the bone is a root bone
-		{
+		if (!theBone.IsParented())
 			theBone.CalculateBoneToWorldSpaceMatrix(Bones);
-		}
 	}
-	//_______________________________________________________________________
 	
+	aiVector3D zeroVec(0.f, 0.f, 0.f);
 
-	//---------------------------load animations-----------------------------
-	if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
+	// Animations
+	if (CurrentNodeNameEquals(reader, "animations"))
 	{
-		DefaultLogger::get()->debug("Loading Animations");
-		NextNode(SkeletonFile);
-		while(string("animation")==SkeletonFile->getNodeName())
+		DefaultLogger::get()->debug("  - Animations");
+
+		NextNode(reader);
+		while(CurrentNodeNameEquals(reader, "animation"))
 		{
-			Animation NewAnimation;
-			NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
-			NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
+			Animation animation;
+			animation.Name = GetAttribute<string>(reader, "name");
+			animation.Length = GetAttribute<float>(reader, "length");
 			
-			//Load all Tracks
-			NextNode(SkeletonFile);
-			if(string("tracks")!=SkeletonFile->getNodeName())
-				throw DeadlyImportError("no tracks node in animation");
-			NextNode(SkeletonFile);
-			while(string("track")==SkeletonFile->getNodeName())
-			{
-				Track NewTrack;
-				NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
-
-				//Load all keyframes;
-				NextNode(SkeletonFile);
-				if(string("keyframes")!=SkeletonFile->getNodeName())
-					throw DeadlyImportError("no keyframes node!");
-				NextNode(SkeletonFile);
-				while(string("keyframe")==SkeletonFile->getNodeName())
-				{
-					KeyFrame NewKeyframe;
-					NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
+			// Tracks
+			NextNode(reader);
+			if (!CurrentNodeNameEquals(reader, "tracks"))
+				throw DeadlyImportError("No <tracks> node found in animation '" + animation.Name + "' in " + skeletonFile);
 
-					//loop over the attributes:
-					
-					while(true) //will quit, if a Node is not a animationkey
-					{
-						NextNode(SkeletonFile);
+			NextNode(reader);
+			while(CurrentNodeNameEquals(reader, "track"))
+			{
+				Track track;
+				track.BoneName = GetAttribute<string>(reader, "bone");
 
-						//If any property doesn't show up, it will keep its initialization value
+				// Keyframes
+				NextNode(reader);
+				if (!CurrentNodeNameEquals(reader, "keyframes"))
+					throw DeadlyImportError("No <keyframes> node found in a track in animation '" + animation.Name + "' in " + skeletonFile);
 
-						//Position:
-						if(string("translate")==SkeletonFile->getNodeName())
+				NextNode(reader);
+				while(CurrentNodeNameEquals(reader, "keyframe"))
+				{
+					KeyFrame keyFrame;
+					keyFrame.Time = GetAttribute<float>(reader, "time");
+				
+					NextNode(reader);
+					while(CurrentNodeNameEquals(reader, "translate") || CurrentNodeNameEquals(reader, "rotate") || CurrentNodeNameEquals(reader, "scale"))
+					{
+						if (CurrentNodeNameEquals(reader, "translate"))
 						{
-							NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
-							NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
-							NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
+							keyFrame.Position.x = GetAttribute<float>(reader, "x");
+							keyFrame.Position.y = GetAttribute<float>(reader, "y");
+							keyFrame.Position.z = GetAttribute<float>(reader, "z");
 						}
-
-						//Rotation:
-						else if(string("rotate")==SkeletonFile->getNodeName())
+						else if (CurrentNodeNameEquals(reader, "rotate"))
 						{
-							float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
-							aiVector3D RotationAxis;
-							NextNode(SkeletonFile);
-							if(string("axis")!=SkeletonFile->getNodeName())
+							float angle = GetAttribute<float>(reader, "angle");
+
+							NextNode(reader);
+							if(string("axis")!=reader->getNodeName())
 								throw DeadlyImportError("No axis for keyframe rotation!");
-							RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
-							RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
-							RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
+								
+							aiVector3D axis;
+							axis.x = GetAttribute<float>(reader, "x");
+							axis.y = GetAttribute<float>(reader, "y");
+							axis.z = GetAttribute<float>(reader, "z");
 
-							if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis
+							if (axis.Equal(zeroVec))
 							{
-								RotationAxis.x=1.0f;
-								if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
-								{
-									DefaultLogger::get()->warn("Invalid Rotation Axis in KeyFrame!");
-								}
+								axis.x = 1.0f;
+								if (angle != 0)
+									DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation '" + animation.Name + "'");
 							}
-							NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
+							keyFrame.Rotation = aiQuaternion(axis, angle);
 						}
-
-						//Scaling:
-						else if(string("scale")==SkeletonFile->getNodeName())
+						else if (CurrentNodeNameEquals(reader, "scale"))
 						{
-							NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
-							NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
-							NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
-						}
-
-						//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
-						else
-							break;
+							keyFrame.Scaling.x = GetAttribute<float>(reader, "x");
+							keyFrame.Scaling.y = GetAttribute<float>(reader, "y");
+							keyFrame.Scaling.z = GetAttribute<float>(reader, "z");
+						}	
+						NextNode(reader);
 					}
-
-					NewTrack.Keyframes.push_back(NewKeyframe);
+					track.Keyframes.push_back(keyFrame);
 				}
-
-				NewAnimation.Tracks.push_back(NewTrack);
+				animation.Tracks.push_back(track);
 			}
-
-			Animations.push_back(NewAnimation);
+			Animations.push_back(animation);
+			
+			DefaultLogger::get()->debug(Formatter::format() << "      " << animation.Name << " (" << animation.Length << " sec, " << animation.Tracks.size() << " tracks)");
 		}
 	}
-	//_____________________________________________________________________________
-
 }
 
-
 void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
 {
 	if(!pScene->mRootNode)
@@ -414,31 +393,22 @@ aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &
 
 void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
 {
-	//Calculate the matrix for this bone:
-
-	aiMatrix4x4 t0,t1;
-	aiMatrix4x4 Transf=	aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
-					*	aiMatrix4x4::Translation(-Position, t0);
+	aiMatrix4x4 t0, t1;
+	aiMatrix4x4 transform = aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) * aiMatrix4x4::Translation(-Position, t0);
 
-	if(-1==ParentId)
-	{
-		BoneToWorldSpace=Transf;
-	}
+	if (!IsParented())
+		BoneToWorldSpace = transform;
 	else
-	{
-		BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
-	}
-	
+		BoneToWorldSpace = transform * Bones[ParentId].BoneToWorldSpace;
 
-	//and recursivly for all children:
-	BOOST_FOREACH(int theChildren, Children)
+	// Recursively for all children now that the parent matrix has been calculated.
+	BOOST_FOREACH(int childId, Children)
 	{
-		Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
+		Bones[childId].CalculateBoneToWorldSpaceMatrix(Bones);
 	}
 }
 
+} // Ogre
+} // Assimp
 
-}//namespace Ogre
-}//namespace Assimp
-
-#endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
+#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER