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