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