#include "../../Engine/Geometry/RuMeshBase.h"
#include "../../NvTriStrip/NvTriStrip.h"
#include <set>

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(IRuMorphTarget, IRuObject)
ruCLASSGUID_DEFINE(IRuMorphTarget, 0x01AB7C30, 0x295A4f41, 0x8E5046BC, 0xA9FE2880)

ruRTTI_DEFINE_SUBCLASS(IRuMesh, IRuObject)
ruCLASSGUID_DEFINE(IRuMesh, 0x88279D3D, 0x5A234bc6, 0x81997A96, 0xA74A06FF)

ruRTTI_DEFINE_SUBCLASS(CRuMorphTarget, IRuMorphTarget)
ruHEAP_DEFINE_SUBCLASS(CRuMorphTarget, "CRuMorphTarget", "IRuMorphTarget")
ruCLASSGUID_DEFINE(CRuMorphTarget, 0x3B2A099C, 0xAF1E49ac, 0x893B5C66, 0x7201CE3B)

ruRTTI_DEFINE_SUBCLASS(CRuMesh, IRuMesh)
ruHEAP_DEFINE_SUBCLASS(CRuMesh, "CRuMesh", "IRuMesh")
ruCLASSGUID_DEFINE(CRuMesh, 0xA8C66048, 0x2D234147, 0xAB0D3AC5, 0xD4812700)

ruRTTI_DEFINE_SUBCLASS(CRuSkinnableMesh, IRuMesh)
ruHEAP_DEFINE_SUBCLASS(CRuSkinnableMesh, "CRuSkinnableMesh", "IRuMesh")
ruCLASSGUID_DEFINE(CRuSkinnableMesh, 0xF9D6E89C, 0x0CB94eb2, 0xA7EB32FD, 0x829825AF)

ruRTTI_DEFINE_SUBCLASS(CRuDynamicMesh, CRuMesh)
ruHEAP_DEFINE_SUBCLASS(CRuDynamicMesh, "CRuDynamicMesh", "CRuMesh")
ruCLASSGUID_DEFINE(CRuDynamicMesh, 0x1F3E3EB3, 0x10B24ab1, 0x852E8A36, 0x51F8ED5F)

ruRTTI_DEFINE_SUBCLASS(CRuProxyMesh, IRuMesh)
ruHEAP_DEFINE_SUBCLASS(CRuProxyMesh, "CRuProxyMesh", "IRuMesh")
ruCLASSGUID_DEFINE(CRuProxyMesh, 0x7B95A9D1, 0xE7814aac, 0xBA2C8D1C, 0x83CEEECF)

// ************************************************************************************************************************************************************

CRuMorphTarget::CRuMorphTarget()
:	m_iterationNumber(0),
	m_vertexFormat(ruVERTEXFORMAT_POSITION),
	m_vertexCount(0),
	m_position(NULL),
	m_normal(NULL),
	m_diffuse(NULL),
	m_specular(NULL),
	m_blendWeight(NULL),
	m_blendIndex(NULL)
{
}

CRuMorphTarget::~CRuMorphTarget()
{
	delete[] m_position;
	delete[] m_normal;
	delete[] m_diffuse;
	delete[] m_specular;
	delete[] m_blendWeight;
	delete[] m_blendIndex;

	for(INT32 i = 0; i < m_textureCoordinates.Count(); ++i)
	{
		delete[] m_textureCoordinates[i];
	}

	m_textureChannels.Clear();
	m_textureCoordinates.Clear();
}

BOOL CRuMorphTarget::Initialize(INT32 numVertices, DWORD vertexFormat)
{
	// Destroy previously allocated memory
	delete[] m_position;
	delete[] m_normal;
	delete[] m_diffuse;
	delete[] m_specular;
	delete[] m_blendWeight;
	delete[] m_blendIndex;

	for(INT32 i = 0; i < m_textureCoordinates.Count(); ++i)
	{
		delete[] m_textureCoordinates[i];
	}

	m_textureChannels.Clear();
	m_textureCoordinates.Clear();

	m_vertexFormat = vertexFormat;
	m_vertexCount = numVertices;
	m_position = NULL;
	m_normal = NULL;
	m_diffuse = NULL;
	m_specular = NULL;
	m_blendWeight = NULL;
	m_blendIndex = NULL;

	// Re-allocate memory according to the specified vertex format
	if(m_vertexFormat & ruVERTEXFORMAT_POSITION)
		m_position = ruNEW CRuVector3 [m_vertexCount];

	if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
		m_normal = ruNEW CRuVector3 [m_vertexCount];

	if(m_vertexFormat & ruVERTEXFORMAT_DIFFUSE)
		m_diffuse = ruNEW DWORD [m_vertexCount];

	if(m_vertexFormat & ruVERTEXFORMAT_SPECULAR)
		m_specular = ruNEW DWORD [m_vertexCount];

	// Allocate blend weight array
	if(m_vertexFormat & ruVERTEXFORMAT_BLENDWEIGHT)
		m_blendWeight = ruNEW float [m_vertexCount * 4];

	// Allocate blend index array
	if(m_vertexFormat & ruVERTEXFORMAT_BLENDINDICES)
		m_blendIndex = ruNEW UINT16 [m_vertexCount * 4];

	return TRUE;
}

BOOL CRuMorphTarget::SetVertexFormat(DWORD vertexFormat)
{
	m_vertexFormat = vertexFormat;

	// Re-allocate memory according to the specified vertex format
	if(m_vertexFormat & ruVERTEXFORMAT_POSITION)
	{
		if(m_position == NULL)
			m_position = ruNEW CRuVector3 [m_vertexCount];
	}
	else
	{
		delete[] m_position;
		m_position = NULL;
	}

	if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
	{
		if(m_normal == NULL)
			m_normal = ruNEW CRuVector3 [m_vertexCount];
	}
	else
	{
		delete[] m_normal;
		m_normal = NULL;
	}

	if(m_vertexFormat & ruVERTEXFORMAT_DIFFUSE)
	{
		if(m_diffuse == NULL)
			m_diffuse = ruNEW DWORD [m_vertexCount];
	}
	else
	{
		delete[] m_diffuse;
		m_diffuse = NULL;
	}

	if(m_vertexFormat & ruVERTEXFORMAT_SPECULAR)
	{
		if(m_specular == NULL)
			m_specular = ruNEW DWORD [m_vertexCount];
	}
	else
	{
		delete[] m_specular;
		m_specular = NULL;
	}

	// Allocate blend weight array
	if(m_vertexFormat & ruVERTEXFORMAT_BLENDWEIGHT)
	{
		if(m_blendWeight == NULL)
			m_blendWeight = ruNEW float [m_vertexCount * 4];
	}
	else
	{
		delete[] m_blendWeight;
		m_blendWeight = NULL;
	}

	// Allocate blend index array
	if(m_vertexFormat & ruVERTEXFORMAT_BLENDINDICES)
	{
		if(m_blendIndex == NULL)
			m_blendIndex = ruNEW UINT16 [m_vertexCount * 4];
	}
	else
	{
		delete[] m_blendIndex;
		m_blendIndex = NULL;
	}

	return TRUE;
}

