/* Open Asset Import Library (assimp) ---------------------------------------------------------------------- Copyright (c) 2006-2012, assimp team All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the assimp team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the assimp team. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER #include "OgreImporter.hpp" #include "TinyFormatter.h" using namespace std; namespace Assimp { namespace Ogre { void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vector &Animations) const { const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene (void)m_CurrentScene; //most likely the skeleton file will only end with .skeleton //But this is a xml reader, so we need: .skeleton.xml FileName+=".xml"; DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName); //Open the File: boost::scoped_ptr File(m_CurrentIOHandler->Open(FileName)); if(NULL==File.get()) throw DeadlyImportError("Failed to open skeleton file "+FileName+"."); //Read the Mesh File: boost::scoped_ptr mIOWrapper(new CIrrXML_IOStreamReader(File.get())); XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get()); if(!SkeletonFile) throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName); //Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!! XmlRead(SkeletonFile); if(string("skeleton")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No node in SkeletonFile: "+FileName); //------------------------------------load bones----------------------------------------- XmlRead(SkeletonFile); if(string("bones")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No bones node in skeleton "+FileName); XmlRead(SkeletonFile); while(string("bone")==SkeletonFile->getNodeName()) { //TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what.... //read a new bone: Bone NewBone; NewBone.Id=GetAttribute(SkeletonFile, "id"); NewBone.Name=GetAttribute(SkeletonFile, "name"); //load the position: XmlRead(SkeletonFile); if(string("position")!=SkeletonFile->getNodeName()) throw DeadlyImportError("Position is not first node in Bone!"); NewBone.Position.x=GetAttribute(SkeletonFile, "x"); NewBone.Position.y=GetAttribute(SkeletonFile, "y"); NewBone.Position.z=GetAttribute(SkeletonFile, "z"); //Rotation: XmlRead(SkeletonFile); if(string("rotation")!=SkeletonFile->getNodeName()) throw DeadlyImportError("Rotation is not the second node in Bone!"); NewBone.RotationAngle=GetAttribute(SkeletonFile, "angle"); XmlRead(SkeletonFile); if(string("axis")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No axis specified for bone rotation!"); NewBone.RotationAxis.x=GetAttribute(SkeletonFile, "x"); NewBone.RotationAxis.y=GetAttribute(SkeletonFile, "y"); NewBone.RotationAxis.z=GetAttribute(SkeletonFile, "z"); //append the newly loaded bone to the bone list Bones.push_back(NewBone); //Proceed to the next bone: XmlRead(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()); //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(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!"+FileName); } DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size())); //________________________________________________________________________________ //----------------------------load bonehierarchy-------------------------------- if(string("bonehierarchy")!=SkeletonFile->getNodeName()) throw DeadlyImportError("no bonehierarchy node in "+FileName); DefaultLogger::get()->debug("loading bonehierarchy..."); XmlRead(SkeletonFile); while(string("boneparent")==SkeletonFile->getNodeName()) { string Child, Parent; Child=GetAttribute(SkeletonFile, "bone"); Parent=GetAttribute(SkeletonFile, "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); XmlRead(SkeletonFile);//I once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks?? } //_____________________________________________________________________________ //--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------ BOOST_FOREACH(Bone &theBone, Bones) { if(-1==theBone.ParentId) //the bone is a root bone { theBone.CalculateBoneToWorldSpaceMatrix(Bones); } } //_______________________________________________________________________ //---------------------------load animations----------------------------- if(string("animations")==SkeletonFile->getNodeName())//animations are optional values { DefaultLogger::get()->debug("Loading Animations"); XmlRead(SkeletonFile); while(string("animation")==SkeletonFile->getNodeName()) { Animation NewAnimation; NewAnimation.Name=GetAttribute(SkeletonFile, "name"); NewAnimation.Length=GetAttribute(SkeletonFile, "length"); //Load all Tracks XmlRead(SkeletonFile); if(string("tracks")!=SkeletonFile->getNodeName()) throw DeadlyImportError("no tracks node in animation"); XmlRead(SkeletonFile); while(string("track")==SkeletonFile->getNodeName()) { Track NewTrack; NewTrack.BoneName=GetAttribute(SkeletonFile, "bone"); //Load all keyframes; XmlRead(SkeletonFile); if(string("keyframes")!=SkeletonFile->getNodeName()) throw DeadlyImportError("no keyframes node!"); XmlRead(SkeletonFile); while(string("keyframe")==SkeletonFile->getNodeName()) { Keyframe NewKeyframe; NewKeyframe.Time=GetAttribute(SkeletonFile, "time"); //loop over the attributes: while(true) //will quit, if a Node is not a animationkey { XmlRead(SkeletonFile); //If any property doesn't show up, it will keep its initialization value //Position: if(string("translate")==SkeletonFile->getNodeName()) { NewKeyframe.Position.x=GetAttribute(SkeletonFile, "x"); NewKeyframe.Position.y=GetAttribute(SkeletonFile, "y"); NewKeyframe.Position.z=GetAttribute(SkeletonFile, "z"); } //Rotation: else if(string("rotate")==SkeletonFile->getNodeName()) { float RotationAngle=GetAttribute(SkeletonFile, "angle"); aiVector3D RotationAxis; XmlRead(SkeletonFile); if(string("axis")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No axis for keyframe rotation!"); RotationAxis.x=GetAttribute(SkeletonFile, "x"); RotationAxis.y=GetAttribute(SkeletonFile, "y"); RotationAxis.z=GetAttribute(SkeletonFile, "z"); if(0==RotationAxis.x && 0==RotationAxis.y && 0==RotationAxis.z)//we have an invalid rotation axis { 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!"); } } NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle); } //Scaling: else if(string("scale")==SkeletonFile->getNodeName()) { NewKeyframe.Scaling.x=GetAttribute(SkeletonFile, "x"); NewKeyframe.Scaling.y=GetAttribute(SkeletonFile, "y"); NewKeyframe.Scaling.z=GetAttribute(SkeletonFile, "z"); } //we suppose, that we read all attributes and this is a new keyframe or the end of the animation else break; } NewTrack.Keyframes.push_back(NewKeyframe); } NewAnimation.Tracks.push_back(NewTrack); } Animations.push_back(NewAnimation); } } //_____________________________________________________________________________ } void OgreImporter::CreateAssimpSkeleton(const std::vector &Bones, const std::vector &/*Animations*/) { if(!m_CurrentScene->mRootNode) throw DeadlyImportError("No root node exists!!"); if(0!=m_CurrentScene->mRootNode->mNumChildren) throw DeadlyImportError("Root Node already has childnodes!"); //Createt the assimp bone hierarchy vector RootBoneNodes; BOOST_FOREACH(const Bone &theBone, Bones) { if(-1==theBone.ParentId) //the bone is a root bone { //which will recursily add all other nodes RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode)); } } if(RootBoneNodes.size() > 0) { m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size(); m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()]; memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size()); } } void OgreImporter::PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations) { //-----------------Create the Assimp Animations -------------------- 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 { m_CurrentScene->mNumAnimations=Animations.size(); m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()]; for(unsigned int i=0; imName=Animations[i].Name; NewAnimation->mDuration=Animations[i].Length; NewAnimation->mTicksPerSecond=1.0f; //Create all tracks in this animation NewAnimation->mNumChannels=Animations[i].Tracks.size(); NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()]; for(unsigned int j=0; jmNodeName=Animations[i].Tracks[j].BoneName; //we need this, to acces the bones default pose, which we need to make keys absolute to the default bone pose vector::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName); aiMatrix4x4 t0, t1; aiMatrix4x4 DefBonePose=aiMatrix4x4::Translation(CurBone->Position, t1) * aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0); //Create the keyframe arrays... unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size(); NewNodeAnim->mNumPositionKeys=KeyframeCount; NewNodeAnim->mNumRotationKeys=KeyframeCount; NewNodeAnim->mNumScalingKeys =KeyframeCount; NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount]; NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount]; NewNodeAnim->mScalingKeys =new aiVectorKey[KeyframeCount]; //...and fill them for(unsigned int k=0; kmPositionKeys[k].mTime=Time; NewNodeAnim->mPositionKeys[k].mValue=Pos; NewNodeAnim->mRotationKeys[k].mTime=Time; NewNodeAnim->mRotationKeys[k].mValue=Rot; NewNodeAnim->mScalingKeys[k].mTime=Time; NewNodeAnim->mScalingKeys[k].mValue=Scale; } NewAnimation->mChannels[j]=NewNodeAnim; } m_CurrentScene->mAnimations[i]=NewAnimation; } } //TODO: Auf nicht vorhandene Animationskeys achten! //#pragma warning (s.o.) //__________________________________________________________________ } aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode) { //----Create the node for this bone and set its values----- aiNode* NewNode=new aiNode(Bones[BoneId].Name); NewNode->mParent=ParentNode; aiMatrix4x4 t0,t1; NewNode->mTransformation= aiMatrix4x4::Translation(Bones[BoneId].Position, t0) *aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1) ; //__________________________________________________________ //---------- recursivly create all children Nodes: ---------- NewNode->mNumChildren=Bones[BoneId].Children.size(); NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()]; for(unsigned int i=0; imChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode); } //____________________________________________________ return NewNode; } void Bone::CalculateBoneToWorldSpaceMatrix(vector &Bones) { //Calculate the matrix for this bone: aiMatrix4x4 t0,t1; aiMatrix4x4 Transf= aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) * aiMatrix4x4::Translation(-Position, t0); if(-1==ParentId) { BoneToWorldSpace=Transf; } else { BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace; } //and recursivly for all children: BOOST_FOREACH(int theChildren, Children) { Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones); } } }//namespace Ogre }//namespace Assimp #endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER