From 521088e134326920ea2335b1fd9b85ec812bfefe Mon Sep 17 00:00:00 2001 From: jonathanklein Date: Mon, 20 Feb 2012 10:27:27 +0000 Subject: [PATCH] Ogre: - better support for OgreXmlConverter generated files git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1177 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/CMakeLists.txt | 3 +- code/ImporterRegistry.cpp | 2 +- code/OgreImporter.cpp | 224 ++++++++++---- code/{OgreImporter.h => OgreImporter.hpp} | 310 ++++++++++---------- code/OgreImporterMaterial.cpp | 233 ++++++++++----- code/{OgreXmlHelper.h => OgreXmlHelper.hpp} | 158 +++++----- doc/dox.h | 8 +- 7 files changed, 566 insertions(+), 372 deletions(-) rename code/{OgreImporter.h => OgreImporter.hpp} (88%) rename code/{OgreXmlHelper.h => OgreXmlHelper.hpp} (96%) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 32bcb28c7..7009bc8cf 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -326,7 +326,8 @@ SET( Obj_SRCS SOURCE_GROUP( Obj FILES ${Obj_SRCS}) SET( Ogre_SRCS - OgreImporter.h + OgreImporter.hpp + OgreXmlHelper.hpp OgreImporter.cpp OgreImporterMaterial.cpp ) diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index 1b3a164e4..1ea349d46 100644 --- a/code/ImporterRegistry.cpp +++ b/code/ImporterRegistry.cpp @@ -140,7 +140,7 @@ corresponding preprocessor flag to selectively disable formats. # include "LWSLoader.h" #endif #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER -# include "OgreImporter.h" +# include "OgreImporter.hpp" #endif #ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER # include "MS3DLoader.h" diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index a4874d88d..2bb5e552a 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -54,7 +54,7 @@ using namespace std; #include "TinyFormatter.h" -#include "OgreImporter.h" +#include "OgreImporter.hpp" #include "irrXMLWrapper.h" @@ -80,7 +80,6 @@ bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandle } - void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) { m_CurrentFilename=pFile; @@ -198,7 +197,6 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass } - void OgreImporter::GetExtensionList(std::set& extensions) { extensions.insert("mesh.xml"); @@ -210,6 +208,7 @@ void OgreImporter::SetupProperties(const Importer* pImp) m_MaterialLibFilename=pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material"); } + void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) { XmlRead(Reader); @@ -242,63 +241,28 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) { //some info logging: unsigned int NumVertices=GetAttribute(Reader, "vertexcount"); - ostringstream ss; ss<<"VertexCount: "<debug(ss.str()); //General Informations about vertices XmlRead(Reader); - if(!(Reader->getNodeName()==string("vertexbuffer"))) + while(Reader->getNodeName()==string("vertexbuffer")) { - throw DeadlyImportError("vertexbuffer node is not first in geometry node!"); + ReadVertexBuffer(theSubMesh, Reader, NumVertices); } - theSubMesh.HasPositions=GetAttribute(Reader, "positions"); - theSubMesh.HasNormals=GetAttribute(Reader, "normals"); - if(!Reader->getAttributeValue("texture_coords"))//we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute - theSubMesh.NumUvs=0; - else - theSubMesh.NumUvs=GetAttribute(Reader, "texture_coords"); - if(theSubMesh.NumUvs>1) - throw DeadlyImportError("too many texcoords (just 1 supported!)"); - //read all the vertices: - XmlRead(Reader); - while(Reader->getNodeName()==string("vertex")) - { - //read all vertex attributes: + //some error checking on the loaded data + if(!theSubMesh.HasPositions) + throw DeadlyImportError("No positions could be loaded!"); - //Position - if(theSubMesh.HasPositions) - { - XmlRead(Reader); - aiVector3D NewPos; - NewPos.x=GetAttribute(Reader, "x"); - NewPos.y=GetAttribute(Reader, "y"); - NewPos.z=GetAttribute(Reader, "z"); - theSubMesh.Positions.push_back(NewPos); - } - - //Normal - if(theSubMesh.HasNormals) - { - XmlRead(Reader); - aiVector3D NewNormal; - NewNormal.x=GetAttribute(Reader, "x"); - NewNormal.y=GetAttribute(Reader, "y"); - NewNormal.z=GetAttribute(Reader, "z"); - theSubMesh.Normals.push_back(NewNormal); - } + if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices) + throw DeadlyImportError("Wrong Number of Normals loaded!"); - //Uv: - if(1==theSubMesh.NumUvs) - { - XmlRead(Reader); - 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); - } - XmlRead(Reader); - } + 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 @@ -324,7 +288,8 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) DefaultLogger::get()->debug((Formatter::format(), "Positionen: ",theSubMesh.Positions.size(), " Normale: ",theSubMesh.Normals.size(), - " TexCoords: ",theSubMesh.Uvs.size() + " TexCoords: ",theSubMesh.Uvs.size(), + " Tantents: ",theSubMesh.Tangents.size() )); DefaultLogger::get()->warn(Reader->getNodeName()); @@ -335,6 +300,7 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) 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)); @@ -349,9 +315,19 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) UniquePositions[3*i+1]=theSubMesh.Positions[Vertex2]; UniquePositions[3*i+2]=theSubMesh.Positions[Vertex3]; - UniqueNormals[3*i+0]=theSubMesh.Normals[Vertex1]; - UniqueNormals[3*i+1]=theSubMesh.Normals[Vertex2]; - UniqueNormals[3*i+2]=theSubMesh.Normals[Vertex3]; + if(theSubMesh.HasNormals) + { + UniqueNormals[3*i+0]=theSubMesh.Normals[Vertex1]; + UniqueNormals[3*i+1]=theSubMesh.Normals[Vertex2]; + UniqueNormals[3*i+2]=theSubMesh.Normals[Vertex3]; + } + + if(theSubMesh.HasTangents) + { + UniqueTangents[3*i+0]=theSubMesh.Tangents[Vertex1]; + UniqueTangents[3*i+1]=theSubMesh.Tangents[Vertex2]; + UniqueTangents[3*i+2]=theSubMesh.Tangents[Vertex3]; + } if(1==theSubMesh.NumUvs) { @@ -360,7 +336,8 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) UniqueUvs[3*i+2]=theSubMesh.Uvs[Vertex3]; } - if (theSubMesh.Weights.size()) { + if(theSubMesh.Weights.size()) + { UniqueWeights[3*i+0]=theSubMesh.Weights[Vertex1]; UniqueWeights[3*i+1]=theSubMesh.Weights[Vertex2]; UniqueWeights[3*i+2]=theSubMesh.Weights[Vertex3]; @@ -374,9 +351,11 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) //_________________________________________________________________________________________ //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); @@ -405,6 +384,119 @@ void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader) } +void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices) +{ + DefaultLogger::get()->debug("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!), no texcoords will be loaded!"); + //___________________________________________________________________ + + + //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); + } + + //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 @@ -418,8 +510,18 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto NewAiMesh->mNumVertices=theSubMesh.Positions.size(); //Normals - NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; - memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); + if(theSubMesh.HasNormals) + { + NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()]; + memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D)); + } + + //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) @@ -816,12 +918,8 @@ void OgreImporter::PutAnimationsInScene(const std::vector &Bones, const st } - -aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode) const +aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode) { - const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene - (void)m_CurrentScene; - //----Create the node for this bone and set its values----- aiNode* NewNode=new aiNode(Bones[BoneId].Name); NewNode->mParent=ParentNode; diff --git a/code/OgreImporter.h b/code/OgreImporter.hpp similarity index 88% rename from code/OgreImporter.h rename to code/OgreImporter.hpp index 3f58ec18c..eaff1d647 100644 --- a/code/OgreImporter.h +++ b/code/OgreImporter.hpp @@ -1,151 +1,159 @@ -#include "BaseImporter.h" - -#include - -#include "OgreXmlHelper.h" -#include "irrXMLWrapper.h" - -namespace Assimp -{ -namespace Ogre -{ - - -//Forward declarations: -struct SubMesh; -struct Face; -struct Weight; -struct Bone; -struct Animation; -struct Track; -struct Keyframe; - -///The Main Ogre Importer Class -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 void GetExtensionList(std::set& extensions); - virtual void SetupProperties(const Importer* pImp); -private: - - /// Helper Functions to read parts of the XML File - void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader);//the submesh reference is the result value - - /// 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 - void PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations); - - /// 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; - - //creates the aiskeleton in current scene - void CreateAssimpSkeleton(const std::vector &Bones, const std::vector &Animations); - - aiMaterial* LoadMaterial(const std::string MaterialName) const; - - ///Recursivly creates a filled aiNode from a given root bone - aiNode* CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode) const; - - //Now we don't have to give theses parameters to all functions - std::string m_CurrentFilename; - 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 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), NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything -}; - -///For the moment just triangles, no other polygon types! -struct Face -{ - unsigned int VertexIndices[3]; -}; - -struct BoneAssignment -{ - unsigned int BoneId;//this is, what we get from ogre - std::string BoneName;//this is, what we need for assimp -}; - -///for a vertex->bone structur -struct Weight -{ - unsigned int BoneId; - 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!*/ -struct Bone -{ - int Id; - int ParentId; - std::string Name; - aiVector3D Position; - float RotationAngle; - aiVector3D RotationAxis; - std::vector Children; - 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); } - - 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 -{ - float Time; - aiVector3D Position; - aiQuaternion Rotation; - aiVector3D Scaling; -}; - -}//namespace Ogre -}//namespace Assimp +#include "BaseImporter.h" + +#include + +#include "OgreXmlHelper.hpp" +#include "irrXMLWrapper.h" + +namespace Assimp +{ +namespace Ogre +{ + + +//Forward declarations: +struct SubMesh; +struct Face; +struct Weight; +struct Bone; +struct Animation; +struct Track; +struct Keyframe; + +///The Main Ogre Importer Class +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 void GetExtensionList(std::set& extensions); + virtual void SetupProperties(const Importer* pImp); +private: + + /// 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); + + /// 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 + void PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations); + + /// 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; + + //creates the aiskeleton in current scene + void CreateAssimpSkeleton(const std::vector &Bones, const std::vector &Animations); + + aiMaterial* LoadMaterial(const std::string MaterialName) const; + static void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial); + + ///Recursivly creates a filled aiNode from a given root bone + static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode); + + //Now we don't have to give theses parameters to all functions + std::string m_CurrentFilename; + 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 +}; + +///For the moment just triangles, no other polygon types! +struct Face +{ + unsigned int VertexIndices[3]; +}; + +struct BoneAssignment +{ + unsigned int BoneId;//this is, what we get from ogre + std::string BoneName;//this is, what we need for assimp +}; + +///for a vertex->bone structur +struct Weight +{ + unsigned int BoneId; + 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!*/ +struct Bone +{ + int Id; + int ParentId; + std::string Name; + aiVector3D Position; + float RotationAngle; + aiVector3D RotationAxis; + std::vector Children; + 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); } + + 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 +{ + float Time; + aiVector3D Position; + aiQuaternion Rotation; + aiVector3D Scaling; +}; + +}//namespace Ogre +}//namespace Assimp diff --git a/code/OgreImporterMaterial.cpp b/code/OgreImporterMaterial.cpp index bf1f84a09..e6b5e93e8 100644 --- a/code/OgreImporterMaterial.cpp +++ b/code/OgreImporterMaterial.cpp @@ -55,7 +55,7 @@ using namespace std; //#include "boost/foreach.hpp" //using namespace boost; -#include "OgreImporter.h" +#include "OgreImporter.hpp" #include "irrXMLWrapper.h" #include "TinyFormatter.h" @@ -71,10 +71,6 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene (void)m_CurrentScene; - aiMaterial *NewMaterial=new aiMaterial(); - - aiString ts(MaterialName.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_NAME); /*For bettetr understanding of the material parser, here is a material example file: material Sarg @@ -100,9 +96,39 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const */ + /*and here is another one: - const string MaterialFileName=m_CurrentFilename.substr(0, m_CurrentFilename.find('.'))+".material"; - DefaultLogger::get()->info("Trying to load " +MaterialFileName); + import * from abstract_base_passes_depth.material + import * from abstract_base.material + import * from mat_shadow_caster.material + import * from mat_character_singlepass.material + + material hero/hair/caster : mat_shadow_caster_skin_areject + { + set $diffuse_map "hero_hair_alpha_c.dds" + } + + material hero/hair_alpha : mat_char_cns_singlepass_areject_4weights + { + set $diffuse_map "hero_hair_alpha_c.dds" + set $specular_map "hero_hair_alpha_s.dds" + set $normal_map "hero_hair_alpha_n.dds" + set $light_map "black_lightmap.dds" + + set $shadow_caster_material "hero/hair/caster" + } + */ + + + //the filename typically ends with .mesh or .mesh.xml + const string MaterialFileName=m_CurrentFilename.substr(0, m_CurrentFilename.rfind(".mesh"))+".material"; + DefaultLogger::get()->info("Trying to load " + MaterialFileName); + + + aiMaterial *NewMaterial=new aiMaterial(); + + aiString ts(MaterialName.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_NAME); //Read the file into memory and put it in a stringstream stringstream ss; @@ -110,11 +136,17 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const IOStream* MatFilePtr=m_CurrentIOHandler->Open(MaterialFileName); if(NULL==MatFilePtr) { - MatFilePtr=m_CurrentIOHandler->Open(m_MaterialLibFilename); + //try the default mat Library if(NULL==MatFilePtr) { - DefaultLogger::get()->error(m_MaterialLibFilename+" and "+MaterialFileName + " could not be opened, Material will not be loaded!"); - return NewMaterial; + + MatFilePtr=m_CurrentIOHandler->Open(m_MaterialLibFilename); + if(NULL==MatFilePtr) + { + DefaultLogger::get()->error(m_MaterialLibFilename+" and "+MaterialFileName + " could not be opened, Material will not be loaded!"); + delete NewMaterial; + return NULL; + } } } boost::scoped_ptr MaterialFile(MatFilePtr); @@ -135,7 +167,10 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const ss >> Line; if(Line==MaterialName)//Load the next material { + string RestOfLine; + getline(ss, RestOfLine);//ignore the rest of the line ss >> Line; + if(Line!="{") throw DeadlyImportError("empty material!"); @@ -145,72 +180,9 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const ss >> Line; if(Line=="technique") { - ss >> Line; - if(Line!="{") - throw DeadlyImportError("empty technique!"); - while(Line!="}")//read until the end of the technique - { - ss >> Line; - if(Line=="pass") - { - ss >> Line; - if(Line!="{") - throw DeadlyImportError("empty pass!"); - while(Line!="}")//read until the end of the pass - { - ss >> Line; - if(Line=="ambient") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_AMBIENT); - } - else if(Line=="diffuse") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_DIFFUSE); - } - else if(Line=="specular") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_SPECULAR); - } - else if(Line=="emmisive") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_EMISSIVE); - } - else if(Line=="texture_unit") - { - ss >> Line; - if(Line!="{") - throw DeadlyImportError("empty texture unit!"); - while(Line!="}")//read until the end of the texture_unit - { - ss >> Line; - if(Line=="texture") - { - ss >> Line; - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); - } - }//end of texture unit - } - } - } - }//end of technique - - + ReadTechnique(ss, NewMaterial); } - DefaultLogger::get()->info(Line); //read informations from a custom material: if(Line=="set") @@ -237,6 +209,54 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const aiString ts(Line.c_str()); NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); } + + if(Line=="$shininess_strength") + { + ss >> Line; + float Shininess=fast_atof(Line.c_str()); + NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS_STRENGTH); + } + + if(Line=="$shininess_exponent") + { + ss >> Line; + float Shininess=fast_atof(Line.c_str()); + NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS); + } + + //Properties from Venetica: + if(Line=="$diffuse_map") + { + ss >> Line; + if(Line[0]=='"')// "file" -> file + Line=Line.substr(1, Line.size()-2); + aiString ts(Line.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + if(Line=="$specular_map") + { + ss >> Line; + if(Line[0]=='"')// "file" -> file + Line=Line.substr(1, Line.size()-2); + aiString ts(Line.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_SHININESS, 0)); + } + if(Line=="$normal_map") + { + ss >> Line; + if(Line[0]=='"')// "file" -> file + Line=Line.substr(1, Line.size()-2); + aiString ts(Line.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); + } + if(Line=="$light_map") + { + ss >> Line; + if(Line[0]=='"')// "file" -> file + Line=Line.substr(1, Line.size()-2); + aiString ts(Line.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0)); + } } }//end of material } @@ -248,6 +268,71 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const return NewMaterial; } +void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial) +{ + string Line; + ss >> Line; + if(Line!="{") + throw DeadlyImportError("empty technique!"); + while(Line!="}")//read until the end of the technique + { + ss >> Line; + if(Line=="pass") + { + ss >> Line; + if(Line!="{") + throw DeadlyImportError("empty pass!"); + while(Line!="}")//read until the end of the pass + { + ss >> Line; + if(Line=="ambient") + { + float r,g,b; + ss >> r >> g >> b; + const aiColor3D Color(r,g,b); + NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_AMBIENT); + } + else if(Line=="diffuse") + { + float r,g,b; + ss >> r >> g >> b; + const aiColor3D Color(r,g,b); + NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_DIFFUSE); + } + else if(Line=="specular") + { + float r,g,b; + ss >> r >> g >> b; + const aiColor3D Color(r,g,b); + NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_SPECULAR); + } + else if(Line=="emmisive") + { + float r,g,b; + ss >> r >> g >> b; + const aiColor3D Color(r,g,b); + NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_EMISSIVE); + } + else if(Line=="texture_unit") + { + ss >> Line; + if(Line!="{") + throw DeadlyImportError("empty texture unit!"); + while(Line!="}")//read until the end of the texture_unit + { + ss >> Line; + if(Line=="texture") + { + ss >> Line; + aiString ts(Line.c_str()); + NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + }//end of texture unit + } + } + } + }//end of technique +} }//namespace Ogre diff --git a/code/OgreXmlHelper.h b/code/OgreXmlHelper.hpp similarity index 96% rename from code/OgreXmlHelper.h rename to code/OgreXmlHelper.hpp index a5eb96f2e..34779a605 100644 --- a/code/OgreXmlHelper.h +++ b/code/OgreXmlHelper.hpp @@ -1,79 +1,79 @@ - -#include "irrXMLWrapper.h" -#include "fast_atof.h" - -namespace Assimp -{ -namespace Ogre -{ - -typedef irr::io::IrrXMLReader XmlReader; - - -//------------Helper Funktion to Get a Attribute Save--------------- -template inline t GetAttribute(XmlReader* Reader, std::string Name); - -/* -{ - 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); - else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); -} - -template<> inline float GetAttribute(XmlReader* Reader, std::string Name) -{ - 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()); -} - -template<> inline std::string GetAttribute(XmlReader* Reader, std::string Name) -{ - 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()); -} - -template<> inline bool GetAttribute(XmlReader* Reader, 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())); - } - else - throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); -} -//__________________________________________________________________ - -inline bool XmlRead(XmlReader* Reader) -{ - do - { - if(!Reader->read()) - return false; - } - while(Reader->getNodeType()!=irr::io::EXN_ELEMENT); - return true; -} - -}//namespace Ogre -}//namespace Assimp + +#include "irrXMLWrapper.h" +#include "fast_atof.h" + +namespace Assimp +{ +namespace Ogre +{ + +typedef irr::io::IrrXMLReader XmlReader; + + +//------------Helper Funktion to Get a Attribute Save--------------- +template inline t GetAttribute(XmlReader* Reader, std::string Name); + +/* +{ + 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); + else + throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); +} + +template<> inline float GetAttribute(XmlReader* Reader, std::string Name) +{ + 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()); +} + +template<> inline std::string GetAttribute(XmlReader* Reader, std::string Name) +{ + 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()); +} + +template<> inline bool GetAttribute(XmlReader* Reader, 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())); + } + else + throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str()); +} +//__________________________________________________________________ + +inline bool XmlRead(XmlReader* Reader) +{ + do + { + if(!Reader->read()) + return false; + } + while(Reader->getNodeType()!=irr::io::EXN_ELEMENT); + return true; +} + +}//namespace Ogre +}//namespace Assimp diff --git a/doc/dox.h b/doc/dox.h index 1b141ef85..e42167776 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -1457,9 +1457,10 @@ try to find the appendant material and skeleton file. The skeleton file must have the same name as the mesh file, e.g. fish.mesh.xml and fish.skeleton.xml. @subsection material Materials -The material file can have the same name as the mesh file, or you can use -Importer::Importer::SetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "materiafile.material") to specify -the name of the material file. This is especially usefull if multiply materials a stored in a single file. +The material file can have the same name as the mesh file (if the file is model.mesh or model.mesh.xml the +loader will try to load model.material), +or you can use Importer::Importer::SetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "materiafile.material") +to specify the name of the material file. This is especially usefull if multiply materials a stored in a single file. The importer will first try to load the material with the same name as the mesh and only if this can't be open try to load the alternate material file. The default material filename is "Scene.material". @@ -1468,6 +1469,7 @@ should read the custom material sektion in the Ogre Blender exporter Help File, can find in scripts/OgreImpoter/Assimp.tlp in the assimp source. If you don't set all values, don't worry, they will be ignored during import. If you want more properties in custom materials, you can easily expand the ogre material loader, it will be just a few lines for each property. +Just look in OgreImporterMaterial.cpp @subsection todo Todo - Load colors in custom materials