BOOL CRuMorphTarget::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	INT32 vertexFormat;
	INT32 vertexCount;
	INT32 texChannelCount;

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	switch(version)
	{
		case 1:
			{
				stream->Read(&vertexFormat, sizeof(INT32));
				stream->Read(&vertexCount, sizeof(INT32));

				// Reinitialize vertex storage if necessary
				if(vertexCount != m_vertexCount)
				{
					Initialize(vertexCount, vertexFormat);
				}

				// Read positions
				for(INT32 j = 0; j < vertexCount; ++j)
				{
					stream->Read(&m_position[j].x, sizeof(float));
					stream->Read(&m_position[j].y, sizeof(float));
					stream->Read(&m_position[j].z, sizeof(float));
				}

				// Read normals
				if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
				{
					for(INT32 j = 0; j < vertexCount; ++j)
					{
						stream->Read(&m_normal[j].x, sizeof(float));
						stream->Read(&m_normal[j].y, sizeof(float));
						stream->Read(&m_normal[j].z, sizeof(float));
					}
				}
			}

			break;

		case 2:
			{
				stream->Read(&vertexFormat, sizeof(INT32));
				stream->Read(&vertexCount, sizeof(INT32));
				stream->Read(&texChannelCount, sizeof(INT32));

				// Reinitialize vertex storage if necessary
				if(vertexCount != m_vertexCount)
				{
					Initialize(vertexCount, vertexFormat);
				}

				// Read positions
				for(INT32 j = 0; j < vertexCount; ++j)
				{
					stream->Read(&m_position[j].x, sizeof(float));
					stream->Read(&m_position[j].y, sizeof(float));
					stream->Read(&m_position[j].z, sizeof(float));
				}

				// Read normals
				if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
				{
					for(INT32 j = 0; j < vertexCount; ++j)
					{
						stream->Read(&m_normal[j].x, sizeof(float));
						stream->Read(&m_normal[j].y, sizeof(float));
						stream->Read(&m_normal[j].z, sizeof(float));
					}
				}

				// Read diffuse
				if(m_vertexFormat & ruVERTEXFORMAT_DIFFUSE)
				{
					stream->Read(m_diffuse, sizeof(DWORD) * vertexCount);
				}

				// Read specular
				if(m_vertexFormat & ruVERTEXFORMAT_SPECULAR)
				{
					stream->Read(m_specular, sizeof(DWORD) * vertexCount);
				}

				if(m_vertexFormat & ruVERTEXFORMAT_BLENDWEIGHT)
				{
					stream->Read(m_blendWeight, sizeof(float) * vertexCount * 4);
				}

				if(m_vertexFormat & ruVERTEXFORMAT_BLENDINDICES)
				{
					stream->Read(m_blendIndex, sizeof(UINT16) * vertexCount * 4);
				}

				// Read texture channels
				for(INT32 i = 0; i < texChannelCount; ++i)
				{
					RuTextureChannel texChannel;
					INT32 numComponents;
					stream->Read(&texChannel, sizeof(RuTextureChannel));
					stream->Read(&numComponents, sizeof(INT32));

					// Read coordinate component data
					AddTextureCoordinate(texChannel);
					stream->Read(GetTextureCoordinate(texChannel), sizeof(float) * m_vertexCount * numComponents);
				}
			}

			break;
	}

	return TRUE;
}

BOOL CRuMorphTarget::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 2;
	stream->Write(&version, sizeof(INT32));

	// Write vertex format flags
	stream->Write(&m_vertexFormat, sizeof(INT32));

	// Write vertex count
	stream->Write(&m_vertexCount, sizeof(INT32));

	// Write number of texture channels
	INT32 numTexChannels = m_textureChannels.Count();
	stream->Write(&numTexChannels, sizeof(INT32));

	// Write positions
	for(INT32 j = 0; j < m_vertexCount; ++j)
	{
		stream->Write(&m_position[j].x, sizeof(float));
		stream->Write(&m_position[j].y, sizeof(float));
		stream->Write(&m_position[j].z, sizeof(float));
	}

	if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
	{
		// Write normals
		for(INT32 j = 0; j < m_vertexCount; ++j)
		{
			stream->Write(&m_normal[j].x, sizeof(float));
			stream->Write(&m_normal[j].y, sizeof(float));
			stream->Write(&m_normal[j].z, sizeof(float));
		}
	}

	if(m_vertexFormat & ruVERTEXFORMAT_DIFFUSE)
	{
		stream->Write(m_diffuse, sizeof(DWORD) * m_vertexCount);
	}

	if(m_vertexFormat & ruVERTEXFORMAT_SPECULAR)
	{
		stream->Write(m_specular, sizeof(DWORD) * m_vertexCount);
	}

	if(m_vertexFormat & ruVERTEXFORMAT_BLENDWEIGHT)
	{
		stream->Write(m_blendWeight, sizeof(float) * m_vertexCount * 4);
	}

	if(m_vertexFormat & ruVERTEXFORMAT_BLENDINDICES)
	{
		stream->Write(m_blendIndex, sizeof(UINT16) * m_vertexCount * 4);
	}

	// Write texture channels
	for(INT32 i = 0; i < m_textureChannels.Count(); ++i)
	{
		RuTextureChannel texChannel = m_textureChannels[i];
		INT32 numComponents = RuTextureChannel_GetNumComponents(texChannel);
		stream->Write(&texChannel, sizeof(RuTextureChannel));
		stream->Write(&numComponents, sizeof(INT32));

		// Read coordinate component data
		stream->Write(GetTextureCoordinate(texChannel), sizeof(float) * m_vertexCount * numComponents);
	}

	return TRUE;
}

BOOL CRuMorphTarget::AddTextureCoordinate(RuTextureChannel type)
{
	if(!m_textureChannels.Contains(type))
	{
		m_textureChannels.Add(type);
		m_textureCoordinates.Add(ruNEW float [m_vertexCount * RuTextureChannel_GetNumComponents(type)]);

		return TRUE;
	}

	return FALSE;
}

BOOL CRuMorphTarget::CopyFrom(IRuMorphTarget *morphTarget)
{
	if(morphTarget->GetType() == CRuMorphTarget::Type)
	{
		// Determine the number of vertices to copy
		INT32 vertsToCopy = min(m_vertexCount, static_cast<CRuMorphTarget *>(morphTarget)->m_vertexCount);

		// If the vertex formats do not match, re-initialize
		if(m_vertexFormat != static_cast<CRuMorphTarget *>(morphTarget)->m_vertexFormat)
		{
			Initialize(vertsToCopy, static_cast<CRuMorphTarget *>(morphTarget)->m_vertexFormat);
		}

		// Copy position
		if(m_vertexFormat & ruVERTEXFORMAT_POSITION)
		{
			memcpy(m_position, morphTarget->GetPosition(), sizeof(CRuVector3) * vertsToCopy);
		}

		// Copy normal
		if(m_vertexFormat & ruVERTEXFORMAT_NORMAL)
		{
			memcpy(m_normal, morphTarget->GetNormal(), sizeof(CRuVector3) * vertsToCopy);
		}

		if(m_vertexFormat & ruVERTEXFORMAT_DIFFUSE)
		{
			memcpy(m_diffuse, morphTarget->GetDiffuse(), sizeof(DWORD) * vertsToCopy);
		}

		if(m_vertexFormat & ruVERTEXFORMAT_SPECULAR)
		{
			memcpy(m_specular, morphTarget->GetSpecular(), sizeof(DWORD) * vertsToCopy);
		}

		if(m_vertexFormat & ruVERTEXFORMAT_BLENDWEIGHT)
		{
			memcpy(m_blendWeight, morphTarget->GetBlendWeight(), sizeof(float) * 4 * vertsToCopy);
		}

		if(m_vertexFormat & ruVERTEXFORMAT_BLENDINDICES)
		{
			memcpy(m_blendIndex, morphTarget->GetBlendIndex(), sizeof(UINT16) * 4 * vertsToCopy);
		}

		for(INT32 i = 0, numTexChannels = static_cast<CRuMorphTarget *>(morphTarget)->m_textureChannels.Count(); i < numTexChannels; ++i)
		{
			AddTextureCoordinate(static_cast<CRuMorphTarget *>(morphTarget)->m_textureChannels[i]);
			memcpy(m_textureCoordinates[i], static_cast<CRuMorphTarget *>(morphTarget)->m_textureCoordinates[i], sizeof(float) * RuTextureChannel_GetNumComponents(static_cast<CRuMorphTarget *>(morphTarget)->m_textureChannels[i]) * vertsToCopy);
		}

		return TRUE;
	}

	return FALSE;
}

