소스 검색

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.

Jonne Nauha 11 년 전
부모
커밋
09517b342b
5개의 변경된 파일562개의 추가작업 그리고 484개의 파일을 삭제
  1. 97 107
      code/OgreImporter.cpp
  2. 129 90
      code/OgreImporter.hpp
  3. 268 223
      code/OgreMesh.cpp
  4. 18 18
      code/OgreSkeleton.cpp
  5. 50 46
      code/OgreXmlHelper.hpp

+ 97 - 107
code/OgreImporter.cpp

@@ -46,7 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <vector>
 #include <sstream>
-using namespace std;
 
 #include "OgreImporter.hpp"
 #include "TinyFormatter.h"
@@ -65,160 +64,153 @@ static const aiImporterDesc desc = {
 	"mesh.xml"
 };
 
+using namespace std;
+
 namespace Assimp
 {
 namespace Ogre
 {
 
-
 bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
 {
-	if(!checkSig)//Check File Extension
+	if (!checkSig)
 	{
-		std::string extension("mesh.xml");
-		int l=extension.length();
-		return pFile.substr(pFile.length()-l, l)==extension;
-	}
-	else//Check file Header
-	{
-		const char* tokens[] = {"<mesh>"};
-		return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+		string ext = "mesh.xml";
+		int len = ext.length();
+		return (ASSIMP_stricmp(pFile.substr(pFile.length()-len, len), ext) == 0);
 	}
+	const char* tokens[] = {"<mesh>"};
+	return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
 }
 
-
 void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
 {
-	m_CurrentFilename=pFile;
-	m_CurrentIOHandler=pIOHandler;
-	m_CurrentScene=pScene;
+	m_CurrentFilename = pFile;
+	m_CurrentIOHandler = pIOHandler;
+	m_CurrentScene = pScene;
 
-	//Open the File:
+	// -------------------- Initial file and XML operations --------------------
+	
+	// Open
 	boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile));
-	if( file.get() == NULL)
-		throw DeadlyImportError("Failed to open file "+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);
+	// 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);
 
-	DefaultLogger::get()->debug("Mesh File opened");
+	// Read root node
+	NextNode(reader.get());
+	if (!CurrentNodeNameEquals(reader, "mesh"))
+		throw DeadlyImportError("Root node is not <mesh> but <" + string(reader->getNodeName()) + ">");
 	
-	//Read root Node:
-	if(!(XmlRead(MeshFile.get()) && string(MeshFile->getNodeName())=="mesh"))
-	{
-		throw DeadlyImportError("Root Node is not <mesh>! "+pFile+"  "+MeshFile->getNodeName());
-	}
-
-	//eventually load shared geometry
-	XmlRead(MeshFile.get());//shared geometry is optional, so we need a reed for the next two if's
-	if(MeshFile->getNodeName()==string("sharedgeometry"))
+	// 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))
 	{
-		unsigned int NumVertices=GetAttribute<int>(MeshFile.get(), "vertexcount");;
+		DefaultLogger::get()->debug("Reading shader geometry");
+		unsigned int NumVertices = GetAttribute<unsigned int>(reader.get(), "vertexcount");
 
-		XmlRead(MeshFile.get());
-		while(MeshFile->getNodeName()==string("vertexbuffer"))
-		{
-			ReadVertexBuffer(m_SharedGeometry, MeshFile.get(), NumVertices);
-		}
+		NextNode(reader.get());
+		while(CurrentNodeNameEquals(reader, nnVertexBuffer))
+			ReadVertexBuffer(m_SharedGeometry, reader.get(), NumVertices);
 	}
 
-	//Go to the submeshs:
-	if(MeshFile->getNodeName()!=string("submeshes"))
-	{
-		throw DeadlyImportError("No <submeshes> node in <mesh> node! "+pFile);
-	}
+	// -------------------- Sub Meshes --------------------
+
+	if (!CurrentNodeNameEquals(reader, nnSubMeshes))
+		throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
 
+	list<boost::shared_ptr<SubMesh> > subMeshes;
+	vector<aiMaterial*> materials;
 
-	//-------------------Read the submeshs and materials:-----------------------
-	std::list<boost::shared_ptr<SubMesh> > SubMeshes;
-	vector<aiMaterial*> Materials;
-	XmlRead(MeshFile.get());
-	while(MeshFile->getNodeName()==string("submesh"))
+	NextNode(reader.get());
+	while(CurrentNodeNameEquals(reader, nnSubMesh))
 	{
-		SubMesh* theSubMesh=new SubMesh();
-		theSubMesh->MaterialName=GetAttribute<string>(MeshFile.get(), "material");
-		DefaultLogger::get()->debug("Loading Submehs with Material: "+theSubMesh->MaterialName);
-		ReadSubMesh(*theSubMesh, MeshFile.get());
+		SubMesh* submesh = new SubMesh();
+		ReadSubMesh(subMeshes.size(), *submesh, reader.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();
+		// Just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n;
+		// so it is important to do this before pushing the mesh in the vector!
+		submesh->MaterialIndex = subMeshes.size();
 
-		SubMeshes.push_back(boost::shared_ptr<SubMesh>(theSubMesh));
+		subMeshes.push_back(boost::shared_ptr<SubMesh>(submesh));
 
-		//Load the Material:
-		aiMaterial* MeshMat=LoadMaterial(theSubMesh->MaterialName);
+		// Load the Material:
+		aiMaterial* MeshMat = LoadMaterial(submesh->MaterialName);
 		
-		//Set the Material:
-		Materials.push_back(MeshMat);
+		// Set the Material:
+		materials.push_back(MeshMat);
 	}
 
-	if(SubMeshes.empty())
-		throw DeadlyImportError("no submesh loaded!");
-	if(SubMeshes.size()!=Materials.size())
-		throw DeadlyImportError("materialcount doesn't match mesh count!");
+	if (subMeshes.empty())
+		throw DeadlyImportError("Could not find <submeshes> node inside root <mesh> node");
 
-	//____________________________________________________________
+	// This is really a internal error if we failed to create dummy materials.
+	if (subMeshes.size() != materials.size())
+		throw DeadlyImportError("Internal Error: Material count does not match the submesh count");
 
-
-	//skip submeshnames (stupid irrxml)
-	if(MeshFile->getNodeName()==string("submeshnames"))
+	// Skip submesh names.
+	/// @todo Should these be read to scene etc. metadata?
+	if (CurrentNodeNameEquals(reader, nnSubMeshNames))
 	{
-		XmlRead(MeshFile.get());
-		while(MeshFile->getNodeName()==string("submesh"))
-			XmlRead(MeshFile.get());
+		NextNode(reader.get());
+		while(CurrentNodeNameEquals(reader, nnSubMesh))
+			NextNode(reader.get());
 	}
 
+	// -------------------- Skeleton --------------------
 
-	//----------------Load the skeleton: -------------------------------
 	vector<Bone> Bones;
 	vector<Animation> Animations;
-	if(MeshFile->getNodeName()==string("skeletonlink"))
+	if (CurrentNodeNameEquals(reader, nnSkeletonLink))
 	{
-		string SkeletonFile=GetAttribute<string>(MeshFile.get(), "name");
-		LoadSkeleton(SkeletonFile, Bones, Animations);
-		XmlRead(MeshFile.get());
+		string SkeletonFile = GetAttribute<string>(reader.get(), "name");
+		if (!SkeletonFile.empty())
+			LoadSkeleton(SkeletonFile, Bones, Animations);
+		else
+			DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty reference");
+		NextNode(reader.get());
 	}
 	else
-	{
-		DefaultLogger::get()->debug("No skeleton file will be loaded");
-		DefaultLogger::get()->debug(MeshFile->getNodeName());
-	}
-	//__________________________________________________________________
-
-
-	//now there might be boneassignments for the shared geometry:
-	if(MeshFile->getNodeName()==string("boneassignments"))
-	{
-		ReadBoneWeights(m_SharedGeometry, MeshFile.get());
-	}
+		DefaultLogger::get()->debug("Mesh has no assigned skeleton with <" + nnSkeletonLink + ">");
 
+	// Now there might be <boneassignments> for the shared geometry
+	if (CurrentNodeNameEquals(reader, "boneassignments"))
+		ReadBoneWeights(m_SharedGeometry, reader.get());
 
-	//----------------- Process Meshs -----------------------
-	BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
+	// -------------------- Process Results --------------------
+	BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, subMeshes)
 	{
 		ProcessSubMesh(*theSubMesh, m_SharedGeometry);
 	}
-	//_______________________________________________________
 
+	// -------------------- Apply to aiScene --------------------
 
-
-	
-	//----------------- Now fill the Assimp scene ---------------------------
-	
 	//put the aiMaterials in the scene:
-	m_CurrentScene->mMaterials=new aiMaterial*[Materials.size()];
-	m_CurrentScene->mNumMaterials=Materials.size();
-	for(unsigned int i=0; i<Materials.size(); ++i)
-		m_CurrentScene->mMaterials[i]=Materials[i];
+	m_CurrentScene->mMaterials=new aiMaterial*[materials.size()];
+	m_CurrentScene->mNumMaterials=materials.size();
+	for(unsigned int i=0; i<materials.size(); ++i)
+		m_CurrentScene->mMaterials[i]=materials[i];
 
 	//create the aiMehs... 
 	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));
 	}
