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.
parent
825a61fbc0
commit
09517b342b
|
@ -46,7 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include "OgreImporter.hpp"
|
#include "OgreImporter.hpp"
|
||||||
#include "TinyFormatter.h"
|
#include "TinyFormatter.h"
|
||||||
|
@ -65,160 +64,153 @@ static const aiImporterDesc desc = {
|
||||||
"mesh.xml"
|
"mesh.xml"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
{
|
{
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
|
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");
|
string ext = "mesh.xml";
|
||||||
int l=extension.length();
|
int len = ext.length();
|
||||||
return pFile.substr(pFile.length()-l, l)==extension;
|
return (ASSIMP_stricmp(pFile.substr(pFile.length()-len, len), ext) == 0);
|
||||||
}
|
|
||||||
else//Check file Header
|
|
||||||
{
|
|
||||||
const char* tokens[] = {"<mesh>"};
|
|
||||||
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
|
||||||
}
|
}
|
||||||
|
const char* tokens[] = {"<mesh>"};
|
||||||
|
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
|
||||||
{
|
{
|
||||||
m_CurrentFilename=pFile;
|
m_CurrentFilename = pFile;
|
||||||
m_CurrentIOHandler=pIOHandler;
|
m_CurrentIOHandler = pIOHandler;
|
||||||
m_CurrentScene=pScene;
|
m_CurrentScene = pScene;
|
||||||
|
|
||||||
//Open the File:
|
// -------------------- Initial file and XML operations --------------------
|
||||||
boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile));
|
|
||||||
if( file.get() == NULL)
|
|
||||||
throw DeadlyImportError("Failed to open file "+pFile+".");
|
|
||||||
|
|
||||||
//Read the Mesh File:
|
|
||||||
boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
|
|
||||||
boost::scoped_ptr<XmlReader> 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");
|
|
||||||
|
|
||||||
//Read root Node:
|
// Open
|
||||||
if(!(XmlRead(MeshFile.get()) && string(MeshFile->getNodeName())=="mesh"))
|
boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile));
|
||||||
|
if (file.get() == NULL)
|
||||||
|
throw DeadlyImportError("Failed to open file " + pFile);
|
||||||
|
|
||||||
|
// Read
|
||||||
|
boost::scoped_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(file.get()));
|
||||||
|
boost::scoped_ptr<XmlReader> 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 <mesh> 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 <mesh>! "+pFile+" "+MeshFile->getNodeName());
|
DefaultLogger::get()->debug("Reading shader geometry");
|
||||||
|
unsigned int NumVertices = GetAttribute<unsigned int>(reader.get(), "vertexcount");
|
||||||
|
|
||||||
|
NextNode(reader.get());
|
||||||
|
while(CurrentNodeNameEquals(reader, nnVertexBuffer))
|
||||||
|
ReadVertexBuffer(m_SharedGeometry, reader.get(), NumVertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
//eventually load shared geometry
|
// -------------------- Sub Meshes --------------------
|
||||||
XmlRead(MeshFile.get());//shared geometry is optional, so we need a reed for the next two if's
|
|
||||||
if(MeshFile->getNodeName()==string("sharedgeometry"))
|
if (!CurrentNodeNameEquals(reader, nnSubMeshes))
|
||||||
|
throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
|
||||||
|
|
||||||
|
list<boost::shared_ptr<SubMesh> > subMeshes;
|
||||||
|
vector<aiMaterial*> materials;
|
||||||
|
|
||||||
|
NextNode(reader.get());
|
||||||
|
while(CurrentNodeNameEquals(reader, nnSubMesh))
|
||||||
{
|
{
|
||||||
unsigned int NumVertices=GetAttribute<int>(MeshFile.get(), "vertexcount");;
|
SubMesh* submesh = new SubMesh();
|
||||||
|
ReadSubMesh(subMeshes.size(), *submesh, reader.get());
|
||||||
|
|
||||||
XmlRead(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;
|
||||||
while(MeshFile->getNodeName()==string("vertexbuffer"))
|
// so it is important to do this before pushing the mesh in the vector!
|
||||||
{
|
submesh->MaterialIndex = subMeshes.size();
|
||||||
ReadVertexBuffer(m_SharedGeometry, MeshFile.get(), NumVertices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Go to the submeshs:
|
subMeshes.push_back(boost::shared_ptr<SubMesh>(submesh));
|
||||||
if(MeshFile->getNodeName()!=string("submeshes"))
|
|
||||||
{
|
|
||||||
throw DeadlyImportError("No <submeshes> node in <mesh> node! "+pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Load the Material:
|
||||||
//-------------------Read the submeshs and materials:-----------------------
|
aiMaterial* MeshMat = LoadMaterial(submesh->MaterialName);
|
||||||
std::list<boost::shared_ptr<SubMesh> > SubMeshes;
|
|
||||||
vector<aiMaterial*> Materials;
|
|
||||||
XmlRead(MeshFile.get());
|
|
||||||
while(MeshFile->getNodeName()==string("submesh"))
|
|
||||||
{
|
|
||||||
SubMesh* theSubMesh=new SubMesh();
|
|
||||||
theSubMesh->MaterialName=GetAttribute<string>(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<SubMesh>(theSubMesh));
|
|
||||||
|
|
||||||
//Load the Material:
|
|
||||||
aiMaterial* MeshMat=LoadMaterial(theSubMesh->MaterialName);
|
|
||||||
|
|
||||||
//Set the Material:
|
// Set the Material:
|
||||||
Materials.push_back(MeshMat);
|
materials.push_back(MeshMat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SubMeshes.empty())
|
if (subMeshes.empty())
|
||||||
throw DeadlyImportError("no submesh loaded!");
|
throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
|
||||||
if(SubMeshes.size()!=Materials.size())
|
|
||||||
throw DeadlyImportError("materialcount doesn't match mesh count!");
|
|
||||||
|
|
||||||
//____________________________________________________________
|
// 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 submesh names.
|
||||||
//skip submeshnames (stupid irrxml)
|
/// @todo Should these be read to scene etc. metadata?
|
||||||
if(MeshFile->getNodeName()==string("submeshnames"))
|
if (CurrentNodeNameEquals(reader, nnSubMeshNames))
|
||||||
{
|
{
|
||||||
XmlRead(MeshFile.get());
|
NextNode(reader.get());
|
||||||
while(MeshFile->getNodeName()==string("submesh"))
|
while(CurrentNodeNameEquals(reader, nnSubMesh))
|
||||||
XmlRead(MeshFile.get());
|
NextNode(reader.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------- Skeleton --------------------
|
||||||
|
|
||||||
//----------------Load the skeleton: -------------------------------
|
|
||||||
vector<Bone> Bones;
|
vector<Bone> Bones;
|
||||||
vector<Animation> Animations;
|
vector<Animation> Animations;
|
||||||
if(MeshFile->getNodeName()==string("skeletonlink"))
|
if (CurrentNodeNameEquals(reader, nnSkeletonLink))
|
||||||
{
|
{
|
||||||
string SkeletonFile=GetAttribute<string>(MeshFile.get(), "name");
|
string SkeletonFile = GetAttribute<string>(reader.get(), "name");
|
||||||
LoadSkeleton(SkeletonFile, Bones, Animations);
|
if (!SkeletonFile.empty())
|
||||||
XmlRead(MeshFile.get());
|
LoadSkeleton(SkeletonFile, Bones, Animations);
|
||||||
|
else
|
||||||
|
DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty reference");
|
||||||
|
NextNode(reader.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
DefaultLogger::get()->debug("Mesh has no assigned skeleton with <" + nnSkeletonLink + ">");
|
||||||
DefaultLogger::get()->debug("No skeleton file will be loaded");
|
|
||||||
DefaultLogger::get()->debug(MeshFile->getNodeName());
|
|
||||||
}
|
|
||||||
//__________________________________________________________________
|
|
||||||
|
|
||||||
|
// Now there might be <boneassignments> for the shared geometry
|
||||||
|
if (CurrentNodeNameEquals(reader, "boneassignments"))
|
||||||
|
ReadBoneWeights(m_SharedGeometry, reader.get());
|
||||||
|
|
||||||
//now there might be boneassignments for the shared geometry:
|
// -------------------- Process Results --------------------
|
||||||
if(MeshFile->getNodeName()==string("boneassignments"))
|
BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, subMeshes)
|
||||||
{
|
|
||||||
ReadBoneWeights(m_SharedGeometry, MeshFile.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//----------------- Process Meshs -----------------------
|
|
||||||
BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
|
|
||||||
{
|
{
|
||||||
ProcessSubMesh(*theSubMesh, m_SharedGeometry);
|
ProcessSubMesh(*theSubMesh, m_SharedGeometry);
|
||||||
}
|
}
|
||||||
//_______________________________________________________
|
|
||||||
|
|
||||||
|
// -------------------- Apply to aiScene --------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//----------------- Now fill the Assimp scene ---------------------------
|
|
||||||
|
|
||||||
//put the aiMaterials in the scene:
|
//put the aiMaterials in the scene:
|
||||||
m_CurrentScene->mMaterials=new aiMaterial*[Materials.size()];
|
m_CurrentScene->mMaterials=new aiMaterial*[materials.size()];
|
||||||
m_CurrentScene->mNumMaterials=Materials.size();
|
m_CurrentScene->mNumMaterials=materials.size();
|
||||||
for(unsigned int i=0; i<Materials.size(); ++i)
|
for(unsigned int i=0; i<materials.size(); ++i)
|
||||||
m_CurrentScene->mMaterials[i]=Materials[i];
|
m_CurrentScene->mMaterials[i]=materials[i];
|
||||||
|
|
||||||
//create the aiMehs...
|
//create the aiMehs...
|
||||||
vector<aiMesh*> aiMeshes;
|
vector<aiMesh*> aiMeshes;
|
||||||
BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
|
BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, subMeshes)
|
||||||
{
|
{
|
||||||
aiMeshes.push_back(CreateAssimpSubMesh(*theSubMesh, Bones));
|
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");
|
m_CurrentScene->mRootNode=new aiNode("root");
|
||||||
|
|
||||||
//link the meshs with the root node:
|
//link the meshs with the root node:
|
||||||
m_CurrentScene->mRootNode->mMeshes=new unsigned int[SubMeshes.size()];
|
m_CurrentScene->mRootNode->mMeshes=new unsigned int[subMeshes.size()];
|
||||||
m_CurrentScene->mRootNode->mNumMeshes=SubMeshes.size();
|
m_CurrentScene->mRootNode->mNumMeshes=subMeshes.size();
|
||||||
for(unsigned int i=0; i<SubMeshes.size(); ++i)
|
|
||||||
m_CurrentScene->mRootNode->mMeshes[i]=i;
|
|
||||||
|
|
||||||
|
|
||||||
|
for(unsigned int i=0; i<subMeshes.size(); ++i)
|
||||||
|
m_CurrentScene->mRootNode->mMeshes[i]=i;
|
||||||
|
|
||||||
CreateAssimpSkeleton(Bones, Animations);
|
CreateAssimpSkeleton(Bones, Animations);
|
||||||
PutAnimationsInScene(Bones, Animations);
|
PutAnimationsInScene(Bones, Animations);
|
||||||
//___________________________________________________________
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,104 +1,126 @@
|
||||||
|
|
||||||
#include "BaseImporter.h"
|
#include "BaseImporter.h"
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "OgreXmlHelper.hpp"
|
#include "OgreXmlHelper.hpp"
|
||||||
#include "irrXMLWrapper.h"
|
#include "irrXMLWrapper.h"
|
||||||
|
|
||||||
/// Ogre Importer TODO
|
#include <vector>
|
||||||
/* - Read Vertex Colors
|
|
||||||
- Read multiple TexCoords
|
/** @todo Read Vertex Colors
|
||||||
|
@todo Read multiple TexCoords
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace Assimp
|
namespace Assimp
|
||||||
{
|
{
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
//Forward declarations:
|
|
||||||
struct Face;
|
struct Face;
|
||||||
struct Weight;
|
struct BoneWeight;
|
||||||
struct Bone;
|
struct Bone;
|
||||||
struct Animation;
|
struct Animation;
|
||||||
struct Track;
|
|
||||||
struct Keyframe;
|
|
||||||
|
|
||||||
///A submesh from Ogre
|
/// Ogre SubMesh
|
||||||
struct SubMesh
|
struct SubMesh
|
||||||
{
|
{
|
||||||
bool SharedData;
|
bool UseSharedGeometry;
|
||||||
|
bool Use32bitIndexes;
|
||||||
|
|
||||||
std::string Name;
|
std::string Name;
|
||||||
std::string MaterialName;
|
std::string MaterialName;
|
||||||
std::vector<Face> FaceList;
|
|
||||||
|
|
||||||
std::vector<aiVector3D> Positions; bool HasPositions;
|
bool HasGeometry;
|
||||||
std::vector<aiVector3D> Normals; bool HasNormals;
|
bool HasPositions;
|
||||||
std::vector<aiVector3D> Tangents; bool HasTangents;
|
bool HasNormals;
|
||||||
std::vector<std::vector<aiVector3D> > 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 HasTangents;
|
||||||
|
|
||||||
std::vector< std::vector<Weight> > Weights;//a list(inner) of bones for each vertex(outer)
|
std::vector<Face> Faces;
|
||||||
int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
|
std::vector<aiVector3D> Positions;
|
||||||
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<aiVector3D> Normals;
|
||||||
|
std::vector<aiVector3D> Tangents;
|
||||||
|
|
||||||
SubMesh(): SharedData(false), HasPositions(false), HasNormals(false), HasTangents(false),
|
/// Arbitrary number of texcoords, they are nearly always 2d, but Assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner).
|
||||||
MaterialIndex(-1), BonesUsed(0) {}//initialize everything
|
std::vector<std::vector<aiVector3D> > Uvs;
|
||||||
|
|
||||||
|
/// A list(inner) of bones for each vertex(outer).
|
||||||
|
std::vector<std::vector<BoneWeight> > 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
|
class OgreImporter : public BaseImporter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
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);
|
virtual bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
|
||||||
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<Bone>& Bones) 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<Bone>& bones) const;
|
||||||
|
|
||||||
//-------------------------------- OgreSkeleton.cpp -------------------------------
|
//-------------------------------- OgreSkeleton.cpp -------------------------------
|
||||||
|
|
||||||
/// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
|
/// 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<Bone> &Bones, std::vector<Animation> &Animations) const;
|
void LoadSkeleton(std::string FileName, std::vector<Bone> &Bones, std::vector<Animation> &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<Bone> &Bones, const std::vector<Animation> &Animations);
|
void PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
|
||||||
|
|
||||||
/// Creates the aiskeleton in current scene
|
/// Creates the aiSkeleton in current scene.
|
||||||
void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
|
void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &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<Bone> &Bones, aiNode* ParentNode);
|
static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode);
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------- OgreMaterial.cpp -------------------------------
|
//-------------------------------- OgreMaterial.cpp -------------------------------
|
||||||
|
|
||||||
aiMaterial* LoadMaterial(const std::string MaterialName) const;
|
aiMaterial* LoadMaterial(const std::string MaterialName) const;
|
||||||
void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial) const;
|
void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial) const;
|
||||||
|
|
||||||
|
|
||||||
|
// Now we don't have to give theses parameters to all functions
|
||||||
|
/// @todo Remove this m_Current* bookkeeping.
|
||||||
//Now we don't have to give theses parameters to all functions
|
|
||||||
std::string m_CurrentFilename;
|
std::string m_CurrentFilename;
|
||||||
std::string m_MaterialLibFilename;
|
std::string m_MaterialLibFilename;
|
||||||
bool m_TextureTypeFromFilename;
|
bool m_TextureTypeFromFilename;
|
||||||
|
@ -107,73 +129,71 @@ private:
|
||||||
SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh
|
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
|
struct Face
|
||||||
{
|
{
|
||||||
unsigned int VertexIndices[3];
|
unsigned int VertexIndices[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Ogre Bone assignment
|
||||||
struct BoneAssignment
|
struct BoneAssignment
|
||||||
{
|
{
|
||||||
unsigned int BoneId;//this is, what we get from ogre
|
/// Bone ID from Ogre.
|
||||||
std::string BoneName;//this is, what we need for assimp
|
unsigned int BoneId;
|
||||||
|
// Bone name for Assimp.
|
||||||
|
std::string BoneName;
|
||||||
};
|
};
|
||||||
|
|
||||||
///for a vertex->bone structur
|
/// Ogre Bone weight
|
||||||
struct Weight
|
struct BoneWeight
|
||||||
{
|
{
|
||||||
unsigned int BoneId;
|
/// Bone ID
|
||||||
|
unsigned int Id;
|
||||||
|
/// BoneWeight
|
||||||
float Value;
|
float Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Helper Class to describe an ogre-bone for the skeleton:
|
/// 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
|
struct Bone
|
||||||
{
|
{
|
||||||
|
std::string Name;
|
||||||
|
|
||||||
int Id;
|
int Id;
|
||||||
int ParentId;
|
int ParentId;
|
||||||
std::string Name;
|
|
||||||
aiVector3D Position;
|
aiVector3D Position;
|
||||||
float RotationAngle;
|
|
||||||
aiVector3D RotationAxis;
|
aiVector3D RotationAxis;
|
||||||
std::vector<int> Children;
|
float RotationAngle;
|
||||||
|
|
||||||
aiMatrix4x4 BoneToWorldSpace;
|
aiMatrix4x4 BoneToWorldSpace;
|
||||||
|
|
||||||
///ctor
|
std::vector<int> Children;
|
||||||
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<rval.Id; }
|
|
||||||
///this operator is needed to find a bone by its name in a vector<Bone>
|
|
||||||
bool operator==(const std::string& rval) const
|
|
||||||
{return Name==rval; }
|
|
||||||
bool operator==(const aiString& rval) const
|
|
||||||
{return Name==std::string(rval.data); }
|
|
||||||
|
|
||||||
// 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<Bone>
|
||||||
|
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<Bone>& Bones);
|
void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Ogre animation key frame
|
||||||
|
/** Transformations for a frame. */
|
||||||
///Describes an Ogre Animation
|
struct KeyFrame
|
||||||
struct Animation
|
|
||||||
{
|
|
||||||
std::string Name;
|
|
||||||
float Length;
|
|
||||||
std::vector<Track> Tracks;
|
|
||||||
};
|
|
||||||
|
|
||||||
///a track (keyframes for one bone) from an animation
|
|
||||||
struct Track
|
|
||||||
{
|
|
||||||
std::string BoneName;
|
|
||||||
std::vector<Keyframe> Keyframes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// keyframe (bone transformation) from a track from a animation
|
|
||||||
struct Keyframe
|
|
||||||
{
|
{
|
||||||
float Time;
|
float Time;
|
||||||
aiVector3D Position;
|
aiVector3D Position;
|
||||||
|
@ -181,5 +201,24 @@ struct Keyframe
|
||||||
aiVector3D Scaling;
|
aiVector3D Scaling;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Ogre animation track
|
||||||
|
/** Keyframes for one bone. */
|
||||||
|
struct Track
|
||||||
|
{
|
||||||
|
std::string BoneName;
|
||||||
|
std::vector<KeyFrame> Keyframes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Ogre animation
|
||||||
|
struct Animation
|
||||||
|
{
|
||||||
|
/// Name
|
||||||
|
std::string Name;
|
||||||
|
/// Length
|
||||||
|
float Length;
|
||||||
|
/// Tracks
|
||||||
|
std::vector<Track> Tracks;
|
||||||
|
};
|
||||||
|
|
||||||
}//namespace Ogre
|
}//namespace Ogre
|
||||||
}//namespace Assimp
|
}//namespace Assimp
|
||||||
|
|
|
@ -52,236 +52,281 @@ namespace Assimp
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void OgreImporter::ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader)
|
||||||
void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
|
|
||||||
{
|
{
|
||||||
if(Reader->getAttributeValue("usesharedvertices"))
|
if (reader->getAttributeValue("material"))
|
||||||
theSubMesh.SharedData=GetAttribute<bool>(Reader, "usesharedvertices");
|
submesh.MaterialName = GetAttribute<string>(reader, "material");
|
||||||
|
if (reader->getAttributeValue("use32bitindexes"))
|
||||||
|
submesh.Use32bitIndexes = GetAttribute<bool>(reader, "use32bitindexes");
|
||||||
|
if (reader->getAttributeValue("usesharedvertices"))
|
||||||
|
submesh.UseSharedGeometry = GetAttribute<bool>(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
|
//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
|
//of faces and geometry changed, and not if we have more than one of one
|
||||||
while( Reader->getNodeName()==string("faces")
|
/// @todo Fix above comment with better read logic below
|
||||||
|| Reader->getNodeName()==string("geometry")
|
|
||||||
|| Reader->getNodeName()==string("boneassignments"))
|
|
||||||
{
|
|
||||||
if(string(Reader->getNodeName())=="faces")//Read the face list
|
|
||||||
{
|
|
||||||
//some info logging:
|
|
||||||
unsigned int NumFaces=GetAttribute<int>(Reader, "count");
|
|
||||||
ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
|
|
||||||
DefaultLogger::get()->debug(ss.str());
|
|
||||||
|
|
||||||
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<unsigned int>(reader, "count");
|
||||||
|
|
||||||
|
NextNode(reader);
|
||||||
|
currentNodeName = reader->getNodeName();
|
||||||
|
|
||||||
|
while(currentNodeName == nnFace)
|
||||||
{
|
{
|
||||||
Face NewFace;
|
Face NewFace;
|
||||||
NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
|
NewFace.VertexIndices[0] = GetAttribute<int>(reader, "v1");
|
||||||
NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
|
NewFace.VertexIndices[1] = GetAttribute<int>(reader, "v2");
|
||||||
NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
|
NewFace.VertexIndices[2] = GetAttribute<int>(reader, "v3");
|
||||||
if(Reader->getAttributeValue("v4"))//this should be supported in the future
|
|
||||||
{
|
/// @todo Support quads
|
||||||
DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!");
|
if (!quadWarned && reader->getAttributeValue("v4"))
|
||||||
//throw DeadlyImportError("Submesh has quads, only traingles are supported!");
|
DefaultLogger::get()->warn("Submesh has quads, only triangles are supported at the moment!");
|
||||||
}
|
|
||||||
theSubMesh.FaceList.push_back(NewFace);
|
submesh.Faces.push_back(NewFace);
|
||||||
|
|
||||||
|
// Advance
|
||||||
|
NextNode(reader);
|
||||||
|
currentNodeName = reader->getNodeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}//end of faces
|
if (submesh.Faces.size() == numFaces)
|
||||||
else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata
|
DefaultLogger::get()->debug(Formatter::format() << " - Faces " << numFaces);
|
||||||
{
|
else
|
||||||
//some info logging:
|
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Faces.size() << " faces when should have read " << numFaces);
|
||||||
unsigned int NumVertices=GetAttribute<int>(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; i<theSubMesh.Uvs.size(); ++i)
|
|
||||||
{
|
|
||||||
if(theSubMesh.Uvs[i].size() != NumVertices)
|
|
||||||
throw DeadlyImportError("Wrong Number of Uvs loaded!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}//end of "geometry
|
|
||||||
|
|
||||||
|
|
||||||
else if(Reader->getNodeName()==string("boneassignments"))
|
|
||||||
{
|
|
||||||
ReadBoneWeights(theSubMesh, Reader);
|
|
||||||
}
|
}
|
||||||
|
else if (currentNodeName == nnGeometry)
|
||||||
|
{
|
||||||
|
unsigned int numVertices = GetAttribute<int>(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 &submesh, XmlReader *reader, const unsigned int numVertices)
|
||||||
void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices)
|
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("new Vertex Buffer");
|
DefaultLogger::get()->debug(Formatter::format() << "Reading vertex buffer with " << numVertices << " vertices");
|
||||||
|
|
||||||
bool ReadPositions=false;
|
|
||||||
bool ReadNormals=false;
|
|
||||||
bool ReadTangents=false;
|
|
||||||
unsigned int NumUvs=0;
|
|
||||||
|
|
||||||
//-------------------- check, what we need to read: --------------------------------
|
|
||||||
if(Reader->getAttributeValue("positions") && GetAttribute<bool>(Reader, "positions"))
|
|
||||||
{
|
|
||||||
ReadPositions=theSubMesh.HasPositions=true;
|
|
||||||
theSubMesh.Positions.reserve(NumVertices);
|
|
||||||
DefaultLogger::get()->debug("reading positions");
|
|
||||||
}
|
|
||||||
if(Reader->getAttributeValue("normals") && GetAttribute<bool>(Reader, "normals"))
|
|
||||||
{
|
|
||||||
ReadNormals=theSubMesh.HasNormals=true;
|
|
||||||
theSubMesh.Normals.reserve(NumVertices);
|
|
||||||
DefaultLogger::get()->debug("reading normals");
|
|
||||||
}
|
|
||||||
if(Reader->getAttributeValue("tangents") && GetAttribute<bool>(Reader, "tangents"))
|
|
||||||
{
|
|
||||||
ReadTangents=theSubMesh.HasTangents=true;
|
|
||||||
theSubMesh.Tangents.reserve(NumVertices);
|
|
||||||
DefaultLogger::get()->debug("reading tangents");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Reader->getAttributeValue("texture_coords"))
|
|
||||||
{
|
|
||||||
NumUvs=GetAttribute<unsigned int>(Reader, "texture_coords");
|
|
||||||
theSubMesh.Uvs.resize(NumUvs);
|
|
||||||
for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i) theSubMesh.Uvs[i].reserve(NumVertices);
|
|
||||||
DefaultLogger::get()->debug("reading texture coords");
|
|
||||||
}
|
|
||||||
//___________________________________________________________________
|
|
||||||
|
|
||||||
|
|
||||||
//check if we will load anything
|
|
||||||
if(!( ReadPositions || ReadNormals || ReadTangents || (NumUvs>0) ))
|
|
||||||
DefaultLogger::get()->warn("vertexbuffer seams to be empty!");
|
|
||||||
|
|
||||||
|
submesh.HasGeometry = true;
|
||||||
|
|
||||||
//read all the vertices:
|
if (reader->getAttributeValue("positions") && GetAttribute<bool>(reader, "positions"))
|
||||||
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->getNodeName()==string("vertex"))
|
submesh.HasPositions = true;
|
||||||
XmlRead(Reader);//Read an attribute tag
|
submesh.Positions.reserve(numVertices);
|
||||||
|
DefaultLogger::get()->debug(" - Has positions");
|
||||||
|
}
|
||||||
|
if (reader->getAttributeValue("normals") && GetAttribute<bool>(reader, "normals"))
|
||||||
|
{
|
||||||
|
submesh.HasNormals = true;
|
||||||
|
submesh.Normals.reserve(numVertices);
|
||||||
|
DefaultLogger::get()->debug(" - Has normals");
|
||||||
|
}
|
||||||
|
if (reader->getAttributeValue("tangents") && GetAttribute<bool>(reader, "tangents"))
|
||||||
|
{
|
||||||
|
submesh.HasTangents = true;
|
||||||
|
submesh.Tangents.reserve(numVertices);
|
||||||
|
DefaultLogger::get()->debug(" - Has tangents");
|
||||||
|
}
|
||||||
|
if (reader->getAttributeValue("texture_coords"))
|
||||||
|
{
|
||||||
|
submesh.Uvs.resize(GetAttribute<unsigned int>(reader, "texture_coords"));
|
||||||
|
for(size_t i=0, len=submesh.Uvs.size(); i<len; ++i)
|
||||||
|
submesh.Uvs[i].reserve(numVertices);
|
||||||
|
DefaultLogger::get()->debug(Formatter::format() << " - Has " << submesh.Uvs.size() << " texture coords");
|
||||||
|
}
|
||||||
|
|
||||||
//Position
|
if (!submesh.HasPositions)
|
||||||
if(ReadPositions && Reader->getNodeName()==string("position"))
|
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;
|
aiVector3D NewPos;
|
||||||
NewPos.x=GetAttribute<float>(Reader, "x");
|
NewPos.x = GetAttribute<float>(reader, "x");
|
||||||
NewPos.y=GetAttribute<float>(Reader, "y");
|
NewPos.y = GetAttribute<float>(reader, "y");
|
||||||
NewPos.z=GetAttribute<float>(Reader, "z");
|
NewPos.z = GetAttribute<float>(reader, "z");
|
||||||
theSubMesh.Positions.push_back(NewPos);
|
submesh.Positions.push_back(NewPos);
|
||||||
}
|
}
|
||||||
|
else if (submesh.HasNormals && currentNodeName == nnNormal)
|
||||||
//Normal
|
|
||||||
else if(ReadNormals && Reader->getNodeName()==string("normal"))
|
|
||||||
{
|
{
|
||||||
aiVector3D NewNormal;
|
aiVector3D NewNormal;
|
||||||
NewNormal.x=GetAttribute<float>(Reader, "x");
|
NewNormal.x = GetAttribute<float>(reader, "x");
|
||||||
NewNormal.y=GetAttribute<float>(Reader, "y");
|
NewNormal.y = GetAttribute<float>(reader, "y");
|
||||||
NewNormal.z=GetAttribute<float>(Reader, "z");
|
NewNormal.z = GetAttribute<float>(reader, "z");
|
||||||
theSubMesh.Normals.push_back(NewNormal);
|
submesh.Normals.push_back(NewNormal);
|
||||||
}
|
}
|
||||||
|
else if (submesh.HasTangents && currentNodeName == nnTangent)
|
||||||
//Tangent
|
|
||||||
else if(ReadTangents && Reader->getNodeName()==string("tangent"))
|
|
||||||
{
|
{
|
||||||
aiVector3D NewTangent;
|
aiVector3D NewTangent;
|
||||||
NewTangent.x=GetAttribute<float>(Reader, "x");
|
NewTangent.x = GetAttribute<float>(reader, "x");
|
||||||
NewTangent.y=GetAttribute<float>(Reader, "y");
|
NewTangent.y = GetAttribute<float>(reader, "y");
|
||||||
NewTangent.z=GetAttribute<float>(Reader, "z");
|
NewTangent.z = GetAttribute<float>(reader, "z");
|
||||||
theSubMesh.Tangents.push_back(NewTangent);
|
submesh.Tangents.push_back(NewTangent);
|
||||||
}
|
}
|
||||||
|
else if (submesh.Uvs.size() > 0 && currentNodeName == nnTexCoord)
|
||||||
//Uv:
|
|
||||||
else if(NumUvs>0 && Reader->getNodeName()==string("texcoord"))
|
|
||||||
{
|
{
|
||||||
for(unsigned int i=0; i<NumUvs; ++i)
|
for(size_t i=0, len=submesh.Uvs.size(); i<len; ++i)
|
||||||
{
|
{
|
||||||
if(Reader->getNodeName()!=string("texcoord"))
|
if (currentNodeName != nnTexCoord)
|
||||||
{
|
throw DeadlyImportError("Vertex buffer declared more UVs than can be found in a vertex");
|
||||||
DefaultLogger::get()->warn(string("Not enough UVs in Vertex: ")+Reader->getNodeName());
|
|
||||||
}
|
|
||||||
aiVector3D NewUv;
|
aiVector3D NewUv;
|
||||||
NewUv.x=GetAttribute<float>(Reader, "u");
|
NewUv.x = GetAttribute<float>(reader, "u");
|
||||||
NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
|
NewUv.y = GetAttribute<float>(reader, "v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????)
|
||||||
theSubMesh.Uvs[i].push_back(NewUv);
|
submesh.Uvs[i].push_back(NewUv);
|
||||||
XmlRead(Reader);
|
|
||||||
|
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
|
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)
|
// Sanity checks
|
||||||
{
|
if (submesh.HasNormals && submesh.Normals.size() != numVertices)
|
||||||
theSubMesh.Weights.resize(theSubMesh.Positions.size());
|
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Normals.size() << " normals when should have read " << numVertices);
|
||||||
while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
|
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<submesh.Uvs.size(); ++i)
|
||||||
{
|
{
|
||||||
Weight NewWeight;
|
if (submesh.Uvs[i].size() != numVertices)
|
||||||
unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
|
throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Uvs[i].size()
|
||||||
NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
|
<< " uvs for uv index " << i << " when should have read " << numVertices);
|
||||||
NewWeight.Value=GetAttribute<float>(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<int>(reader, "boneindex");
|
||||||
|
weight.Value = GetAttribute<float>(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<int>(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)-----------------------
|
//---------------Make all Vertexes unique: (this is required by assimp)-----------------------
|
||||||
vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
|
vector<Face> UniqueFaceList(submesh.Faces.size());
|
||||||
unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
|
unsigned int UniqueVertexCount=submesh.Faces.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
|
||||||
|
|
||||||
vector<aiVector3D> UniquePositions(UniqueVertexCount);
|
vector<aiVector3D> UniquePositions(UniqueVertexCount);
|
||||||
|
|
||||||
|
@ -289,9 +334,9 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr
|
||||||
|
|
||||||
vector<aiVector3D> UniqueTangents(UniqueVertexCount);
|
vector<aiVector3D> UniqueTangents(UniqueVertexCount);
|
||||||
|
|
||||||
vector< vector<Weight> > UniqueWeights(UniqueVertexCount);
|
vector< vector<BoneWeight> > UniqueWeights(UniqueVertexCount);
|
||||||
|
|
||||||
vector< vector<aiVector3D> > UniqueUvs(theSubMesh.Uvs.size());
|
vector< vector<aiVector3D> > UniqueUvs(submesh.Uvs.size());
|
||||||
for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
|
for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,25 +345,25 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr
|
||||||
/*We can use this loop to copy vertex informations from the shared data pool. In order to do so
|
/*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*/
|
we just use a reference to a submodel instead of our submodel itself*/
|
||||||
|
|
||||||
SubMesh& VertexSource= theSubMesh.SharedData ? theSharedGeometry : theSubMesh;
|
SubMesh& VertexSource= submesh.UseSharedGeometry ? sharedGeometry : submesh;
|
||||||
if(theSubMesh.SharedData)//copy vertexinformations to our mesh:
|
if(submesh.UseSharedGeometry)//copy vertexinformations to our mesh:
|
||||||
{
|
{
|
||||||
theSubMesh.HasPositions=theSharedGeometry.HasPositions;
|
submesh.HasPositions=sharedGeometry.HasPositions;
|
||||||
theSubMesh.HasNormals=theSharedGeometry.HasNormals;
|
submesh.HasNormals=sharedGeometry.HasNormals;
|
||||||
theSubMesh.HasTangents=theSharedGeometry.HasTangents;
|
submesh.HasTangents=sharedGeometry.HasTangents;
|
||||||
|
|
||||||
theSubMesh.BonesUsed=theSharedGeometry.BonesUsed;
|
submesh.BonesUsed=sharedGeometry.BonesUsed;
|
||||||
|
|
||||||
UniqueUvs.resize(theSharedGeometry.Uvs.size());
|
UniqueUvs.resize(sharedGeometry.Uvs.size());
|
||||||
for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
|
for(unsigned int i=0; i<UniqueUvs.size(); ++i) UniqueUvs[i].resize(UniqueVertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
for(unsigned int i=0; i<submesh.Faces.size(); ++i)
|
||||||
{
|
{
|
||||||
//We precalculate the index vlaues her, because we need them in all vertex attributes
|
//We precalculate the index vlaues her, because we need them in all vertex attributes
|
||||||
unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
|
unsigned int Vertex1=submesh.Faces[i].VertexIndices[0];
|
||||||
unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
|
unsigned int Vertex2=submesh.Faces[i].VertexIndices[1];
|
||||||
unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
|
unsigned int Vertex3=submesh.Faces[i].VertexIndices[2];
|
||||||
|
|
||||||
UniquePositions[3*i+0]=VertexSource.Positions[Vertex1];
|
UniquePositions[3*i+0]=VertexSource.Positions[Vertex1];
|
||||||
UniquePositions[3*i+1]=VertexSource.Positions[Vertex2];
|
UniquePositions[3*i+1]=VertexSource.Positions[Vertex2];
|
||||||
|
@ -364,33 +409,33 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr
|
||||||
|
|
||||||
//now we have the unique datas, but want them in the SubMesh, so we swap all the containers:
|
//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
|
//if we don't have one of them, we just swap empty containers, so everything is ok
|
||||||
theSubMesh.FaceList.swap(UniqueFaceList);
|
submesh.Faces.swap(UniqueFaceList);
|
||||||
theSubMesh.Positions.swap(UniquePositions);
|
submesh.Positions.swap(UniquePositions);
|
||||||
theSubMesh.Normals.swap(UniqueNormals);
|
submesh.Normals.swap(UniqueNormals);
|
||||||
theSubMesh.Tangents.swap(UniqueTangents);
|
submesh.Tangents.swap(UniqueTangents);
|
||||||
theSubMesh.Uvs.swap(UniqueUvs);
|
submesh.Uvs.swap(UniqueUvs);
|
||||||
theSubMesh.Weights.swap(UniqueWeights);
|
submesh.Weights.swap(UniqueWeights);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//------------- normalize weights -----------------------------
|
//------------- normalize weights -----------------------------
|
||||||
//The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
|
//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:
|
//so we have to make this sure:
|
||||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
for(unsigned int VertexId=0; VertexId<submesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||||
{
|
{
|
||||||
float WeightSum=0.0f;
|
float WeightSum=0.0f;
|
||||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
for(unsigned int BoneId=0; BoneId<submesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||||
{
|
{
|
||||||
WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
|
WeightSum+=submesh.Weights[VertexId][BoneId].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the sum is too far away from 1
|
//check if the sum is too far away from 1
|
||||||
if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
|
if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
|
||||||
{
|
{
|
||||||
//normalize all weights:
|
//normalize all weights:
|
||||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
for(unsigned int BoneId=0; BoneId<submesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||||
{
|
{
|
||||||
theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
|
submesh.Weights[VertexId][BoneId].Value/=WeightSum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,7 +445,7 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
|
aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& submesh, const vector<Bone>& bones) const
|
||||||
{
|
{
|
||||||
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
|
||||||
(void)m_CurrentScene;
|
(void)m_CurrentScene;
|
||||||
|
@ -408,63 +453,63 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
|
||||||
aiMesh* NewAiMesh=new aiMesh();
|
aiMesh* NewAiMesh=new aiMesh();
|
||||||
|
|
||||||
//Positions
|
//Positions
|
||||||
NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
|
NewAiMesh->mVertices=new aiVector3D[submesh.Positions.size()];
|
||||||
memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
|
memcpy(NewAiMesh->mVertices, &submesh.Positions[0], submesh.Positions.size()*sizeof(aiVector3D));
|
||||||
NewAiMesh->mNumVertices=theSubMesh.Positions.size();
|
NewAiMesh->mNumVertices=submesh.Positions.size();
|
||||||
|
|
||||||
//Normals
|
//Normals
|
||||||
if(theSubMesh.HasNormals)
|
if(submesh.HasNormals)
|
||||||
{
|
{
|
||||||
NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
|
NewAiMesh->mNormals=new aiVector3D[submesh.Normals.size()];
|
||||||
memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
|
memcpy(NewAiMesh->mNormals, &submesh.Normals[0], submesh.Normals.size()*sizeof(aiVector3D));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//until we have support for bitangents, no tangents will be written
|
//until we have support for bitangents, no tangents will be written
|
||||||
/*
|
/*
|
||||||
//Tangents
|
//Tangents
|
||||||
if(theSubMesh.HasTangents)
|
if(submesh.HasTangents)
|
||||||
{
|
{
|
||||||
NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()];
|
NewAiMesh->mTangents=new aiVector3D[submesh.Tangents.size()];
|
||||||
memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D));
|
memcpy(NewAiMesh->mTangents, &submesh.Tangents[0], submesh.Tangents.size()*sizeof(aiVector3D));
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//Uvs
|
//Uvs
|
||||||
if(theSubMesh.Uvs.size()>0)
|
if(submesh.Uvs.size()>0)
|
||||||
{
|
{
|
||||||
for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i)
|
for(unsigned int i=0; i<submesh.Uvs.size(); ++i)
|
||||||
{
|
{
|
||||||
NewAiMesh->mNumUVComponents[i]=2;
|
NewAiMesh->mNumUVComponents[i]=2;
|
||||||
NewAiMesh->mTextureCoords[i]=new aiVector3D[theSubMesh.Uvs[i].size()];
|
NewAiMesh->mTextureCoords[i]=new aiVector3D[submesh.Uvs[i].size()];
|
||||||
memcpy(NewAiMesh->mTextureCoords[i], &(theSubMesh.Uvs[i][0]), theSubMesh.Uvs[i].size()*sizeof(aiVector3D));
|
memcpy(NewAiMesh->mTextureCoords[i], &(submesh.Uvs[i][0]), submesh.Uvs[i].size()*sizeof(aiVector3D));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------- Bones --------------------------------------------
|
//---------------------------------------- bones --------------------------------------------
|
||||||
|
|
||||||
//Copy the weights in in Bone-Vertices Struktur
|
//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
|
//(we have them in a Vertex-bones Structur, this is much easier for making them unique, which is required by assimp
|
||||||
vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
|
vector< vector<aiVertexWeight> > aiWeights(submesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
|
||||||
for(unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
|
for(unsigned int VertexId=0; VertexId<submesh.Weights.size(); ++VertexId)//iterate over all vertices
|
||||||
{
|
{
|
||||||
for(unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
for(unsigned int BoneId=0; BoneId<submesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
|
||||||
{
|
{
|
||||||
aiVertexWeight NewWeight;
|
aiVertexWeight NewWeight;
|
||||||
NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
|
NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
|
||||||
NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
|
NewWeight.mWeight=submesh.Weights[VertexId][BoneId].Value;
|
||||||
aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
|
aiWeights[submesh.Weights[VertexId][BoneId].Id].push_back(NewWeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vector<aiBone*> aiBones;
|
vector<aiBone*> 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
|
//create all the bones and fill them with informations
|
||||||
for(unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
|
for(unsigned int i=0; i<submesh.BonesUsed; ++i)
|
||||||
{
|
{
|
||||||
if(aiWeights[i].size()>0)
|
if(aiWeights[i].size()>0)
|
||||||
{
|
{
|
||||||
|
@ -472,8 +517,8 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
|
||||||
NewBone->mNumWeights=aiWeights[i].size();
|
NewBone->mNumWeights=aiWeights[i].size();
|
||||||
NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
|
NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
|
||||||
memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(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->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->mOffsetMatrix=bones[i].BoneToWorldSpace;
|
||||||
|
|
||||||
aiBones.push_back(NewBone);
|
aiBones.push_back(NewBone);
|
||||||
}
|
}
|
||||||
|
@ -491,20 +536,20 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
|
||||||
|
|
||||||
|
|
||||||
//Faces
|
//Faces
|
||||||
NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
|
NewAiMesh->mFaces=new aiFace[submesh.Faces.size()];
|
||||||
for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
|
for(unsigned int i=0; i<submesh.Faces.size(); ++i)
|
||||||
{
|
{
|
||||||
NewAiMesh->mFaces[i].mNumIndices=3;
|
NewAiMesh->mFaces[i].mNumIndices=3;
|
||||||
NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
|
NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
|
||||||
|
|
||||||
NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
|
NewAiMesh->mFaces[i].mIndices[0]=submesh.Faces[i].VertexIndices[0];
|
||||||
NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
|
NewAiMesh->mFaces[i].mIndices[1]=submesh.Faces[i].VertexIndices[1];
|
||||||
NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
|
NewAiMesh->mFaces[i].mIndices[2]=submesh.Faces[i].VertexIndices[2];
|
||||||
}
|
}
|
||||||
NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
|
NewAiMesh->mNumFaces=submesh.Faces.size();
|
||||||
|
|
||||||
//Link the material:
|
//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;
|
return NewAiMesh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,18 +77,18 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
if(!SkeletonFile)
|
if(!SkeletonFile)
|
||||||
throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
|
throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
|
||||||
|
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("skeleton")!=SkeletonFile->getNodeName())
|
if(string("skeleton")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
|
throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------load bones-----------------------------------------
|
//------------------------------------load bones-----------------------------------------
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("bones")!=SkeletonFile->getNodeName())
|
if(string("bones")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("No bones node in skeleton "+FileName);
|
throw DeadlyImportError("No bones node in skeleton "+FileName);
|
||||||
|
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
|
|
||||||
while(string("bone")==SkeletonFile->getNodeName())
|
while(string("bone")==SkeletonFile->getNodeName())
|
||||||
{
|
{
|
||||||
|
@ -100,7 +100,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
|
NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
|
||||||
|
|
||||||
//load the position:
|
//load the position:
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("position")!=SkeletonFile->getNodeName())
|
if(string("position")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("Position is not first node in Bone!");
|
throw DeadlyImportError("Position is not first node in Bone!");
|
||||||
NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
|
||||||
|
@ -108,11 +108,11 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
|
||||||
|
|
||||||
//Rotation:
|
//Rotation:
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("rotation")!=SkeletonFile->getNodeName())
|
if(string("rotation")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
throw DeadlyImportError("Rotation is not the second node in Bone!");
|
||||||
NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("axis")!=SkeletonFile->getNodeName())
|
if(string("axis")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("No axis specified for bone rotation!");
|
throw DeadlyImportError("No axis specified for bone rotation!");
|
||||||
NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||||
|
@ -123,7 +123,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
Bones.push_back(NewBone);
|
Bones.push_back(NewBone);
|
||||||
|
|
||||||
//Proceed to the next bone:
|
//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:
|
//The bones in the file a not neccesarly ordered by there id's so we do it now:
|
||||||
std::sort(Bones.begin(), Bones.end());
|
std::sort(Bones.begin(), Bones.end());
|
||||||
|
@ -153,7 +153,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
throw DeadlyImportError("no bonehierarchy node in "+FileName);
|
throw DeadlyImportError("no bonehierarchy node in "+FileName);
|
||||||
|
|
||||||
DefaultLogger::get()->debug("loading bonehierarchy...");
|
DefaultLogger::get()->debug("loading bonehierarchy...");
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
while(string("boneparent")==SkeletonFile->getNodeName())
|
while(string("boneparent")==SkeletonFile->getNodeName())
|
||||||
{
|
{
|
||||||
string Child, Parent;
|
string Child, Parent;
|
||||||
|
@ -167,7 +167,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
Bones[ChildId].ParentId=ParentId;
|
Bones[ChildId].ParentId=ParentId;
|
||||||
Bones[ParentId].Children.push_back(ChildId);
|
Bones[ParentId].Children.push_back(ChildId);
|
||||||
|
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
}
|
}
|
||||||
//_____________________________________________________________________________
|
//_____________________________________________________________________________
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
|
if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("Loading Animations");
|
DefaultLogger::get()->debug("Loading Animations");
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
while(string("animation")==SkeletonFile->getNodeName())
|
while(string("animation")==SkeletonFile->getNodeName())
|
||||||
{
|
{
|
||||||
Animation NewAnimation;
|
Animation NewAnimation;
|
||||||
|
@ -195,30 +195,30 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
|
NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
|
||||||
|
|
||||||
//Load all Tracks
|
//Load all Tracks
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("tracks")!=SkeletonFile->getNodeName())
|
if(string("tracks")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("no tracks node in animation");
|
throw DeadlyImportError("no tracks node in animation");
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
while(string("track")==SkeletonFile->getNodeName())
|
while(string("track")==SkeletonFile->getNodeName())
|
||||||
{
|
{
|
||||||
Track NewTrack;
|
Track NewTrack;
|
||||||
NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
|
NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
|
||||||
|
|
||||||
//Load all keyframes;
|
//Load all keyframes;
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("keyframes")!=SkeletonFile->getNodeName())
|
if(string("keyframes")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("no keyframes node!");
|
throw DeadlyImportError("no keyframes node!");
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
while(string("keyframe")==SkeletonFile->getNodeName())
|
while(string("keyframe")==SkeletonFile->getNodeName())
|
||||||
{
|
{
|
||||||
Keyframe NewKeyframe;
|
KeyFrame NewKeyframe;
|
||||||
NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
|
NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
|
||||||
|
|
||||||
//loop over the attributes:
|
//loop over the attributes:
|
||||||
|
|
||||||
while(true) //will quit, if a Node is not a animationkey
|
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
|
//If any property doesn't show up, it will keep its initialization value
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
{
|
{
|
||||||
float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
|
||||||
aiVector3D RotationAxis;
|
aiVector3D RotationAxis;
|
||||||
XmlRead(SkeletonFile);
|
NextNode(SkeletonFile);
|
||||||
if(string("axis")!=SkeletonFile->getNodeName())
|
if(string("axis")!=SkeletonFile->getNodeName())
|
||||||
throw DeadlyImportError("No axis for keyframe rotation!");
|
throw DeadlyImportError("No axis for keyframe rotation!");
|
||||||
RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
|
||||||
|
@ -247,7 +247,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
|
||||||
RotationAxis.x=1.0f;
|
RotationAxis.x=1.0f;
|
||||||
if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
|
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);
|
NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
|
||||||
|
|
|
@ -9,80 +9,84 @@ namespace Ogre
|
||||||
|
|
||||||
typedef irr::io::IrrXMLReader XmlReader;
|
typedef irr::io::IrrXMLReader XmlReader;
|
||||||
|
|
||||||
|
static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "")
|
||||||
//------------Helper Funktion to Get a Attribute Save---------------
|
|
||||||
template<typename t> inline t GetAttribute(XmlReader* Reader, std::string Name);
|
|
||||||
|
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
BOOST_STATIC_ASSERT(false);
|
if (!error.empty())
|
||||||
return t();
|
throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'");
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
template<> inline int GetAttribute<int>(XmlReader* Reader, std::string Name)
|
|
||||||
{
|
|
||||||
const char* Value=Reader->getAttributeValue(Name.c_str());
|
|
||||||
if(Value)
|
|
||||||
return atoi(Value);
|
|
||||||
else
|
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<unsigned int>(XmlReader* Reader, std::string Name)
|
template<typename T>
|
||||||
|
inline T GetAttribute(const XmlReader* reader, const std::string &name);
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline int GetAttribute<int>(const XmlReader* reader, const std::string &name)
|
||||||
{
|
{
|
||||||
const char* Value=Reader->getAttributeValue(Name.c_str());
|
const char* value = reader->getAttributeValue(name.c_str());
|
||||||
if(Value)
|
if (value)
|
||||||
return static_cast<unsigned int>(atoi(Value));//yes, ugly, but pfff
|
return atoi(value);
|
||||||
else
|
else
|
||||||
throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
|
ThrowAttibuteError(reader, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline float GetAttribute<float>(XmlReader* Reader, std::string Name)
|
template<>
|
||||||
|
inline unsigned int GetAttribute<unsigned int>(const XmlReader* reader, const std::string &name)
|
||||||
{
|
{
|
||||||
const char* Value=Reader->getAttributeValue(Name.c_str());
|
const char* value = reader->getAttributeValue(name.c_str());
|
||||||
if(Value)
|
if (value)
|
||||||
return fast_atof(Value);
|
return static_cast<unsigned int>(atoi(value)); ///< @todo Find a better way...
|
||||||
else
|
else
|
||||||
throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
|
ThrowAttibuteError(reader, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline std::string GetAttribute<std::string>(XmlReader* Reader, std::string Name)
|
template<>
|
||||||
|
inline float GetAttribute<float>(const XmlReader* reader, const std::string &name)
|
||||||
{
|
{
|
||||||
const char* Value=Reader->getAttributeValue(Name.c_str());
|
const char* value = reader->getAttributeValue(name.c_str());
|
||||||
if(Value)
|
if (value)
|
||||||
return std::string(Value);
|
return fast_atof(value);
|
||||||
else
|
else
|
||||||
throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
|
ThrowAttibuteError(reader, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline bool GetAttribute<bool>(XmlReader* Reader, std::string Name)
|
template<>
|
||||||
|
inline std::string GetAttribute<std::string>(const XmlReader* reader, const std::string &name)
|
||||||
{
|
{
|
||||||
const char* Value=Reader->getAttributeValue(Name.c_str());
|
const char* value = reader->getAttributeValue(name.c_str());
|
||||||
if(Value)
|
if (value)
|
||||||
{
|
return std::string(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
|
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<bool>(const XmlReader* reader, const std::string &name)
|
||||||
|
{
|
||||||
|
std::string value = GetAttribute<std::string>(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
|
do
|
||||||
{
|
{
|
||||||
if(!Reader->read())
|
if (!reader->read())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while(Reader->getNodeType()!=irr::io::EXN_ELEMENT);
|
while(reader->getNodeType() != irr::io::EXN_ELEMENT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}//namespace Ogre
|
inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &name)
|
||||||
}//namespace Assimp
|
{
|
||||||
|
return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Ogre
|
||||||
|
} // Assimp
|
||||||
|
|
Loading…
Reference in New Issue