// ************************************************************************************************************************************************************

CRuMesh::CRuMesh()
:	m_meshIterationNumber(0),
	m_topologyIterationNumber(0),
	m_primitiveType(ruPRIMTYPE_TRIANGLELIST),
	m_numVertices(0),
	m_numIndices(0),
	m_vertexFormat(0x00000000),
	m_indices(NULL),
	m_collisionStructure(NULL)
{
}

CRuMesh::~CRuMesh()
{
	for(INT32 i = 0; i < m_morphTargets.Count(); i++)
	{
		m_morphTargets[i]->Release();
	}
	m_morphTargets.Clear();

	delete[] m_indices;

	for(INT32 i = 0; i < m_batches.Count(); ++i)
	{
		delete m_batches[i];
	}

	m_batches.Clear();

	// Release reference to the collision structure
	ruSAFE_RELEASE(m_collisionStructure);
}

BOOL CRuMesh::Initialize(RuPrimitiveType primitiveType, INT32 numVertices, INT32 numPrimitives, DWORD vertexFormat, INT32 numMorphTargets)
{
	// Delete previous morph targets
	for(INT32 i = 0; i < m_morphTargets.Count(); i++)
	{
		m_morphTargets[i]->Release();
	}
	m_morphTargets.Clear();

	delete[] m_indices;

	m_primitiveType = primitiveType;
	m_numVertices = numVertices;
	m_numIndices = RuPrimitiveToIndexCount(numPrimitives, primitiveType);
	m_vertexFormat = vertexFormat;

	// Create morph targets and add to morph target list
	while(numMorphTargets > 0)
	{
		CRuMorphTarget *morphTarget = ruNEW CRuMorphTarget();
		morphTarget->Initialize(m_numVertices, m_vertexFormat);
		m_morphTargets.Add(morphTarget);

		--numMorphTargets;
	}

	// Allocate index array
	m_indices = ruNEW UINT16 [m_numIndices];

	// Setup one default batch
	BatchInfo *defaultBatch = ruNEW BatchInfo();
	defaultBatch->m_offset = 0;
	defaultBatch->m_numPrimitives = numPrimitives;
	m_batches.Add(defaultBatch);

	return TRUE;
}

BOOL CRuMesh::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	RuPrimitiveType primitiveType;
	DWORD vertexFormatFlags;
	INT32 vertexCount, texChannelCount, primitiveCount, mtCount;

	// Read signature and version
	INT32 signature;
	INT32 version = 1;
	stream->Read(&signature, sizeof(INT32));

	if(signature == 0xFFFFFFFF)
	{
		// Signature found, read version 
		stream->Read(&version, sizeof(INT32));
	}
	else
	{
		// No signature, this is an old model stream, so rewind
		stream->Seek(-4, ruSSM_Current);
	}

	switch(version)
	{
		case 1:
			{
				DWORD *diffuse = NULL;
				DWORD *specular = NULL;
				float *blendWeight = NULL;
				UINT16 *blendIndex = NULL;
				CRuArrayList<RuTextureChannel> textureChannels;
				CRuArrayList<float *> textureCoordinates;

				// Read mesh header
				stream->Read(&primitiveType, sizeof(RuPrimitiveType));
				stream->Read(&vertexFormatFlags, sizeof(DWORD));
				stream->Read(&vertexCount, sizeof(INT32));
				stream->Read(&texChannelCount, sizeof(INT32));
				stream->Read(&primitiveCount, sizeof(INT32));
				stream->Read(&mtCount, sizeof(INT32));

				// Initialize mesh storage
				Initialize(primitiveType, vertexCount, primitiveCount, vertexFormatFlags, 0);

				if(vertexFormatFlags & ruVERTEXFORMAT_DIFFUSE)
				{
					diffuse = ruNEW DWORD [vertexCount];
					stream->Read(diffuse, sizeof(DWORD) * vertexCount);
				}

				if(vertexFormatFlags & ruVERTEXFORMAT_SPECULAR)
				{
					specular = ruNEW DWORD [vertexCount];
					stream->Read(specular, sizeof(DWORD) * vertexCount);
				}

				if(vertexFormatFlags & ruVERTEXFORMAT_BLENDWEIGHT)
				{
					blendWeight = ruNEW float [vertexCount * 4];
					stream->Read(blendWeight, sizeof(float) * vertexCount * 4);
				}

				if(vertexFormatFlags & ruVERTEXFORMAT_BLENDINDICES)
				{
					blendIndex = ruNEW UINT16 [vertexCount * 4];
					stream->Read(blendIndex, sizeof(UINT16) * vertexCount * 4);
				}

				// Read texture channels
				for(INT32 i = 0; i < texChannelCount; ++i)
				{
					RuTextureChannel texChannel;
					INT32 numComponents;
					stream->Read(&texChannel, sizeof(RuTextureChannel));
					stream->Read(&numComponents, sizeof(INT32));

					// Read coordinate component data
					textureChannels.Add(texChannel);
					textureCoordinates.Add(ruNEW float [m_numVertices * numComponents]);
					stream->Read(textureCoordinates[i], sizeof(float) * m_numVertices * numComponents);
				}

				// Read face index array
				stream->Read(m_indices, sizeof(UINT16) * m_numIndices);

				// Read morph targets
				for(INT32 i = 0; i < mtCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object->GetType().IsSubClassOf(CRuMorphTarget::Type))
					{
						// Add morph target to list of morph targets
						m_morphTargets.Add((CRuMorphTarget *) object);
						m_morphTargets[i]->SetVertexFormat(vertexFormatFlags);

						// Set other channel data into morph target
						if(diffuse)
						{
							memcpy(m_morphTargets[i]->GetDiffuse(), diffuse, sizeof(DWORD) * vertexCount);
						}

						if(specular)
						{
							memcpy(m_morphTargets[i]->GetSpecular(), specular, sizeof(DWORD) * vertexCount);
						}

						if(blendWeight)
						{
							memcpy(m_morphTargets[i]->GetBlendWeight(), blendWeight, sizeof(float) * vertexCount * 4);
						}

						if(blendIndex)
						{
							memcpy(m_morphTargets[i]->GetBlendIndex(), blendIndex, sizeof(UINT16) * vertexCount * 4);
						}

						for(INT32 j = 0; j < textureChannels.Count(); ++j)
						{
							m_morphTargets[i]->AddTextureCoordinate(textureChannels[j]);
							memcpy(m_morphTargets[i]->GetTextureCoordinate(textureChannels[j]), textureCoordinates[j], sizeof(float) * vertexCount * RuTextureChannel_GetNumComponents(textureChannels[j]));
						}
					}
				}

				// Free temporary resources
				delete[] diffuse;
				delete[] specular;
				delete[] blendWeight;
				delete[] blendIndex;

				for(INT32 i = 0; i < textureChannels.Count(); ++i)
				{
					delete[] textureCoordinates[i];
				}
			}

			break;

		case 2:
			{
				// Read mesh header
				stream->Read(&primitiveType, sizeof(RuPrimitiveType));
				stream->Read(&vertexFormatFlags, sizeof(DWORD));
				stream->Read(&vertexCount, sizeof(INT32));
				stream->Read(&primitiveCount, sizeof(INT32));
				stream->Read(&mtCount, sizeof(INT32));

				// Initialize mesh storage
				Initialize(primitiveType, vertexCount, primitiveCount, vertexFormatFlags, 0);

				// Read face index array
				stream->Read(m_indices, sizeof(UINT16) * m_numIndices);

				// Read morph targets
				for(INT32 i = 0; i < mtCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object->GetType().IsSubClassOf(CRuMorphTarget::Type))
					{
						// Add morph target to list of morph targets
						m_morphTargets.Add((CRuMorphTarget *) object);
					}
				}
			}

			break;

		case 3:	// Version 2005.09.30
			{
				// Read mesh header
				stream->Read(&primitiveType, sizeof(RuPrimitiveType));
				stream->Read(&vertexFormatFlags, sizeof(DWORD));
				stream->Read(&vertexCount, sizeof(INT32));
				stream->Read(&primitiveCount, sizeof(INT32));
				stream->Read(&mtCount, sizeof(INT32));

				// Initialize mesh storage
				Initialize(primitiveType, vertexCount, primitiveCount, vertexFormatFlags, 0);

				// Read face index array
				stream->Read(m_indices, sizeof(UINT16) * m_numIndices);

				// Read morph targets
				for(INT32 i = 0; i < mtCount; ++i)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object->GetType().IsSubClassOf(CRuMorphTarget::Type))
					{
						// Add morph target to list of morph targets
						m_morphTargets.Add((CRuMorphTarget *) object);
					}
				}

				// Read whether collision data exists
				INT32 colDataExists = 0;
				stream->Read(&colDataExists, sizeof(INT32));

				// If collision data exists, read it
				if(colDataExists)
				{
					IRuObject *object = g_ruObjectFactory.CreateObject(stream);

					if(object && object->GetType().IsSubClassOf(IRuCollisionStructure::Type))
					{
						// Attach collision structure
						m_collisionStructure = static_cast<IRuCollisionStructure *>(object);
						m_collisionStructure->AttachToMesh(this);
					}
				}
			}

			break;
	}

	// Build bounding sphere using morph target 0 (for now)
	CalculateBounds();