@@ -231,16 +223,14 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
 	m_CurrentScene->mRootNode=new aiNode("root");
 
 	//link the meshs with the root node:
-	m_CurrentScene->mRootNode->mMeshes=new unsigned int[SubMeshes.size()];
-	m_CurrentScene->mRootNode->mNumMeshes=SubMeshes.size();
-	for(unsigned int i=0; i<SubMeshes.size(); ++i)
-		m_CurrentScene->mRootNode->mMeshes[i]=i;
-
+	m_CurrentScene->mRootNode->mMeshes=new unsigned int[subMeshes.size()];
+	m_CurrentScene->mRootNode->mNumMeshes=subMeshes.size();
 	
+	for(unsigned int i=0; i<subMeshes.size(); ++i)
+		m_CurrentScene->mRootNode->mMeshes[i]=i;
 
 	CreateAssimpSkeleton(Bones, Animations);
 	PutAnimationsInScene(Bones, Animations);
-	//___________________________________________________________
 }
 
 

+ 129 - 90
code/OgreImporter.hpp

@@ -1,104 +1,126 @@
-#include "BaseImporter.h"
-
-#include <vector>
 
+#include "BaseImporter.h"
 #include "OgreXmlHelper.hpp"
 #include "irrXMLWrapper.h"
 
-/// Ogre Importer TODO
-/*	- Read Vertex Colors
-	- Read multiple TexCoords
-*/
-
+#include <vector>
 
