Selaa lähdekoodia

OgreImporter: Implement binary skeleton serialization. Fix bone/animation matrix stuff to be simpler (aka read as Quats to internal structures). Cleanup code for pull request.

Jonne Nauha 11 vuotta sitten
vanhempi
commit
1129ae5a6e

+ 292 - 9
code/OgreBinarySerializer.cpp

@@ -39,6 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #include "OgreBinarySerializer.h"
+#include "OgreXmlSerializer.h"
+#include "OgreParsingUtils.h"
+
 #include "TinyFormatter.h"
 
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
@@ -51,9 +54,15 @@ namespace Assimp
 namespace Ogre
 {
 
-const std::string		VERSION_1_8				= "[MeshSerializer_v1.8]";
-const unsigned short	HEADER_CHUNK_ID			= 0x1000;
-const long				MSTREAM_OVERHEAD_SIZE	= sizeof(uint16_t) + sizeof(uint32_t);
+const std::string       MESH_VERSION_1_8        = "[MeshSerializer_v1.8]";
+const std::string       SKELETON_VERSION_1_8    = "[Serializer_v1.80]";
+const std::string       SKELETON_VERSION_1_1    = "[Serializer_v1.10]";
+
+const unsigned short    HEADER_CHUNK_ID         = 0x1000;
+
+const long              MSTREAM_OVERHEAD_SIZE               = sizeof(uint16_t) + sizeof(uint32_t);
+const long              MSTREAM_BONE_SIZE_WITHOUT_SCALE     = MSTREAM_OVERHEAD_SIZE + sizeof(unsigned short) + (sizeof(float) * 7);
+const long              MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8);
 
 template<> 
 inline bool OgreBinarySerializer::Read<bool>()
@@ -118,6 +127,16 @@ void OgreBinarySerializer::ReadVector(aiVector3D &vec)
 	m_reader->CopyAndAdvance(&vec.x, sizeof(float)*3);
 }
 
+void OgreBinarySerializer::ReadQuaternion(aiQuaternion &quat)
+{
+	float temp[4];
+	m_reader->CopyAndAdvance(temp, sizeof(float)*4);
+	quat.x = temp[0];
+	quat.y = temp[1];
+	quat.z = temp[2];
+	quat.w = temp[3];
+}
+
 bool OgreBinarySerializer::AtEnd() const
 {
 	return (m_reader->GetRemainingSize() == 0);
@@ -152,7 +171,10 @@ uint16_t OgreBinarySerializer::ReadHeader(bool readLen)
 
 #if (OGRE_BINARY_SERIALIZER_DEBUG == 1)
 	if (id != HEADER_CHUNK_ID)
-		DefaultLogger::get()->debug(Formatter::format() << MeshHeaderToString(static_cast<MeshChunkId>(id)));
+	{
+		DefaultLogger::get()->debug(Formatter::format() << (assetMode == AM_Mesh 
+			? MeshHeaderToString(static_cast<MeshChunkId>(id)) : SkeletonHeaderToString(static_cast<SkeletonChunkId>(id))));
+	}
 #endif
 
 	return id;
@@ -172,9 +194,11 @@ void OgreBinarySerializer::SkipBytes(size_t numBytes)
 	m_reader->IncPtr(numBytes);
 }
 
+// Mesh
+
 Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream)
 {
-	OgreBinarySerializer serializer(stream);
+	OgreBinarySerializer serializer(stream, OgreBinarySerializer::AM_Mesh);
 	
 	uint16_t id = serializer.ReadHeader(false);
 	if (id != HEADER_CHUNK_ID) {
@@ -183,8 +207,11 @@ Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream)
 
 	/// @todo Check what we can actually support.
 	std::string version = serializer.ReadLine();
-	if (version != VERSION_1_8)
-		throw DeadlyExportError("Mesh version " + version + " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.");
+	if (version != MESH_VERSION_1_8)
+	{
+		throw DeadlyExportError(Formatter::format() << "Mesh version " << version << " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again."
+			<< " Supported versions: " << MESH_VERSION_1_8);
+	}
 
 	Mesh *mesh = new Mesh();
 	while (!serializer.AtEnd())
@@ -732,8 +759,7 @@ void OgreBinarySerializer::ReadAnimations(Mesh *mesh)
 }
 
 void OgreBinarySerializer::ReadAnimation(Animation *anim)