/*
	// Optimize
	unsigned short numGroups;
	PrimitiveGroup *primGroup;
	SetListsOnly(true);
	if(GenerateStrips(m_indices, m_numIndices * 3, &primGroup, &numGroups, false))
	{
		memcpy(m_indices, primGroup->indices, sizeof(UINT16) * m_numIndices * 3);
	}
	else
	{
		MessageBox(NULL, "MOO", "", MB_OK);
	}
*/

	return TRUE;
}

BOOL CRuMesh::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write signature
	INT32 signature = 0xFFFFFFFF;
	stream->Write(&signature, sizeof(INT32));

	// Write version
	INT32 version = 3;
	stream->Write(&version, sizeof(INT32));

	// Write primitive type
	stream->Write(&m_primitiveType, sizeof(RuPrimitiveType));

	// Write vertex format flags
	stream->Write(&m_vertexFormat, sizeof(DWORD));

	// Write vertex count
	stream->Write(&m_numVertices, sizeof(INT32));

	// Write face count
	INT32 primitiveCount = RuIndexToPrimitiveCount(m_numIndices, m_primitiveType);
	stream->Write(&primitiveCount, sizeof(INT32));

	// Write morph target count
	INT32 mtCount = m_morphTargets.Count();
	stream->Write(&mtCount, sizeof(INT32));

	// Write face index array
	stream->Write(m_indices, sizeof(UINT16) * m_numIndices);

	// Write morph targets
	for(INT32 i = 0; i < m_morphTargets.Count(); ++i)
	{
		m_morphTargets[i]->SerializeTo(stream);
	}

	// Write collision data existence flag
	INT32 colDataExists = (m_collisionStructure != NULL);
	stream->Write(&colDataExists, sizeof(INT32));

	// Write collision data
	if(m_collisionStructure)
	{
		m_collisionStructure->SerializeTo(stream);
	}

	return TRUE;
}

BOOL CRuMesh::AddTextureCoordinate(RuTextureChannel type)
{
	// Iterate through all morph targets and add the specified texture channel
	for(INT32 i = 0; i < m_morphTargets.Count(); ++i)
	{
		m_morphTargets[i]->AddTextureCoordinate(type);
	}

	return TRUE;
}

INT32 CRuMesh::AddBatch(INT32 offset, INT32 numPrimitives)
{
	BatchInfo *newBatch = ruNEW BatchInfo();

	newBatch->m_offset = offset;
	newBatch->m_numPrimitives = numPrimitives;
	newBatch->m_indexMap = NULL;

	m_batches.Add(newBatch);

	return m_batches.Count() - 1;
}

void CRuMesh::ClearBatches()
{
	// Clear existing batches
	while(m_batches.Count() > 0)
	{
		delete m_batches[0];
		m_batches.RemoveAt(0);
	}
}

BOOL CRuMesh::GetBatch(INT32 index, INT32 &offsetOut, INT32 &numPrimitivesOut) const
{
	if(index >= 0 && index < m_batches.Count())
	{
		offsetOut = m_batches[index]->m_offset;
		numPrimitivesOut = m_batches[index]->m_numPrimitives;

		return TRUE;
	}

	return FALSE;
}

CRuParameterBlock *CRuMesh::GetBatchParameterBlock(INT32 index)
{
	if(index >= 0 && index < m_batches.Count())
	{
		return &m_batches[index]->m_parameterBlock;
	}

	return NULL;
}

IRuCollisionStructure *CRuMesh::GetCollisionStructure()
{
	return m_collisionStructure;
}

BOOL CRuMesh::SetCollisionStructure(IRuCollisionStructure *collisionStructure)
{
	ruSAFE_RELEASE(m_collisionStructure);

	if(collisionStructure)
	{
		m_collisionStructure = collisionStructure;
		m_collisionStructure->AddRef();
	}

	return TRUE;
}

BOOL CRuMesh::QueryCollision(CRuCollisionQuery *colQuery)
{
	// If no collision structure exists, build a default one
	if(m_collisionStructure == NULL)
	{
		// Use a simple collision structure
		m_collisionStructure = ruNEW CRuCollisionStructure_Simple();
		m_collisionStructure->BuildCollisionStructure(this, NULL);
	}

	return m_collisionStructure->QueryCollision(colQuery);
}

void CRuMesh::CalculateBounds()
{
	// Build bounding box using morph target 0 (for now)
	if(m_morphTargets.Count() > 0)
	{
		// Build initial bounds using morph target 0
		CRuMinSphereBuilder minSphereBuilder;
		minSphereBuilder.BuildSphere(m_morphTargets[0]->GetPosition(), m_numVertices, m_bounds);

		// Merge any additional morph target bounds
		for(INT32 i = 1; i < m_morphTargets.Count(); ++i)
		{
			CRuSphere mtBounds;
			minSphereBuilder.BuildSphere(m_morphTargets[i]->GetPosition(), m_numVertices, mtBounds);
			m_bounds.Merge(mtBounds);
		}
	}
	else
	{
		m_bounds.Set(g_ruVec3_Origin, 0.0f);
	}
}