+/** @todo Read Vertex Colors
+    @todo Read multiple TexCoords
+*/
 
 namespace Assimp
 {
 namespace Ogre
 {
 
-
-//Forward declarations:
 struct Face;
-struct Weight;
+struct BoneWeight;
 struct Bone;
 struct Animation;
-struct Track;
-struct Keyframe;
 
-///A submesh from Ogre
+/// Ogre SubMesh
 struct SubMesh
-{	
-	bool SharedData;
+{
+	bool UseSharedGeometry;
+	bool Use32bitIndexes;
 
 	std::string Name;
 	std::string MaterialName;
-	std::vector<Face> FaceList;
-
-	std::vector<aiVector3D> Positions; bool HasPositions;
-	std::vector<aiVector3D> Normals; bool HasNormals;
-	std::vector<aiVector3D> Tangents; bool HasTangents;
-	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)
-
-	std::vector< std::vector<Weight> > Weights;//a list(inner) of bones for each vertex(outer)
-	int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
-	unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices)
 
-	SubMesh(): SharedData(false), HasPositions(false), HasNormals(false), HasTangents(false),
-		MaterialIndex(-1), BonesUsed(0) {}//initialize everything
+	bool HasGeometry;
+	bool HasPositions;
+	bool HasNormals;
+	bool HasTangents;
+
+	std::vector<Face> Faces;
+	std::vector<aiVector3D> Positions;
+	std::vector<aiVector3D> Normals;
+	std::vector<aiVector3D> Tangents;
+
+	/// Arbitrary number of texcoords, they are nearly always 2d, but Assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner).
+	std::vector<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
 {
 public:
-	virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
-	virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+	/// BaseImporter override.
+	virtual bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+	
+	/// BaseImporter override.
+	virtual void InternReadFile(const std::string &pFile, aiScene* pScene, IOSystem* pIOHandler);
+	
+	/// BaseImporter override.
 	virtual const aiImporterDesc* GetInfo () const;
+	
+	/// BaseImporter override.
 	virtual void SetupProperties(const Importer* pImp);
-private:
-
 
+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);
+	/// Helper Functions to read parts of the XML File.
+	void ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader);
 
-	/// Reads bone weights are stores them into the given submesh
-	static void ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader);
+	/// Reads a single Vertexbuffer and writes its data in the Submesh.
+	static void ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices);
 
-	/// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights)
-	static void ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometry);
+	/// Reads bone weights are stores them into the given submesh.
+	static void ReadBoneWeights(SubMesh &submesh, XmlReader *reader);
 
-	/// 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;
-	
+	/// 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 -------------------------------
+
 	/// 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;
 
-	/// 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);
 
-	/// Creates the aiskeleton in current scene
+	/// Creates the aiSkeleton in current scene.
 	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);
-	
 
 	//-------------------------------- OgreMaterial.cpp -------------------------------
+
 	aiMaterial* LoadMaterial(const std::string MaterialName) const;
 	void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial) const;
-	
-
 
-
-	//Now we don't have to give theses parameters to all functions
+	// Now we don't have to give theses parameters to all functions
+	/// @todo Remove this m_Current* bookkeeping.
 	std::string m_CurrentFilename;
 	std::string m_MaterialLibFilename;
 	bool m_TextureTypeFromFilename;
@@ -107,78 +129,95 @@ private:
 	SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh
 };
 
-///For the moment just triangles, no other polygon types!
+/// Simplified face.
+/** @todo Support other polygon types than just just triangles. Move to using aiFace. */
 struct Face
 {
 	unsigned int VertexIndices[3];
 };
 
+/// Ogre Bone assignment
 struct BoneAssignment
 {
-	unsigned int BoneId;//this is, what we get from ogre
-	std::string BoneName;//this is, what we need for assimp
+	/// Bone ID from Ogre.
+	unsigned int BoneId;
+	// Bone name for Assimp.
+	std::string BoneName;
 };
 
-///for a vertex->bone structur
-struct Weight
+/// Ogre Bone weight
+struct BoneWeight
 {
-	unsigned int BoneId;
+	/// Bone ID
+	unsigned int Id;
+	/// BoneWeight
 	float Value;
 };
 
 
 /// Helper Class to describe an ogre-bone for the skeleton:
-/** All Id's are signed ints, because than we have -1 as a simple INVALID_ID Value (we start from 0 so 0 is a valid bone ID!*/
+/** All Id's are signed ints, because than we have -1 as a simple INVALID_ID Value (we start from 0 so 0 is a valid bone ID!
+	@todo Cleanup if possible. Seems like this is overly complex for what we are reading. */
 struct Bone
 {
+	std::string Name;
+
 	int Id;
 	int ParentId;
-	std::string Name;
+	
 	aiVector3D Position;
-	float RotationAngle;
 	aiVector3D RotationAxis;
-	std::vector<int> Children;
+	float RotationAngle;
+	
 	aiMatrix4x4 BoneToWorldSpace;
 
-	///ctor
-	Bone(): Id(-1), ParentId(-1), RotationAngle(0.0f) {}
-	///this operator is needed to sort the bones after Id's
-	bool operator<(const Bone& rval) const
-		{return Id<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
-	void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
-};
+	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 &other) const { return Id < other.Id; }
 
-///Describes an Ogre Animation
-struct Animation
+	/// 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);
+};
+
+/// Ogre animation key frame
+/** Transformations for a frame. */
+struct KeyFrame
 {
-	std::string Name;
-	float Length;
-	std::vector<Track> Tracks;
+	float Time;
+	aiVector3D Position;
+	aiQuaternion Rotation;
+	aiVector3D Scaling;
 };
 
-///a track (keyframes for one bone) from an animation
+/// Ogre animation track
+/** Keyframes for one bone. */
 struct Track
 {
 	std::string BoneName;
-	std::vector<Keyframe> Keyframes;
+	std::vector<KeyFrame> Keyframes;
 };
 
-/// keyframe (bone transformation) from a track from a animation
-struct Keyframe
+/// Ogre animation
+struct Animation
 {
-	float Time;
-	aiVector3D Position;
-	aiQuaternion Rotation;
-	aiVector3D Scaling;
+	/// Name
+	std::string Name;
+	/// Length
+	float Length;
+	/// Tracks
+	std::vector<Track> Tracks;
 };
 
 }//namespace Ogre

+ 268 - 223
code/OgreMesh.cpp