-{
-	
+{	
 	if (!AtEnd())
 	{
 		uint16_t id = ReadHeader();
@@ -821,6 +847,263 @@ void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimati
 	}
 }
 
+// Skeleton
+
+bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh)
+{
+	if (!mesh || mesh->skeletonRef.empty())
+		return false;
+
+	// Highly unusual to see in read world cases but support
+	// binary mesh referencing a XML skeleton file.
+	if (EndsWith(mesh->skeletonRef, ".skeleton.xml", false))
+	{
+		OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh);
+		return false;
+	}
+	
+	MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
+		
+	Skeleton *skeleton = new Skeleton();
+	OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton);
+	serializer.ReadSkeleton(skeleton);
+	mesh->skeleton = skeleton;
+	return true;
+}
+
+bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh)
+{
+	if (!mesh || mesh->skeletonRef.empty())
+		return false;
+
+	MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
+	if (!reader.get())
+		return false;
+
+	Skeleton *skeleton = new Skeleton();
+	OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton);
+	serializer.ReadSkeleton(skeleton);
+	mesh->skeleton = skeleton;
+	return true;
+}
+
+MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename)
+{
+	if (!EndsWith(filename, ".skeleton", false))
+	{
+		DefaultLogger::get()->error("Imported Mesh is referencing to unsupported '" + filename + "' skeleton file.");
+		return MemoryStreamReaderPtr();
+	}
+
+	if (!pIOHandler->Exists(filename))
+	{
+		DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "' that is referenced by imported Mesh.");
+		return MemoryStreamReaderPtr();
+	}
+
+	IOStream *f = pIOHandler->Open(filename, "rb");
+	if (!f) {
+		throw DeadlyImportError("Failed to open skeleton file " + filename);
+	}
+
+	return MemoryStreamReaderPtr(new MemoryStreamReader(f));
+}
+
+void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton)
+{
+	uint16_t id = ReadHeader(false);
+	if (id != HEADER_CHUNK_ID) {
+		throw DeadlyExportError("Invalid Ogre Skeleton file header.");
+	}
+
+	// This deserialization supports both versions of the skeleton spec
+	std::string version = ReadLine();
+	if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1)
+	{
+		throw DeadlyExportError(Formatter::format() << "Skeleton version " << version << " not supported by this importer."
+			<< " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1);
+	}
+	
+	DefaultLogger::get()->debug("Reading Skeleton");
+	
+	bool firstBone = true;
+	bool firstAnim = true;
+
+	while (!AtEnd())
+	{
+		id = ReadHeader();
+		switch(id)
+		{
+			case SKELETON_BLENDMODE:
+			{
+				skeleton->blendMode = static_cast<Skeleton::BlendMode>(Read<uint16_t>());
+				break;
+			}
+			case SKELETON_BONE:
+			{
+				if (firstBone)
+				{
+					DefaultLogger::get()->debug("  - Bones");
+					firstBone = false;
+				}
+
+				ReadBone(skeleton);
+				break;
+			}
+			case SKELETON_BONE_PARENT:
+			{
+				ReadBoneParent(skeleton);
+				break;
+			}
+			case SKELETON_ANIMATION:
+			{
+				if (firstAnim)
+				{
+					DefaultLogger::get()->debug("  - Animations");
+					firstAnim = false;
+				}
+
+				ReadSkeletonAnimation(skeleton);
+				break;
+			}
+			case SKELETON_ANIMATION_LINK:
+			{
+				ReadSkeletonAnimationLink(skeleton);
+				break;
+			}
+		}
+	}
+	
+	// Calculate bone matrices for root bones. Recursively calculates their children.
+	for (size_t i=0, len=skeleton->bones.size(); i<len; ++i)
+	{
+		Bone *bone = skeleton->bones[i];
+		if (!bone->IsParented())
+			bone->CalculateWorldMatrixAndDefaultPose(skeleton);
+	}
+}
+
+void OgreBinarySerializer::ReadBone(Skeleton *skeleton)
+{
+	Bone *bone = new Bone();
+	bone->name = ReadLine();
+	bone->id = Read<uint16_t>();
+
+	// Pos and rot
+	ReadVector(bone->position);
+	ReadQuaternion(bone->rotation);
+
+	// Scale (optional)
+	if (m_currentLen > MSTREAM_BONE_SIZE_WITHOUT_SCALE)
+		ReadVector(bone->scale);
+
+	// Bone indexes need to start from 0 and be contiguous
+	if (bone->id != skeleton->bones.size()) {
+		throw DeadlyImportError(Formatter::format() << "Ogre Skeleton bone indexes not contiguous. Error at bone index " << bone->id);
+	}
+
+	DefaultLogger::get()->debug(Formatter::format() << "    " << bone->id << " " << bone->name);
+
+	skeleton->bones.push_back(bone);
+}
+
+void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton)
+{
+	uint16_t childId = Read<uint16_t>();
+	uint16_t parentId = Read<uint16_t>();
+	
+	Bone *child = skeleton->BoneById(childId);
+	Bone *parent = skeleton->BoneById(parentId);
+	
+	if (child && parent)
+		parent->AddChild(child);
+	else
+		throw DeadlyImportError(Formatter::format() << "Failed to find bones for parenting: Child id " << childId << " for parent id " << parentId);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton)
+{
+	Animation *anim = new Animation(skeleton);
+	anim->name = ReadLine();
+	anim->length = Read<float>();
+	
+	if (!AtEnd())
+	{
+		uint16_t id = ReadHeader();
+		if (id == SKELETON_ANIMATION_BASEINFO)
+		{
+			anim->baseName = ReadLine();
+			anim->baseTime = Read<float>();
+
+			// Advance to first track
+			id = ReadHeader();
+		}
+
+		while (!AtEnd() && id == SKELETON_ANIMATION_TRACK)
+		{
+			ReadSkeletonAnimationTrack(skeleton, anim);
+
+			if (!AtEnd())
+				id = ReadHeader();
+		}
+		if (!AtEnd())
+			RollbackHeader();
+	}
+	
+	skeleton->animations.push_back(anim);
+	
+	DefaultLogger::get()->debug(Formatter::format() << "    " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)");	
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest)
+{
+	uint16_t boneId = Read<uint16_t>();
+	Bone *bone = dest->parentSkeleton->BoneById(boneId);
+	if (!bone) {
+		throw DeadlyImportError(Formatter::format() << "Cannot read animation track, target bone " << boneId << " not in target Skeleton");
+	}
+	
+	VertexAnimationTrack track;
+	track.type = VertexAnimationTrack::VAT_TRANSFORM;
+	track.boneName = bone->name;
+	
+	uint16_t id = ReadHeader();
+	while (!AtEnd() && id == SKELETON_ANIMATION_TRACK_KEYFRAME)
+	{
+		ReadSkeletonAnimationKeyFrame(&track);
+
+		if (!AtEnd())
+			id = ReadHeader();
+	}
+	if (!AtEnd())
+		RollbackHeader();
+
+	dest->tracks.push_back(track);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest)
+{
+	TransformKeyFrame keyframe;
+	keyframe.timePos = Read<float>();
+	
+	// Rot and pos
+	ReadQuaternion(keyframe.rotation);
+	ReadVector(keyframe.position);
+	
+	// Scale (optional)
+	if (m_currentLen > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE)
+		ReadVector(keyframe.scale);
+		
+	dest->transformKeyFrames.push_back(keyframe);
+}
+
+void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton *skeleton)
+{
+	// Skip bounds, not compatible with Assimp.
+	ReadLine(); // skeleton name
+	SkipBytes(sizeof(float) * 3); // scale
+}
+
 } // Ogre
 } // Assimp
 

+ 347 - 245
code/OgreBinarySerializer.h

@@ -49,263 +49,365 @@ namespace Assimp
 {
 namespace Ogre
 {
-	class OgreBinarySerializer
+
+typedef Assimp::StreamReaderLE MemoryStreamReader;
+typedef boost::shared_ptr<MemoryStreamReader> MemoryStreamReaderPtr;
+
+class OgreBinarySerializer
+{
+public:
+	/// Imports mesh and returns the result.
+	/** @note Fatal unrecoverable errors will throw a DeadlyImportError. */
+	static Mesh *ImportMesh(MemoryStreamReader *reader);
+
+	/// Imports skeleton to @c mesh into Mesh::skeleton.
+	/** If mesh does not have a skeleton reference or the skeleton file
+		cannot be found it is not a fatal DeadlyImportError. */
+	static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh);
+	static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh);
+
+private:
+	enum AssetMode
 	{
-	public:
-		static Mesh *ImportMesh(MemoryStreamReader *reader);
-
-	private:
-		OgreBinarySerializer(MemoryStreamReader *reader) :
-			m_reader(reader),
-			m_currentLen(0)
-		{
-		}
-
-		bool AtEnd() const;
-
-		void ReadMesh(Mesh *mesh);
-		void ReadMeshLodInfo(Mesh *mesh);
-		void ReadMeshSkeletonLink(Mesh *mesh);
-		void ReadMeshBounds(Mesh *mesh);
-		void ReadMeshExtremes(Mesh *mesh);
-
-		void ReadSubMesh(Mesh *mesh);
-		void ReadSubMeshNames(Mesh *mesh);
-		void ReadSubMeshOperation(SubMesh *submesh);
-		void ReadSubMeshTextureAlias(SubMesh *submesh);
-
-		void ReadBoneAssignment(VertexData *dest);
-
-		void ReadGeometry(VertexData *dest);
-		void ReadGeometryVertexDeclaration(VertexData *dest);
-		void ReadGeometryVertexElement(VertexData *dest);
-		void ReadGeometryVertexBuffer(VertexData *dest);
-
-		void ReadEdgeList(Mesh *mesh);
-		void ReadPoses(Mesh *mesh);
-		void ReadPoseVertices(Pose *pose);
-		
-		void ReadAnimations(Mesh *mesh);
-		void ReadAnimation(Animation *anim);
-		void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track);
-		
-		void NormalizeBoneWeights(VertexData *vertexData) const;
-
-		uint16_t ReadHeader(bool readLen = true);
-		void RollbackHeader();
-
-		template<typename T> 
-		inline T Read();
-
-		void ReadBytes(char *dest, size_t numBytes);
-		void ReadBytes(uint8_t *dest, size_t numBytes);
-		void ReadBytes(void *dest, size_t numBytes);
-		uint8_t *ReadBytes(size_t numBytes);
-		
-		void ReadVector(aiVector3D &vec);
-
-		std::string ReadString(size_t len);
-		std::string ReadLine();
-
-		void SkipBytes(size_t numBytes);
-
-		uint32_t m_currentLen;
-		MemoryStreamReader *m_reader;
+		AM_Mesh,
+		AM_Skeleton
 	};