void CRuMesh::SetBounds(const CRuSphere &bounds)
{
	m_bounds = bounds;
}
/*
BOOL CRuMesh::SkinnedMesh_IsSkinned()
{
	if(m_blendIndex != NULL && m_blendWeight != NULL)
		return TRUE;

	return FALSE;
}

INT32 CRuMesh::SkinnedMesh_GetNumBones()
{
	INT32 maxBoneIndex = 0;

	const UINT16 *blendIndex = m_morphTargets[0]->GetBlendIndex();

	if(blendIndex)
	{
		// Iterate through each blend index and find the largest value
		INT32 numBlendIndices = m_numVertices * 4;
		for(INT32 i = 0; i < numBlendIndices; ++i)
		{
			maxBoneIndex = max(maxBoneIndex, blendIndex[i] + 1);
		}
	}

	return maxBoneIndex;
}

void CRuMesh::SkinnedMesh_Split(INT32 maxBones)
{
	// Can only perform split on a skinned mesh
	if(!SkinnedMesh_IsSkinned())
		return;

	// Setup source position & normal arrays
	CRuVector3 *position = m_morphTargets[0]->GetPosition();
	CRuVector3 *normal = m_morphTargets[0]->GetNormal();
	DWORD *diffuse = m_morphTargets[0]->GetDiffuse();
	DWORD *specular = m_morphTargets[0]->GetSpecular();
	UINT16 *blendIndex = m_morphTargets[0]->GetBlendIndex();
	float *blendWeight= m_morphTargets[0]->GetBlendWeight();

	INT32 batchStart = 0, batchEnd = 0, batchNumBones = 0, i = 0;

	CRuArrayList<UINT16> curFaceBones;

	INT32 totalBonesUsed = 0;
	INT32 numBones = SkinnedMesh_GetNumBones();
	UINT8 *boneUsed = ruNEW UINT8 [numBones];
	UINT8 *boneMap = ruNEW UINT8 [numBones];
	memset(boneUsed, 0, sizeof(UINT8) * numBones);

	// Clear existing batches
	while(m_batches.Count() > 0)
	{
		delete m_batches[0];
		m_batches.RemoveAt(0);
	}

	// Iterate through every face
	i = batchStart;
	while(batchEnd < m_numIndices)
	{
		curFaceBones.Clear();

		// Iterate through each vertex of the face
		for(INT32 curIndex = 0; curIndex < 3; curIndex++, i++)
		{
			// Iterate through each blend index of the vertex
			for(INT32 j = m_indices[i] * 4, k = 0; k < 4; k++, j++)
			{
				// Add unique bones to the bone list for the current face
				if(!curFaceBones.Contains(blendIndex[j]))
				{
					curFaceBones.Add(blendIndex[j]);
				}
			}
		}

		// Determine how much space we need for the bones of this face
		INT32 numNewBones = 0;
		for(INT32 j = 0; j < curFaceBones.Count(); j++)
		{
			if(boneUsed[curFaceBones[j]] == 0)
			{
				++numNewBones;
			}
		}

		if(numNewBones + totalBonesUsed <= maxBones)
		{
			// Update bone usage
			for(INT32 j = 0; j < curFaceBones.Count(); j++)
			{
				boneUsed[curFaceBones[j]] = 1;
			}
			totalBonesUsed += numNewBones;

			// Swap indices if necessary
			if(i - 3 > batchEnd)
			{
				UINT16 temp;
				temp = m_indices[i - 3];
				m_indices[i - 3] = m_indices[batchEnd];
				m_indices[batchEnd] = temp;

				temp = m_indices[i - 2];
				m_indices[i - 2] = m_indices[batchEnd + 1];
				m_indices[batchEnd + 1] = temp;

				temp = m_indices[i - 1];
				m_indices[i - 1] = m_indices[batchEnd + 2];
				m_indices[batchEnd + 2] = temp;
			}

			batchEnd += 3;
		}

		// Check if we've hit the end of the index list
		if(i == m_numIndices)
		{
			// Add batch to list
			BatchInfo *batchInfo = ruNEW BatchInfo();
			batchInfo->m_offset = batchStart / 3;
			batchInfo->m_numPrimitives = (batchEnd - batchStart) / 3;
			m_batches.Add(batchInfo);

			// Restart face iteration at the last split index
			batchStart = batchEnd;
			i = batchStart;
			totalBonesUsed = 0;
			memset(boneUsed, 0, sizeof(UINT8) * numBones);
		}
	}

	// Now rework the blend transform mappings if we did split the mesh
	if(m_batches.Count() > 0)
	{
		// Setup vertex mapping workspace
		bool *usedVertexMap = ruNEW bool [m_numVertices];
		UINT16 *vertexMapping = ruNEW UINT16 [m_numVertices];

		CRuArrayList<CRuVector3> newVertices;
		CRuArrayList<CRuVector3> newNormals;
		CRuArrayList<DWORD> newDiffuse;
		CRuArrayList<DWORD> newSpecular;
		CRuArrayList<float> newBlendWeights;
		CRuArrayList<UINT16> newBlendIndices;
		CRuArrayList<RuTextureChannel> newTextureChannels;
		CRuArrayList< CRuArrayList<float> * > newTextureCoordinates;
		CRuArrayList<UINT16> newIndices;

		// Setup texture coordinate arrays
		for(i = 0; i < m_textureChannels.Count(); ++i)
		{
			newTextureChannels.Add(m_textureChannels[i]);
			newTextureCoordinates.Add(ruNEW CRuArrayList<float>());
		}

		CRuArrayList<UINT16> newIndexMapping;

		for(i = 0; i < m_batches.Count(); i++)
		{
			// Reset working arrays
			memset(boneUsed, 0, sizeof(UINT8) * numBones);
			memset(boneMap, 0, sizeof(UINT8) * numBones);
			memset(usedVertexMap, 0, sizeof(bool) * m_numVertices);
			memset(vertexMapping, 0, sizeof(UINT16) * m_numVertices);
			newIndexMapping.Clear();

			// Remap vertex data for the batch
			INT32 batchNumIndices = m_batches[i]->m_numPrimitives * 3;
			for(INT32 j = 0, k = m_batches[i]->m_offset * 3; j < batchNumIndices; j++, k++)
			{
				UINT16 originalIndex = m_indices[k];

				// Map the vertex if it isn't mapped already
				if(usedVertexMap[originalIndex] == 0)
				{
					// Set vertex mapping
					vertexMapping[originalIndex] = newVertices.Count();
					usedVertexMap[originalIndex] = 1;

					// Copy position, normal, diffuse, and specular components
					newVertices.Add(position[originalIndex]);
					
					if(normal)
						newNormals.Add(normal[originalIndex]);

					if(diffuse)
						newDiffuse.Add(diffuse[originalIndex]);

					if(specular)
						newSpecular.Add(specular[originalIndex]);

					// Copy weights and adjust blend indices
					for(INT32 n = 0, m = originalIndex * 4; n < 4; n++, m++)
					{
						float weight = blendWeight[m];
						UINT16 boneIndex = blendIndex[m];

						newBlendWeights.Add(weight);

						if(weight > 0.0f)
						{
							if(boneUsed[boneIndex] == 0)
							{
								boneUsed[boneIndex] = 1;
								boneMap[boneIndex] = newIndexMapping.Count();
								newIndexMapping.Add(boneIndex);
							}

							newBlendIndices.Add(boneMap[boneIndex]);
						}
						else
						{
							newBlendIndices.Add(0);
						}
					}

					// Copy texture coordinates
					for(INT32 l = 0; l < m_textureChannels.Count(); ++l)
					{
						newTextureCoordinates[l]->Add(m_textureCoordinates[l][originalIndex * 2]);
						newTextureCoordinates[l]->Add(m_textureCoordinates[l][originalIndex * 2 + 1]);
					}
				}

				// Add new index
				newIndices.Add(vertexMapping[originalIndex]);
			}

			// Copy index map into batch structure
			m_batches[i]->m_indexMap = ruNEW UINT16 [newIndexMapping.Count()];
			memcpy(m_batches[i]->m_indexMap, newIndexMapping.GetArray(), sizeof(UINT16) * newIndexMapping.Count());
			m_batches[i]->m_parameterBlock.SetParameter(ruPARAMTYPE_SKIN_INDEXMAP, (DWORD) m_batches[i]->m_indexMap);
			m_batches[i]->m_parameterBlock.SetParameter(ruPARAMTYPE_SKIN_NODECOUNT, (DWORD) newIndexMapping.Count());
		}

		// Store new number of vertices
		m_numVertices = newVertices.Count();

		// Reinitialize default morph target
		if(newNormals.Count() > 0)
			m_morphTargets[0]->Initialize(m_numVertices, ruVERTEXFORMAT_POSITION | ruVERTEXFORMAT_NORMAL);
		else
			m_morphTargets[0]->Initialize(m_numVertices, ruVERTEXFORMAT_POSITION);

		// Copy position
		memcpy(m_morphTargets[0]->GetPosition(), newVertices.GetArray(), sizeof(CRuVector3) * m_numVertices);

		// Copy normal
		if(newNormals.Count() > 0)
			memcpy(m_morphTargets[0]->GetNormal(), newNormals.GetArray(), sizeof(CRuVector3) * m_numVertices);

		// Copy diffuse
		if(newDiffuse.Count() > 0)
		{
			delete[] diffuse;
			diffuse = ruNEW DWORD [newDiffuse.Count()];
			memcpy(diffuse, newDiffuse.GetArray(), sizeof(DWORD) * newDiffuse.Count());
		}

		// Copy specular
		if(newSpecular.Count() > 0)
		{
			delete[] specular;
			specular = ruNEW DWORD [newSpecular.Count()];
			memcpy(specular, newSpecular.GetArray(), sizeof(DWORD) * newSpecular.Count());
		}

		// Copy blend weights
		delete[] blendWeight;
		blendWeight = ruNEW float [newBlendWeights.Count()];
		memcpy(blendWeight, newBlendWeights.GetArray(), sizeof(float) * newBlendWeights.Count());

		// Copy blend indices
		delete[] blendIndex;
		blendIndex = ruNEW UINT16 [newBlendIndices.Count()];
		memcpy(blendIndex, newBlendIndices.GetArray(), sizeof(UINT16) * newBlendIndices.Count());

		// Copy texture coordinates
		for(i = 0; i < m_textureChannels.Count(); ++i)
		{
			// Delete old texture coordinate array
			delete[] m_textureCoordinates[i];

			// Allocate new texture coordinate array and copy contents
			m_textureCoordinates[i] = ruNEW float [newTextureCoordinates[i]->Count()];
			memcpy(m_textureCoordinates[i], newTextureCoordinates[i]->GetArray(), sizeof(float) * newTextureCoordinates[i]->Count());

			// Destroy temporary texture coordinate array
			delete newTextureCoordinates[i];
		}

		// Copy indices (number of indices never change as a result of a split)
		memcpy(m_indices, newIndices.GetArray(), sizeof(UINT16) * newIndices.Count());

		// Destroy vertex mapping workspace
		delete[] usedVertexMap;
		delete[] vertexMapping;
	}

	delete[] boneUsed;
	delete[] boneMap;
}
*/
BOOL CRuMesh::MeshUtility_MeshCopy(IRuMesh *srcMesh)
{
	return FALSE;
}