@@ -52,236 +52,281 @@ namespace Assimp
 namespace Ogre
 {
 
-
-void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
+void OgreImporter::ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader)
 {
-	if(Reader->getAttributeValue("usesharedvertices"))
-		theSubMesh.SharedData=GetAttribute<bool>(Reader, "usesharedvertices");
+	if (reader->getAttributeValue("material"))
+		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
 	//of faces and geometry changed, and not if we have more than one of one
-	while(	Reader->getNodeName()==string("faces")
-		||	Reader->getNodeName()==string("geometry")
-		||	Reader->getNodeName()==string("boneassignments"))
+	/// @todo Fix above comment with better read logic below
+
+	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(string(Reader->getNodeName())=="faces")//Read the face list
+		if (currentNodeName == nnFaces)
 		{
-			//some info logging:
-			unsigned int NumFaces=GetAttribute<int>(Reader, "count");
-			ostringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
-			DefaultLogger::get()->debug(ss.str());
+			unsigned int numFaces = GetAttribute<unsigned int>(reader, "count");
 
-			while(XmlRead(Reader) && Reader->getNodeName()==string("face"))
-			{
-				Face NewFace;
-				NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
-				NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
-				NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
-				if(Reader->getAttributeValue("v4"))//this should be supported in the future
-				{
-					DefaultLogger::get()->warn("Submesh has quads, only traingles are supported!");
-					//throw DeadlyImportError("Submesh has quads, only traingles are supported!");
-				}
-				theSubMesh.FaceList.push_back(NewFace);
-			}
+			NextNode(reader);
+			currentNodeName = reader->getNodeName();
 
-		}//end of faces
-		else if(string(Reader->getNodeName())=="geometry")//Read the vertexdata
-		{	
-			//some info logging:
-			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"))
+			while(currentNodeName == nnFace)
 			{
-				ReadVertexBuffer(theSubMesh, Reader, NumVertices);
-			}
-
-			//some error checking on the loaded data
-			if(!theSubMesh.HasPositions)
-				throw DeadlyImportError("No positions could be loaded!");
+				Face NewFace;
+				NewFace.VertexIndices[0] = GetAttribute<int>(reader, "v1");
+				NewFace.VertexIndices[1] = GetAttribute<int>(reader, "v2");
+				NewFace.VertexIndices[2] = GetAttribute<int>(reader, "v3");
 
-			if(theSubMesh.HasNormals && theSubMesh.Normals.size() != NumVertices)
-				throw DeadlyImportError("Wrong Number of Normals loaded!");
+				/// @todo Support quads
+				if (!quadWarned && reader->getAttributeValue("v4"))
+					DefaultLogger::get()->warn("Submesh has quads, only triangles are supported at the moment!");
 
-			if(theSubMesh.HasTangents && theSubMesh.Tangents.size() != NumVertices)
-				throw DeadlyImportError("Wrong Number of Tangents loaded!");
+				submesh.Faces.push_back(NewFace);
 
-			for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i)
-			{
-				if(theSubMesh.Uvs[i].size() != NumVertices)
-					throw DeadlyImportError("Wrong Number of Uvs loaded!");
+				// Advance
+				NextNode(reader);
+				currentNodeName = reader->getNodeName();
 			}
 
-		}//end of "geometry
-
+			if (submesh.Faces.size() == numFaces)
+				DefaultLogger::get()->debug(Formatter::format() << "  - Faces " << numFaces);
+			else
+				throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Faces.size() << " faces when should have read " << numFaces);
+		}
+		else if (currentNodeName == nnGeometry)
+		{	
+			unsigned int numVertices = GetAttribute<int>(reader, "vertexcount");
 
-		else if(Reader->getNodeName()==string("boneassignments"))
-		{
-			ReadBoneWeights(theSubMesh, Reader);
+			NextNode(reader);
+			while(string(reader->getNodeName()) == nnVertexBuffer)
+				ReadVertexBuffer(submesh, reader, numVertices);
 		}
+		else if (reader->getNodeName() == nnBoneAssignments)
+			ReadBoneWeights(submesh, reader);
+			
+		currentNodeName = reader->getNodeName();
 	}
-	DefaultLogger::get()->debug((Formatter::format(),
-		"Positionen: ",theSubMesh.Positions.size(),
-		" Normale: ",theSubMesh.Normals.size(),
-		" TexCoords: ",theSubMesh.Uvs.size(),
-		" Tantents: ",theSubMesh.Tangents.size()
-	));
 }
 
-
-void OgreImporter::ReadVertexBuffer(SubMesh &theSubMesh, XmlReader *Reader, unsigned int NumVertices)
+void OgreImporter::ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices)
 {
-	DefaultLogger::get()->debug("new Vertex Buffer");
-
-	bool ReadPositions=false;
-	bool ReadNormals=false;
-	bool ReadTangents=false;
-	unsigned int NumUvs=0;
+	DefaultLogger::get()->debug(Formatter::format() << "Reading vertex buffer with " << numVertices << " vertices");
+	
+	submesh.HasGeometry = true;
 
-	//-------------------- check, what we need to read: --------------------------------
-	if(Reader->getAttributeValue("positions") && GetAttribute<bool>(Reader, "positions"))
+	if (reader->getAttributeValue("positions") && GetAttribute<bool>(reader, "positions"))
 	{
-		ReadPositions=theSubMesh.HasPositions=true;
-		theSubMesh.Positions.reserve(NumVertices);
-		DefaultLogger::get()->debug("reading positions");
+		submesh.HasPositions = true;
+		submesh.Positions.reserve(numVertices);
+		DefaultLogger::get()->debug("  - Has positions");
 	}
-	if(Reader->getAttributeValue("normals") && GetAttribute<bool>(Reader, "normals"))
+	if (reader->getAttributeValue("normals") && GetAttribute<bool>(reader, "normals"))
 	{
-		ReadNormals=theSubMesh.HasNormals=true;
-		theSubMesh.Normals.reserve(NumVertices);
-		DefaultLogger::get()->debug("reading normals");
+		submesh.HasNormals = true;
+		submesh.Normals.reserve(numVertices);
+		DefaultLogger::get()->debug("  - Has normals");
 	}
-	if(Reader->getAttributeValue("tangents") && GetAttribute<bool>(Reader, "tangents"))
+	if (reader->getAttributeValue("tangents") && GetAttribute<bool>(reader, "tangents"))
 	{
-		ReadTangents=theSubMesh.HasTangents=true;
-		theSubMesh.Tangents.reserve(NumVertices);
-		DefaultLogger::get()->debug("reading tangents");
+		submesh.HasTangents = true;
+		submesh.Tangents.reserve(numVertices);
+		DefaultLogger::get()->debug("  - Has tangents");
 	}
-
-	if(Reader->getAttributeValue("texture_coords"))
+	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");
+		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");
 	}
