1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
7 
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
11 
12 * Redistributions of source code must retain the above
13   copyright notice, this list of conditions and the
14   following disclaimer.
15 
16 * Redistributions in binary form must reproduce the above
17   copyright notice, this list of conditions and the
18   following disclaimer in the documentation and/or other
19   materials provided with the distribution.
20 
21 * Neither the name of the assimp team, nor the names of its
22   contributors may be used to endorse or promote products
23   derived from this software without specific prior
24   written permission of the assimp team.
25 
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 
38 ----------------------------------------------------------------------
39 */
40 
41 #include "AssimpPCH.h"
42 
43 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
44 
45 #include "OgreImporter.hpp"
46 #include "TinyFormatter.h"
47 
48 using namespace std;
49 
50 namespace Assimp
51 {
52 namespace Ogre
53 {
54 
55 
56 
LoadSkeleton(std::string FileName,vector<Bone> & Bones,vector<Animation> & Animations) const57 void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
58 {
59 	const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
60 	(void)m_CurrentScene;
61 
62 
63 	//most likely the skeleton file will only end with .skeleton
64 	//But this is a xml reader, so we need: .skeleton.xml
65 	FileName+=".xml";
66 
67 	DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
68 
69 	//Open the File:
70 	boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
71 	if(NULL==File.get())
72 		throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
73 
74 	//Read the Mesh File:
75 	boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
76 	XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
77 	if(!SkeletonFile)
78 		throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
79 
80 	//Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!!
81 
82 	XmlRead(SkeletonFile);
83 	if(string("skeleton")!=SkeletonFile->getNodeName())
84 		throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
85 
86 
87 
88 	//------------------------------------load bones-----------------------------------------
89 	XmlRead(SkeletonFile);
90 	if(string("bones")!=SkeletonFile->getNodeName())
91 		throw DeadlyImportError("No bones node in skeleton "+FileName);
92 
93 	XmlRead(SkeletonFile);
94 
95 	while(string("bone")==SkeletonFile->getNodeName())
96 	{
97 		//TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
98 
99 		//read a new bone:
100 		Bone NewBone;
101 		NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
102 		NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
103 
104 		//load the position:
105 		XmlRead(SkeletonFile);
106 		if(string("position")!=SkeletonFile->getNodeName())
107 			throw DeadlyImportError("Position is not first node in Bone!");
108 		NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
109 		NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
110 		NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
111 
112 		//Rotation:
113 		XmlRead(SkeletonFile);
114 		if(string("rotation")!=SkeletonFile->getNodeName())
115 			throw DeadlyImportError("Rotation is not the second node in Bone!");
116 		NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
117 		XmlRead(SkeletonFile);
118 		if(string("axis")!=SkeletonFile->getNodeName())
119 			throw DeadlyImportError("No axis specified for bone rotation!");
120 		NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
121 		NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
122 		NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
123 
124 		//append the newly loaded bone to the bone list
125 		Bones.push_back(NewBone);
126 
127 		//Proceed to the next bone:
128 		XmlRead(SkeletonFile);
129 	}
130 	//The bones in the file a not neccesarly ordered by there id's so we do it now:
131 	std::sort(Bones.begin(), Bones.end());
132 
133 	//now the id of each bone should be equal to its position in the vector:
134 	//so we do a simple check:
135 	{
136 		bool IdsOk=true;
137 		for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
138 		{
139 			if(Bones[i].Id!=i)
140 				IdsOk=false;
141 		}
142 		if(!IdsOk)
143 			throw DeadlyImportError("Bone Ids are not valid!"+FileName);
144 	}
145 	DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
146 	//________________________________________________________________________________
147 
148 
149 
150 
151 
152 
153 	//----------------------------load bonehierarchy--------------------------------
154 	if(string("bonehierarchy")!=SkeletonFile->getNodeName())
155 		throw DeadlyImportError("no bonehierarchy node in "+FileName);
156 
157 	DefaultLogger::get()->debug("loading bonehierarchy...");
158 	XmlRead(SkeletonFile);
159 	while(string("boneparent")==SkeletonFile->getNodeName())
160 	{
161 		string Child, Parent;
162 		Child=GetAttribute<string>(SkeletonFile, "bone");
163 		Parent=GetAttribute<string>(SkeletonFile, "parent");
164 
165 		unsigned int ChildId, ParentId;
166 		ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
167 		ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
168 
169 		Bones[ChildId].ParentId=ParentId;
170 		Bones[ParentId].Children.push_back(ChildId);
171 
172 		XmlRead(SkeletonFile);//i once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks??
173 	}
174 	//_____________________________________________________________________________
175 
176 
177 	//--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------
178 	BOOST_FOREACH(Bone &theBone, Bones)
179 	{
180 		if(-1==theBone.ParentId) //the bone is a root bone
181 		{
182 			theBone.CalculateBoneToWorldSpaceMatrix(Bones);
183 		}
184 	}
185 	//_______________________________________________________________________
186 
187 
188 	//---------------------------load animations-----------------------------
189 	if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
190 	{
191 		DefaultLogger::get()->debug("Loading Animations");
192 		XmlRead(SkeletonFile);
193 		while(string("animation")==SkeletonFile->getNodeName())
194 		{
195 			Animation NewAnimation;
196 			NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
197 			NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
198 
199 			//Load all Tracks
200 			XmlRead(SkeletonFile);
201 			if(string("tracks")!=SkeletonFile->getNodeName())
202 				throw DeadlyImportError("no tracks node in animation");
203 			XmlRead(SkeletonFile);
204 			while(string("track")==SkeletonFile->getNodeName())
205 			{
206 				Track NewTrack;
207 				NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
208 
209 				//Load all keyframes;
210 				XmlRead(SkeletonFile);
211 				if(string("keyframes")!=SkeletonFile->getNodeName())
212 					throw DeadlyImportError("no keyframes node!");
213 				XmlRead(SkeletonFile);
214 				while(string("keyframe")==SkeletonFile->getNodeName())
215 				{
216 					Keyframe NewKeyframe;
217 					NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
218 
219 					//loop over the attributes:
220 
221 					while(true)
222 					{
223 						XmlRead(SkeletonFile);
224 
225 						//If any property doesn't show up, it will keep its initialization value
226 
227 						//Position:
228 						if(string("translate")==SkeletonFile->getNodeName())
229 						{
230 							NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
231 							NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
232 							NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
233 						}
234 
235 						//Rotation:
236 						else if(string("rotate")==SkeletonFile->getNodeName())
237 						{
238 							float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
239 							aiVector3D RotationAxis;
240 							XmlRead(SkeletonFile);
241 							if(string("axis")!=SkeletonFile->getNodeName())
242 								throw DeadlyImportError("No axis for keyframe rotation!");
243 							RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
244 							RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
245 							RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
246 
247 							if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis
248 							{
249 								RotationAxis.x=1.0f;
250 								if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
251 								{
252 									DefaultLogger::get()->warn("Invalid Rotation Axis in Keyframe!");
253 								}
254 							}
255 							NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
256 						}
257 
258 						//Scaling:
259 						else if(string("scale")==SkeletonFile->getNodeName())
260 						{
261 							NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
262 							NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
263 							NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
264 						}
265 
266 						//we suppose, that we read all attributes and this is a new keyframe or the end of the animation
267 						else
268 							break;
269 					}
270 
271 
272 					NewTrack.Keyframes.push_back(NewKeyframe);
273 					//XmlRead(SkeletonFile);
274 				}
275 
276 
277 				NewAnimation.Tracks.push_back(NewTrack);
278 			}
279 
280 			Animations.push_back(NewAnimation);
281 		}
282 	}
283 	//_____________________________________________________________________________
284 
285 }
286 
287 
CreateAssimpSkeleton(const std::vector<Bone> & Bones,const std::vector<Animation> &)288 void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
289 {
290 	if(!m_CurrentScene->mRootNode)
291 		throw DeadlyImportError("No root node exists!!");
292 	if(0!=m_CurrentScene->mRootNode->mNumChildren)
293 		throw DeadlyImportError("Root Node already has childnodes!");
294 
295 
296 	//Createt the assimp bone hierarchy
297 	vector<aiNode*> RootBoneNodes;
298 	BOOST_FOREACH(Bone theBone, Bones)
299 	{
300 		if(-1==theBone.ParentId) //the bone is a root bone
301 		{
302 			//which will recursily add all other nodes
303 			RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));
304 		}
305 	}
306 
307 	if (RootBoneNodes.size()) {
308 		m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
309 		m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
310 		memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
311 	}
312 }
313 
314 
PutAnimationsInScene(const std::vector<Bone> & Bones,const std::vector<Animation> & Animations)315 void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
316 {
317 	//-----------------Create the Assimp Animations --------------------
318 	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
319 	{
320 		m_CurrentScene->mNumAnimations=Animations.size();
321 		m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
322 		for(unsigned int i=0; i<Animations.size(); ++i)//create all animations
323 		{
324 			aiAnimation* NewAnimation=new aiAnimation();
325 			NewAnimation->mName=Animations[i].Name;
326 			NewAnimation->mDuration=Animations[i].Length;
327 			NewAnimation->mTicksPerSecond=1.0f;
328 
329 			//Create all tracks in this animation
330 			NewAnimation->mNumChannels=Animations[i].Tracks.size();
331 			NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
332 			for(unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
333 			{
334 				aiNodeAnim* NewNodeAnim=new aiNodeAnim();
335 				NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
336 
337 				//we need this, to acces the bones default pose, which we need to make keys absolute to the default bone pose
338 				vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
339 				aiMatrix4x4 t0, t1;
340 				aiMatrix4x4 DefBonePose=aiMatrix4x4::Translation(CurBone->Position, t1)
341 									 *	aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0);
342 
343 
344 				//Create the keyframe arrays...
345 				unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
346 				NewNodeAnim->mNumPositionKeys=KeyframeCount;
347 				NewNodeAnim->mNumRotationKeys=KeyframeCount;
348 				NewNodeAnim->mNumScalingKeys =KeyframeCount;
349 				NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
350 				NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
351 				NewNodeAnim->mScalingKeys =new aiVectorKey[KeyframeCount];
352 
353 				//...and fill them
354 				for(unsigned int k=0; k<KeyframeCount; ++k)
355 				{
356 					aiMatrix4x4 t2, t3;
357 
358 					//Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
359 					aiMatrix4x4 PoseToKey=
360 									  aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3)	//pos
361 									* aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix())		//rot
362 									* aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2);	//scale
363 
364 
365 					//calculate the complete transformation from world space to bone space
366 					aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
367 
368 					aiVector3D Pos;
369 					aiQuaternion Rot;
370 					aiVector3D Scale;
371 
372 					CompleteTransform.Decompose(Scale, Rot, Pos);
373 
374 					double Time=Animations[i].Tracks[j].Keyframes[k].Time;
375 
376 					NewNodeAnim->mPositionKeys[k].mTime=Time;
377 					NewNodeAnim->mPositionKeys[k].mValue=Pos;
378 
379 					NewNodeAnim->mRotationKeys[k].mTime=Time;
380 					NewNodeAnim->mRotationKeys[k].mValue=Rot;
381 
382 					NewNodeAnim->mScalingKeys[k].mTime=Time;
383 					NewNodeAnim->mScalingKeys[k].mValue=Scale;
384 				}
385 
386 				NewAnimation->mChannels[j]=NewNodeAnim;
387 			}
388 
389 			m_CurrentScene->mAnimations[i]=NewAnimation;
390 		}
391 	}
392 //TODO: Auf nicht vorhandene Animationskeys achten!
393 //#pragma warning (s.o.)
394 	//__________________________________________________________________
395 }
396 
397 
CreateAiNodeFromBone(int BoneId,const std::vector<Bone> & Bones,aiNode * ParentNode)398 aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode)
399 {
400 	//----Create the node for this bone and set its values-----
401 	aiNode* NewNode=new aiNode(Bones[BoneId].Name);
402 	NewNode->mParent=ParentNode;
403 
404 	aiMatrix4x4 t0,t1;
405 	NewNode->mTransformation=
406 		aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
407 		*aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
408 	;
409 	//__________________________________________________________
410 
411 
412 	//---------- recursivly create all children Nodes: ----------
413 	NewNode->mNumChildren=Bones[BoneId].Children.size();
414 	NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
415 	for(unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
416 	{
417 		NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
418 	}
419 	//____________________________________________________
420 
421 
422 	return NewNode;
423 }
424 
425 
CalculateBoneToWorldSpaceMatrix(vector<Bone> & Bones)426 void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
427 {
428 	//Calculate the matrix for this bone:
429 
430 	aiMatrix4x4 t0,t1;
431 	aiMatrix4x4 Transf=	aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
432 					*	aiMatrix4x4::Translation(-Position, t0);
433 
434 	if(-1==ParentId)
435 	{
436 		BoneToWorldSpace=Transf;
437 	}
438 	else
439 	{
440 		BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
441 	}
442 
443 
444 	//and recursivly for all children:
445 	BOOST_FOREACH(int theChildren, Children)
446 	{
447 		Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
448 	}
449 }
450 
451 
452 }//namespace Ogre
453 }//namespace Assimp
454 
455 #endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
456