// ************************************************************************************************************************************************************

CRuSkinnableMesh::CRuSkinnableMesh()
:	m_ignoreNormalTransforms(FALSE),
	m_srcMesh(NULL),
	m_cachedX(NULL),
	m_cachedY(NULL),
	m_cachedZ(NULL),
	m_cachedNX(NULL),
	m_cachedNY(NULL),
	m_cachedNZ(NULL),
	m_cachedBlendIndex(NULL),
	m_cachedBlendWeight(NULL),
	m_cachedBoneMatrixArraySize(0),
	m_cachedBoneMatrixArray(NULL),
	m_iterationNumber(0),
	m_skinnedMT(NULL)
{
}

CRuSkinnableMesh::~CRuSkinnableMesh()
{
	FreeResources();
}

void CRuSkinnableMesh::SetSourceMesh(IRuMesh *srcMesh)
{
	// Release old resources
	FreeResources();

	// Store reference to the new source mesh
	m_srcMesh = srcMesh;
	m_srcMesh->AddRef();

	// Initialize new skinned morph target
	m_skinnedMT = ruNEW CRuMorphTarget();

	// TODO: Initialize SSE-optimized vertex and normal arrays
	m_cachedSrcIteration = m_srcMesh->GetMeshIteration();
	m_numVertexGroups = (m_srcMesh->GetNumVertices() / 4) + ((m_srcMesh->GetNumVertices() % 4) ? 1 : 0);

	m_skinnedMT->Initialize(m_numVertexGroups * 4, m_srcMesh->GetVertexFormat());


	m_cachedX = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);
	m_cachedY = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);
	m_cachedZ = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);
	m_cachedNX = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);
	m_cachedNY = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);
	m_cachedNZ = (__m128 *) _aligned_malloc(m_numVertexGroups * sizeof(__m128), 16);

	const CRuVector3 *position = m_srcMesh->GetMorphTarget(0)->GetPosition();
	const CRuVector3 *normal = m_srcMesh->GetMorphTarget(0)->GetNormal();

	INT32 i = 0, j = 0;
	for(INT32 k = 1; k < m_numVertexGroups; ++k, i += 4, ++j)
	{
		RuSSE_LoadFourFloats(&position[i].x, &position[i + 1].x, &position[i + 2].x, &position[i + 3].x, &m_cachedX[j]);
		RuSSE_LoadFourFloats(&position[i].y, &position[i + 1].y, &position[i + 2].y, &position[i + 3].y, &m_cachedY[j]);
		RuSSE_LoadFourFloats(&position[i].z, &position[i + 1].z, &position[i + 2].z, &position[i + 3].z, &m_cachedZ[j]);

		RuSSE_LoadFourFloats(&normal[i].x, &normal[i + 1].x, &normal[i + 2].x, &normal[i + 3].x, &m_cachedNX[j]);
		RuSSE_LoadFourFloats(&normal[i].y, &normal[i + 1].y, &normal[i + 2].y, &normal[i + 3].y, &m_cachedNY[j]);
		RuSSE_LoadFourFloats(&normal[i].z, &normal[i + 1].z, &normal[i + 2].z, &normal[i + 3].z, &m_cachedNZ[j]);
	}

	CRuVector3 leftoverPosition[4];
	CRuVector3 leftoverNormal[4];
	for(INT32 k = 0; i < m_srcMesh->GetNumVertices(); ++i, ++k)
	{
		leftoverPosition[k] = position[i];
		leftoverNormal[k] = normal[k];
	}

	RuSSE_LoadFourFloats(&leftoverPosition[0].x, &leftoverPosition[1].x, &leftoverPosition[2].x, &leftoverPosition[3].x, &m_cachedX[j]);
	RuSSE_LoadFourFloats(&leftoverPosition[0].y, &leftoverPosition[1].y, &leftoverPosition[2].y, &leftoverPosition[3].y, &m_cachedY[j]);
	RuSSE_LoadFourFloats(&leftoverPosition[0].z, &leftoverPosition[1].z, &leftoverPosition[2].z, &leftoverPosition[3].z, &m_cachedZ[j]);

	RuSSE_LoadFourFloats(&leftoverNormal[0].x, &leftoverNormal[1].x, &leftoverNormal[2].x, &leftoverNormal[3].x, &m_cachedNX[j]);
	RuSSE_LoadFourFloats(&leftoverNormal[0].y, &leftoverNormal[1].y, &leftoverNormal[2].y, &leftoverNormal[3].y, &m_cachedNY[j]);
	RuSSE_LoadFourFloats(&leftoverNormal[0].z, &leftoverNormal[1].z, &leftoverNormal[2].z, &leftoverNormal[3].z, &m_cachedNZ[j]);

	m_cachedBlendIndex = (UINT16 *) malloc(m_numVertexGroups * 4 * sizeof(UINT16) * 4);
	m_cachedBlendWeight = (float *) malloc(m_numVertexGroups * 4 * sizeof(float) * 4);

	memset(m_cachedBlendIndex, 0, m_numVertexGroups * 4 * sizeof(UINT16) * 4);
	memset(m_cachedBlendWeight, 0, m_numVertexGroups * 4 * sizeof(float) * 4);

	if(m_srcMesh->GetMorphTarget(0)->GetBlendIndex())
	{
		memcpy(m_cachedBlendIndex, m_srcMesh->GetMorphTarget(0)->GetBlendIndex(), m_srcMesh->GetNumVertices() * sizeof(UINT16) * 4);
	}

	if(m_srcMesh->GetMorphTarget(0)->GetBlendWeight())
	{
		memcpy(m_cachedBlendWeight, m_srcMesh->GetMorphTarget(0)->GetBlendWeight(), m_srcMesh->GetNumVertices() * sizeof(float) * 4);
	}

	// Copy morph target zero into the skinned morph target
	m_skinnedMT->CopyFrom(m_srcMesh->GetMorphTarget(0));
}