-
-	enum MeshChunkId
+	
+	OgreBinarySerializer(MemoryStreamReader *reader, AssetMode mode) :
+		m_reader(reader),
+		m_currentLen(0),
+		assetMode(mode)
 	{
-		M_HEADER = 0x1000,
-			// char*		  version		   : Version number check
-		M_MESH   = 0x3000,
-			// bool skeletallyAnimated   // important flag which affects h/w buffer policies
-			// Optional M_GEOMETRY chunk
-			M_SUBMESH			 = 0x4000, 
-				// char* materialName
-				// bool useSharedVertices
-				// unsigned int indexCount
-				// bool indexes32Bit
-				// unsigned int* faceVertexIndices (indexCount)
-				// OR
-				// unsigned short* faceVertexIndices (indexCount)
-				// M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
-				M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
-					// unsigned short operationType
-				M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
-					// Optional bone weights (repeating section)
-					// unsigned int vertexIndex;
-					// unsigned short boneIndex;
-					// float weight;
-				// Optional chunk that matches a texture name to an alias
-				// a texture alias is sent to the submesh material to use this texture name
-				// instead of the one in the texture unit with a matching alias name
-				M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section
-					// char* aliasName;
-					// char* textureName;
-
-			M_GEOMETRY		  = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
-				// unsigned int vertexCount
-				M_GEOMETRY_VERTEX_DECLARATION = 0x5100,
-					M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section
-						// unsigned short source;  	// buffer bind source
-						// unsigned short type;		// VertexElementType
-						// unsigned short semantic; // VertexElementSemantic
-						// unsigned short offset;	// start offset in buffer in bytes
-						// unsigned short index;	// index of the semantic (for colours and texture coords)
-				M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section
-					// unsigned short bindIndex;	// Index to bind this buffer to
-					// unsigned short vertexSize;	// Per-vertex size, must agree with declaration at this index
-					M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210,
-						// raw buffer data
-			M_MESH_SKELETON_LINK = 0x6000,
-				// Optional link to skeleton
-				// char* skeletonName		   : name of .skeleton to use
-			M_MESH_BONE_ASSIGNMENT = 0x7000,
+	}
+	
+	static MemoryStreamReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename);
+
+	// Header
+
+	uint16_t ReadHeader(bool readLen = true);
+	void RollbackHeader();
+
+	// Mesh
+
+	void ReadMesh(Mesh *mesh);
+	void ReadMeshLodInfo(Mesh *mesh);
+	void ReadMeshSkeletonLink(Mesh *mesh);
+	void ReadMeshBounds(Mesh *mesh);
+	void ReadMeshExtremes(Mesh *mesh);
+
+	void ReadSubMesh(Mesh *mesh);
+	void ReadSubMeshNames(Mesh *mesh);
+	void ReadSubMeshOperation(SubMesh *submesh);
+	void ReadSubMeshTextureAlias(SubMesh *submesh);
+
+	void ReadBoneAssignment(VertexData *dest);
+
+	void ReadGeometry(VertexData *dest);
+	void ReadGeometryVertexDeclaration(VertexData *dest);
+	void ReadGeometryVertexElement(VertexData *dest);
+	void ReadGeometryVertexBuffer(VertexData *dest);
+
+	void ReadEdgeList(Mesh *mesh);
+	void ReadPoses(Mesh *mesh);
+	void ReadPoseVertices(Pose *pose);
+	
+	void ReadAnimations(Mesh *mesh);
+	void ReadAnimation(Animation *anim);
+	void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track);
+
+	void NormalizeBoneWeights(VertexData *vertexData) const;
+
+	// Skeleton
+	
+	void ReadSkeleton(Skeleton *skeleton);
+	
+	void ReadBone(Skeleton *skeleton);
+	void ReadBoneParent(Skeleton *skeleton);
+	
+	void ReadSkeletonAnimation(Skeleton *skeleton);
+	void ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest);
+	void ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest);
+	void ReadSkeletonAnimationLink(Skeleton *skeleton);
+
+	// Reader utils
+	bool AtEnd() const;
+	
+	template<typename T> 
+	inline T Read();
+
+	void ReadBytes(char *dest, size_t numBytes);
+	void ReadBytes(uint8_t *dest, size_t numBytes);
+	void ReadBytes(void *dest, size_t numBytes);
+	uint8_t *ReadBytes(size_t numBytes);
+	
+	void ReadVector(aiVector3D &vec);
+	void ReadQuaternion(aiQuaternion &quat);
+
+	std::string ReadString(size_t len);
+	std::string ReadLine();
+
+	void SkipBytes(size_t numBytes);
+
+	uint32_t m_currentLen;
+	MemoryStreamReader *m_reader;
+
+	AssetMode assetMode;
+};
+
+enum MeshChunkId
+{
+	M_HEADER = 0x1000,
+		// char*		  version		   : Version number check
+	M_MESH   = 0x3000,
+		// bool skeletallyAnimated   // important flag which affects h/w buffer policies
+		// Optional M_GEOMETRY chunk
+		M_SUBMESH			 = 0x4000, 
+			// char* materialName
+			// bool useSharedVertices
+			// unsigned int indexCount
+			// bool indexes32Bit
+			// unsigned int* faceVertexIndices (indexCount)
+			// OR
+			// unsigned short* faceVertexIndices (indexCount)
+			// M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
+			M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
+				// unsigned short operationType
+			M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
 				// Optional bone weights (repeating section)
 				// unsigned int vertexIndex;
 				// unsigned short boneIndex;
 				// float weight;
-			M_MESH_LOD = 0x8000,
-				// Optional LOD information
-				// string strategyName;
-				// unsigned short numLevels;
-				// bool manual;  (true for manual alternate meshes, false for generated)
-				M_MESH_LOD_USAGE = 0x8100,
-				// Repeating section, ordered in increasing depth
-				// NB LOD 0 (full detail from 0 depth) is omitted
-				// LOD value - this is a distance, a pixel count etc, based on strategy
-				// float lodValue;
-					M_MESH_LOD_MANUAL = 0x8110,
-					// Required if M_MESH_LOD section manual = true
-					// String manualMeshName;
-					M_MESH_LOD_GENERATED = 0x8120,
-					// Required if M_MESH_LOD section manual = false
-					// Repeating section (1 per submesh)
-					// unsigned int indexCount;
-					// bool indexes32Bit
-					// unsigned short* faceIndexes;  (indexCount)
-					// OR
-					// unsigned int* faceIndexes;  (indexCount)
-			M_MESH_BOUNDS = 0x9000,
-				// float minx, miny, minz
-				// float maxx, maxy, maxz
-				// float radius
-					
-			// Added By DrEvil
-			// optional chunk that contains a table of submesh indexes and the names of
-			// the sub-meshes.
-			M_SUBMESH_NAME_TABLE = 0xA000,
-				// Subchunks of the name table. Each chunk contains an index & string
-				M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100,
-					// short index
-					// char* name
-			// Optional chunk which stores precomputed edge data					 
-			M_EDGE_LISTS = 0xB000,
-				// Each LOD has a separate edge list
-				M_EDGE_LIST_LOD = 0xB100,
-					// unsigned short lodIndex
-					// bool isManual			// If manual, no edge data here, loaded from manual mesh
-						// bool isClosed
-						// unsigned long numTriangles
-						// unsigned long numEdgeGroups
-						// Triangle* triangleList
-							// unsigned long indexSet
-							// unsigned long vertexSet
-							// unsigned long vertIndex[3]
-							// unsigned long sharedVertIndex[3] 
-							// float normal[4] 
-
-						M_EDGE_GROUP = 0xB110,
-							// unsigned long vertexSet
-							// unsigned long triStart
-							// unsigned long triCount
-							// unsigned long numEdges
-							// Edge* edgeList
-								// unsigned long  triIndex[2]
-								// unsigned long  vertIndex[2]
-								// unsigned long  sharedVertIndex[2]
-								// bool degenerate
-			// Optional poses section, referred to by pose keyframes
-			M_POSES = 0xC000,
-				M_POSE = 0xC100,
-					// char* name (may be blank)
-					// unsigned short target	// 0 for shared geometry, 
+			// Optional chunk that matches a texture name to an alias
+			// a texture alias is sent to the submesh material to use this texture name
+			// instead of the one in the texture unit with a matching alias name
+			M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section
+				// char* aliasName;
+				// char* textureName;
+
+		M_GEOMETRY		  = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
+			// unsigned int vertexCount
+			M_GEOMETRY_VERTEX_DECLARATION = 0x5100,
+				M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section
+					// unsigned short source;  	// buffer bind source
+					// unsigned short type;		// VertexElementType
+					// unsigned short semantic; // VertexElementSemantic
+					// unsigned short offset;	// start offset in buffer in bytes
+					// unsigned short index;	// index of the semantic (for colours and texture coords)
+			M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section
+				// unsigned short bindIndex;	// Index to bind this buffer to
+				// unsigned short vertexSize;	// Per-vertex size, must agree with declaration at this index
+				M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210,
+					// raw buffer data
+		M_MESH_SKELETON_LINK = 0x6000,
+			// Optional link to skeleton
+			// char* skeletonName		   : name of .skeleton to use
+		M_MESH_BONE_ASSIGNMENT = 0x7000,
+			// Optional bone weights (repeating section)
+			// unsigned int vertexIndex;
+			// unsigned short boneIndex;
+			// float weight;
+		M_MESH_LOD = 0x8000,
+			// Optional LOD information
+			// string strategyName;
+			// unsigned short numLevels;
+			// bool manual;  (true for manual alternate meshes, false for generated)
+			M_MESH_LOD_USAGE = 0x8100,
+			// Repeating section, ordered in increasing depth
+			// NB LOD 0 (full detail from 0 depth) is omitted
+			// LOD value - this is a distance, a pixel count etc, based on strategy
+			// float lodValue;
+				M_MESH_LOD_MANUAL = 0x8110,
+				// Required if M_MESH_LOD section manual = true
+				// String manualMeshName;
+				M_MESH_LOD_GENERATED = 0x8120,
+				// Required if M_MESH_LOD section manual = false
+				// Repeating section (1 per submesh)
+				// unsigned int indexCount;
+				// bool indexes32Bit
+				// unsigned short* faceIndexes;  (indexCount)
+				// OR
+				// unsigned int* faceIndexes;  (indexCount)
+		M_MESH_BOUNDS = 0x9000,
+			// float minx, miny, minz
+			// float maxx, maxy, maxz
+			// float radius
+				
+		// Added By DrEvil
+		// optional chunk that contains a table of submesh indexes and the names of
+		// the sub-meshes.
+		M_SUBMESH_NAME_TABLE = 0xA000,
+			// Subchunks of the name table. Each chunk contains an index & string
+			M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100,
+				// short index
+				// char* name
+		// Optional chunk which stores precomputed edge data					 
+		M_EDGE_LISTS = 0xB000,
+			// Each LOD has a separate edge list
+			M_EDGE_LIST_LOD = 0xB100,
+				// unsigned short lodIndex
+				// bool isManual			// If manual, no edge data here, loaded from manual mesh
+					// bool isClosed
+					// unsigned long numTriangles
+					// unsigned long numEdgeGroups
+					// Triangle* triangleList
+						// unsigned long indexSet
+						// unsigned long vertexSet
+						// unsigned long vertIndex[3]
+						// unsigned long sharedVertIndex[3] 
+						// float normal[4] 
+
+					M_EDGE_GROUP = 0xB110,
+						// unsigned long vertexSet
+						// unsigned long triStart
+						// unsigned long triCount
+						// unsigned long numEdges
+						// Edge* edgeList
+							// unsigned long  triIndex[2]
+							// unsigned long  vertIndex[2]
+							// unsigned long  sharedVertIndex[2]
+							// bool degenerate
+		// Optional poses section, referred to by pose keyframes
+		M_POSES = 0xC000,
+			M_POSE = 0xC100,
+				// char* name (may be blank)
+				// unsigned short target	// 0 for shared geometry, 
+											// 1+ for submesh index + 1
+				// bool includesNormals [1.8+]
+				M_POSE_VERTEX = 0xC111,
+					// unsigned long vertexIndex
+					// float xoffset, yoffset, zoffset
+					// float xnormal, ynormal, znormal (optional, 1.8+)
+		// Optional vertex animation chunk
+		M_ANIMATIONS = 0xD000, 
+			M_ANIMATION = 0xD100,
+			// char* name
+			// float length
+			M_ANIMATION_BASEINFO = 0xD105,
+			// [Optional] base keyframe information (pose animation only)
+			// char* baseAnimationName (blank for self)
+			// float baseKeyFrameTime
+			M_ANIMATION_TRACK = 0xD110,
+				// unsigned short type			// 1 == morph, 2 == pose
+				// unsigned short target		// 0 for shared geometry, 
 												// 1+ for submesh index + 1
+				M_ANIMATION_MORPH_KEYFRAME = 0xD111,
+					// float time
 					// bool includesNormals [1.8+]
-					M_POSE_VERTEX = 0xC111,
-						// unsigned long vertexIndex
-						// float xoffset, yoffset, zoffset
-						// float xnormal, ynormal, znormal (optional, 1.8+)
-			// Optional vertex animation chunk
-			M_ANIMATIONS = 0xD000, 
-				M_ANIMATION = 0xD100,
-				// char* name
-				// float length
-				M_ANIMATION_BASEINFO = 0xD105,
-				// [Optional] base keyframe information (pose animation only)
-				// char* baseAnimationName (blank for self)
-				// float baseKeyFrameTime
-				M_ANIMATION_TRACK = 0xD110,
-					// unsigned short type			// 1 == morph, 2 == pose
-					// unsigned short target		// 0 for shared geometry, 
-													// 1+ for submesh index + 1
-					M_ANIMATION_MORPH_KEYFRAME = 0xD111,
-						// float time
-						// bool includesNormals [1.8+]
-						// float x,y,z			// repeat by number of vertices in original geometry
-					M_ANIMATION_POSE_KEYFRAME = 0xD112,
-						// float time
-						M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses
-							// unsigned short poseIndex 
-							// float influence
-			// Optional submesh extreme vertex list chink
-			M_TABLE_EXTREMES = 0xE000,
-			// unsigned short submesh_index;
-			// float extremes [n_extremes][3];
-	};
-	
-	static std::string MeshHeaderToString(MeshChunkId id)
+					// float x,y,z			// repeat by number of vertices in original geometry
+				M_ANIMATION_POSE_KEYFRAME = 0xD112,
+					// float time
+					M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses
+						// unsigned short poseIndex 
+						// float influence
+		// Optional submesh extreme vertex list chink
+		M_TABLE_EXTREMES = 0xE000,
+		// unsigned short submesh_index;
+		// float extremes [n_extremes][3];
+};
+
+static std::string MeshHeaderToString(MeshChunkId id)
+{
+	switch(id)
 	{
-		switch(id)
-		{
-			case M_HEADER:						return "HEADER";
-			case M_MESH:						return "MESH";
-			case M_SUBMESH:						return "SUBMESH";
-			case M_SUBMESH_OPERATION:			return "SUBMESH_OPERATION";
-			case M_SUBMESH_BONE_ASSIGNMENT:		return "SUBMESH_BONE_ASSIGNMENT";
-			case M_SUBMESH_TEXTURE_ALIAS:		return "SUBMESH_TEXTURE_ALIAS";
-			case M_GEOMETRY:					return "GEOMETRY";
-			case M_GEOMETRY_VERTEX_DECLARATION: return "GEOMETRY_VERTEX_DECLARATION";
-			case M_GEOMETRY_VERTEX_ELEMENT:		return "GEOMETRY_VERTEX_ELEMENT";
-			case M_GEOMETRY_VERTEX_BUFFER:		return "GEOMETRY_VERTEX_BUFFER";
-			case M_GEOMETRY_VERTEX_BUFFER_DATA: return "GEOMETRY_VERTEX_BUFFER_DATA";
-			case M_MESH_SKELETON_LINK:			return "MESH_SKELETON_LINK";
-			case M_MESH_BONE_ASSIGNMENT:		return "MESH_BONE_ASSIGNMENT";
-			case M_MESH_LOD:					return "MESH_LOD";
-			case M_MESH_LOD_USAGE:				return "MESH_LOD_USAGE";
-			case M_MESH_LOD_MANUAL:				return "MESH_LOD_MANUAL";
-			case M_MESH_LOD_GENERATED:			return "MESH_LOD_GENERATED";
-			case M_MESH_BOUNDS:					return "MESH_BOUNDS";
-			case M_SUBMESH_NAME_TABLE:			return "SUBMESH_NAME_TABLE";
-			case M_SUBMESH_NAME_TABLE_ELEMENT:	return "SUBMESH_NAME_TABLE_ELEMENT";
-			case M_EDGE_LISTS:					return "EDGE_LISTS";
-			case M_EDGE_LIST_LOD:				return "EDGE_LIST_LOD";
-			case M_EDGE_GROUP:					return "EDGE_GROUP";
-			case M_POSES:						return "POSES";
-			case M_POSE:						return "POSE";
-			case M_POSE_VERTEX:					return "POSE_VERTEX";
-			case M_ANIMATIONS:					return "ANIMATIONS";
-			case M_ANIMATION:					return "ANIMATION";
-			case M_ANIMATION_BASEINFO:			return "ANIMATION_BASEINFO";
-			case M_ANIMATION_TRACK:				return "ANIMATION_TRACK";
-			case M_ANIMATION_MORPH_KEYFRAME:	return "ANIMATION_MORPH_KEYFRAME";
-			case M_ANIMATION_POSE_KEYFRAME:		return "ANIMATION_POSE_KEYFRAME";
-			case M_ANIMATION_POSE_REF:			return "ANIMATION_POSE_REF";
-			case M_TABLE_EXTREMES:				return "TABLE_EXTREMES";
-		}
-		return "Uknown_MeshChunkId";
+		case M_HEADER:						return "HEADER";
+		case M_MESH:						return "MESH";
+		case M_SUBMESH:						return "SUBMESH";
+		case M_SUBMESH_OPERATION:			return "SUBMESH_OPERATION";
+		case M_SUBMESH_BONE_ASSIGNMENT:		return "SUBMESH_BONE_ASSIGNMENT";
+		case M_SUBMESH_TEXTURE_ALIAS:		return "SUBMESH_TEXTURE_ALIAS";
+		case M_GEOMETRY:					return "GEOMETRY";
+		case M_GEOMETRY_VERTEX_DECLARATION: return "GEOMETRY_VERTEX_DECLARATION";
+		case M_GEOMETRY_VERTEX_ELEMENT:		return "GEOMETRY_VERTEX_ELEMENT";
+		case M_GEOMETRY_VERTEX_BUFFER:		return "GEOMETRY_VERTEX_BUFFER";
+		case M_GEOMETRY_VERTEX_BUFFER_DATA: return "GEOMETRY_VERTEX_BUFFER_DATA";
+		case M_MESH_SKELETON_LINK:			return "MESH_SKELETON_LINK";
+		case M_MESH_BONE_ASSIGNMENT:		return "MESH_BONE_ASSIGNMENT";
+		case M_MESH_LOD:					return "MESH_LOD";
+		case M_MESH_LOD_USAGE:				return "MESH_LOD_USAGE";
+		case M_MESH_LOD_MANUAL:				return "MESH_LOD_MANUAL";
+		case M_MESH_LOD_GENERATED:			return "MESH_LOD_GENERATED";
+		case M_MESH_BOUNDS:					return "MESH_BOUNDS";
+		case M_SUBMESH_NAME_TABLE:			return "SUBMESH_NAME_TABLE";
+		case M_SUBMESH_NAME_TABLE_ELEMENT:	return "SUBMESH_NAME_TABLE_ELEMENT";
+		case M_EDGE_LISTS:					return "EDGE_LISTS";
+		case M_EDGE_LIST_LOD:				return "EDGE_LIST_LOD";
+		case M_EDGE_GROUP:					return "EDGE_GROUP";
+		case M_POSES:						return "POSES";
+		case M_POSE:						return "POSE";
+		case M_POSE_VERTEX:					return "POSE_VERTEX";
+		case M_ANIMATIONS:					return "ANIMATIONS";
+		case M_ANIMATION:					return "ANIMATION";
+		case M_ANIMATION_BASEINFO:			return "ANIMATION_BASEINFO";
+		case M_ANIMATION_TRACK:				return "ANIMATION_TRACK";
+		case M_ANIMATION_MORPH_KEYFRAME:	return "ANIMATION_MORPH_KEYFRAME";
+		case M_ANIMATION_POSE_KEYFRAME:		return "ANIMATION_POSE_KEYFRAME";
+		case M_ANIMATION_POSE_REF:			return "ANIMATION_POSE_REF";
+		case M_TABLE_EXTREMES:				return "TABLE_EXTREMES";
 	}
+	return "Unknown_MeshChunkId";
+}
+
+enum SkeletonChunkId
+{
+	SKELETON_HEADER				= 0x1000,
+		// char* version		   : Version number check
+		SKELETON_BLENDMODE		= 0x1010, // optional
+			// unsigned short blendmode		: SkeletonAnimationBlendMode
+	SKELETON_BONE				= 0x2000,
+	// Repeating section defining each bone in the system. 
+	// Bones are assigned indexes automatically based on their order of declaration
+	// starting with 0.
+		// char* name					   : name of the bone
+		// unsigned short handle			: handle of the bone, should be contiguous & start at 0
+		// Vector3 position				 : position of this bone relative to parent 
+		// Quaternion orientation		   : orientation of this bone relative to parent 
+		// Vector3 scale					: scale of this bone relative to parent 
+	SKELETON_BONE_PARENT		= 0x3000,
+	// Record of the parent of a single bone, used to build the node tree
+	// Repeating section, listed in Bone Index order, one per Bone
+		// unsigned short handle			 : child bone
+		// unsigned short parentHandle   : parent bone
+	SKELETON_ANIMATION			= 0x4000,
+	// A single animation for this skeleton
+		// char* name					   : Name of the animation
+		// float length					  : Length of the animation in seconds
+		SKELETON_ANIMATION_BASEINFO = 0x4010,
+		// [Optional] base keyframe information
+		// char* baseAnimationName (blank for self)
+		// float baseKeyFrameTime
+		SKELETON_ANIMATION_TRACK	= 0x4100,
+		// A single animation track (relates to a single bone)
+		// Repeating section (within SKELETON_ANIMATION)
+			// unsigned short boneIndex	 : Index of bone to apply to
+			SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110,
+			// A single keyframe within the track
+			// Repeating section
+				// float time					: The time position (seconds)
+				// Quaternion rotate			: Rotation to apply at this keyframe
+				// Vector3 translate			: Translation to apply at this keyframe
+				// Vector3 scale				: Scale to apply at this keyframe
+	SKELETON_ANIMATION_LINK		= 0x5000
+	// Link to another skeleton, to re-use its animations
+		// char* skeletonName					: name of skeleton to get animations from
+		// float scale							: scale to apply to trans/scale keys
+};
 