-	//___________________________________________________________________
 
-
-	//check if we will load anything
-	if(!( ReadPositions || ReadNormals || ReadTangents || (NumUvs>0) ))
-		DefaultLogger::get()->warn("vertexbuffer seams to be empty!");
+	if (!submesh.HasPositions)
+		throw DeadlyImportError("Vertex buffer does not contain positions!");
+
+	string nnVertex        = "vertex";
+	string nnPosition      = "position";
+	string nnNormal        = "normal";
+	string nnTangent       = "tangent";
+	string nnBinormal      = "binormal";
+	string nnTexCoord      = "texcoord";
+	string nnColorDiffuse  = "colour_diffuse";
+	string nnColorSpecular = "colour_specular";
 	
-
-	//read all the vertices:
-	XmlRead(Reader);
-
-	/*it might happen, that we have more than one attribute per vertex (they are not splitted to different buffers)
-	so the break condition is a bit tricky */
-	while(Reader->getNodeName()==string("vertex")
-		||Reader->getNodeName()==string("position")
-		||Reader->getNodeName()==string("normal")
-		||Reader->getNodeName()==string("tangent")
-		||Reader->getNodeName()==string("texcoord")
-		||Reader->getNodeName()==string("colour_diffuse"))
+	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(Reader->getNodeName()==string("vertex"))
-			XmlRead(Reader);//Read an attribute tag
+		if (currentNodeName == nnVertex)
+		{
+			NextNode(reader);
+			currentNodeName = reader->getNodeName();
+		}
+		
+		/// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular
 
-		//Position
-		if(ReadPositions && Reader->getNodeName()==string("position"))
+		if (submesh.HasPositions && currentNodeName == nnPosition)
 		{
 			aiVector3D NewPos;
-			NewPos.x=GetAttribute<float>(Reader, "x");
-			NewPos.y=GetAttribute<float>(Reader, "y");
-			NewPos.z=GetAttribute<float>(Reader, "z");
-			theSubMesh.Positions.push_back(NewPos);
+			NewPos.x = GetAttribute<float>(reader, "x");
+			NewPos.y = GetAttribute<float>(reader, "y");
+			NewPos.z = GetAttribute<float>(reader, "z");
+			submesh.Positions.push_back(NewPos);
 		}
-				
-		//Normal
-		else if(ReadNormals && Reader->getNodeName()==string("normal"))
+		else if (submesh.HasNormals && currentNodeName == nnNormal)
 		{
 			aiVector3D NewNormal;
-			NewNormal.x=GetAttribute<float>(Reader, "x");
-			NewNormal.y=GetAttribute<float>(Reader, "y");
-			NewNormal.z=GetAttribute<float>(Reader, "z");
-			theSubMesh.Normals.push_back(NewNormal);
+			NewNormal.x = GetAttribute<float>(reader, "x");
+			NewNormal.y = GetAttribute<float>(reader, "y");
+			NewNormal.z = GetAttribute<float>(reader, "z");
+			submesh.Normals.push_back(NewNormal);
 		}
-				
-		//Tangent
-		else if(ReadTangents && Reader->getNodeName()==string("tangent"))
+		else if (submesh.HasTangents && currentNodeName == nnTangent)
 		{
 			aiVector3D NewTangent;
-			NewTangent.x=GetAttribute<float>(Reader, "x");
-			NewTangent.y=GetAttribute<float>(Reader, "y");
-			NewTangent.z=GetAttribute<float>(Reader, "z");
-			theSubMesh.Tangents.push_back(NewTangent);
+			NewTangent.x = GetAttribute<float>(reader, "x");
+			NewTangent.y = GetAttribute<float>(reader, "y");
+			NewTangent.z = GetAttribute<float>(reader, "z");
+			submesh.Tangents.push_back(NewTangent);
 		}
-
-		//Uv:
-		else if(NumUvs>0 && Reader->getNodeName()==string("texcoord"))
+		else if (submesh.Uvs.size() > 0 && currentNodeName == nnTexCoord)
 		{
-			for(unsigned int i=0; i<NumUvs; ++i)
+			for(size_t i=0, len=submesh.Uvs.size(); i<len; ++i)
 			{
-				if(Reader->getNodeName()!=string("texcoord"))
-				{
-					DefaultLogger::get()->warn(string("Not enough UVs in Vertex: ")+Reader->getNodeName());
-				}
+				if (currentNodeName != nnTexCoord)
+					throw DeadlyImportError("Vertex buffer declared more UVs than can be found in a vertex");
+
 				aiVector3D NewUv;
-				NewUv.x=GetAttribute<float>(Reader, "u");
-				NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
-				theSubMesh.Uvs[i].push_back(NewUv);
-				XmlRead(Reader);
+				NewUv.x = GetAttribute<float>(reader, "u");
+				NewUv.y = GetAttribute<float>(reader, "v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????)
+				submesh.Uvs[i].push_back(NewUv);
+				
+				NextNode(reader);
+				currentNodeName = reader->getNodeName();
 			}
-			continue;//because we already read the next node...
-		}
-
-		//Color:
-		//TODO: actually save this data!
-		else if(Reader->getNodeName()==string("colour_diffuse"))
-		{
-			//do nothing, because we not yet support them
+			// Continue main loop as above already read next node
+			continue;
 		}
-
-		//Attribute could not be read
 		else
 		{
-			DefaultLogger::get()->warn(string("Attribute was not read: ")+Reader->getNodeName());
+			/// @todo Remove this stuff once implemented. We only want to log warnings once per element.
+			bool warn = true;
+			if (currentNodeName == nnBinormal)
+			{
+				if (warnBinormal)
+					warnBinormal = false;
+				else
+					warn = false;
+			}
+			else if (currentNodeName == nnColorDiffuse)
+			{
+				if (warnColorDiffuse)
+					warnColorDiffuse = false;
+				else
+					warn = false;
+			}
+			else if (currentNodeName == nnColorSpecular)
+			{
+				if (warnColorSpecular)
+					warnColorSpecular = false;
+				else
+					warn = false;
+			}
+			if (warn)
+				DefaultLogger::get()->warn(string("Vertex buffer attribute read not implemented for element: ") + currentNodeName);
 		}
 
-		XmlRead(Reader);//Read the Vertex tag
+		// Advance
+		NextNode(reader);
+		currentNodeName = reader->getNodeName();
 	}