void CRuSkinnableMesh::SetPose(CRuMatrix4x4 *skinMatrices, INT32 numSkinMatrices)
{
	// Resize the cached bone matrix array if necessary
	if(m_cachedBoneMatrixArraySize < numSkinMatrices)
	{
		m_cachedBoneMatrixArraySize = numSkinMatrices;
		m_cachedBoneMatrixArray = (__m128 *) _aligned_malloc(numSkinMatrices * 3 * sizeof(__m128), 16);
	}

	// Fill bone matrix array
	for(INT32 i = 0, j = 0; i < numSkinMatrices; ++i)
	{
		RuSSE_LoadFourFloats(&skinMatrices[i]._11, &skinMatrices[i]._21, &skinMatrices[i]._31, &skinMatrices[i]._41, &m_cachedBoneMatrixArray[j++]);
		RuSSE_LoadFourFloats(&skinMatrices[i]._12, &skinMatrices[i]._22, &skinMatrices[i]._32, &skinMatrices[i]._42, &m_cachedBoneMatrixArray[j++]);
		RuSSE_LoadFourFloats(&skinMatrices[i]._13, &skinMatrices[i]._23, &skinMatrices[i]._33, &skinMatrices[i]._43, &m_cachedBoneMatrixArray[j++]);
	}

	__m128 collapsedMatrices[12];
	CRuVector3 *position = m_skinnedMT->GetPosition();
	CRuVector3 *normal = m_skinnedMT->GetNormal();

	const UINT16 *blendIndex = m_cachedBlendIndex;
	const float *blendWeight = m_cachedBlendWeight;

	if(blendWeight && blendIndex)
	{
		for(INT32 i = 0, j = 0, b0 = 0, b1 = 1, b2 = 2, b3 = 3; i < m_numVertexGroups; ++i, j += 4)
		{
			RuSSE_Collapse4Mat(&m_cachedBoneMatrixArray[blendIndex[b0] * 3], &m_cachedBoneMatrixArray[blendIndex[b1] * 3], &m_cachedBoneMatrixArray[blendIndex[b2] * 3], &m_cachedBoneMatrixArray[blendIndex[b3] * 3], blendWeight[b0], blendWeight[b1], blendWeight[b2], blendWeight[b3], &collapsedMatrices[0]);
			b0 += 4;
			b1 += 4;
			b2 += 4;
			b3 += 4;

			RuSSE_Collapse4Mat(&m_cachedBoneMatrixArray[blendIndex[b0] * 3], &m_cachedBoneMatrixArray[blendIndex[b1] * 3], &m_cachedBoneMatrixArray[blendIndex[b2] * 3], &m_cachedBoneMatrixArray[blendIndex[b3] * 3], blendWeight[b0], blendWeight[b1], blendWeight[b2], blendWeight[b3], &collapsedMatrices[3]);
			b0 += 4;
			b1 += 4;
			b2 += 4;
			b3 += 4;

			RuSSE_Collapse4Mat(&m_cachedBoneMatrixArray[blendIndex[b0] * 3], &m_cachedBoneMatrixArray[blendIndex[b1] * 3], &m_cachedBoneMatrixArray[blendIndex[b2] * 3], &m_cachedBoneMatrixArray[blendIndex[b3] * 3], blendWeight[b0], blendWeight[b1], blendWeight[b2], blendWeight[b3], &collapsedMatrices[6]);
			b0 += 4;
			b1 += 4;
			b2 += 4;
			b3 += 4;

			RuSSE_Collapse4Mat(&m_cachedBoneMatrixArray[blendIndex[b0] * 3], &m_cachedBoneMatrixArray[blendIndex[b1] * 3], &m_cachedBoneMatrixArray[blendIndex[b2] * 3], &m_cachedBoneMatrixArray[blendIndex[b3] * 3], blendWeight[b0], blendWeight[b1], blendWeight[b2], blendWeight[b3], &collapsedMatrices[9]);
			b0 += 4;
			b1 += 4;
			b2 += 4;
			b3 += 4;

			if(m_ignoreNormalTransforms)
			{
				RuSSE_Vert4Mat4Normalise(&collapsedMatrices[0], &m_cachedX[i], &m_cachedY[i], &m_cachedZ[i], &position[j]);
			}
			else
			{
				RuSSE_Vert4Mat4Normalise(&collapsedMatrices[0], &m_cachedX[i], &m_cachedY[i], &m_cachedZ[i], &m_cachedNX[i], &m_cachedNY[i], &m_cachedNZ[i], &position[j], &normal[j]);
			}
		}

		// Increment iteration
		++m_iterationNumber;
	}
}

INT32 CRuSkinnableMesh::GetMeshIteration() const
{
	return m_iterationNumber;
}

BOOL CRuSkinnableMesh::IsMeshDynamic() const
{
	return TRUE;
}

INT32 CRuSkinnableMesh::GetTopologyIteration() const
{
	return m_srcMesh->GetTopologyIteration();
}

BOOL CRuSkinnableMesh::IsTopologyDynamic() const
{
	return m_srcMesh->IsTopologyDynamic();
}

INT32 CRuSkinnableMesh::GetNumMorphTargets() const
{
	return 1;
}

IRuMorphTarget *CRuSkinnableMesh::GetMorphTarget(INT32 morphTargetIndex)
{
	return m_skinnedMT;
}

const UINT16 *CRuSkinnableMesh::GetIndices() const
{
	return m_srcMesh->GetIndices();
}

BOOL CRuSkinnableMesh::GetBatch(INT32 index, INT32 &offsetOut, INT32 &numPrimitivesOut) const
{
	return m_srcMesh->GetBatch(index, offsetOut, numPrimitivesOut);
}

CRuParameterBlock *CRuSkinnableMesh::GetBatchParameterBlock(INT32 index)
{
	return m_srcMesh->GetBatchParameterBlock(index);
}

RuPrimitiveType CRuSkinnableMesh::GetPrimitiveType() const
{
	return m_srcMesh->GetPrimitiveType();
}

INT32 CRuSkinnableMesh::GetNumPrimitives() const
{
	return m_srcMesh->GetNumPrimitives();
}

INT32 CRuSkinnableMesh::GetNumVertices() const
{
	return m_srcMesh->GetNumVertices();
}

INT32 CRuSkinnableMesh::GetNumIndices() const
{
	return m_srcMesh->GetNumIndices();
}

const CRuSphere &CRuSkinnableMesh::GetBounds() const
{
	return m_srcMesh->GetBounds();
}
DWORD CRuSkinnableMesh::GetVertexFormat() const
{
	return m_srcMesh->GetVertexFormat();
}

BOOL CRuSkinnableMesh::QueryCollision(CRuCollisionQuery *colQuery)
{
	return TRUE;
}