+static std::string SkeletonHeaderToString(SkeletonChunkId id)
+{
+	switch(id)
+	{
+		case SKELETON_HEADER:					return "HEADER";
+		case SKELETON_BLENDMODE:				return "BLENDMODE";
+		case SKELETON_BONE:						return "BONE";
+		case SKELETON_BONE_PARENT:				return "BONE_PARENT";
+		case SKELETON_ANIMATION:				return "ANIMATION";
+		case SKELETON_ANIMATION_BASEINFO:		return "ANIMATION_BASEINFO";
+		case SKELETON_ANIMATION_TRACK:			return "ANIMATION_TRACK";
+		case SKELETON_ANIMATION_TRACK_KEYFRAME: return "ANIMATION_TRACK_KEYFRAME";
+		case SKELETON_ANIMATION_LINK:			return "ANIMATION_LINK";
+	}
+	return "Unknown_SkeletonChunkId";
+}
 } // Ogre
 } // Assimp
 

+ 3 - 0
code/OgreImporter.cpp

@@ -110,6 +110,9 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
 		// Import mesh
 		boost::scoped_ptr<Mesh> mesh = OgreBinarySerializer::ImportMesh(&reader);
 
+		// Import skeleton
+		OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh);
+
 		// Import mesh referenced materials
 		ReadMaterials(pFile, pIOHandler, pScene, mesh.get());
 