-}
 
+	DefaultLogger::get()->debug(Formatter::format() <<
+		"  - Positions " << submesh.Positions.size() <<
+		" Normals "   << submesh.Normals.size() <<
+		" TexCoords " << submesh.Uvs.size() <<
+		" Tangents "  << submesh.Tangents.size());
+
+	// Sanity checks
+	if (submesh.HasNormals && submesh.Normals.size() != numVertices)
+		throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Normals.size() << " normals when should have read " << numVertices);
+	if (submesh.HasTangents && submesh.Tangents.size() != numVertices)
+		throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Tangents.size() << " tangents when should have read " << numVertices);
+	for(unsigned int i=0; i<submesh.Uvs.size(); ++i)
+	{
+		if (submesh.Uvs[i].size() != numVertices)
+			throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Uvs[i].size() 
+				<< " uvs for uv index " << i << " when should have read " << numVertices);
+	}
+}
 
-void OgreImporter::ReadBoneWeights(SubMesh &theSubMesh, XmlReader *Reader)
+void OgreImporter::ReadBoneWeights(SubMesh &submesh, XmlReader *reader)
 {
-	theSubMesh.Weights.resize(theSubMesh.Positions.size());
-	while(XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
+	submesh.Weights.resize(submesh.Positions.size());
+
+	unsigned int numRead = 0;
+	string nnVertexBoneAssignment = "vertexboneassignment";
+
+	NextNode(reader);
+	while(CurrentNodeNameEquals(reader, nnVertexBoneAssignment))
 	{
-		Weight NewWeight;
-		unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
-		NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
-		NewWeight.Value=GetAttribute<float>(Reader, "weight");
+		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)
-		theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);
+		/// @todo This can probably be refactored to something else.
+		submesh.BonesUsed = max(submesh.BonesUsed, weight.Id+1);
 
-		theSubMesh.Weights[VertexId].push_back(NewWeight);
+		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 &theSubMesh, SubMesh &theSharedGeometry)
+void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry)
 {
 	//---------------Make all Vertexes unique: (this is required by assimp)-----------------------
-	vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
-	unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
+	vector<Face> UniqueFaceList(submesh.Faces.size());
+	unsigned int UniqueVertexCount=submesh.Faces.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
 
 	vector<aiVector3D> UniquePositions(UniqueVertexCount);
 
@@ -289,9 +334,9 @@ void OgreImporter::ProcessSubMesh(SubMesh &theSubMesh, SubMesh &theSharedGeometr
 
 	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);
 
 
@@ -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 just use a reference to a submodel instead of our submodel itself*/
 
-	SubMesh& VertexSource= theSubMesh.SharedData ? theSharedGeometry : theSubMesh;
-	if(theSubMesh.SharedData)//copy vertexinformations to our mesh:
+	SubMesh& VertexSource= submesh.UseSharedGeometry ? sharedGeometry : submesh;
+	if(submesh.UseSharedGeometry)//copy vertexinformations to our mesh:
 	{
-		theSubMesh.HasPositions=theSharedGeometry.HasPositions;
-		theSubMesh.HasNormals=theSharedGeometry.HasNormals;
-		theSubMesh.HasTangents=theSharedGeometry.HasTangents;
+		submesh.HasPositions=sharedGeometry.HasPositions;
+		submesh.HasNormals=sharedGeometry.HasNormals;
+		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<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
-		unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
-		unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
-		unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
+		unsigned int Vertex1=submesh.Faces[i].VertexIndices[0];
+		unsigned int Vertex2=submesh.Faces[i].VertexIndices[1];
+		unsigned int Vertex3=submesh.Faces[i].VertexIndices[2];
 
 		UniquePositions[3*i+0]=VertexSource.Positions[Vertex1];
 		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:
 	//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);
+	submesh.Faces.swap(UniqueFaceList);
+	submesh.Positions.swap(UniquePositions);
+	submesh.Normals.swap(UniqueNormals);
+	submesh.Tangents.swap(UniqueTangents);
+	submesh.Uvs.swap(UniqueUvs);
+	submesh.Weights.swap(UniqueWeights);
 
 
 
 	//------------- normalize weights -----------------------------
 	//The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
 	//so we have to make this sure:
-	for(unsigned int VertexId=0; 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;
-		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
 		if(WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
 		{
 			//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
 	(void)m_CurrentScene;
@@ -408,63 +453,63 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
 	aiMesh* NewAiMesh=new aiMesh();
 		
 	//Positions
-	NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
-	memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
-	NewAiMesh->mNumVertices=theSubMesh.Positions.size();
+	NewAiMesh->mVertices=new aiVector3D[submesh.Positions.size()];
+	memcpy(NewAiMesh->mVertices, &submesh.Positions[0], submesh.Positions.size()*sizeof(aiVector3D));
+	NewAiMesh->mNumVertices=submesh.Positions.size();
 
 	//Normals
-	if(theSubMesh.HasNormals)
+	if(submesh.HasNormals)
 	{
-		NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
-		memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
+		NewAiMesh->mNormals=new aiVector3D[submesh.Normals.size()];
+		memcpy(NewAiMesh->mNormals, &submesh.Normals[0], submesh.Normals.size()*sizeof(aiVector3D));
 	}
 
 
 	//until we have support for bitangents, no tangents will be written
 	/*
 	//Tangents
-	if(theSubMesh.HasTangents)
+	if(submesh.HasTangents)
 	{
-		NewAiMesh->mTangents=new aiVector3D[theSubMesh.Tangents.size()];
-		memcpy(NewAiMesh->mTangents, &theSubMesh.Tangents[0], theSubMesh.Tangents.size()*sizeof(aiVector3D));
+		NewAiMesh->mTangents=new aiVector3D[submesh.Tangents.size()];
+		memcpy(NewAiMesh->mTangents, &submesh.Tangents[0], submesh.Tangents.size()*sizeof(aiVector3D));
 	}
 	*/
 
 	//Uvs
-	if(theSubMesh.Uvs.size()>0)
+	if(submesh.Uvs.size()>0)
 	{
-		for(unsigned int i=0; i<theSubMesh.Uvs.size(); ++i)
+		for(unsigned int i=0; i<submesh.Uvs.size(); ++i)
 		{
 			NewAiMesh->mNumUVComponents[i]=2;
-			NewAiMesh->mTextureCoords[i]=new aiVector3D[theSubMesh.Uvs[i].size()];
-			memcpy(NewAiMesh->mTextureCoords[i], &(theSubMesh.Uvs[i][0]), theSubMesh.Uvs[i].size()*sizeof(aiVector3D));
+			NewAiMesh->mTextureCoords[i]=new aiVector3D[submesh.Uvs[i].size()];
+			memcpy(NewAiMesh->mTextureCoords[i], &(submesh.Uvs[i][0]), submesh.Uvs[i].size()*sizeof(aiVector3D));
 		}
 	}
 
 
-	//---------------------------------------- Bones --------------------------------------------
+	//---------------------------------------- bones --------------------------------------------
 
 	//Copy the weights in in Bone-Vertices Struktur
-	//(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
-	vector< vector<aiVertexWeight> > aiWeights(theSubMesh.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
+	//(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(submesh.BonesUsed);//now the outer list are the bones, and the inner vector the 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;
 			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;
-			aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
+			NewWeight.mWeight=submesh.Weights[VertexId][BoneId].Value;
+			aiWeights[submesh.Weights[VertexId][BoneId].Id].push_back(NewWeight);
 		}
 	}
 
 	
 
 	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
-	for(unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
+	for(unsigned int i=0; i<submesh.BonesUsed; ++i)
 	{
 		if(aiWeights[i].size()>0)
 		{
@@ -472,8 +517,8 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
 			NewBone->mNumWeights=aiWeights[i].size();
 			NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
 			memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
-			NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
-			NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
+			NewBone->mName=bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
+			NewBone->mOffsetMatrix=bones[i].BoneToWorldSpace;
 				
 			aiBones.push_back(NewBone);
 		}
@@ -491,20 +536,20 @@ aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vecto
 
 
 	//Faces
-	NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
-	for(unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
+	NewAiMesh->mFaces=new aiFace[submesh.Faces.size()];
+	for(unsigned int i=0; i<submesh.Faces.size(); ++i)
 	{
 		NewAiMesh->mFaces[i].mNumIndices=3;
 		NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
 
-		NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
-		NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
-		NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
+		NewAiMesh->mFaces[i].mIndices[0]=submesh.Faces[i].VertexIndices[0];
+		NewAiMesh->mFaces[i].mIndices[1]=submesh.Faces[i].VertexIndices[1];
+		NewAiMesh->mFaces[i].mIndices[2]=submesh.Faces[i].VertexIndices[2];
 	}
-	NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
+	NewAiMesh->mNumFaces=submesh.Faces.size();
 
 	//Link the material:
-	NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
+	NewAiMesh->mMaterialIndex=submesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
 
 	return NewAiMesh;
 }

+ 18 - 18
code/OgreSkeleton.cpp

@@ -77,18 +77,18 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 	if(!SkeletonFile)
 		throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
 
-	XmlRead(SkeletonFile);
+	NextNode(SkeletonFile);
 	if(string("skeleton")!=SkeletonFile->getNodeName())
 		throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
 
 
 
 	//------------------------------------load bones-----------------------------------------
-	XmlRead(SkeletonFile);
+	NextNode(SkeletonFile);
 	if(string("bones")!=SkeletonFile->getNodeName())
 		throw DeadlyImportError("No bones node in skeleton "+FileName);
 
-	XmlRead(SkeletonFile);
+	NextNode(SkeletonFile);
 
 	while(string("bone")==SkeletonFile->getNodeName())
 	{
@@ -100,7 +100,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 		NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
 
 		//load the position:
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 		if(string("position")!=SkeletonFile->getNodeName())
 			throw DeadlyImportError("Position is not first node in Bone!");
 		NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
@@ -108,11 +108,11 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 		NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
 
 		//Rotation:
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 		if(string("rotation")!=SkeletonFile->getNodeName())
 			throw DeadlyImportError("Rotation is not the second node in Bone!");
 		NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 		if(string("axis")!=SkeletonFile->getNodeName())
 			throw DeadlyImportError("No axis specified for bone rotation!");
 		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);
 
 		//Proceed to the next bone:
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 	}
 	//The bones in the file a not neccesarly ordered by there id's so we do it now:
 	std::sort(Bones.begin(), Bones.end());
@@ -153,7 +153,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 		throw DeadlyImportError("no bonehierarchy node in "+FileName);
 
 	DefaultLogger::get()->debug("loading bonehierarchy...");
-	XmlRead(SkeletonFile);
+	NextNode(SkeletonFile);
 	while(string("boneparent")==SkeletonFile->getNodeName())
 	{
 		string Child, Parent;
@@ -167,7 +167,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 		Bones[ChildId].ParentId=ParentId;
 		Bones[ParentId].Children.push_back(ChildId);
 
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 	}
 	//_____________________________________________________________________________
 
@@ -187,7 +187,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 	if(string("animations")==SkeletonFile->getNodeName())//animations are optional values
 	{
 		DefaultLogger::get()->debug("Loading Animations");
-		XmlRead(SkeletonFile);
+		NextNode(SkeletonFile);
 		while(string("animation")==SkeletonFile->getNodeName())
 		{
 			Animation NewAnimation;
@@ -195,30 +195,30 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 			NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
 			
 			//Load all Tracks
-			XmlRead(SkeletonFile);
+			NextNode(SkeletonFile);
 			if(string("tracks")!=SkeletonFile->getNodeName())
 				throw DeadlyImportError("no tracks node in animation");
-			XmlRead(SkeletonFile);
+			NextNode(SkeletonFile);
 			while(string("track")==SkeletonFile->getNodeName())
 			{
 				Track NewTrack;
 				NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
 
 				//Load all keyframes;
-				XmlRead(SkeletonFile);
+				NextNode(SkeletonFile);
 				if(string("keyframes")!=SkeletonFile->getNodeName())
 					throw DeadlyImportError("no keyframes node!");
-				XmlRead(SkeletonFile);
+				NextNode(SkeletonFile);
 				while(string("keyframe")==SkeletonFile->getNodeName())
 				{
-					Keyframe NewKeyframe;
+					KeyFrame NewKeyframe;
 					NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
 
 					//loop over the attributes:
 					
 					while(true) //will quit, if a Node is not a animationkey
 					{
-						XmlRead(SkeletonFile);
+						NextNode(SkeletonFile);
 
 						//If any property doesn't show up, it will keep its initialization value
 
@@ -235,7 +235,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 						{
 							float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
 							aiVector3D RotationAxis;
-							XmlRead(SkeletonFile);
+							NextNode(SkeletonFile);
 							if(string("axis")!=SkeletonFile->getNodeName())
 								throw DeadlyImportError("No axis for keyframe rotation!");
 							RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
@@ -247,7 +247,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vecto
 								RotationAxis.x=1.0f;
 								if(0!=RotationAngle)//if we don't rotate at all, the axis does not matter
 								{
-									DefaultLogger::get()->warn("Invalid Rotation Axis in Keyframe!");
+									DefaultLogger::get()->warn("Invalid Rotation Axis in KeyFrame!");
 								}
 							}
 							NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);

+ 50 - 46
code/OgreXmlHelper.hpp

@@ -9,80 +9,84 @@ namespace Ogre
 	
 typedef irr::io::IrrXMLReader XmlReader;
 
-
-//------------Helper Funktion to Get a Attribute Save---------------
-template<typename t> inline t GetAttribute(XmlReader* Reader, std::string Name);
-
-/*
+static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "")
 {
-	BOOST_STATIC_ASSERT(false);
-	return t();
+	if (!error.empty())
+		throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'");
+	else
+		throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'");
 }
-*/
 
-template<> inline int GetAttribute<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());
-	if(Value)
-		return atoi(Value);
+	const char* value = reader->getAttributeValue(name.c_str());
+	if (value)
+		return atoi(value);
 	else
-		throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+		ThrowAttibuteError(reader, name);
 }
 
-template<> inline unsigned int GetAttribute<unsigned int>(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());
-	if(Value)
-		return static_cast<unsigned int>(atoi(Value));//yes, ugly, but pfff
+	const char* value = reader->getAttributeValue(name.c_str());
+	if (value)
+		return static_cast<unsigned int>(atoi(value)); ///< @todo Find a better way...
 	else
-		throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+		ThrowAttibuteError(reader, name);
 }
 
-template<> inline float GetAttribute<float>(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());
-	if(Value)
-		return fast_atof(Value);
+	const char* value = reader->getAttributeValue(name.c_str());
+	if (value)
+		return fast_atof(value);
 	else
-		throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+		ThrowAttibuteError(reader, name);
 }
 
-template<> inline std::string GetAttribute<std::string>(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());
-	if(Value)
-		return std::string(Value);
+	const char* value = reader->getAttributeValue(name.c_str());
+	if (value)
+		return std::string(value);
 	else
-		throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+		ThrowAttibuteError(reader, name);
 }
 
-template<> inline bool GetAttribute<bool>(XmlReader* Reader, std::string Name)
+template<> 
+inline bool GetAttribute<bool>(const XmlReader* reader, const std::string &name)
 {
-	const char* Value=Reader->getAttributeValue(Name.c_str());
-	if(Value)
-	{
-		if(Value==std::string("true"))
-			return true;
-		else if(Value==std::string("false"))
-			return false;
-		else
-			throw DeadlyImportError(std::string("Bool value has invalid value: "+Name+" / "+Value+" / "+Reader->getNodeName()));
-	}
+	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
-		throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+		ThrowAttibuteError(reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'");
 }
-//__________________________________________________________________
 
-inline bool XmlRead(XmlReader* Reader)
+inline bool NextNode(XmlReader* reader)
 {
 	do
 	{
-		if(!Reader->read())
+		if (!reader->read())
 			return false;
 	}
-	while(Reader->getNodeType()!=irr::io::EXN_ELEMENT);
+	while(reader->getNodeType() != irr::io::EXN_ELEMENT);
 	return true;
 }
 
-}//namespace Ogre
-}//namespace Assimp
+inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &name)
+{
+	return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0);
+}
+
+} // Ogre
+} // Assimp