diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 7009bc8cf..6bb06ae66 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -329,7 +329,9 @@ SET( Ogre_SRCS OgreImporter.hpp OgreXmlHelper.hpp OgreImporter.cpp - OgreImporterMaterial.cpp + OgreMaterial.cpp + OgreMesh.cpp + OgreSkeleton.cpp ) SOURCE_GROUP( Ogre FILES ${Ogre_SRCS}) diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index d45566ecf..13fd3b9e6 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -48,16 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include using namespace std; -//#include "boost/format.hpp" -//#include "boost/foreach.hpp" -//using namespace boost; - -#include "TinyFormatter.h" - #include "OgreImporter.hpp" +#include "TinyFormatter.h" #include "irrXMLWrapper.h" - namespace Assimp { namespace Ogre @@ -106,8 +100,21 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass throw DeadlyImportError("Root Node is not ! "+pFile+" "+MeshFile->getNodeName()); } + //eventually load shared geometry + XmlRead(MeshFile);//shared geometry is optional, so we need a reed for the next two if's + if(MeshFile->getNodeName()==string("sharedgeometry")) + { + unsigned int NumVertices=GetAttribute(MeshFile, "vertexcount");; + + XmlRead(MeshFile); + while(MeshFile->getNodeName()==string("vertexbuffer")) + { + ReadVertexBuffer(m_SharedGeometry, MeshFile, NumVertices); + } + } + //Go to the submeshs: - if(!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="submeshes")) + if(MeshFile->getNodeName()!=string("submeshes")) { throw DeadlyImportError("No node in node! "+pFile); } @@ -209,793 +216,6 @@ void OgreImporter::SetupProperties(const Importer* pImp) } -void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) -{ - XmlRead(Reader); - //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order - //of faces and geometry changed, and not if we have more than one of one - while(Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry" || Reader->getNodeName()==string("boneassignments")) - { - if(string(Reader->getNodeName())=="faces")//Read the face list - { - //some info logging: - unsigned int NumFaces=GetAttribute(Reader, "count"); - ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces."; - DefaultLogger::get()->debug(ss.str()); - - while(XmlRead(Reader) && Reader->getNodeName()==string("face")) - { - Face NewFace; - NewFace.VertexIndices[0]=GetAttribute(Reader, "v1"); - NewFace.VertexIndices[1]=GetAttribute(Reader, "v2"); - NewFace.VertexIndices[2]=GetAttribute(Reader, "v3"); - if(Reader->getAttributeValue("v4"))//this should be supported in the future - { - throw DeadlyImportError("Submesh has quads, only traingles are supported!"); - } - theSubMesh.FaceList.push_back(NewFace); - } - - }//end of faces - else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata - { - //some info logging: - unsigned int NumVertices=GetAttribute(Reader, "vertexcount"); - ostringstream ss; ss<<"VertexCount: " << NumVertices; - DefaultLogger::get()->debug(ss.str()); - - //General Informations about vertices - XmlRead(Reader); - while(Reader->getNodeName()==string("vertexbuffer")) - { - ReadVertexBuffer(theSubMesh, Reader, NumVertices); - } - - //some error checking on the loaded data - if(!theSubMesh.HasPositions) - throw DeadlyImportError("No positions could be loaded!"); - - if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices) - throw DeadlyImportError("Wrong Number of Normals loaded!"); - - if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices) - throw DeadlyImportError("Wrong Number of Tangents loaded!"); - - if(theSubMesh.NumUvs==1 && theSubMesh.Uvs.size() != NumVertices) - throw DeadlyImportError("Wrong Number of Uvs loaded!"); - - }//end of "geometry - - - else if(string(Reader->getNodeName())=="boneassignments") - { - theSubMesh.Weights.resize(theSubMesh.Positions.size()); - 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"); - theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) - - theSubMesh.Weights[VertexId].push_back(NewWeight); - - //XmlRead(Reader);//Once i had this line, and than i got only every second boneassignment, but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!! - } - - }//end of boneassignments - } - DefaultLogger::get()->debug((Formatter::format(), - "Positionen: ",theSubMesh.Positions.size(), - " Normale: ",theSubMesh.Normals.size(), - " TexCoords: ",theSubMesh.Uvs.size(), - " Tantents: ",theSubMesh.Tangents.size() - )); - DefaultLogger::get()->warn(Reader->getNodeName()); - - - - //---------------Make all Vertexes unique: (this is required by assimp)----------------------- - vector UniqueFaceList(theSubMesh.FaceList.size()); - unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^ - vector UniquePositions(UniqueVertexCount); - vector UniqueNormals(UniqueVertexCount); - vector UniqueTangents(UniqueVertexCount); - vector UniqueUvs(UniqueVertexCount); - vector< vector > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0)); - - for(unsigned int i=0; i1.0f+0.05f) - { - //normalize all weights: - for(unsigned int BoneId=0; BoneIddebug("new Vertex Buffer"); - - bool ReadPositions=false; - bool ReadNormals=false; - bool ReadTangents=false; - bool ReadUvs=false; - - //-------------------- check, what we need to read: -------------------------------- - if(Reader->getAttributeValue("positions") && GetAttribute(Reader, "positions")) - { - ReadPositions=theSubMesh.HasPositions=true; - theSubMesh.Positions.reserve(NumVertices); - DefaultLogger::get()->debug("reading positions"); - } - if(Reader->getAttributeValue("normals") && GetAttribute(Reader, "normals")) - { - ReadNormals=theSubMesh.HasNormals=true; - theSubMesh.Normals.reserve(NumVertices); - DefaultLogger::get()->debug("reading positions"); - } - if(Reader->getAttributeValue("tangents") && GetAttribute(Reader, "tangents")) - { - ReadTangents=theSubMesh.HasTangents=true; - theSubMesh.Tangents.reserve(NumVertices); - DefaultLogger::get()->debug("reading positions"); - } - - - //we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute - if(!Reader->getAttributeValue("texture_coords")) - theSubMesh.NumUvs=0; - else - { - ReadUvs=!!(theSubMesh.NumUvs=GetAttribute(Reader, "texture_coords")); - theSubMesh.Uvs.reserve(NumVertices); - DefaultLogger::get()->debug("reading texture coords"); - } - if(theSubMesh.NumUvs>1) - { - DefaultLogger::get()->warn("too many texcoords (just 1 supported!), just the first texcoords will be loaded!"); - theSubMesh.NumUvs=1; - } - //___________________________________________________________________ - - - //check if we will load anything - if(!(ReadPositions || ReadNormals || ReadTangents || ReadUvs)) - DefaultLogger::get()->warn("vertexbuffer seams to be empty!"); - - - //read all the vertices: - XmlRead(Reader); - - /*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers) - so the break condition is a bit tricky (well, IrrXML just sucks :( )*/ - while(Reader->getNodeName()==string("vertex") - ||Reader->getNodeName()==string("position") - ||Reader->getNodeName()==string("normal") - ||Reader->getNodeName()==string("tangent") - ||Reader->getNodeName()==string("texcoord")) - { - if(Reader->getNodeName()==string("vertex")) - XmlRead(Reader);//Read an attribute tag - - //Position - if(ReadPositions && Reader->getNodeName()==string("position")) - { - aiVector3D NewPos; - NewPos.x=GetAttribute(Reader, "x"); - NewPos.y=GetAttribute(Reader, "y"); - NewPos.z=GetAttribute(Reader, "z"); - theSubMesh.Positions.push_back(NewPos); - } - - //Normal - else if(ReadNormals && Reader->getNodeName()==string("normal")) - { - aiVector3D NewNormal; - NewNormal.x=GetAttribute(Reader, "x"); - NewNormal.y=GetAttribute(Reader, "y"); - NewNormal.z=GetAttribute(Reader, "z"); - theSubMesh.Normals.push_back(NewNormal); - } - - //Tangent - else if(ReadTangents && Reader->getNodeName()==string("tangent")) - { - aiVector3D NewTangent; - NewTangent.x=GetAttribute(Reader, "x"); - NewTangent.y=GetAttribute(Reader, "y"); - NewTangent.z=GetAttribute(Reader, "z"); - theSubMesh.Tangents.push_back(NewTangent); - } - - //Uv: - else if(ReadUvs && Reader->getNodeName()==string("texcoord")) - { - aiVector3D NewUv; - NewUv.x=GetAttribute(Reader, "u"); - NewUv.y=GetAttribute(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so! - theSubMesh.Uvs.push_back(NewUv); - - //skip all the following texcoords: - while(Reader->getNodeName()==string("texcoord")) - XmlRead(Reader); - continue;//don't read another line at the end of the loop - } - - //Attribute could not be read - else - { - DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName()); - } - - XmlRead(Reader);//Read the Vertex tag - } -} - - -aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector& Bones) const -{ - const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene - (void)m_CurrentScene; - - aiMesh* NewAiMesh=new aiMesh(); - - //Positions - NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()]; - memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D)); - NewAiMesh->mNumVertices=theSubMesh.Positions.size(); - - //Normals - if(theSubMesh.HasNormals) - { - NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; - memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); - } - - - //until we have support for bitangents, no tangents will be written - /* - //Tangents - if(theSubMesh.HasTangents) - { - NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()]; - memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D)); - } - */ - - //Uvs - if(0!=theSubMesh.NumUvs) - { - NewAiMesh->mNumUVComponents[0]=2; - NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()]; - memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D)); - } - - - //---------------------------------------- Bones -------------------------------------------- - - //Copy the weights in in Bone-Vertices Struktur - //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp - vector< vector > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices - for(unsigned int VertexId=0; VertexId aiBones; - aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex) - - //create all the bones and fill them with informations - for(unsigned int i=0; i0) - { - aiBone* NewBone=new aiBone(); - NewBone->mNumWeights=aiWeights[i].size(); - NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()]; - memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size()); - NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton - NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace; - - aiBones.push_back(NewBone); - } - } - NewAiMesh->mNumBones=aiBones.size(); - - // mBones must be NULL if mNumBones is non 0 or the validation fails. - if (aiBones.size()) { - NewAiMesh->mBones=new aiBone* [aiBones.size()]; - memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*)); - } - - //______________________________________________________________________________________________________ - - - - //Faces - NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()]; - for(unsigned int i=0; imFaces[i].mNumIndices=3; - NewAiMesh->mFaces[i].mIndices=new unsigned int[3]; - - NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0]; - NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1]; - NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2]; - } - NewAiMesh->mNumFaces=theSubMesh.FaceList.size(); - - //Link the material: - NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh - - return NewAiMesh; -} - - -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) - { - 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"); - 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); - XmlRead(SkeletonFile); - } - - - 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 - 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));//which will recursily add all other nodes - } - } - - if (RootBoneNodes.size()) { - 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 - vector::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName); - aiMatrix4x4 t0, t1; - aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value - aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0) - * aiMatrix4x4::Translation(CurBone->Position, t1); - - //Create the keyframe arrays... - unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size(); - NewNodeAnim->mNumPositionKeys=KeyframeCount; - NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount]; - NewNodeAnim->mNumRotationKeys=KeyframeCount; - NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount]; - NewNodeAnim->mNumScalingKeys=KeyframeCount; - NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount]; - - //...and fill them - for(unsigned int k=0; kmPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time; - NewNodeAnim->mPositionKeys[k].mValue=Pos; - - NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time; - NewNodeAnim->mRotationKeys[k].mValue=Rot; - - NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].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; - //create a matrix from the transformation values of the ogre bone - NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1) - * aiMatrix4x4::Translation(Bones[BoneId].Position, t0) - - ; - //__________________________________________________________ - - - //---------- 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::Translation(-Position, t0) - * aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) - ; - 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 diff --git a/code/OgreImporter.hpp b/code/OgreImporter.hpp index eaff1d647..baa9a7f15 100644 --- a/code/OgreImporter.hpp +++ b/code/OgreImporter.hpp @@ -12,7 +12,6 @@ namespace Ogre //Forward declarations: -struct SubMesh; struct Face; struct Weight; struct Bone; @@ -20,6 +19,27 @@ struct Animation; struct Track; struct Keyframe; +///A submesh from Ogre +struct SubMesh +{ + std::string Name; + std::string MaterialName; + std::vector FaceList; + + std::vector Positions; bool HasPositions; + std::vector Normals; bool HasNormals; + std::vector Tangents; bool HasTangents; + std::vector Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords + + std::vector< std::vector > Weights;//a list of bones for each vertex + int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh + unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices) + + SubMesh(): HasPositions(false), HasNormals(false), HasTangents(false), + NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything +}; + + ///The Main Ogre Importer Class class OgreImporter : public BaseImporter { @@ -59,26 +79,7 @@ private: std::string m_MaterialLibFilename; IOSystem* m_CurrentIOHandler; aiScene *m_CurrentScene; -}; - -///A submesh from Ogre -struct SubMesh -{ - std::string Name; - std::string MaterialName; - std::vector FaceList; - - std::vector Positions; bool HasPositions; - std::vector Normals; bool HasNormals; - std::vector Tangents; bool HasTangents; - std::vector Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords - - std::vector< std::vector > Weights;//a list of bones for each vertex - int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh - unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices) - - SubMesh(): HasPositions(false), HasNormals(false), HasTangents(false), - NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything + SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh }; ///For the moment just triangles, no other polygon types! diff --git a/code/OgreImporterMaterial.cpp b/code/OgreMaterial.cpp similarity index 94% rename from code/OgreImporterMaterial.cpp rename to code/OgreMaterial.cpp index e6b5e93e8..1de8a4fd1 100644 --- a/code/OgreImporterMaterial.cpp +++ b/code/OgreMaterial.cpp @@ -172,7 +172,10 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const ss >> Line; if(Line!="{") - throw DeadlyImportError("empty material!"); + { + DefaultLogger::get()->warn("empyt material!"); + return NULL; + } while(Line!="}")//read until the end of the material { @@ -273,7 +276,10 @@ void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial) string Line; ss >> Line; if(Line!="{") - throw DeadlyImportError("empty technique!"); + { + DefaultLogger::get()->warn("empty technique!"); + return; + } while(Line!="}")//read until the end of the technique { ss >> Line; @@ -281,7 +287,10 @@ void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial) { ss >> Line; if(Line!="{") - throw DeadlyImportError("empty pass!"); + { + DefaultLogger::get()->warn("empty pass!"); + return; + } while(Line!="}")//read until the end of the pass { ss >> Line; diff --git a/code/OgreMesh.cpp b/code/OgreMesh.cpp new file mode 100644 index 000000000..6c10a5462 --- /dev/null +++ b/code/OgreMesh.cpp @@ -0,0 +1,485 @@ +/* +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::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) +{ + //see, if we use shared vertices + bool bSharedData=false; + if(Reader->getAttributeValue("usesharedvertices")) + bSharedData=GetAttribute(Reader, "usesharedvertices"); + + XmlRead(Reader); + //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order + //of faces and geometry changed, and not if we have more than one of one + while( Reader->getNodeName()==string("faces") + || Reader->getNodeName()==string("geometry") + || Reader->getNodeName()==string("boneassignments")) + { + if(string(Reader->getNodeName())=="faces")//Read the face list + { + //some info logging: + unsigned int NumFaces=GetAttribute(Reader, "count"); + ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces."; + DefaultLogger::get()->debug(ss.str()); + + while(XmlRead(Reader) && Reader->getNodeName()==string("face")) + { + Face NewFace; + NewFace.VertexIndices[0]=GetAttribute(Reader, "v1"); + NewFace.VertexIndices[1]=GetAttribute(Reader, "v2"); + NewFace.VertexIndices[2]=GetAttribute(Reader, "v3"); + if(Reader->getAttributeValue("v4"))//this should be supported in the future + { + DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!"); + //throw DeadlyImportError("Submesh has quads, only traingles are supported!"); + } + theSubMesh.FaceList.push_back(NewFace); + } + + }//end of faces + else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata + { + //some info logging: + unsigned int NumVertices=GetAttribute(Reader, "vertexcount"); + ostringstream ss; ss<<"VertexCount: " << NumVertices; + DefaultLogger::get()->debug(ss.str()); + + //General Informations about vertices + XmlRead(Reader); + while(Reader->getNodeName()==string("vertexbuffer")) + { + ReadVertexBuffer(theSubMesh, Reader, NumVertices); + } + + //some error checking on the loaded data + if(!theSubMesh.HasPositions) + throw DeadlyImportError("No positions could be loaded!"); + + if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices) + throw DeadlyImportError("Wrong Number of Normals loaded!"); + + if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices) + throw DeadlyImportError("Wrong Number of Tangents loaded!"); + + if(theSubMesh.NumUvs==1 && theSubMesh.Uvs.size() != NumVertices) + throw DeadlyImportError("Wrong Number of Uvs loaded!"); + + }//end of "geometry + + + else if(string(Reader->getNodeName())=="boneassignments") + { + theSubMesh.Weights.resize(theSubMesh.Positions.size()); + 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"); + theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) + + theSubMesh.Weights[VertexId].push_back(NewWeight); + + //Once i had this line, and than i got only every second boneassignment, + //but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!! + //XmlRead(Reader); + } + + }//end of boneassignments + } + DefaultLogger::get()->debug((Formatter::format(), + "Positionen: ",theSubMesh.Positions.size(), + " Normale: ",theSubMesh.Normals.size(), + " TexCoords: ",theSubMesh.Uvs.size(), + " Tantents: ",theSubMesh.Tangents.size() + )); + DefaultLogger::get()->warn(Reader->getNodeName()); + + + + //---------------Make all Vertexes unique: (this is required by assimp)----------------------- + vector UniqueFaceList(theSubMesh.FaceList.size()); + unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^ + vector UniquePositions(UniqueVertexCount); + vector UniqueNormals(UniqueVertexCount); + vector UniqueTangents(UniqueVertexCount); + vector UniqueUvs(UniqueVertexCount); + vector< vector > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0)); + + //Support for shared data: + /*We can use this loop to copy vertex informations from the shared data pool. In order to do so + we just use a reference to a submodel instead of our submodel itself*/ + + SubMesh& VertexSource= bSharedData ? m_SharedGeometry : theSubMesh; + + if(VertexSource.NumUvs > 0) + { + DefaultLogger::get()->error("Not all Uvs will be made unique!"); + } + + for(unsigned int i=0; i 0) + { + UniqueUvs[3*i+0]=VertexSource.Uvs[Vertex1]; + UniqueUvs[3*i+1]=VertexSource.Uvs[Vertex2]; + UniqueUvs[3*i+2]=VertexSource.Uvs[Vertex3]; + } + + if(VertexSource.Weights.size()) + { + //I don't think, that bone assinements can be shared, but who knows? + UniqueWeights[3*i+0]=VertexSource.Weights[Vertex1]; + UniqueWeights[3*i+1]=VertexSource.Weights[Vertex2]; + UniqueWeights[3*i+2]=VertexSource.Weights[Vertex3]; + } + + //The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...) + UniqueFaceList[i].VertexIndices[0]=3*i+0; + UniqueFaceList[i].VertexIndices[1]=3*i+1; + UniqueFaceList[i].VertexIndices[2]=3*i+2; + } + //_________________________________________________________________________________________ + + //now we have the unique datas, but want them in the SubMesh, so we swap all the containers: + //if we don't have one of them, we just swap empty containers, so everything is ok + theSubMesh.FaceList.swap(UniqueFaceList); + theSubMesh.Positions.swap(UniquePositions); + theSubMesh.Normals.swap(UniqueNormals); + theSubMesh.Tangents.swap(UniqueTangents); + theSubMesh.Uvs.swap(UniqueUvs); + theSubMesh.Weights.swap(UniqueWeights); + + //------------- normalize weights ----------------------------- + //The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not, + //so we have to make this sure: + for(unsigned int VertexId=0; VertexId1.0f+0.05f) + { + //normalize all weights: + for(unsigned int BoneId=0; BoneIddebug("new Vertex Buffer"); + + bool ReadPositions=false; + bool ReadNormals=false; + bool ReadTangents=false; + bool ReadUvs=false; + + //-------------------- check, what we need to read: -------------------------------- + if(Reader->getAttributeValue("positions") && GetAttribute(Reader, "positions")) + { + ReadPositions=theSubMesh.HasPositions=true; + theSubMesh.Positions.reserve(NumVertices); + DefaultLogger::get()->debug("reading positions"); + } + if(Reader->getAttributeValue("normals") && GetAttribute(Reader, "normals")) + { + ReadNormals=theSubMesh.HasNormals=true; + theSubMesh.Normals.reserve(NumVertices); + DefaultLogger::get()->debug("reading normals"); + } + if(Reader->getAttributeValue("tangents") && GetAttribute(Reader, "tangents")) + { + ReadTangents=theSubMesh.HasTangents=true; + theSubMesh.Tangents.reserve(NumVertices); + DefaultLogger::get()->debug("reading tangents"); + } + + + //we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute + if(!Reader->getAttributeValue("texture_coords")) + theSubMesh.NumUvs=0; + else + { + ReadUvs=!!(theSubMesh.NumUvs=GetAttribute(Reader, "texture_coords")); + theSubMesh.Uvs.reserve(NumVertices); + DefaultLogger::get()->debug("reading texture coords"); + } + if(theSubMesh.NumUvs>1) + { + DefaultLogger::get()->warn("too many texcoords (just 1 supported!), just the first texcoords will be loaded!"); + theSubMesh.NumUvs=1; + } + //___________________________________________________________________ + + + //check if we will load anything + if(!(ReadPositions || ReadNormals || ReadTangents || ReadUvs)) + DefaultLogger::get()->warn("vertexbuffer seams to be empty!"); + + + //read all the vertices: + XmlRead(Reader); + + /*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers) + so the break condition is a bit tricky (well, IrrXML just sucks :( )*/ + while(Reader->getNodeName()==string("vertex") + ||Reader->getNodeName()==string("position") + ||Reader->getNodeName()==string("normal") + ||Reader->getNodeName()==string("tangent") + ||Reader->getNodeName()==string("texcoord")) + { + if(Reader->getNodeName()==string("vertex")) + XmlRead(Reader);//Read an attribute tag + + //Position + if(ReadPositions && Reader->getNodeName()==string("position")) + { + aiVector3D NewPos; + NewPos.x=GetAttribute(Reader, "x"); + NewPos.y=GetAttribute(Reader, "y"); + NewPos.z=GetAttribute(Reader, "z"); + theSubMesh.Positions.push_back(NewPos); + } + + //Normal + else if(ReadNormals && Reader->getNodeName()==string("normal")) + { + aiVector3D NewNormal; + NewNormal.x=GetAttribute(Reader, "x"); + NewNormal.y=GetAttribute(Reader, "y"); + NewNormal.z=GetAttribute(Reader, "z"); + theSubMesh.Normals.push_back(NewNormal); + } + + //Tangent + else if(ReadTangents && Reader->getNodeName()==string("tangent")) + { + aiVector3D NewTangent; + NewTangent.x=GetAttribute(Reader, "x"); + NewTangent.y=GetAttribute(Reader, "y"); + NewTangent.z=GetAttribute(Reader, "z"); + theSubMesh.Tangents.push_back(NewTangent); + } + + //Uv: + else if(ReadUvs && Reader->getNodeName()==string("texcoord")) + { + aiVector3D NewUv; + NewUv.x=GetAttribute(Reader, "u"); + NewUv.y=GetAttribute(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so! + theSubMesh.Uvs.push_back(NewUv); + + //skip all the following texcoords: + while(Reader->getNodeName()==string("texcoord")) + XmlRead(Reader); + continue;//don't read another line at the end of the loop + } + + //Attribute could not be read + else + { + DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName()); + } + + XmlRead(Reader);//Read the Vertex tag + } +} + + + +aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector& Bones) const +{ + const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene + (void)m_CurrentScene; + + aiMesh* NewAiMesh=new aiMesh(); + + //Positions + NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()]; + memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D)); + NewAiMesh->mNumVertices=theSubMesh.Positions.size(); + + //Normals + if(theSubMesh.HasNormals) + { + NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; + memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); + } + + + //until we have support for bitangents, no tangents will be written + /* + //Tangents + if(theSubMesh.HasTangents) + { + NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()]; + memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D)); + } + */ + + //Uvs + if(0!=theSubMesh.NumUvs) + { + NewAiMesh->mNumUVComponents[0]=2; + NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()]; + memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D)); + } + + + //---------------------------------------- Bones -------------------------------------------- + + //Copy the weights in in Bone-Vertices Struktur + //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp + vector< vector > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices + for(unsigned int VertexId=0; VertexId aiBones; + aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex) + + //create all the bones and fill them with informations + for(unsigned int i=0; i0) + { + aiBone* NewBone=new aiBone(); + NewBone->mNumWeights=aiWeights[i].size(); + NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()]; + memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size()); + NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton + NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace; + + aiBones.push_back(NewBone); + } + } + NewAiMesh->mNumBones=aiBones.size(); + + // mBones must be NULL if mNumBones is non 0 or the validation fails. + if (aiBones.size()) { + NewAiMesh->mBones=new aiBone* [aiBones.size()]; + memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*)); + } + + //______________________________________________________________________________________________________ + + + + //Faces + NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()]; + for(unsigned int i=0; imFaces[i].mNumIndices=3; + NewAiMesh->mFaces[i].mIndices=new unsigned int[3]; + + NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0]; + NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1]; + NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2]; + } + NewAiMesh->mNumFaces=theSubMesh.FaceList.size(); + + //Link the material: + NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh + + return NewAiMesh; +} + + +}//namespace Ogre +}//namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreSkeleton.cpp b/code/OgreSkeleton.cpp new file mode 100644 index 000000000..a3c2b8288 --- /dev/null +++ b/code/OgreSkeleton.cpp @@ -0,0 +1,445 @@ +/* +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) + { + 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"); + 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); + XmlRead(SkeletonFile); + } + + + 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 + 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));//which will recursily add all other nodes + } + } + + if (RootBoneNodes.size()) { + 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 + vector::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName); + aiMatrix4x4 t0, t1; + aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value + aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0) + * aiMatrix4x4::Translation(CurBone->Position, t1); + + //Create the keyframe arrays... + unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size(); + NewNodeAnim->mNumPositionKeys=KeyframeCount; + NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount]; + NewNodeAnim->mNumRotationKeys=KeyframeCount; + NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount]; + NewNodeAnim->mNumScalingKeys=KeyframeCount; + NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount]; + + //...and fill them + for(unsigned int k=0; kmPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time; + NewNodeAnim->mPositionKeys[k].mValue=Pos; + + NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time; + NewNodeAnim->mRotationKeys[k].mValue=Rot; + + NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].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; + //create a matrix from the transformation values of the ogre bone + NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1) + * aiMatrix4x4::Translation(Bones[BoneId].Position, t0) + + ; + //__________________________________________________________ + + + //---------- 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::Translation(-Position, t0) + * aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) + ; + 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