+ 55 - 27
code/OgreStructs.cpp

@@ -459,6 +459,35 @@ void Mesh::ConvertToAssimpScene(aiScene* dest)
 		dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this);
 		dest->mRootNode->mMeshes[i] = i;
 	}
+
+	// Export skeleton
+	if (skeleton)
+	{
+		// Bones
+		if (!skeleton->bones.empty())
+		{
+			BoneList rootBones = skeleton->RootBones();
+			dest->mRootNode->mNumChildren = rootBones.size();
+			dest->mRootNode->mChildren = new aiNode*[dest->mRootNode->mNumChildren];
+
+			for(size_t i=0, len=rootBones.size(); i<len; ++i)
+			{
+				dest->mRootNode->mChildren[i] = rootBones[i]->ConvertToAssimpNode(skeleton, dest->mRootNode);
+			}
+		}
+
+		// Animations
+		if (!skeleton->animations.empty())
+		{
+			dest->mNumAnimations = skeleton->animations.size();
+			dest->mAnimations = new aiAnimation*[dest->mNumAnimations];
+
+			for(size_t i=0, len=skeleton->animations.size(); i<len; ++i)
+			{
+				dest->mAnimations[i] = skeleton->animations[i]->ConvertToAssimpAnimation();
+			}
+		}
+	}
 }
 
 // ISubMesh