void CRuSkinnableMesh::FreeResources()
{
	// Release reference to source mesh
	ruSAFE_RELEASE(m_srcMesh);

	// Release reference to the skinned morph target
	ruSAFE_RELEASE(m_skinnedMT);

	// Free SSE-optimized vertex and normal arrays
	if(m_cachedX)
		_aligned_free(m_cachedX);

	if(m_cachedY)
		_aligned_free(m_cachedY);

	if(m_cachedZ)
		_aligned_free(m_cachedZ);

	if(m_cachedNX)
		_aligned_free(m_cachedNX);

	if(m_cachedNY)
		_aligned_free(m_cachedNY);

	if(m_cachedNZ)
		_aligned_free(m_cachedNZ);

	if(m_cachedBoneMatrixArray)
		_aligned_free(m_cachedBoneMatrixArray);

	if(m_cachedBlendIndex)
		free(m_cachedBlendIndex);

	if(m_cachedBlendWeight)
		free(m_cachedBlendWeight);

	m_cachedX = NULL;
	m_cachedY = NULL;
	m_cachedZ = NULL;
	m_cachedNX = NULL;
	m_cachedNY = NULL;
	m_cachedNZ = NULL;
	m_cachedBlendIndex = NULL;
	m_cachedBlendWeight = NULL;
	m_cachedBoneMatrixArraySize = 0;
	m_cachedBoneMatrixArray = NULL;
}

// ************************************************************************************************************************************************************

CRuDynamicMesh::CRuDynamicMesh(BOOL forceStaticPool)
:	m_forceStaticPool(forceStaticPool),
	m_actualNumVertices(0),
	m_actualNumIndices(0)
{
}

CRuDynamicMesh::~CRuDynamicMesh()
{
}

BOOL CRuDynamicMesh::IsMeshDynamic() const
{
	if(m_forceStaticPool)
	{
		return FALSE;
	}

	return TRUE;
}

BOOL CRuDynamicMesh::IsTopologyDynamic() const
{
	if(m_forceStaticPool)
	{
		return FALSE;
	}

	return TRUE;
}

INT32 CRuDynamicMesh::GetMaxPrimitives() const
{
	// Return maximum number of allowed primitives by this mesh
	switch(m_primitiveType)
	{
		case ruPRIMTYPE_TRIANGLELIST:
			return m_numIndices / 3;

		case ruPRIMTYPE_LINELIST:
			return m_numIndices / 2;
	}

	return 0;
}

INT32 CRuDynamicMesh::GetMaxVertices() const
{
	// Return maximum number of allowed vertices by this mesh
	return m_numVertices;
}

INT32 CRuDynamicMesh::GetMaxIndices() const
{
	// Return maximum number of allowed indices by this mesh
	return m_numIndices;
}

INT32 CRuDynamicMesh::GetNumPrimitives() const
{
	switch(m_primitiveType)
	{
		case ruPRIMTYPE_TRIANGLELIST:
			return m_actualNumIndices / 3;

		case ruPRIMTYPE_LINELIST:
			return m_actualNumIndices / 2;
	}

	return 0;
}

INT32 CRuDynamicMesh::GetNumVertices() const
{
	return m_actualNumVertices;
}

INT32 CRuDynamicMesh::GetNumIndices() const
{
	return m_actualNumIndices;
}

BOOL CRuDynamicMesh::SetNumPrimitives(INT32 numPrimitives)
{
	// Determine the actual number of indices required by the number of primitives
	INT32 numIndices = 0;
	switch(m_primitiveType)
	{
		case ruPRIMTYPE_TRIANGLELIST:
			numIndices = numPrimitives * 3;
			break;

		case ruPRIMTYPE_LINELIST:
			numIndices = numPrimitives * 2;
			break;
	}

	// Only set the index count if it is within the maximum size limit
	if(numIndices > 0 && m_numIndices >= numIndices)
	{
		// Set number of indices actually used
		m_actualNumIndices = numIndices;

		// Update default batch with the new number of primitives
		m_batches[0]->m_numPrimitives = numPrimitives;

		return TRUE;
	}

	return FALSE;
}

BOOL CRuDynamicMesh::SetNumVertices(INT32 numVertices)
{
	if(m_numVertices >= numVertices)
	{
		m_actualNumVertices = numVertices;

		return TRUE;
	}

	return FALSE;
}

// ************************************************************************************************************************************************************

CRuProxyMesh::CRuProxyMesh()
:	m_mesh(NULL),
	m_proxiedBatchIndex(0)
{
}

CRuProxyMesh::~CRuProxyMesh()
{
	// Release reference to source mesh
	ruSAFE_RELEASE(m_mesh);
}


BOOL CRuProxyMesh::SetProxiedMesh(IRuMesh *proxiedMesh, INT32 proxiedBatchIndex)
{
	// Release reference to prior source mesh
	ruSAFE_RELEASE(m_mesh);

	// Verify the proxy information is correct
	INT32 batchOffset;
	INT32 batchNumPrimitives;
	if(proxiedMesh->GetBatch(proxiedBatchIndex, batchOffset, batchNumPrimitives))
	{
		// Store proxy information
		m_mesh = proxiedMesh;
		m_proxiedBatchIndex = proxiedBatchIndex;

		// Increment reference count on the proxied mesh
		m_mesh->AddRef();

		return TRUE;
	}

	return FALSE;
}

INT32 CRuProxyMesh::GetProxiedBatchIndex() const
{
	return m_proxiedBatchIndex;
}

const CRuGUID &CRuProxyMesh::GetGUID() const
{
	return m_mesh->GetGUID();
}

INT32 CRuProxyMesh::GetMeshIteration() const
{
	return m_mesh->GetMeshIteration();
}

BOOL CRuProxyMesh::IsMeshDynamic() const
{
	return m_mesh->IsMeshDynamic();
}

INT32 CRuProxyMesh::GetTopologyIteration() const
{
	return m_mesh->GetTopologyIteration();
}

BOOL CRuProxyMesh::IsTopologyDynamic() const
{
	return m_mesh->IsTopologyDynamic();
}

INT32 CRuProxyMesh::GetNumMorphTargets() const
{
	return m_mesh->GetNumMorphTargets();
}

IRuMorphTarget *CRuProxyMesh::GetMorphTarget(INT32 morphTargetIndex)
{
	return m_mesh->GetMorphTarget(morphTargetIndex);
}

const UINT16 *CRuProxyMesh::GetIndices() const
{
	return m_mesh->GetIndices();
}

BOOL CRuProxyMesh::GetBatch(INT32 index, INT32 &offsetOut, INT32 &numPrimitivesOut) const
{
	if(index == 0)
	{
		m_mesh->GetBatch(m_proxiedBatchIndex, offsetOut, numPrimitivesOut);

		return TRUE;
	}

	return FALSE;
}

CRuParameterBlock *CRuProxyMesh::GetBatchParameterBlock(INT32 index)
{
	if(index == 0)
	{
		return m_mesh->GetBatchParameterBlock(m_proxiedBatchIndex);
	}

	return NULL;
}

RuPrimitiveType CRuProxyMesh::GetPrimitiveType() const
{
	return m_mesh->GetPrimitiveType();
}

INT32 CRuProxyMesh::GetNumPrimitives() const
{
	return m_mesh->GetNumPrimitives();
}

INT32 CRuProxyMesh::GetNumVertices() const
{
	return m_mesh->GetNumVertices();
}

INT32 CRuProxyMesh::GetNumIndices() const
{
	return m_mesh->GetNumIndices();
}

const CRuSphere &CRuProxyMesh::GetBounds() const
{
	return m_mesh->GetBounds();
}
DWORD CRuProxyMesh::GetVertexFormat() const
{
	return m_mesh->GetVertexFormat();
}

BOOL CRuProxyMesh::QueryCollision(CRuCollisionQuery *colQuery)
{
	return TRUE;
}

// ************************************************************************************************************************************************************

#pragma managed
