From 09517b342b620fd66f1078304e109ce531187c0f Mon Sep 17 00:00:00 2001 From: Jonne Nauha Date: Wed, 30 Apr 2014 05:43:13 +0300 Subject: [PATCH] OgreImporter: Started cleanup and refactoring. Aim is to get this into a shape that its easy to read and understand before I start making any new features. --- code/OgreImporter.cpp | 218 ++++++++--------- code/OgreImporter.hpp | 235 ++++++++++-------- code/OgreMesh.cpp | 523 ++++++++++++++++++++++------------------- code/OgreSkeleton.cpp | 36 +-- code/OgreXmlHelper.hpp | 102 ++++---- 5 files changed, 596 insertions(+), 518 deletions(-) diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index 21b974969..1a51489d9 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -46,7 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -using namespace std; #include "OgreImporter.hpp" #include "TinyFormatter.h" @@ -65,160 +64,153 @@ static const aiImporterDesc desc = { "mesh.xml" }; +using namespace std; + namespace Assimp { namespace Ogre { - bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const { - if(!checkSig)//Check File Extension + if (!checkSig) { - std::string extension("mesh.xml"); - int l=extension.length(); - return pFile.substr(pFile.length()-l, l)==extension; - } - else//Check file Header - { - const char* tokens[] = {""}; - return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + string ext = "mesh.xml"; + int len = ext.length(); + return (ASSIMP_stricmp(pFile.substr(pFile.length()-len, len), ext) == 0); } + const char* tokens[] = {""}; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } - void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) { - m_CurrentFilename=pFile; - m_CurrentIOHandler=pIOHandler; - m_CurrentScene=pScene; + m_CurrentFilename = pFile; + m_CurrentIOHandler = pIOHandler; + m_CurrentScene = pScene; - //Open the File: - boost::scoped_ptr file(pIOHandler->Open(pFile)); - if( file.get() == NULL) - throw DeadlyImportError("Failed to open file "+pFile+"."); - - //Read the Mesh File: - boost::scoped_ptr mIOWrapper( new CIrrXML_IOStreamReader( file.get())); - boost::scoped_ptr MeshFile(irr::io::createIrrXMLReader(mIOWrapper.get())); - if(!MeshFile)//parse the xml file - throw DeadlyImportError("Failed to create XML Reader for "+pFile); - - - DefaultLogger::get()->debug("Mesh File opened"); + // -------------------- Initial file and XML operations -------------------- - //Read root Node: - if(!(XmlRead(MeshFile.get()) && string(MeshFile->getNodeName())=="mesh")) + // Open + boost::scoped_ptr file(pIOHandler->Open(pFile)); + if (file.get() == NULL) + throw DeadlyImportError("Failed to open file " + pFile); + + // Read + boost::scoped_ptr xmlStream(new CIrrXML_IOStreamReader(file.get())); + boost::scoped_ptr reader(irr::io::createIrrXMLReader(xmlStream.get())); + if (!reader) + throw DeadlyImportError("Failed to create XML Reader for " + pFile); + + DefaultLogger::get()->debug("Opened a XML reader for " + pFile); + + // Read root node + NextNode(reader.get()); + if (!CurrentNodeNameEquals(reader, "mesh")) + throw DeadlyImportError("Root node is not but <" + string(reader->getNodeName()) + ">"); + + // Node names + string nnSharedGeometry = "sharedgeometry"; + string nnVertexBuffer = "vertexbuffer"; + string nnSubMeshes = "submeshes"; + string nnSubMesh = "submesh"; + string nnSubMeshNames = "submeshnames"; + string nnSkeletonLink = "skeletonlink"; + + // -------------------- Shared Geometry -------------------- + // This can be used to share geometry between submeshes + + NextNode(reader.get()); + if (CurrentNodeNameEquals(reader, nnSharedGeometry)) { - throw DeadlyImportError("Root Node is not ! "+pFile+" "+MeshFile->getNodeName()); + DefaultLogger::get()->debug("Reading shader geometry"); + unsigned int NumVertices = GetAttribute(reader.get(), "vertexcount"); + + NextNode(reader.get()); + while(CurrentNodeNameEquals(reader, nnVertexBuffer)) + ReadVertexBuffer(m_SharedGeometry, reader.get(), NumVertices); } - //eventually load shared geometry - XmlRead(MeshFile.get());//shared geometry is optional, so we need a reed for the next two if's - if(MeshFile->getNodeName()==string("sharedgeometry")) + // -------------------- Sub Meshes -------------------- + + if (!CurrentNodeNameEquals(reader, nnSubMeshes)) + throw DeadlyImportError("Could not find node inside root node"); + + list > subMeshes; + vector materials; + + NextNode(reader.get()); + while(CurrentNodeNameEquals(reader, nnSubMesh)) { - unsigned int NumVertices=GetAttribute(MeshFile.get(), "vertexcount");; + SubMesh* submesh = new SubMesh(); + ReadSubMesh(subMeshes.size(), *submesh, reader.get()); - XmlRead(MeshFile.get()); - while(MeshFile->getNodeName()==string("vertexbuffer")) - { - ReadVertexBuffer(m_SharedGeometry, MeshFile.get(), NumVertices); - } - } + // Just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n; + // so it is important to do this before pushing the mesh in the vector! + submesh->MaterialIndex = subMeshes.size(); - //Go to the submeshs: - if(MeshFile->getNodeName()!=string("submeshes")) - { - throw DeadlyImportError("No node in node! "+pFile); - } + subMeshes.push_back(boost::shared_ptr(submesh)); - - //-------------------Read the submeshs and materials:----------------------- - std::list > SubMeshes; - vector Materials; - XmlRead(MeshFile.get()); - while(MeshFile->getNodeName()==string("submesh")) - { - SubMesh* theSubMesh=new SubMesh(); - theSubMesh->MaterialName=GetAttribute(MeshFile.get(), "material"); - DefaultLogger::get()->debug("Loading Submehs with Material: "+theSubMesh->MaterialName); - ReadSubMesh(*theSubMesh, MeshFile.get()); - - //just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n; - //so it is important to do this before pushing the mesh in the vector! - theSubMesh->MaterialIndex=SubMeshes.size(); - - SubMeshes.push_back(boost::shared_ptr(theSubMesh)); - - //Load the Material: - aiMaterial* MeshMat=LoadMaterial(theSubMesh->MaterialName); + // Load the Material: + aiMaterial* MeshMat = LoadMaterial(submesh->MaterialName); - //Set the Material: - Materials.push_back(MeshMat); + // Set the Material: + materials.push_back(MeshMat); } - if(SubMeshes.empty()) - throw DeadlyImportError("no submesh loaded!"); - if(SubMeshes.size()!=Materials.size()) - throw DeadlyImportError("materialcount doesn't match mesh count!"); + if (subMeshes.empty()) + throw DeadlyImportError("Could not find node inside root node"); - //____________________________________________________________ + // This is really a internal error if we failed to create dummy materials. + if (subMeshes.size() != materials.size()) + throw DeadlyImportError("Internal Error: Material count does not match the submesh count"); - - //skip submeshnames (stupid irrxml) - if(MeshFile->getNodeName()==string("submeshnames")) + // Skip submesh names. + /// @todo Should these be read to scene etc. metadata? + if (CurrentNodeNameEquals(reader, nnSubMeshNames)) { - XmlRead(MeshFile.get()); - while(MeshFile->getNodeName()==string("submesh")) - XmlRead(MeshFile.get()); + NextNode(reader.get()); + while(CurrentNodeNameEquals(reader, nnSubMesh)) + NextNode(reader.get()); } + // -------------------- Skeleton -------------------- - //----------------Load the skeleton: ------------------------------- vector Bones; vector Animations; - if(MeshFile->getNodeName()==string("skeletonlink")) + if (CurrentNodeNameEquals(reader, nnSkeletonLink)) { - string SkeletonFile=GetAttribute(MeshFile.get(), "name"); - LoadSkeleton(SkeletonFile, Bones, Animations); - XmlRead(MeshFile.get()); + string SkeletonFile = GetAttribute(reader.get(), "name"); + if (!SkeletonFile.empty()) + LoadSkeleton(SkeletonFile, Bones, Animations); + else + DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty reference"); + NextNode(reader.get()); } else - { - DefaultLogger::get()->debug("No skeleton file will be loaded"); - DefaultLogger::get()->debug(MeshFile->getNodeName()); - } - //__________________________________________________________________ + DefaultLogger::get()->debug("Mesh has no assigned skeleton with <" + nnSkeletonLink + ">"); + // Now there might be for the shared geometry + if (CurrentNodeNameEquals(reader, "boneassignments")) + ReadBoneWeights(m_SharedGeometry, reader.get()); - //now there might be boneassignments for the shared geometry: - if(MeshFile->getNodeName()==string("boneassignments")) - { - ReadBoneWeights(m_SharedGeometry, MeshFile.get()); - } - - - //----------------- Process Meshs ----------------------- - BOOST_FOREACH(boost::shared_ptr theSubMesh, SubMeshes) + // -------------------- Process Results -------------------- + BOOST_FOREACH(boost::shared_ptr theSubMesh, subMeshes) { ProcessSubMesh(*theSubMesh, m_SharedGeometry); } - //_______________________________________________________ + // -------------------- Apply to aiScene -------------------- - - - //----------------- Now fill the Assimp scene --------------------------- - //put the aiMaterials in the scene: - m_CurrentScene->mMaterials=new aiMaterial*[Materials.size()]; - m_CurrentScene->mNumMaterials=Materials.size(); - for(unsigned int i=0; imMaterials[i]=Materials[i]; + m_CurrentScene->mMaterials=new aiMaterial*[materials.size()]; + m_CurrentScene->mNumMaterials=materials.size(); + for(unsigned int i=0; imMaterials[i]=materials[i]; //create the aiMehs... vector aiMeshes; - BOOST_FOREACH(boost::shared_ptr theSubMesh, SubMeshes) + BOOST_FOREACH(boost::shared_ptr theSubMesh, subMeshes) { aiMeshes.push_back(CreateAssimpSubMesh(*theSubMesh, Bones)); } @@ -231,16 +223,14 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass m_CurrentScene->mRootNode=new aiNode("root"); //link the meshs with the root node: - m_CurrentScene->mRootNode->mMeshes=new unsigned int[SubMeshes.size()]; - m_CurrentScene->mRootNode->mNumMeshes=SubMeshes.size(); - for(unsigned int i=0; imRootNode->mMeshes[i]=i; - + m_CurrentScene->mRootNode->mMeshes=new unsigned int[subMeshes.size()]; + m_CurrentScene->mRootNode->mNumMeshes=subMeshes.size(); + for(unsigned int i=0; imRootNode->mMeshes[i]=i; CreateAssimpSkeleton(Bones, Animations); PutAnimationsInScene(Bones, Animations); - //___________________________________________________________ } diff --git a/code/OgreImporter.hpp b/code/OgreImporter.hpp index 20f3bc576..cf1d0bda2 100644 --- a/code/OgreImporter.hpp +++ b/code/OgreImporter.hpp @@ -1,104 +1,126 @@ + #include "BaseImporter.h" - -#include - #include "OgreXmlHelper.hpp" #include "irrXMLWrapper.h" -/// Ogre Importer TODO -/* - Read Vertex Colors - - Read multiple TexCoords +#include + +/** @todo Read Vertex Colors + @todo Read multiple TexCoords */ - - namespace Assimp { namespace Ogre { - -//Forward declarations: struct Face; -struct Weight; +struct BoneWeight; struct Bone; struct Animation; -struct Track; -struct Keyframe; -///A submesh from Ogre +/// Ogre SubMesh struct SubMesh -{ - bool SharedData; +{ + bool UseSharedGeometry; + bool Use32bitIndexes; 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;//arbitrary number of texcoords, they are nearly always 2d, but assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner) + bool HasGeometry; + bool HasPositions; + bool HasNormals; + bool HasTangents; - std::vector< std::vector > Weights;//a list(inner) of bones for each vertex(outer) - 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) + std::vector Faces; + std::vector Positions; + std::vector Normals; + std::vector Tangents; - SubMesh(): SharedData(false), HasPositions(false), HasNormals(false), HasTangents(false), - MaterialIndex(-1), BonesUsed(0) {}//initialize everything + /// Arbitrary number of texcoords, they are nearly always 2d, but Assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner). + std::vector > Uvs; + + /// A list(inner) of bones for each vertex(outer). + std::vector > Weights; + + /// The Index in the Assimp material array from the material witch is attached to this submesh. + int MaterialIndex; + + // The highest index of a bone from a bone weight, this is needed to create the Assimp bone struct. Converting from vertex-bones to bone-vertices. + unsigned int BonesUsed; + + SubMesh() : + UseSharedGeometry(false), + Use32bitIndexes(false), + HasGeometry(false), + HasPositions(false), + HasNormals(false), + HasTangents(false), + MaterialIndex(-1), + BonesUsed(0) + { + } }; - -///The Main Ogre Importer Class +// ------------------------------------------------------------------------------------------------ +/// \class OgreImporter +/// \brief Importer for Ogre mesh, skeleton and material formats. +// ------------------------------------------------------------------------------------------------ class OgreImporter : public BaseImporter { public: - virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; - virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - virtual const aiImporterDesc* GetInfo () const; - virtual void SetupProperties(const Importer* pImp); -private: - - - //-------------------------------- OgreMesh.cpp ------------------------------- - /// Helper Functions to read parts of the XML File - void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader);//the submesh reference is the result value - - /// Reads a single Vertexbuffer and writes its data in the Submesh - static void ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices); - - /// Reads bone weights are stores them into the given submesh - static void ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader); - - /// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights) - static void ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry); - - /// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned - aiMesh* CreateAssimpSubMesh(const SubMesh &theSubMesh, const std::vector& Bones) const; + /// BaseImporter override. + virtual bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + /// BaseImporter override. + virtual void InternReadFile(const std::string &pFile, aiScene* pScene, IOSystem* pIOHandler); + + /// BaseImporter override. + virtual const aiImporterDesc* GetInfo () const; + + /// BaseImporter override. + virtual void SetupProperties(const Importer* pImp); + +private: + //-------------------------------- OgreMesh.cpp ------------------------------- + + /// Helper Functions to read parts of the XML File. + void ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader); + + /// Reads a single Vertexbuffer and writes its data in the Submesh. + static void ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices); + + /// Reads bone weights are stores them into the given submesh. + static void ReadBoneWeights(SubMesh &submesh, XmlReader *reader); + + /// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights). + static void ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry); + + /// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned. + aiMesh* CreateAssimpSubMesh(const SubMesh &submesh, const std::vector& bones) const; //-------------------------------- OgreSkeleton.cpp ------------------------------- + /// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it! void LoadSkeleton(std::string FileName, std::vector &Bones, std::vector &Animations) const; - /// Converts the animations in aiAnimations and puts them into the scene + /// Converts the animations in aiAnimations and puts them into the scene. void PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations); - /// Creates the aiskeleton in current scene + /// Creates the aiSkeleton in current scene. void CreateAssimpSkeleton(const std::vector &Bones, const std::vector &Animations); - /// Recursivly creates a filled aiNode from a given root bone + /// Recursivly creates a filled aiNode from a given root bone. static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode); - //-------------------------------- OgreMaterial.cpp ------------------------------- + aiMaterial* LoadMaterial(const std::string MaterialName) const; void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial) const; - - - - //Now we don't have to give theses parameters to all functions + // Now we don't have to give theses parameters to all functions + /// @todo Remove this m_Current* bookkeeping. std::string m_CurrentFilename; std::string m_MaterialLibFilename; bool m_TextureTypeFromFilename; @@ -107,73 +129,71 @@ private: SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh }; -///For the moment just triangles, no other polygon types! +/// Simplified face. +/** @todo Support other polygon types than just just triangles. Move to using aiFace. */ struct Face { unsigned int VertexIndices[3]; }; +/// Ogre Bone assignment struct BoneAssignment { - unsigned int BoneId;//this is, what we get from ogre - std::string BoneName;//this is, what we need for assimp + /// Bone ID from Ogre. + unsigned int BoneId; + // Bone name for Assimp. + std::string BoneName; }; -///for a vertex->bone structur -struct Weight +/// Ogre Bone weight +struct BoneWeight { - unsigned int BoneId; + /// Bone ID + unsigned int Id; + /// BoneWeight float Value; }; /// Helper Class to describe an ogre-bone for the skeleton: -/** 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!*/ +/** 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! + @todo Cleanup if possible. Seems like this is overly complex for what we are reading. */ struct Bone { + std::string Name; + int Id; int ParentId; - std::string Name; + aiVector3D Position; - float RotationAngle; aiVector3D RotationAxis; - std::vector Children; + float RotationAngle; + aiMatrix4x4 BoneToWorldSpace; - ///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) const - {return Id - bool operator==(const std::string& rval) const - {return Name==rval; } - bool operator==(const aiString& rval) const - {return Name==std::string(rval.data); } + std::vector Children; - // implemented in OgreSkeleton.cpp + Bone() : + Id(-1), + ParentId(-1), + RotationAngle(0.0f) + { + } + + /// This operator is needed to sort the bones after Id's + bool operator<(const Bone &other) const { return Id < other.Id; } + + /// This operator is needed to find a bone by its name in a vector + bool operator==(const std::string& other) const { return Name == other; } + bool operator==(const aiString& other) const { return Name == std::string(other.data); } + + /// @note Implemented in OgreSkeleton.cpp void CalculateBoneToWorldSpaceMatrix(std::vector& Bones); }; - - -///Describes an Ogre Animation -struct Animation -{ - std::string Name; - float Length; - std::vector Tracks; -}; - -///a track (keyframes for one bone) from an animation -struct Track -{ - std::string BoneName; - std::vector Keyframes; -}; - -/// keyframe (bone transformation) from a track from a animation -struct Keyframe +/// Ogre animation key frame +/** Transformations for a frame. */ +struct KeyFrame { float Time; aiVector3D Position; @@ -181,5 +201,24 @@ struct Keyframe aiVector3D Scaling; }; +/// Ogre animation track +/** Keyframes for one bone. */ +struct Track +{ + std::string BoneName; + std::vector Keyframes; +}; + +/// Ogre animation +struct Animation +{ + /// Name + std::string Name; + /// Length + float Length; + /// Tracks + std::vector Tracks; +}; + }//namespace Ogre }//namespace Assimp diff --git a/code/OgreMesh.cpp b/code/OgreMesh.cpp index 4abdee635..e4fba43d3 100644 --- a/code/OgreMesh.cpp +++ b/code/OgreMesh.cpp @@ -52,236 +52,281 @@ namespace Assimp namespace Ogre { - -void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) +void OgreImporter::ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader) { - if(Reader->getAttributeValue("usesharedvertices")) - theSubMesh.SharedData=GetAttribute(Reader, "usesharedvertices"); + if (reader->getAttributeValue("material")) + submesh.MaterialName = GetAttribute(reader, "material"); + if (reader->getAttributeValue("use32bitindexes")) + submesh.Use32bitIndexes = GetAttribute(reader, "use32bitindexes"); + if (reader->getAttributeValue("usesharedvertices")) + submesh.UseSharedGeometry = GetAttribute(reader, "usesharedvertices"); + + DefaultLogger::get()->debug(Formatter::format() << "Reading submesh " << submeshIndex); + DefaultLogger::get()->debug(Formatter::format() << " - Material '" << submesh.MaterialName << "'"); + DefaultLogger::get()->debug(Formatter::format() << " - Shader geometry = " << (submesh.UseSharedGeometry ? "true" : "false") << + ", 32bit indexes = " << (submesh.Use32bitIndexes ? "true" : "false")); - 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()); + /// @todo Fix above comment with better read logic below - while(XmlRead(Reader) && Reader->getNodeName()==string("face")) + NextNode(reader); + string currentNodeName = reader->getNodeName(); + + string nnFaces = "faces"; + string nnFace = "face"; + string nnGeometry = "geometry"; + string nnBoneAssignments = "boneassignments"; + string nnVertexBuffer = "vertexbuffer"; + + bool quadWarned = false; + + while(currentNodeName == nnFaces || + currentNodeName == nnGeometry || + currentNodeName == nnBoneAssignments) + { + if (currentNodeName == nnFaces) + { + unsigned int numFaces = GetAttribute(reader, "count"); + + NextNode(reader); + currentNodeName = reader->getNodeName(); + + while(currentNodeName == nnFace) { 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); + NewFace.VertexIndices[0] = GetAttribute(reader, "v1"); + NewFace.VertexIndices[1] = GetAttribute(reader, "v2"); + NewFace.VertexIndices[2] = GetAttribute(reader, "v3"); + + /// @todo Support quads + if (!quadWarned && reader->getAttributeValue("v4")) + DefaultLogger::get()->warn("Submesh has quads, only triangles are supported at the moment!"); + + submesh.Faces.push_back(NewFace); + + // Advance + NextNode(reader); + currentNodeName = reader->getNodeName(); } - }//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!"); - - for(unsigned int i=0; igetNodeName()==string("boneassignments")) - { - ReadBoneWeights(theSubMesh, Reader); + if (submesh.Faces.size() == numFaces) + DefaultLogger::get()->debug(Formatter::format() << " - Faces " << numFaces); + else + throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Faces.size() << " faces when should have read " << numFaces); } + else if (currentNodeName == nnGeometry) + { + unsigned int numVertices = GetAttribute(reader, "vertexcount"); + + NextNode(reader); + while(string(reader->getNodeName()) == nnVertexBuffer) + ReadVertexBuffer(submesh, reader, numVertices); + } + else if (reader->getNodeName() == nnBoneAssignments) + ReadBoneWeights(submesh, reader); + + currentNodeName = reader->getNodeName(); } - DefaultLogger::get()->debug((Formatter::format(), - "Positionen: ",theSubMesh.Positions.size(), - " Normale: ",theSubMesh.Normals.size(), - " TexCoords: ",theSubMesh.Uvs.size(), - " Tantents: ",theSubMesh.Tangents.size() - )); } - -void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices) +void OgreImporter::ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices) { - DefaultLogger::get()->debug("new Vertex Buffer"); - - bool ReadPositions=false; - bool ReadNormals=false; - bool ReadTangents=false; - unsigned int NumUvs=0; - - //-------------------- 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"); - } - - if(Reader->getAttributeValue("texture_coords")) - { - NumUvs=GetAttribute(Reader, "texture_coords"); - theSubMesh.Uvs.resize(NumUvs); - for(unsigned int i=0; idebug("reading texture coords"); - } - //___________________________________________________________________ - - - //check if we will load anything - if(!( ReadPositions || ReadNormals || ReadTangents || (NumUvs>0) )) - DefaultLogger::get()->warn("vertexbuffer seams to be empty!"); + DefaultLogger::get()->debug(Formatter::format() << "Reading vertex buffer with " << numVertices << " vertices"); + submesh.HasGeometry = true; - //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 */ - while(Reader->getNodeName()==string("vertex") - ||Reader->getNodeName()==string("position") - ||Reader->getNodeName()==string("normal") - ||Reader->getNodeName()==string("tangent") - ||Reader->getNodeName()==string("texcoord") - ||Reader->getNodeName()==string("colour_diffuse")) + if (reader->getAttributeValue("positions") && GetAttribute(reader, "positions")) { - if(Reader->getNodeName()==string("vertex")) - XmlRead(Reader);//Read an attribute tag + submesh.HasPositions = true; + submesh.Positions.reserve(numVertices); + DefaultLogger::get()->debug(" - Has positions"); + } + if (reader->getAttributeValue("normals") && GetAttribute(reader, "normals")) + { + submesh.HasNormals = true; + submesh.Normals.reserve(numVertices); + DefaultLogger::get()->debug(" - Has normals"); + } + if (reader->getAttributeValue("tangents") && GetAttribute(reader, "tangents")) + { + submesh.HasTangents = true; + submesh.Tangents.reserve(numVertices); + DefaultLogger::get()->debug(" - Has tangents"); + } + if (reader->getAttributeValue("texture_coords")) + { + submesh.Uvs.resize(GetAttribute(reader, "texture_coords")); + for(size_t i=0, len=submesh.Uvs.size(); idebug(Formatter::format() << " - Has " << submesh.Uvs.size() << " texture coords"); + } - //Position - if(ReadPositions && Reader->getNodeName()==string("position")) + if (!submesh.HasPositions) + throw DeadlyImportError("Vertex buffer does not contain positions!"); + + string nnVertex = "vertex"; + string nnPosition = "position"; + string nnNormal = "normal"; + string nnTangent = "tangent"; + string nnBinormal = "binormal"; + string nnTexCoord = "texcoord"; + string nnColorDiffuse = "colour_diffuse"; + string nnColorSpecular = "colour_specular"; + + bool warnBinormal = true; + bool warnColorDiffuse = true; + bool warnColorSpecular = true; + + NextNode(reader); + string currentNodeName = reader->getNodeName(); + + /// @todo Make this loop nicer. + while(currentNodeName == nnVertex || + currentNodeName == nnPosition || + currentNodeName == nnNormal || + currentNodeName == nnTangent || + currentNodeName == nnBinormal || + currentNodeName == nnTexCoord || + currentNodeName == nnColorDiffuse || + currentNodeName == nnColorSpecular) + { + if (currentNodeName == nnVertex) + { + NextNode(reader); + currentNodeName = reader->getNodeName(); + } + + /// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular + + if (submesh.HasPositions && currentNodeName == nnPosition) { aiVector3D NewPos; - NewPos.x=GetAttribute(Reader, "x"); - NewPos.y=GetAttribute(Reader, "y"); - NewPos.z=GetAttribute(Reader, "z"); - theSubMesh.Positions.push_back(NewPos); + NewPos.x = GetAttribute(reader, "x"); + NewPos.y = GetAttribute(reader, "y"); + NewPos.z = GetAttribute(reader, "z"); + submesh.Positions.push_back(NewPos); } - - //Normal - else if(ReadNormals && Reader->getNodeName()==string("normal")) + else if (submesh.HasNormals && currentNodeName == nnNormal) { aiVector3D NewNormal; - NewNormal.x=GetAttribute(Reader, "x"); - NewNormal.y=GetAttribute(Reader, "y"); - NewNormal.z=GetAttribute(Reader, "z"); - theSubMesh.Normals.push_back(NewNormal); + NewNormal.x = GetAttribute(reader, "x"); + NewNormal.y = GetAttribute(reader, "y"); + NewNormal.z = GetAttribute(reader, "z"); + submesh.Normals.push_back(NewNormal); } - - //Tangent - else if(ReadTangents && Reader->getNodeName()==string("tangent")) + else if (submesh.HasTangents && currentNodeName == nnTangent) { aiVector3D NewTangent; - NewTangent.x=GetAttribute(Reader, "x"); - NewTangent.y=GetAttribute(Reader, "y"); - NewTangent.z=GetAttribute(Reader, "z"); - theSubMesh.Tangents.push_back(NewTangent); + NewTangent.x = GetAttribute(reader, "x"); + NewTangent.y = GetAttribute(reader, "y"); + NewTangent.z = GetAttribute(reader, "z"); + submesh.Tangents.push_back(NewTangent); } - - //Uv: - else if(NumUvs>0 && Reader->getNodeName()==string("texcoord")) + else if (submesh.Uvs.size() > 0 && currentNodeName == nnTexCoord) { - for(unsigned int i=0; igetNodeName()!=string("texcoord")) - { - DefaultLogger::get()->warn(string("Not enough UVs in Vertex: ")+Reader->getNodeName()); - } + if (currentNodeName != nnTexCoord) + throw DeadlyImportError("Vertex buffer declared more UVs than can be found in a vertex"); + aiVector3D NewUv; - NewUv.x=GetAttribute(Reader, "u"); - NewUv.y=GetAttribute(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so! - theSubMesh.Uvs[i].push_back(NewUv); - XmlRead(Reader); + NewUv.x = GetAttribute(reader, "u"); + NewUv.y = GetAttribute(reader, "v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????) + submesh.Uvs[i].push_back(NewUv); + + NextNode(reader); + currentNodeName = reader->getNodeName(); } - continue;//because we already read the next node... + // Continue main loop as above already read next node + continue; } - - //Color: - //TODO: actually save this data! - else if(Reader->getNodeName()==string("colour_diffuse")) - { - //do nothing, because we not yet support them - } - - //Attribute could not be read else { - DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName()); + /// @todo Remove this stuff once implemented. We only want to log warnings once per element. + bool warn = true; + if (currentNodeName == nnBinormal) + { + if (warnBinormal) + warnBinormal = false; + else + warn = false; + } + else if (currentNodeName == nnColorDiffuse) + { + if (warnColorDiffuse) + warnColorDiffuse = false; + else + warn = false; + } + else if (currentNodeName == nnColorSpecular) + { + if (warnColorSpecular) + warnColorSpecular = false; + else + warn = false; + } + if (warn) + DefaultLogger::get()->warn(string("Vertex buffer attribute read not implemented for element: ") + currentNodeName); } - XmlRead(Reader);//Read the Vertex tag + // Advance + NextNode(reader); + currentNodeName = reader->getNodeName(); } -} + DefaultLogger::get()->debug(Formatter::format() << + " - Positions " << submesh.Positions.size() << + " Normals " << submesh.Normals.size() << + " TexCoords " << submesh.Uvs.size() << + " Tangents " << submesh.Tangents.size()); -void OgreImporter::ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader) -{ - theSubMesh.Weights.resize(theSubMesh.Positions.size()); - while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment")) + // Sanity checks + if (submesh.HasNormals && submesh.Normals.size() != numVertices) + throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Normals.size() << " normals when should have read " << numVertices); + if (submesh.HasTangents && submesh.Tangents.size() != numVertices) + throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Tangents.size() << " tangents when should have read " << numVertices); + for(unsigned int i=0; i(Reader, "vertexindex"); - NewWeight.BoneId=GetAttribute(Reader, "boneindex"); - NewWeight.Value=GetAttribute(Reader, "weight"); - //calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) - theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1); - - theSubMesh.Weights[VertexId].push_back(NewWeight); + if (submesh.Uvs[i].size() != numVertices) + throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Uvs[i].size() + << " uvs for uv index " << i << " when should have read " << numVertices); } } +void OgreImporter::ReadBoneWeights(SubMesh &submesh, XmlReader *reader) +{ + submesh.Weights.resize(submesh.Positions.size()); + unsigned int numRead = 0; + string nnVertexBoneAssignment = "vertexboneassignment"; -void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry) + NextNode(reader); + while(CurrentNodeNameEquals(reader, nnVertexBoneAssignment)) + { + numRead++; + + BoneWeight weight; + weight.Id = GetAttribute(reader, "boneindex"); + weight.Value = GetAttribute(reader, "weight"); + + //calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) + /// @todo This can probably be refactored to something else. + submesh.BonesUsed = max(submesh.BonesUsed, weight.Id+1); + + const unsigned int vertexId = GetAttribute(reader, "vertexindex"); + submesh.Weights[vertexId].push_back(weight); + + NextNode(reader); + } + DefaultLogger::get()->debug(Formatter::format() << " - Bone weights " << numRead); +} + +void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry) { //---------------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 UniqueFaceList(submesh.Faces.size()); + unsigned int UniqueVertexCount=submesh.Faces.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^ vector UniquePositions(UniqueVertexCount); @@ -289,9 +334,9 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr vector UniqueTangents(UniqueVertexCount); - vector< vector > UniqueWeights(UniqueVertexCount); + vector< vector > UniqueWeights(UniqueVertexCount); - vector< vector > UniqueUvs(theSubMesh.Uvs.size()); + vector< vector > UniqueUvs(submesh.Uvs.size()); for(unsigned int i=0; i1.0f+0.05f) { //normalize all weights: - for(unsigned int BoneId=0; BoneId& Bones) const +aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& submesh, 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; @@ -408,63 +453,63 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto 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(); + NewAiMesh->mVertices=new aiVector3D[submesh.Positions.size()]; + memcpy(NewAiMesh->mVertices, &submesh.Positions[0], submesh.Positions.size()*sizeof(aiVector3D)); + NewAiMesh->mNumVertices=submesh.Positions.size(); //Normals - if(theSubMesh.HasNormals) + if(submesh.HasNormals) { - NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; - memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); + NewAiMesh->mNormals=new aiVector3D[submesh.Normals.size()]; + memcpy(NewAiMesh->mNormals, &submesh.Normals[0], submesh.Normals.size()*sizeof(aiVector3D)); } //until we have support for bitangents, no tangents will be written /* //Tangents - if(theSubMesh.HasTangents) + if(submesh.HasTangents) { - NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()]; - memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D)); + NewAiMesh->mTangents=new aiVector3D[submesh.Tangents.size()]; + memcpy(NewAiMesh->mTangents, &submesh.Tangents[0], submesh.Tangents.size()*sizeof(aiVector3D)); } */ //Uvs - if(theSubMesh.Uvs.size()>0) + if(submesh.Uvs.size()>0) { - for(unsigned int i=0; imNumUVComponents[i]=2; - NewAiMesh->mTextureCoords[i]=new aiVector3D[theSubMesh.Uvs[i].size()]; - memcpy(NewAiMesh->mTextureCoords[i], &(theSubMesh.Uvs[i][0]), theSubMesh.Uvs[i].size()*sizeof(aiVector3D)); + NewAiMesh->mTextureCoords[i]=new aiVector3D[submesh.Uvs[i].size()]; + memcpy(NewAiMesh->mTextureCoords[i], &(submesh.Uvs[i][0]), submesh.Uvs[i].size()*sizeof(aiVector3D)); } } - //---------------------------------------- Bones -------------------------------------------- + //---------------------------------------- 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 > aiWeights(submesh.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) + aiBones.reserve(submesh.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) { @@ -472,8 +517,8 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto 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; + 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); } @@ -491,20 +536,20 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto //Faces - NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()]; - for(unsigned int i=0; imFaces=new aiFace[submesh.Faces.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->mFaces[i].mIndices[0]=submesh.Faces[i].VertexIndices[0]; + NewAiMesh->mFaces[i].mIndices[1]=submesh.Faces[i].VertexIndices[1]; + NewAiMesh->mFaces[i].mIndices[2]=submesh.Faces[i].VertexIndices[2]; } - NewAiMesh->mNumFaces=theSubMesh.FaceList.size(); + NewAiMesh->mNumFaces=submesh.Faces.size(); //Link the material: - NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh + NewAiMesh->mMaterialIndex=submesh.MaterialIndex;//the index is set by the function who called ReadSubMesh return NewAiMesh; } diff --git a/code/OgreSkeleton.cpp b/code/OgreSkeleton.cpp index 376829cfb..627c56ab8 100644 --- a/code/OgreSkeleton.cpp +++ b/code/OgreSkeleton.cpp @@ -77,18 +77,18 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto if(!SkeletonFile) throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("skeleton")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No node in SkeletonFile: "+FileName); //------------------------------------load bones----------------------------------------- - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("bones")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No bones node in skeleton "+FileName); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); while(string("bone")==SkeletonFile->getNodeName()) { @@ -100,7 +100,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto NewBone.Name=GetAttribute(SkeletonFile, "name"); //load the position: - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("position")!=SkeletonFile->getNodeName()) throw DeadlyImportError("Position is not first node in Bone!"); NewBone.Position.x=GetAttribute(SkeletonFile, "x"); @@ -108,11 +108,11 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto NewBone.Position.z=GetAttribute(SkeletonFile, "z"); //Rotation: - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("rotation")!=SkeletonFile->getNodeName()) throw DeadlyImportError("Rotation is not the second node in Bone!"); NewBone.RotationAngle=GetAttribute(SkeletonFile, "angle"); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("axis")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No axis specified for bone rotation!"); NewBone.RotationAxis.x=GetAttribute(SkeletonFile, "x"); @@ -123,7 +123,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto Bones.push_back(NewBone); //Proceed to the next bone: - XmlRead(SkeletonFile); + NextNode(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()); @@ -153,7 +153,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto throw DeadlyImportError("no bonehierarchy node in "+FileName); DefaultLogger::get()->debug("loading bonehierarchy..."); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); while(string("boneparent")==SkeletonFile->getNodeName()) { string Child, Parent; @@ -167,7 +167,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto Bones[ChildId].ParentId=ParentId; Bones[ParentId].Children.push_back(ChildId); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); } //_____________________________________________________________________________ @@ -187,7 +187,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto if(string("animations")==SkeletonFile->getNodeName())//animations are optional values { DefaultLogger::get()->debug("Loading Animations"); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); while(string("animation")==SkeletonFile->getNodeName()) { Animation NewAnimation; @@ -195,30 +195,30 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto NewAnimation.Length=GetAttribute(SkeletonFile, "length"); //Load all Tracks - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("tracks")!=SkeletonFile->getNodeName()) throw DeadlyImportError("no tracks node in animation"); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); while(string("track")==SkeletonFile->getNodeName()) { Track NewTrack; NewTrack.BoneName=GetAttribute(SkeletonFile, "bone"); //Load all keyframes; - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("keyframes")!=SkeletonFile->getNodeName()) throw DeadlyImportError("no keyframes node!"); - XmlRead(SkeletonFile); + NextNode(SkeletonFile); while(string("keyframe")==SkeletonFile->getNodeName()) { - Keyframe NewKeyframe; + KeyFrame NewKeyframe; NewKeyframe.Time=GetAttribute(SkeletonFile, "time"); //loop over the attributes: while(true) //will quit, if a Node is not a animationkey { - XmlRead(SkeletonFile); + NextNode(SkeletonFile); //If any property doesn't show up, it will keep its initialization value @@ -235,7 +235,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto { float RotationAngle=GetAttribute(SkeletonFile, "angle"); aiVector3D RotationAxis; - XmlRead(SkeletonFile); + NextNode(SkeletonFile); if(string("axis")!=SkeletonFile->getNodeName()) throw DeadlyImportError("No axis for keyframe rotation!"); RotationAxis.x=GetAttribute(SkeletonFile, "x"); @@ -247,7 +247,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto 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!"); + DefaultLogger::get()->warn("Invalid Rotation Axis in KeyFrame!"); } } NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle); diff --git a/code/OgreXmlHelper.hpp b/code/OgreXmlHelper.hpp index 8a71a4be5..74445b86d 100644 --- a/code/OgreXmlHelper.hpp +++ b/code/OgreXmlHelper.hpp @@ -9,80 +9,84 @@ namespace Ogre typedef irr::io::IrrXMLReader XmlReader; - -//------------Helper Funktion to Get a Attribute Save--------------- -template inline t GetAttribute(XmlReader* Reader, std::string Name); - -/* +static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") { - BOOST_STATIC_ASSERT(false); - return t(); -} -*/ - -template<> inline int GetAttribute(XmlReader* Reader, std::string Name) -{ - const char* Value=Reader->getAttributeValue(Name.c_str()); - if(Value) - return atoi(Value); + if (!error.empty()) + throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'"); else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); + throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'"); } -template<> inline unsigned int GetAttribute(XmlReader* Reader, std::string Name) +template +inline T GetAttribute(const XmlReader* reader, const std::string &name); + +template<> +inline int GetAttribute(const XmlReader* reader, const std::string &name) { - const char* Value=Reader->getAttributeValue(Name.c_str()); - if(Value) - return static_cast(atoi(Value));//yes, ugly, but pfff + const char* value = reader->getAttributeValue(name.c_str()); + if (value) + return atoi(value); else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); + ThrowAttibuteError(reader, name); } -template<> inline float GetAttribute(XmlReader* Reader, std::string Name) +template<> +inline unsigned int GetAttribute(const XmlReader* reader, const std::string &name) { - const char* Value=Reader->getAttributeValue(Name.c_str()); - if(Value) - return fast_atof(Value); + const char* value = reader->getAttributeValue(name.c_str()); + if (value) + return static_cast(atoi(value)); ///< @todo Find a better way... else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); + ThrowAttibuteError(reader, name); } -template<> inline std::string GetAttribute(XmlReader* Reader, std::string Name) +template<> +inline float GetAttribute(const XmlReader* reader, const std::string &name) { - const char* Value=Reader->getAttributeValue(Name.c_str()); - if(Value) - return std::string(Value); + const char* value = reader->getAttributeValue(name.c_str()); + if (value) + return fast_atof(value); else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); + ThrowAttibuteError(reader, name); } -template<> inline bool GetAttribute(XmlReader* Reader, std::string Name) +template<> +inline std::string GetAttribute(const XmlReader* reader, const std::string &name) { - const char* Value=Reader->getAttributeValue(Name.c_str()); - if(Value) - { - if(Value==std::string("true")) - return true; - else if(Value==std::string("false")) - return false; - else - throw DeadlyImportError(std::string("Bool value has invalid value: "+Name+" / "+Value+" / "+Reader->getNodeName())); - } + const char* value = reader->getAttributeValue(name.c_str()); + if (value) + return std::string(value); else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); + ThrowAttibuteError(reader, name); } -//__________________________________________________________________ -inline bool XmlRead(XmlReader* Reader) +template<> +inline bool GetAttribute(const XmlReader* reader, const std::string &name) +{ + std::string value = GetAttribute(reader, name); + if (ASSIMP_stricmp(value, "true") == 0) + return true; + else if (ASSIMP_stricmp(value, "false") == 0) + return false; + else + ThrowAttibuteError(reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'"); +} + +inline bool NextNode(XmlReader* reader) { do { - if(!Reader->read()) + if (!reader->read()) return false; } - while(Reader->getNodeType()!=irr::io::EXN_ELEMENT); + while(reader->getNodeType() != irr::io::EXN_ELEMENT); return true; } -}//namespace Ogre -}//namespace Assimp +inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &name) +{ + return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0); +} + +} // Ogre +} // Assimp