@@ -652,7 +681,7 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent)
 			}
 		}
 	}
-	
+
 	// Bones and bone weights
 	if (parent->skeleton && boneAssignments)
 	{
@@ -926,7 +955,8 @@ aiAnimation *Animation::ConvertToAssimpAnimation()
 
 // Skeleton
 
-Skeleton::Skeleton()
+Skeleton::Skeleton() :
+	blendMode(ANIMBLEND_AVERAGE)
 {
 }
 
@@ -995,7 +1025,6 @@ Bone::Bone() :
 	id(0),
 	parent(0),
 	parentId(-1),
-	rotationAngle(0.0f),
 	scale(1.0f, 1.0f, 1.0f)
 {
 }
@@ -1024,16 +1053,12 @@ void Bone::AddChild(Bone *bone)
 
 void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton)
 {
-	aiMatrix4x4 t0, t1;
-	aiMatrix4x4 transform = aiMatrix4x4::Rotation(-rotationAngle, rotation, t1) * aiMatrix4x4::Translation(-position, t0);
-
 	if (!IsParented())
-		worldMatrix = transform;
+		worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse();
 	else
-		worldMatrix = transform * parent->worldMatrix;
+		worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse() * parent->worldMatrix;
 
-	aiMatrix4x4 t2, t3; /// @todo t0 and t1 could probably be reused here?
-	defaultPose = aiMatrix4x4::Translation(position, t2) * aiMatrix4x4::Rotation(rotationAngle, rotation, t3);
+	defaultPose = aiMatrix4x4(scale, rotation, position);
 
 	// Recursively for all children now that the parent matrix has been calculated.
 	for (size_t i=0, len=children.size(); i<len; ++i)
@@ -1048,8 +1073,6 @@ void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton)
 
 aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode)
 {
-	aiMatrix4x4 t0,t1;
-
 	// Bone node
 	aiNode* node = new aiNode(name);
 	node->mParent = parentNode;
@@ -1123,35 +1146,40 @@ aiNodeAnim *VertexAnimationTrack::ConvertToAssimpAnimationNode(Skeleton *skeleto
 
 	for(size_t kfi=0; kfi<numKeyframes; ++kfi)
 	{
-		const TransformKeyFrame &kfSource = transformKeyFrames[kfi];
-
-		// Create a matrix to transform a vector from the bones 
-		// default pose to the bone bones in this animation key
-		aiMatrix4x4 t0, t1;
-		aiMatrix4x4 keyBonePose =
-			aiMatrix4x4::Translation(kfSource.position, t0) *
-			aiMatrix4x4(kfSource.rotation.GetMatrix()) *
-			aiMatrix4x4::Scaling(kfSource.scale, t1);
+		TransformKeyFrame &kfSource = transformKeyFrames[kfi];
 
 		// Calculate the complete transformation from world space to bone space
-		aiMatrix4x4 finalTransform = bone->defaultPose * keyBonePose;
+		aiVector3D pos; aiQuaternion rot; aiVector3D scale;
 
-		aiVector3D kfPos; aiQuaternion kfRot; aiVector3D kfScale;
-		finalTransform.Decompose(kfScale, kfRot, kfPos);
+		aiMatrix4x4 finalTransform = bone->defaultPose * kfSource.Transform();		
+		finalTransform.Decompose(scale, rot, pos);
 
 		double t = static_cast<double>(kfSource.timePos);
 		nodeAnim->mPositionKeys[kfi].mTime = t;
 		nodeAnim->mRotationKeys[kfi].mTime = t;
 		nodeAnim->mScalingKeys[kfi].mTime = t;
 
-		nodeAnim->mPositionKeys[kfi].mValue = kfPos;					
-		nodeAnim->mRotationKeys[kfi].mValue = kfRot;
-		nodeAnim->mScalingKeys[kfi].mValue = kfScale;
+		nodeAnim->mPositionKeys[kfi].mValue = pos;					
+		nodeAnim->mRotationKeys[kfi].mValue = rot;
+		nodeAnim->mScalingKeys[kfi].mValue = scale;
 	}
 
 	return nodeAnim;
 }
 
+// TransformKeyFrame
+
+TransformKeyFrame::TransformKeyFrame() : 
+	timePos(0.0f),
+	scale(1.0f, 1.0f, 1.0f)
+{
+}
+
+aiMatrix4x4 TransformKeyFrame::Transform()
+{
+	return aiMatrix4x4(scale, rotation, position);
+}
+
 } // Ogre
 } // Assimp
 

