vrshoot

view 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 source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
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:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
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.
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.
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.
38 ----------------------------------------------------------------------
39 */
41 #include "AssimpPCH.h"
43 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
45 #include "OgreImporter.hpp"
46 #include "TinyFormatter.h"
48 using namespace std;
50 namespace Assimp
51 {
52 namespace Ogre
53 {
57 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;
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";
67 DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
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+".");
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);
80 XmlRead(SkeletonFile);
81 if(string("skeleton")!=SkeletonFile->getNodeName())
82 throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
86 //------------------------------------load bones-----------------------------------------
87 XmlRead(SkeletonFile);
88 if(string("bones")!=SkeletonFile->getNodeName())
89 throw DeadlyImportError("No bones node in skeleton "+FileName);
91 XmlRead(SkeletonFile);
93 while(string("bone")==SkeletonFile->getNodeName())
94 {
95 //TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
97 //read a new bone:
98 Bone NewBone;
99 NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
100 NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
102 //load the position:
103 XmlRead(SkeletonFile);
104 if(string("position")!=SkeletonFile->getNodeName())
105 throw DeadlyImportError("Position is not first node in Bone!");
106 NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
107 NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
108 NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
110 //Rotation:
111 XmlRead(SkeletonFile);
112 if(string("rotation")!=SkeletonFile->getNodeName())
113 throw DeadlyImportError("Rotation is not the second node in Bone!");
114 NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
115 XmlRead(SkeletonFile);
116 if(string("axis")!=SkeletonFile->getNodeName())
117 throw DeadlyImportError("No axis specified for bone rotation!");
118 NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
119 NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
120 NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
122 //append the newly loaded bone to the bone list
123 Bones.push_back(NewBone);
125 //Proceed to the next bone:
126 XmlRead(SkeletonFile);
127 }
128 //The bones in the file a not neccesarly ordered by there id's so we do it now:
129 std::sort(Bones.begin(), Bones.end());
131 //now the id of each bone should be equal to its position in the vector:
132 //so we do a simple check:
133 {
134 bool IdsOk=true;
135 for(int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
136 {
137 if(Bones[i].Id!=i)
138 IdsOk=false;
139 }
140 if(!IdsOk)
141 throw DeadlyImportError("Bone Ids are not valid!"+FileName);
142 }
143 DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
144 //________________________________________________________________________________
151 //----------------------------load bonehierarchy--------------------------------
152 if(string("bonehierarchy")!=SkeletonFile->getNodeName())
153 throw DeadlyImportError("no bonehierarchy node in "+FileName);
155 DefaultLogger::get()->debug("loading bonehierarchy...");
156 XmlRead(SkeletonFile);
157 while(string("boneparent")==SkeletonFile->getNodeName())
158 {
159 string Child, Parent;
160 Child=GetAttribute<string>(SkeletonFile, "bone");
161 Parent=GetAttribute<string>(SkeletonFile, "parent");
163 unsigned int ChildId, ParentId;
164 ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
165 ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
167 Bones[ChildId].ParentId=ParentId;
168 Bones[ParentId].Children.push_back(ChildId);
170 XmlRead(SkeletonFile);
171 }
172 //_____________________________________________________________________________
175 //--------- Calculate the WorldToBoneSpace Matrix recursively for all bones: ------------------
176 BOOST_FOREACH(Bone &theBone, Bones)
177 {
178 if(-1==theBone.ParentId) //the bone is a root bone
179 {
180 theBone.CalculateBoneToWorldSpaceMatrix(Bones);
181 }
182 }
183 //_______________________________________________________________________
186 //---------------------------load animations-----------------------------
187 if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
188 {
189 DefaultLogger::get()->debug("Loading Animations");
190 XmlRead(SkeletonFile);
191 while(string("animation")==SkeletonFile->getNodeName())
192 {
193 Animation NewAnimation;
194 NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
195 NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
197 //Load all Tracks
198 XmlRead(SkeletonFile);
199 if(string("tracks")!=SkeletonFile->getNodeName())
200 throw DeadlyImportError("no tracks node in animation");
201 XmlRead(SkeletonFile);
202 while(string("track")==SkeletonFile->getNodeName())
203 {
204 Track NewTrack;
205 NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
207 //Load all keyframes;
208 XmlRead(SkeletonFile);
209 if(string("keyframes")!=SkeletonFile->getNodeName())
210 throw DeadlyImportError("no keyframes node!");
211 XmlRead(SkeletonFile);
212 while(string("keyframe")==SkeletonFile->getNodeName())
213 {
214 Keyframe NewKeyframe;
215 NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
217 //loop over the attributes:
219 while(true) //will quit, if a Node is not a animationkey
220 {
221 XmlRead(SkeletonFile);
223 //If any property doesn't show up, it will keep its initialization value
225 //Position:
226 if(string("translate")==SkeletonFile->getNodeName())
227 {
228 NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
229 NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
230 NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
231 }
233 //Rotation:
234 else if(string("rotate")==SkeletonFile->getNodeName())
235 {
236 float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
237 aiVector3D RotationAxis;
238 XmlRead(SkeletonFile);
239 if(string("axis")!=SkeletonFile->getNodeName())
240 throw DeadlyImportError("No axis for keyframe rotation!");
241 RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
242 RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
243 RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
245 if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis
246 {
247 RotationAxis.x=1.0f;
248 if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
249 {
250 DefaultLogger::get()->warn("Invalid Rotation Axis in Keyframe!");
251 }
252 }
253 NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
254 }
256 //Scaling:
257 else if(string("scale")==SkeletonFile->getNodeName())
258 {
259 NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
260 NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
261 NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
262 }
264 //we suppose, that we read all attributes and this is a new keyframe or the end of the animation
265 else
266 break;
267 }
269 NewTrack.Keyframes.push_back(NewKeyframe);
270 }
272 NewAnimation.Tracks.push_back(NewTrack);
273 }
275 Animations.push_back(NewAnimation);
276 }
277 }
278 //_____________________________________________________________________________
280 }
283 void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
284 {
285 if(!m_CurrentScene->mRootNode)
286 throw DeadlyImportError("No root node exists!!");
287 if(0!=m_CurrentScene->mRootNode->mNumChildren)
288 throw DeadlyImportError("Root Node already has childnodes!");
291 //Createt the assimp bone hierarchy
292 vector<aiNode*> RootBoneNodes;
293 BOOST_FOREACH(const Bone &theBone, Bones)
294 {
295 if(-1==theBone.ParentId) //the bone is a root bone
296 {
297 //which will recursily add all other nodes
298 RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));
299 }
300 }
302 if(RootBoneNodes.size() > 0)
303 {
304 m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
305 m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
306 memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
307 }
308 }
311 void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
312 {
313 //-----------------Create the Assimp Animations --------------------
314 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
315 {
316 m_CurrentScene->mNumAnimations=Animations.size();
317 m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
318 for(unsigned int i=0; i<Animations.size(); ++i)//create all animations
319 {
320 aiAnimation* NewAnimation=new aiAnimation();
321 NewAnimation->mName=Animations[i].Name;
322 NewAnimation->mDuration=Animations[i].Length;
323 NewAnimation->mTicksPerSecond=1.0f;
325 //Create all tracks in this animation
326 NewAnimation->mNumChannels=Animations[i].Tracks.size();
327 NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
328 for(unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
329 {
330 aiNodeAnim* NewNodeAnim=new aiNodeAnim();
331 NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
333 //we need this, to acces the bones default pose, which we need to make keys absolute to the default bone pose
334 vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
335 aiMatrix4x4 t0, t1;
336 aiMatrix4x4 DefBonePose=aiMatrix4x4::Translation(CurBone->Position, t1)
337 * aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0);
340 //Create the keyframe arrays...
341 unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
342 NewNodeAnim->mNumPositionKeys=KeyframeCount;
343 NewNodeAnim->mNumRotationKeys=KeyframeCount;
344 NewNodeAnim->mNumScalingKeys =KeyframeCount;
345 NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
346 NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
347 NewNodeAnim->mScalingKeys =new aiVectorKey[KeyframeCount];
349 //...and fill them
350 for(unsigned int k=0; k<KeyframeCount; ++k)
351 {
352 aiMatrix4x4 t2, t3;
354 //Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
355 aiMatrix4x4 PoseToKey=
356 aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3) //pos
357 * aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix()) //rot
358 * aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2); //scale
361 //calculate the complete transformation from world space to bone space
362 aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
364 aiVector3D Pos;
365 aiQuaternion Rot;
366 aiVector3D Scale;
368 CompleteTransform.Decompose(Scale, Rot, Pos);
370 double Time=Animations[i].Tracks[j].Keyframes[k].Time;
372 NewNodeAnim->mPositionKeys[k].mTime=Time;
373 NewNodeAnim->mPositionKeys[k].mValue=Pos;
375 NewNodeAnim->mRotationKeys[k].mTime=Time;
376 NewNodeAnim->mRotationKeys[k].mValue=Rot;
378 NewNodeAnim->mScalingKeys[k].mTime=Time;
379 NewNodeAnim->mScalingKeys[k].mValue=Scale;
380 }
382 NewAnimation->mChannels[j]=NewNodeAnim;
383 }
385 m_CurrentScene->mAnimations[i]=NewAnimation;
386 }
387 }
388 //TODO: Auf nicht vorhandene Animationskeys achten!
389 //#pragma warning (s.o.)
390 //__________________________________________________________________
391 }
394 aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode)
395 {
396 //----Create the node for this bone and set its values-----
397 aiNode* NewNode=new aiNode(Bones[BoneId].Name);
398 NewNode->mParent=ParentNode;
400 aiMatrix4x4 t0,t1;
401 NewNode->mTransformation=
402 aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
403 *aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
404 ;
405 //__________________________________________________________
408 //---------- recursivly create all children Nodes: ----------
409 NewNode->mNumChildren=Bones[BoneId].Children.size();
410 NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
411 for(unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
412 {
413 NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
414 }
415 //____________________________________________________
418 return NewNode;
419 }
422 void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
423 {
424 //Calculate the matrix for this bone:
426 aiMatrix4x4 t0,t1;
427 aiMatrix4x4 Transf= aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
428 * aiMatrix4x4::Translation(-Position, t0);
430 if(-1==ParentId)
431 {
432 BoneToWorldSpace=Transf;
433 }
434 else
435 {
436 BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
437 }
440 //and recursivly for all children:
441 BOOST_FOREACH(int theChildren, Children)
442 {
443 Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
444 }
445 }
448 }//namespace Ogre
449 }//namespace Assimp
451 #endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER