From d4d8c290db077ff50b00d93e0d742bc54d619526 Mon Sep 17 00:00:00 2001 From: jonathanklein Date: Sun, 13 Sep 2009 18:26:54 +0000 Subject: [PATCH] Ogre Importer now loads the skeleton, animation loading will be available soon git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@478 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/OgreImporter.cpp | 241 +++++++++++++++++++++++++++++++++++++++--- code/OgreImporter.h | 57 ++++++++++ doc/dox.h | 4 +- 3 files changed, 285 insertions(+), 17 deletions(-) diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index 8f7a60d03..198c6bf38 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -7,6 +7,7 @@ using namespace std; #include "boost/format.hpp" +#include "boost/foreach.hpp" using namespace boost; #include "OgreImporter.h" @@ -79,19 +80,28 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass DefaultLogger::get()->debug("Loading Submehs with Material: "+NewSubMesh.MaterialName); ReadSubMesh(NewSubMesh, MeshFile); } - //_______________________________________________________________- + //____________________________________________________________ - - //-----------------Read the skeleton:---------------------- - //Create the root node + //-----------------Create the root node----------------------- pScene->mRootNode=new aiNode("root"); //link the mesh with the root node: pScene->mRootNode->mMeshes=new unsigned int[1]; pScene->mRootNode->mMeshes[0]=0; pScene->mRootNode->mNumMeshes=1; - //_________________________________________________________ + //____________________________________________________________ + + + //----------------Load the skeleton: ------------------------------- + if(MeshFile->getNodeName()==string("skeletonlink")) + { + string SkeletonFile=GetAttribute(MeshFile, "name"); + LoadSkeleton(SkeletonFile); + } + //__________________________________________________________________ + + } @@ -113,11 +123,12 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) vector Positions; bool HasPositions=false; vector Normals; bool HasNormals=false; vector Uvs; unsigned int NumUvs=0;//nearly always 2d, but assimp has always 3d texcoords + vector< vector > Weights; XmlRead(Reader); - //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work korrekt, wenn the order + //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, wenn the order //of faces and geometry changed, and not if we habe more than one of one - while(Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry") + while(Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry" || Reader->getNodeName()==string("boneassignments")) { if(string(Reader->getNodeName())=="faces")//Read the face list { @@ -139,7 +150,7 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) FaceList.push_back(NewFace); } - } + }//end of faces else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata { //some info logging: @@ -199,16 +210,36 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) XmlRead(Reader); } - } + }//end of "geometry + else if(string(Reader->getNodeName())=="boneassignments") + { + Weights.resize(Positions.size()); + XmlRead(Reader); + while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment")) + { + Weight NewWeight; + unsigned int VertexId=GetAttribute(Reader, "vertexindex"); + NewWeight.BoneId=GetAttribute(Reader, "boneindex"); + NewWeight.Value=GetAttribute(Reader, "weight"); + + Weights[VertexId].push_back(NewWeight); + + XmlRead(Reader); + } + + }//end of boneassignments } DefaultLogger::get()->debug(str(format("Positionen: %1% Normale: %2% TexCoords: %3%") % Positions.size() % Normals.size() % Uvs.size())); + DefaultLogger::get()->debug(Reader->getNodeName()); - //Make all Vertexes unique: (this is required by assimp) + + //---------------Make all Vertexes unique: (this is required by assimp)----------------------- vector UniqueFaceList(FaceList.size()); vector UniquePositions(FaceList.size()*3);//*3 because each face consits of 3 vertexes, because we only support triangles^^ vector UniqueNormals(FaceList.size()*3); vector UniqueUvs(FaceList.size()*3); + vector< vector > UniqueWeights(FaceList.size()*3); for(unsigned int i=0; imNumMeshes!=0) throw new ImportErrorException("Currently only one mesh per File is allowed!!"); @@ -255,6 +292,11 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) NewAiMesh->mTextureCoords[0]= new aiVector3D[UniqueUvs.size()]; memcpy(NewAiMesh->mTextureCoords[0], &UniqueUvs[0], UniqueUvs.size()*sizeof(aiVector3D)); + //Bones + + + + //Faces NewAiMesh->mFaces=new aiFace[UniqueFaceList.size()]; for(unsigned int i=0; imMaterials=new aiMaterial*[1]; m_CurrentScene->mNumMaterials=1; m_CurrentScene->mMaterials[0]=MeshMat; - //________________________________________________________________ + //_____________________________________________________________________________ //Attach the mesh to the scene: m_CurrentScene->mNumMeshes=1; m_CurrentScene->mMeshes=new aiMesh*; m_CurrentScene->mMeshes[0]=NewAiMesh; - - - //stringstream ss; ss <<"Last Node: <" << Reader->getNodeName() << ">"; - //throw new ImportErrorException(ss.str()); } aiMaterial* OgreImporter::LoadMaterial(std::string MaterialName) @@ -447,6 +485,179 @@ aiMaterial* OgreImporter::LoadMaterial(std::string MaterialName) return NewMaterial; } +void OgreImporter::LoadSkeleton(std::string FileName) +{ + //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 new ImportErrorException("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 new ImportErrorException(string("Failed to create XML Reader for ")+FileName); + + //Variables to store the data from the skeleton file: + vector Bones; + + //Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!! + + XmlRead(SkeletonFile); + if(string("skeleton")!=SkeletonFile->getNodeName()) + throw new ImportErrorException("No node in SkeletonFile: "+FileName); + + //load bones + XmlRead(SkeletonFile); + if(string("bones")==SkeletonFile->getNodeName()) + { + XmlRead(SkeletonFile); + while(string("bone")==SkeletonFile->getNodeName()) + { + //TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appera, 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 new ImportErrorException("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 new ImportErrorException("Rotation is not the second node in Bone!"); + NewBone.RotationAngle=GetAttribute(SkeletonFile, "angle"); + XmlRead(SkeletonFile); + if(string("axis")!=SkeletonFile->getNodeName()) + throw new ImportErrorException("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: + 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 new ImportErrorException("Bone Ids are not valid!"+FileName); + } + + DefaultLogger::get()->debug(str(format("Number of bones: %1%") % Bones.size())); + + + + + + + //load bonehierarchy + if(string("bonehierarchy")==SkeletonFile->getNodeName()) + { + 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?? + } + } + + + + + //load animations + + + + + + + //-----------------skeleton is completly loaded, now but it in the assimp scene:------------------------------- + + if(!m_CurrentScene->mRootNode) + throw new ImportErrorException("No root node exists!!"); + if(0!=m_CurrentScene->mRootNode->mNumChildren) + throw new ImportErrorException("Root Node already has childnodes!"); + + //create a node for all root bones: + DefaultLogger::get()->debug("Root Bones"); + vector RootBoneNodes; + BOOST_FOREACH(Bone theBone, Bones) + { + if(-1==theBone.ParentId) //the bone is a root bone + { + DefaultLogger::get()->debug(theBone.Name); + RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode)); + } + } + 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()); +} + +aiNode* CreateAiNodeFromBone(int BoneId, 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; + //create a matrix from the transformation values of the ogre bone + NewNode->mTransformation=aiMatrix4x4::Translation(Bones[BoneId].Position, aiMatrix4x4()) + * + aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, aiMatrix4x4()) + ; + //__________________________________________________________ + + + //----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; +} + + }//namespace Ogre }//namespace Assimp diff --git a/code/OgreImporter.h b/code/OgreImporter.h index afb449e82..e96b4a4ff 100644 --- a/code/OgreImporter.h +++ b/code/OgreImporter.h @@ -14,7 +14,12 @@ typedef irr::io::IrrXMLReader XmlReader; //Forward declarations: struct Face; +struct Weight; struct SubMesh; +struct Bone; +struct Animation; +struct Track; +struct Keyframe; ///The Main Ogre Importer Class class OgreImporter : public BaseImporter @@ -30,6 +35,7 @@ private: /** @param Filename We need this to check for a material File with the same name.*/ void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader); aiMaterial* LoadMaterial(std::string MaterialName); + void LoadSkeleton(std::string FileName); //Now we don't have to give theses parameters to all functions std::string m_CurrentFilename; @@ -110,6 +116,12 @@ struct Face unsigned int VertexIndices[3]; }; +struct Weight +{ + unsigned int BoneId; + float Value; +}; + /// Helper Class to describe a complete SubMesh struct SubMesh { @@ -118,5 +130,50 @@ struct SubMesh std::vector Faces; }; + +/// Helper Class to describe an ogre-bone +/** All Id's are signed ints, because than we have -1 as a simple INVALID_ID Value (we start from 0 so 0 is a valid bone ID!*/ +struct Bone +{ + int Id; + int ParentId; + std::string Name; + aiVector3D Position; + float RotationAngle; + aiVector3D RotationAxis; + std::vector Children; + + ///ctor + Bone(): Id(-1), ParentId(-1), RotationAngle(0.0f) {} + ///this operator is needed to sort the bones after Id's + bool operator<(const Bone& rval) + {return Id + bool operator==(const std::string& rval) + {return Name==rval; } + +}; + +///Recursivly creates a filled aiNode from a given root bone +aiNode* CreateAiNodeFromBone(int BoneId, std::vector Bones, aiNode* ParentNode); + + +///Describes an Ogre Animation +struct Animation +{ + std::string Name; + float Length; +}; + +///a track (keyframes for one bone) from an animation +struct Track +{ +}; + +/// keyframe (bone transformation) from a track from a animation +struct Keyframe +{ +}; + }//namespace Ogre }//namespace Assimp \ No newline at end of file diff --git a/doc/dox.h b/doc/dox.h index d65afe9ad..7984ceee6 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -1440,11 +1440,11 @@ If you want more propertiesin custom materials, you can easily expand the ogre m What will be loaded? -Mesh: Faces, Positions, Normals and one Uv pair. The Materialname will be used to load the material +Mesh: Faces, Positions, Normals and one Uv pair. The Materialname will be used to load the material. No Bone-Assignments yet. Material: The right material in the file will be searched, the importer should work with materials who have 1 technique and 1 pass in this technique. From there, the texturename (for 1 color- and 1 normalmap) will be loaded. Also, the materialname will be set. -Skeleton: Nothing, yet. +Skeleton: Skeleton with Bone hierarchie, names and transformations, but no animations. */ \ No newline at end of file