+ 17 - 4
code/OgreStructs.h

@@ -65,7 +65,6 @@ class Skeleton;
 #define OGRE_SAFE_DELETE(p) delete p; p=0;
 
 // Typedefs
-typedef Assimp::StreamReaderLE MemoryStreamReader;
 typedef Assimp::MemoryIOStream MemoryStream;
 typedef boost::shared_ptr<MemoryStream> MemoryStreamPtr;
 typedef std::map<uint16_t, MemoryStreamPtr> VertexBufferBindings;
@@ -318,6 +317,10 @@ typedef std::vector<MorphKeyFrame> MorphKeyFrameList;
 /// Ogre animation key frame
 struct TransformKeyFrame
 {
+	TransformKeyFrame();
+	
+	aiMatrix4x4 Transform();
+
 	float timePos;
 	
 	aiQuaternion rotation;
@@ -435,9 +438,8 @@ public:
 	std::vector<uint16_t> children;
 
 	aiVector3D position;
-	aiVector3D rotation;
-	aiVector3D scale; ///< @todo Implement taking scale into account in matrix/pose calculations!
-	float rotationAngle;
+	aiQuaternion rotation;
+	aiVector3D scale;
 	
 	aiMatrix4x4 worldMatrix;
 	aiMatrix4x4 defaultPose;
@@ -448,6 +450,14 @@ typedef std::vector<Bone*> BoneList;
 class Skeleton
 {
 public:
+	enum BlendMode
+	{
+		/// Animations are applied by calculating a weighted average of all animations
+		ANIMBLEND_AVERAGE = 0,
+		/// Animations are applied by calculating a weighted cumulative total
+		ANIMBLEND_CUMULATIVE = 1
+	};
+
 	Skeleton();
 	~Skeleton();
 
@@ -468,6 +478,9 @@ public:
 	
 	BoneList bones;
 	AnimationList animations;
+	
+	/// @todo Take blend mode into account, but where?
+	BlendMode blendMode;
 };
 
 /// Ogre Sub Mesh interface, inherited by the binary and XML implementations.

+ 68 - 23
code/OgreXmlSerializer.cpp

@@ -39,8 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #include "OgreXmlSerializer.h"
+#include "OgreBinarySerializer.h"
+#include "OgreParsingUtils.h"
 
-#include "irrXMLWrapper.h"
 #include "TinyFormatter.h"
 
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
@@ -638,9 +639,10 @@ void OgreXmlSerializer::ReadBoneAssignments(VertexDataXml *dest)
 		ba.vertexIndex = ReadAttribute<uint32_t>(anVertexIndex);
 		ba.boneIndex = ReadAttribute<uint16_t>(anBoneIndex);
 		ba.weight = ReadAttribute<float>(anWeight);
+
 		dest->boneAssignments.push_back(ba);
-		
 		influencedVertices.insert(ba.vertexIndex);
+
 		NextNode();
 	}
 
@@ -675,23 +677,61 @@ void OgreXmlSerializer::ReadBoneAssignments(VertexDataXml *dest)
 
 void OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh)
 {
-	if (mesh->skeletonRef.empty())
+	if (!mesh || mesh->skeletonRef.empty())
+		return;
+
+	// Highly unusual to see in read world cases but support
+	// XML mesh referencing a binary skeleton file.
+	if (EndsWith(mesh->skeletonRef, ".skeleton", false))
+	{
+		if (OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh))
+			return;
+		
+		/** Last fallback if .skeleton failed to be read.
+			Try reading from .skeleton.xml even if the XML file
+			referenced a binary skeleton.
+			@note This logic was in the previous version and
+			I don't want to break old code that depends on it. */
+		mesh->skeletonRef = mesh->skeletonRef + ".xml";
+	}
+
+	XmlReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
+	if (!reader.get())
+		return;
+
+	Skeleton *skeleton = new Skeleton();
+	OgreXmlSerializer serializer(reader.get());
+	serializer.ReadSkeleton(skeleton);
+	mesh->skeleton = skeleton;
+}
+
+void OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh)
+{
+	if (!mesh || mesh->skeletonRef.empty())
+		return;
+
+	XmlReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef);
+	if (!reader.get())
 		return;
 
-	/** @todo Also support referencing a binary skeleton from a XML mesh?
-		This will involves new interfacing to cross ref from MeshXml... */
+	Skeleton *skeleton = new Skeleton();
+	OgreXmlSerializer serializer(reader.get());
+	serializer.ReadSkeleton(skeleton);
+	mesh->skeleton = skeleton;
+}
 
-	std::string filename = mesh->skeletonRef;
-	if (EndsWith(filename, ".skeleton"))
+XmlReaderPtr OgreXmlSerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename)
+{
+	if (!EndsWith(filename, ".skeleton.xml", false))
 	{
-		DefaultLogger::get()->warn("Mesh is referencing a Ogre binary skeleton. Parsing binary Ogre assets is not supported at the moment. Trying to find .skeleton.xml file instead.");
-		filename += ".xml";
+		DefaultLogger::get()->error("Imported Mesh is referencing to unsupported '" + filename + "' skeleton file.");
+		return XmlReaderPtr();
 	}
 
 	if (!pIOHandler->Exists(filename))
 	{
 		DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "' that is referenced by imported Mesh.");
-		return;
+		return XmlReaderPtr();
 	}
 
 	boost::scoped_ptr<IOStream> file(pIOHandler->Open(filename));
@@ -700,15 +740,11 @@ void OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *me
 	}
 
 	boost::scoped_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
-	XmlReader* reader = irr::io::createIrrXMLReader(stream.get());
-	if (!reader) {
+	XmlReaderPtr reader = XmlReaderPtr(irr::io::createIrrXMLReader(stream.get()));
+	if (!reader.get()) {
 		throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
 	}
-	
-	Skeleton *skeleton = new Skeleton();
-	OgreXmlSerializer serializer(reader);
-	serializer.ReadSkeleton(skeleton);
-	mesh->skeleton = skeleton;
+	return reader;
 }
 
 void OgreXmlSerializer::ReadSkeleton(Skeleton *skeleton)
@@ -718,6 +754,12 @@ void OgreXmlSerializer::ReadSkeleton(Skeleton *skeleton)
 	}
 	
 	DefaultLogger::get()->debug("Reading Skeleton");
+	
+	// Optional blend mode from root node
+	if (HasAttribute("blendmode")) {
+		skeleton->blendMode = (ToLower(ReadAttribute<std::string>("blendmode")) == "cumulative" 
+			? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
+	}
 
 	NextNode();
 
@@ -854,10 +896,10 @@ void OgreXmlSerializer::ReadBoneHierarchy(Skeleton *skeleton)
 		if (bone && parent)
 			parent->AddChild(bone);
 		else
-			DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + name + " for parent " + parentName);
+			throw DeadlyImportError("Failed to find bones for parenting: Child " + name + " for parent " + parentName);
 	}
 	
-	// Calculate bone matrices for root bones. Recursively calcutes their children.
+	// Calculate bone matrices for root bones. Recursively calculates their children.
 	for (size_t i=0, len=skeleton->bones.size(); i<len; ++i)
 	{
 		Bone *bone = skeleton->bones[i];
@@ -895,15 +937,18 @@ void OgreXmlSerializer::ReadBones(Skeleton *skeleton)
 			}
 			else if (m_currentNodeName == nnRotation)
 			{
-				bone->rotationAngle = ReadAttribute<float>("angle");
+				float angle = ReadAttribute<float>("angle");
 
 				if (NextNode() != nnAxis) {
 					throw DeadlyImportError(Formatter::format() << "No axis specified for bone rotation in bone " << bone->id);
 				}
 
-				bone->rotation.x = ReadAttribute<float>(anX);
-				bone->rotation.y = ReadAttribute<float>(anY);
-				bone->rotation.z = ReadAttribute<float>(anZ);
+				aiVector3D axis;
+				axis.x = ReadAttribute<float>(anX);
+				axis.y = ReadAttribute<float>(anY);
+				axis.z = ReadAttribute<float>(anZ);
+				
+				bone->rotation = aiQuaternion(axis, angle);
 			}
 			else if (m_currentNodeName == nnScale)
 			{

+ 5 - 3
code/OgreXmlSerializer.h

@@ -44,8 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
 
 #include "OgreStructs.h"
-#include "OgreParsingUtils.h"
-
 #include "irrXMLWrapper.h"
 
 namespace Assimp
@@ -54,6 +52,7 @@ namespace Ogre
 {
 
 typedef irr::io::IrrXMLReader XmlReader;
+typedef boost::shared_ptr<XmlReader> XmlReaderPtr;
 
 class OgreXmlSerializer
 {
@@ -62,16 +61,19 @@ public:
 	/** @note Fatal unrecoverable errors will throw a DeadlyImportError. */
 	static MeshXml *ImportMesh(XmlReader *reader);
 	
-	/// Imports skeleton to @c mesh into MeshXML::skeleton.
+	/// Imports skeleton to @c mesh.
 	/** If mesh does not have a skeleton reference or the skeleton file
 		cannot be found it is not a fatal DeadlyImportError. */
 	static void ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh);
+	static void ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh);
 
 private:
 	OgreXmlSerializer(XmlReader *reader) :
 		m_reader(reader)
 	{
 	}
+	
+	static XmlReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename);
 
 	// Mesh
 	void ReadMesh(MeshXml *mesh);