//////////////////////////////////////////////////////////////////////////////
//
// Asura_CollisionMesh.cpp
//
// Created by Mike Healey.
//
// Copyright (c) 2002 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Mesh/Asura_CollisionMesh.h"

#include "Animation/Asura_AnimationAttachment.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_LOS.h"
#include "Core/Asura_MaterialFlags.h"
#include "Core/Asura_Material_System.h"
#include "Core/Memory/Asura_Mem.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Module.h"

#ifdef ASURAIMPORT_EXPORTS
#include "ChunkSystem/Asura_Chunk_Mesh.h"
#endif

#ifdef ASURA_TOOLS
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif


////////////////////////////////////////
// Old class: Asura_CollisionMesh_Tri //
////////////////////////////////////////
class Asura_CollisionMesh_Tri
{
public:
	u_short m_usVertexID[3];
    u_short m_usNormalAndFlagsID;
};


/////////////////
// Static data //
/////////////////

//
// Version history:
//
//     (AW  - 25.03.2003) -  3 -
//
//     (RAR - 16.06.2004) -  4 - All polygon flag values shuffled around.
//
//     (MP  - 19.07.2004) -  5 - Nasty fix for flag mix up in Rogue.
//
//     (RAR - 09.02.2005) -  6&7 - No LOS flag added to polygons with Dont Render.
//
//     (RAR - 23.03.2007) -  8 - Added tools polygon flags.
//
//     (AAM - 26.04.2007) -  9 - Added material response hash IDs.
//

static const int ASURA_CHUNK_COLLISION_MESH_CHUNK_VERSION = 9;




#ifdef ASURA_TOOLS
bool Asura_CollisionMesh::s_bIncludeDontExportPolys = false;
Asura_CollisionMesh::fnProjectExcludePolygonFromExport*	Asura_CollisionMesh::s_pfnProjectExcludePolygonFromExport	= NULL;
#endif


/////////////////////////
// Asura_CollisionMesh //
/////////////////////////
Asura_CollisionMesh::~Asura_CollisionMesh()
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		Asura_ResourceDeallocateArray(m_pxVertices);
		Asura_ResourceDeallocateArray(m_pxPolys);
		Asura_ResourceDeallocateArray(m_pusPolygonFlags);
#ifdef ASURA_TOOLS
        Asura_ResourceDeallocateArray( m_puToolsPolygonFlags );
#endif
		Asura_ResourceDeallocateArray(m_psMaterialIDs);
		Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);
	}
	Asura_ResourcePopType();
}


////////////////////////////////////////
// Construct for empty collision mesh //
////////////////////////////////////////
Asura_CollisionMesh::Asura_CollisionMesh()
: Asura_Collision_Object()
, m_uNumberOfVertices(0)
, m_pxVertices(0)
, m_uNumberOfPolys(0)
, m_pxPolys(0)
, m_pusPolygonFlags( NULL )
#ifdef ASURA_TOOLS
, m_puToolsPolygonFlags( NULL )
#endif
, m_puMaterialResponseHashIDs(0)
, m_psMaterialIDs(0)
#ifdef ASURA_TOOLS
, m_bValid(true)
#endif
{
}

////////////////////////////////
// Construct from chunkbuffer //
////////////////////////////////
Asura_CollisionMesh::Asura_CollisionMesh(const char*& pcChunkBuffer)
: Asura_Collision_Object()
, m_uNumberOfVertices(0)
, m_pxVertices(0)
, m_uNumberOfPolys(0)
, m_pxPolys(0)
, m_pusPolygonFlags( NULL )

#ifdef ASURA_TOOLS
, m_puToolsPolygonFlags( NULL )
#endif

, m_puMaterialResponseHashIDs(0)
, m_psMaterialIDs(0)

#ifdef ASURA_TOOLS
, m_bValid(true)
#endif
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	ReadFromChunkStream(xStream);
}

void Asura_CollisionMesh::ReadFromChunkStream(Asura_Chunk_Stream& xStream, bool bConvertMaterialIDs /*=true*/, int* piOutputVersion /*=0*/)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		int iVersion;
		xStream >> iVersion;
		if(piOutputVersion) *piOutputVersion = iVersion;

#ifdef ASURA_TOOLS
		Asura_Assert(iVersion >= 0 && iVersion <= ASURA_CHUNK_COLLISION_MESH_CHUNK_VERSION , "Unknown Collision Mesh version number");
		if (iVersion == 0)
		{
			unsigned int uNumFlagsOrNormals;
			u_int uHasFlagsArray;
			m_usOverallFlags = 0;
			xStream >> m_uNumberOfVertices;
			xStream >> m_uNumberOfPolys;
			xStream >> uNumFlagsOrNormals;
			xStream >> uHasFlagsArray;
			xStream >> m_xBoundingBox;
			xStream >> m_fBoundingRadius;

			ResetBB();

			Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
			Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys); // All polys are triangles

			for (unsigned int u = 0; u < m_uNumberOfVertices; ++u)
			{
				xStream >> m_pxVertices[u];
			}

			Asura_ResourcePushType(ASURA_RESOURCE_TEMPORARY);
			{
				// Temporary storage.
				Asura_CollisionMesh_Tri* pxTri;
				Asura_ResourceAllocateArray(pxTri, m_uNumberOfPolys);

				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
				{
					xStream >> pxTri[u].m_usVertexID[0];
					xStream >> pxTri[u].m_usVertexID[1];
					xStream >> pxTri[u].m_usVertexID[2];
					xStream >> pxTri[u].m_usNormalAndFlagsID;

					m_pxPolys[u].m_usVertexID[0] = pxTri[u].m_usVertexID[0];
					m_pxPolys[u].m_usVertexID[1] = pxTri[u].m_usVertexID[1];
					m_pxPolys[u].m_usVertexID[2] = pxTri[u].m_usVertexID[2];
					m_pxPolys[u].m_usVertexID[3] = 0xffff;
				}

				// Dump all the normals.
				for (unsigned int u = 0; u < uNumFlagsOrNormals; ++u)
				{
					Asura_Vector_3 xNormal;
					xStream >> xNormal;
				}

				if (uHasFlagsArray)
				{
					int * piOldFlags;
					int * piIntFlags;
					Asura_ResourceAllocateArray(piOldFlags, uNumFlagsOrNormals);
					Asura_ResourceAllocateArray(piIntFlags, m_uNumberOfPolys);

					Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
					{
						Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys);
					}
					Asura_ResourcePopType();
					for (unsigned int u = 0; u < uNumFlagsOrNormals; ++u)
					{
						xStream >> piOldFlags[u];
					}

					// Loop over triangles copying old flags to new flags.
					for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
					{
						u_short usFlagIndex = pxTri[u].m_usNormalAndFlagsID;
						Asura_Assert(usFlagIndex<uNumFlagsOrNormals,"Invalid index");
						piIntFlags[u] = piOldFlags[usFlagIndex];
						m_pusPolygonFlags[u] = (unsigned short)piIntFlags[u];
					}

					Asura_ResourceDeallocateArray(piOldFlags);
					Asura_ResourceDeallocateArray(piIntFlags);
				}

				Asura_ResourceDeallocateArray(pxTri);
			}
			Asura_ResourcePopType();
		}
		else if (iVersion == 1)
		{
			// Version 1
			unsigned int uHasFlagsArray = 0;
			xStream >> m_uNumberOfVertices;
			xStream >> m_uNumberOfPolys;
			xStream >> uHasFlagsArray;
			int iOverallFlags;
			xStream >> iOverallFlags;
			m_usOverallFlags = (unsigned short)iOverallFlags;
			xStream >> m_xBoundingBox;
			xStream >> m_fBoundingRadius;

			ResetBB();

			Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
			for (unsigned int u = 0; u < m_uNumberOfVertices; ++u)
			{
				xStream >> m_pxVertices[u];
			}

			Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys);
			for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
			{
				xStream >> m_pxPolys[u].m_usVertexID[0];
				xStream >> m_pxPolys[u].m_usVertexID[1];
				xStream >> m_pxPolys[u].m_usVertexID[2];
				xStream >> m_pxPolys[u].m_usVertexID[3];
			}

			if (uHasFlagsArray)
			{
				Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys);
				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
				{
					int iFlag;
					xStream >> iFlag;
					m_pusPolygonFlags[u] = (unsigned short)iFlag;
				}
			}
		}

		if (iVersion == 2)
		{
			// Version 2
			unsigned int uHasFlagsArray = 0;
			unsigned int uHasMaterialArray = 0;
			xStream >> m_uNumberOfVertices;
			xStream >> m_uNumberOfPolys;
			xStream >> uHasFlagsArray;
			xStream >> uHasMaterialArray;
			int iOverallFlags;
			xStream >> iOverallFlags;
			m_usOverallFlags = (unsigned short)iOverallFlags;
			int iOverallMaterial;
			xStream >> iOverallMaterial;
			m_sOverallMaterial = (short)iOverallMaterial;
			xStream >> m_xBoundingBox;
			xStream >> m_fBoundingRadius;

			ResetBB();

			Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
			for (unsigned int u = 0; u < m_uNumberOfVertices; ++u)
			{
				xStream >> m_pxVertices[u];
			}

			Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys);
			for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
			{
				xStream >> m_pxPolys[u].m_usVertexID[0];
				xStream >> m_pxPolys[u].m_usVertexID[1];
				xStream >> m_pxPolys[u].m_usVertexID[2];
				xStream >> m_pxPolys[u].m_usVertexID[3];
			}

			if (uHasFlagsArray)
			{
				Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys);
				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
				{
					int iFlag;
					xStream >> iFlag;
					m_pusPolygonFlags[u] = (unsigned short)iFlag;
				}
			}
			if (uHasMaterialArray)
			{
				Asura_ResourceAllocateArray(m_psMaterialIDs, m_uNumberOfPolys);
				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
				{
					int iMaterialID;
					xStream >> iMaterialID;
					m_psMaterialIDs[u] = (short)iMaterialID;
				}
			}
		}
		else if (iVersion < 2)
		{
			m_sOverallMaterial = ASURA_MATERIAL_NONE;
			m_psMaterialIDs = NULL;
			//Flag as invalid. Whatever called this can try regenerating
			//from the Asura_Mesh if its available. RAR 23/03/2004
			m_bValid = false;
		}
#else
		Asura_Assert( 3 <= iVersion && iVersion <= ASURA_CHUNK_COLLISION_MESH_CHUNK_VERSION , "Unknown Collision Mesh version number");
#endif

		if (iVersion >= 3)
		{
			unsigned int uHasFlagsArray = 0;
			unsigned int uHasMaterialArray = 0;
			unsigned int uHasMaterialResponseArray = 0;

			xStream >> m_uNumberOfVertices;
			xStream >> m_uNumberOfPolys;
			xStream >> uHasFlagsArray;
			xStream >> uHasMaterialArray;

			if (iVersion >= 9)
			{
				xStream >> uHasMaterialResponseArray;
			}

			xStream >> m_usOverallFlags;
			xStream >> m_sOverallMaterial;
			if (m_sOverallMaterial != ASURA_MATERIAL_NONE &&
				m_sOverallMaterial < ASURA_MATERIAL_FIRSTID &&
				bConvertMaterialIDs)
			{
				m_sOverallMaterial = (short)Asura_Material_System::ConvertIndex(m_sOverallMaterial);
			}

			if (iVersion >= 9)
			{
				xStream >> m_uOverallMaterialResponse;
			}

			xStream >> m_xBoundingBox;
			xStream >> m_fBoundingRadius;

			ResetBB();

			if( m_uNumberOfVertices > 0 )
			{
				Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
				xStream.Read( m_uNumberOfVertices * sizeof(Asura_Vector_3), m_pxVertices );
			}

			if( m_uNumberOfPolys > 0 )
			{
				Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys);
				xStream.Read( m_uNumberOfPolys * sizeof(Asura_CollisionMesh_Poly), m_pxPolys );

				if (uHasFlagsArray)
				{
					Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys);
					xStream.Read( m_uNumberOfPolys * sizeof(u_short), m_pusPolygonFlags );
				}

				if (uHasMaterialArray)
				{
					Asura_ResourceAllocateArray(m_psMaterialIDs, m_uNumberOfPolys);
					for (unsigned int u = 0; u < m_uNumberOfPolys; ++u)
					{
						xStream >> m_psMaterialIDs[u];

						if (m_psMaterialIDs[u] != ASURA_MATERIAL_NONE &&
							m_psMaterialIDs[u] < ASURA_MATERIAL_FIRSTID &&
							bConvertMaterialIDs)
						{
							m_psMaterialIDs[u] = (short)Asura_Material_System::ConvertIndex(m_psMaterialIDs[u]);
						}
					}
				}

				if (uHasMaterialResponseArray)
				{
					Asura_ResourceAllocateArray(m_puMaterialResponseHashIDs, m_uNumberOfPolys);
					xStream.Read( sizeof( Asura_Hash_ID ) * m_uNumberOfPolys, m_puMaterialResponseHashIDs );
				}
			}
		}

		//Convert polygon flags from old values to new if needed
		if(iVersion<4)
		{
			//Need to convert the polygon flags
			if(m_pusPolygonFlags)
			{
				u_short* pusFlags = m_pusPolygonFlags;
				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u,pusFlags++)
				{
					u_short usFlags = 0;
					if((*pusFlags) & OldAsuraPolygonFlag_DoubleSided) usFlags |= AsuraPolygonFlag_DoubleSided;
					if((*pusFlags) & OldAsuraPolygonFlag_DontRender) usFlags |= AsuraPolygonFlag_DontRender;
					if((*pusFlags) & OldAsuraPolygonFlag_NoCollisions) usFlags |= AsuraPolygonFlag_NoCollisions;
					if((*pusFlags) & OldAsuraPolygonFlag_IgnoreLOS) usFlags |= AsuraPolygonFlag_IgnoreLOS;
					*pusFlags = usFlags;

				}
			}
			u_short usOverallFlags = 0;
			if(m_usOverallFlags & OldAsuraPolygonFlag_DoubleSided) usOverallFlags |= AsuraPolygonFlag_DoubleSided;
			if(m_usOverallFlags & OldAsuraPolygonFlag_DontRender) usOverallFlags |= AsuraPolygonFlag_DontRender;
			if(m_usOverallFlags & OldAsuraPolygonFlag_NoCollisions) usOverallFlags |= AsuraPolygonFlag_NoCollisions;
			if(m_usOverallFlags & OldAsuraPolygonFlag_IgnoreLOS) usOverallFlags |= AsuraPolygonFlag_IgnoreLOS;
			m_usOverallFlags = usOverallFlags;

		}
		else if( iVersion == 4 )
		{
			// (MP - 19.07.2004) Nasty fix for flag mix up in Rogue. Fortunately
			// no other project had its own flags yet...
			if( m_pusPolygonFlags )
			{
				for( u_int u = 0; u < m_uNumberOfPolys; ++u )
				{
					m_pusPolygonFlags[u] |= (m_pusPolygonFlags[u] & 0x00F0) << 4;
				}
			}
		}
		if(iVersion<7)
		{
#ifdef ASURA_TOOLS
			//add ignore by los to polygons with dont render
			if(m_pusPolygonFlags)
			{
				u_short* pusFlags = m_pusPolygonFlags;
				for (unsigned int u = 0; u < m_uNumberOfPolys; ++u,pusFlags++)
				{
					*pusFlags = (u_short) Asura_Mesh::ConvertSinglePolygonFlag_AddIgnoreLOS(*pusFlags);
				}
			}
			m_usOverallFlags = (u_short) Asura_Mesh::ConvertSinglePolygonFlag_AddIgnoreLOS(m_usOverallFlags);
#else
			Asura_Assert(!m_pusPolygonFlags && !m_usOverallFlags,"Ignored by LOS flag won't be added to polygons with Don't Render until the collision mesh is updated in tools.");
#endif
		}

		if(iVersion<8)
		{
			#ifdef ASURA_TOOLS
			//don't have the full polygon flags in this version, so copy from the short version
			m_uToolsOverallFlags = m_usOverallFlags;
			if(m_pusPolygonFlags)
			{

				Asura_ResourceAllocateArray(m_puToolsPolygonFlags, m_uNumberOfPolys);

				for(u_int i=0;i<m_uNumberOfPolys;i++)
				{
					m_puToolsPolygonFlags[i] = m_pusPolygonFlags[i];
				}
				
			}
			#endif //ASURA_TOOLS
		}
		else
		{
			#ifdef ASURA_TOOLS
			xStream >> m_uToolsOverallFlags;
			#else
			xStream.Skip(sizeof(u_int));
			#endif

			//is there a tools flags array?
			u_int uHasToolsFlags;
			xStream >> uHasToolsFlags;
			
			if(uHasToolsFlags)
			{
				#ifdef ASURA_TOOLS
				Asura_ResourceAllocateArray(m_puToolsPolygonFlags, m_uNumberOfPolys);

				xStream.Read(sizeof(u_int)*m_uNumberOfPolys, m_puToolsPolygonFlags);
				#else
				//not needed out of tools , so just skip
				xStream.Skip( sizeof(u_int)*m_uNumberOfPolys);
				#endif
			}
		}
	}
	Asura_ResourcePopType();
}


//////////////////////////////////
// DestroyPolygons (Tools only) //
//////////////////////////////////
#ifdef ASURA_TOOLS
void Asura_CollisionMesh::DestroyPolygons (void)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		// Destroy the data...
		Asura_ResourceDeallocateArray( m_pxVertices );
		Asura_ResourceDeallocateArray( m_pxPolys );
		Asura_ResourceDeallocateArray( m_pusPolygonFlags );
        Asura_ResourceDeallocateArray( m_puToolsPolygonFlags );
		Asura_ResourceDeallocateArray( m_psMaterialIDs );
		Asura_ResourceDeallocateArray( m_puMaterialResponseHashIDs );

		// Reset the counts...
		m_uNumberOfVertices	= 0;
		m_uNumberOfPolys	= 0;
	}
	Asura_ResourcePopType();
}
#endif

/////////////////////////////////////
// GetChunkBufferSize (Tools only) //
/////////////////////////////////////
#ifdef ASURA_TOOLS
int Asura_CollisionMesh::GetChunkBufferSize(void) const
{
	int iChunkSize = sizeof(int);				// iVersion
	iChunkSize += sizeof(u_int);				// m_uNumberOfVertices
	iChunkSize += sizeof(u_int);				// m_uNumberOfPolys
	iChunkSize += sizeof(u_int);				// uHasFlagsArray
	iChunkSize += sizeof(u_int);				// uHasMaterialArray
	iChunkSize += sizeof(Asura_Hash_ID);		// uHasMaterialResponseArray
	iChunkSize += sizeof(unsigned short);		// m_sOverallFlags
	iChunkSize += sizeof(short);				// m_sOverallMaterial
	iChunkSize += sizeof(Asura_Hash_ID);		// m_uOverallMaterialResponse
	iChunkSize += sizeof(Asura_Bounding_Box);	// m_xBoundingBox
	iChunkSize += sizeof(float);				// m_fBoundingRadius

	iChunkSize += sizeof(Asura_Vector_3) * m_uNumberOfVertices;
	iChunkSize += sizeof(Asura_CollisionMesh_Poly) * m_uNumberOfPolys;

	if (m_pusPolygonFlags)
    {
        iChunkSize += sizeof(unsigned short) * m_uNumberOfPolys;
    }
	if (m_psMaterialIDs)
	{
		iChunkSize += sizeof(short) * m_uNumberOfPolys;
	}
	if (m_puMaterialResponseHashIDs)
	{
		iChunkSize += sizeof(Asura_Hash_ID) * m_uNumberOfPolys;
	}
	iChunkSize += sizeof(u_int); //m_usOverallToolsFlags
	iChunkSize += sizeof(u_int); // uHasToolsFlagsArray
	if (m_puToolsPolygonFlags && !Tools_Level_Export::IsExport())
    {
        iChunkSize += sizeof(u_int) * m_uNumberOfPolys;
    }
    return iChunkSize;
}
#endif //ASURA_TOOLS


////////////////////////////////////
// FillInChunkBuffer (tools only) //
////////////////////////////////////
#ifdef ASURA_TOOLS
#ifdef ASURA_MAIN
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif //ASURA_MAIN
void Asura_CollisionMesh::FillInChunkBuffer(char*& pcChunkBuffer, const int* piMaterialIDToIndexConversion) const
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	WriteToChunkStream(xStream, false, piMaterialIDToIndexConversion);
}


void Asura_CollisionMesh::WriteToChunkStream(Asura_Chunk_Stream& xStream, bool bPrefixWithSize,const int* piMaterialIDToIndexConversion /*= 0*/) const
{
	u_int uStartPosition = xStream.GetPosition();

	if(bPrefixWithSize)
	{
		//fill in placeholder for size for now
		xStream << (u_int) 0;
	}
	
	int iVersion = ASURA_CHUNK_COLLISION_MESH_CHUNK_VERSION;

	u_int uHasFlagsArray = m_pusPolygonFlags ? 1 : 0;
	u_int uHasToolsFlagsArray = (m_puToolsPolygonFlags && !Tools_Level_Export::IsExport()) ? 1 : 0;
	u_int uHasMaterialArray = m_psMaterialIDs ? 1 : 0;
	u_int uHasMaterialResponseArray = m_puMaterialResponseHashIDs ? 1 : 0;



    xStream << iVersion;
	xStream << m_uNumberOfVertices;
   	xStream << m_uNumberOfPolys;
	xStream << uHasFlagsArray;
	xStream << uHasMaterialArray;
	xStream << uHasMaterialResponseArray;
	xStream << m_usOverallFlags;

	short sMaterialIndex = (short) Asura_Material_System::ConvertIDToIndex(m_sOverallMaterial,piMaterialIDToIndexConversion);
	xStream << sMaterialIndex;
	xStream << m_uOverallMaterialResponse;

	xStream << m_xBoundingBox;
	xStream << m_fBoundingRadius;

    for (u_int i = 0; i < m_uNumberOfVertices; i++)
    {
        xStream << m_pxVertices[i];
    }

    for (u_int i = 0; i < m_uNumberOfPolys; i++)
    {
        xStream << m_pxPolys[i].m_usVertexID[0];
        xStream << m_pxPolys[i].m_usVertexID[1];
        xStream << m_pxPolys[i].m_usVertexID[2];
        xStream << m_pxPolys[i].m_usVertexID[3];
     
    }

    if (uHasFlagsArray)
    {
        for (u_int i = 0; i < m_uNumberOfPolys; i++)
        {
            xStream << m_pusPolygonFlags[i];
        }
    }
	if (uHasMaterialArray)
	{
		for (u_int i=0;i<m_uNumberOfPolys;i++)
		{
			xStream << (short) Asura_Material_System::ConvertIDToIndex(m_psMaterialIDs[i], piMaterialIDToIndexConversion);
		}
	}

	if (uHasMaterialResponseArray)
	{
		for (u_int i=0;i<m_uNumberOfPolys;i++)
		{
			xStream << m_puMaterialResponseHashIDs[i];
		}
	}

	xStream << m_uToolsOverallFlags;
	xStream << uHasToolsFlagsArray;
    if (uHasToolsFlagsArray)
    {
        for (u_int i = 0; i < m_uNumberOfPolys; i++)
        {
            xStream << m_puToolsPolygonFlags[i];
        }
    }

	if(bPrefixWithSize)
	{
		//Go back and write the size at the start of the data.
		u_int uSize = xStream.GetPosition() - uStartPosition - sizeof(u_int);
		if(xStream.ShouldSwapEndian()) uSize = Asura_Endian::SwapBytes(uSize);

		xStream.Write(sizeof(u_int), &uSize, uStartPosition);
	}
}

#endif //ASURA_TOOLS

////////////////////////////////////////////////////////////////////////////////
// Constructor for tools generation from mesh.                                //
////////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_TOOLS
Asura_CollisionMesh::Asura_CollisionMesh(Asura_Mesh* pxAsuraMesh)
: Asura_Collision_Object()
, m_uNumberOfVertices(0)
, m_pxVertices(0)
, m_uNumberOfPolys(0)
, m_pxPolys(0)
, m_pusPolygonFlags(0)
, m_puToolsPolygonFlags( NULL )
, m_puMaterialResponseHashIDs(0)
, m_psMaterialIDs(0)
, m_bValid(true)
{
	UpdateFromMesh(pxAsuraMesh);
}
#endif

#ifdef ASURA_TOOLS
void Asura_CollisionMesh::UpdatePolygonFromMesh(const Asura_Mesh* pxAsuraMesh,
												const u_int uMeshPoly,
												const u_int uCollPoly,
												const bool bFlipPolygon,
												int* piVertConv,
												bool& bCanDiscardFlags,
												bool& bCanDiscardMaterialIDs)
{
	const Asura_Mesh_Polygon&		pxMeshPoly		= pxAsuraMesh->PolygonDataPtr[uMeshPoly];
	const Asura_Mesh_PolygonFlags&	xMeshPolyFlags	= pxAsuraMesh->PolygonFlagDataPtr[uMeshPoly];
	Asura_CollisionMesh_Poly*		pxCollPoly		= &m_pxPolys[uCollPoly];

	for (int j=0; j<4; j++)
	{
		const u_int		uIndex		= (bFlipPolygon && (j < 3)) ? (2 - j) : j;
		const u_short	usVertID	= pxMeshPoly.VertexID[uIndex];

		if(usVertID == 0xffff)
		{
			pxCollPoly->m_usVertexID[j] = usVertID;
		}
		else
		{
			if(piVertConv[usVertID] == -1)
			{
				piVertConv[usVertID] = (int)m_uNumberOfVertices++;
			}
			pxCollPoly->m_usVertexID[j] = (u_short)piVertConv[usVertID];
		}
	}

	if (m_pusPolygonFlags)
	{
		m_pusPolygonFlags[uCollPoly] = static_cast<u_short>(xMeshPolyFlags.Flags);

		if (m_pusPolygonFlags[uCollPoly] != m_usOverallFlags)
		{
			bCanDiscardFlags = false;
		}
		m_usOverallFlags &= m_pusPolygonFlags[uCollPoly];
	}
	if ( m_puToolsPolygonFlags )
	{
		m_puToolsPolygonFlags[ uCollPoly ] = static_cast<u_int>(xMeshPolyFlags.Flags);

		if (m_puToolsPolygonFlags[ uCollPoly ] != m_uToolsOverallFlags)
		{
			bCanDiscardFlags = false;
		}
		m_uToolsOverallFlags &= m_puToolsPolygonFlags[ uCollPoly ];
	}
	if (m_psMaterialIDs)
	{
		m_psMaterialIDs[uCollPoly] = (short)pxAsuraMesh->TextureDataPtr[uMeshPoly].m_iMaterialID;
		if (m_psMaterialIDs[uCollPoly] != m_sOverallMaterial)
		{
			bCanDiscardMaterialIDs = false;
		}
	}
	if (m_puMaterialResponseHashIDs)
	{
		u_int uPreferredMaterialResponseHashIDIndex = 0;
		if( pxAsuraMesh->TextureDataPtr[uMeshPoly].m_auMaterialResponseHashID[1] != ASURA_HASH_ID_UNSET )
		{
			const u_int uNumberOfVertices = pxMeshPoly.NumberOfVertices();
			float fAlphaMean = 0.f;
			for( u_int uVertex = 0; uVertex < uNumberOfVertices; ++uVertex )
			{
				fAlphaMean += pxAsuraMesh->TextureDataPtr[uMeshPoly].m_afAlpha[uVertex];
			}
			
			if( fAlphaMean < (0.5f * uNumberOfVertices) )		// to avoid div by uVertexTot
			{
				uPreferredMaterialResponseHashIDIndex = 1;
			}
		}

		m_puMaterialResponseHashIDs[uCollPoly] = pxAsuraMesh->TextureDataPtr[uMeshPoly].m_auMaterialResponseHashID[uPreferredMaterialResponseHashIDIndex];
		if (m_puMaterialResponseHashIDs[uCollPoly] != m_uOverallMaterialResponse)
		{
			bCanDiscardMaterialIDs = false;
		}
	}

#if _DEBUG
	// Debugging code
	//const Asura_Vector_3& xNormal1 = pxAsuraMesh->NormalsDataPtr[uMeshPoly].FaceNormal;
	//Asura_Vector_3 xNormal2;
	//GetNormal(uMeshPoly, xNormal2);

	//Asura_Assert(xNormal1.ApproximatelyEquals(xNormal2),"Warning: Normals don't match");
#endif //_DEBUG
}

void Asura_CollisionMesh::UpdateFromMesh(Asura_Mesh* pxAsuraMesh)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		//Delete any old collision mesh data. RAR 28/03/03
		Asura_ResourceDeallocateArray(m_pxVertices);
		Asura_ResourceDeallocateArray(m_pxPolys);
		Asura_ResourceDeallocateArray(m_pusPolygonFlags);
        Asura_ResourceDeallocateArray( m_puToolsPolygonFlags );
		Asura_ResourceDeallocateArray(m_psMaterialIDs);
		Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);

		m_uNumberOfVertices = 0;
		m_pxVertices = 0;
		m_uNumberOfPolys = 0;
		m_pxPolys = 0;
		m_pusPolygonFlags = 0;
        m_puToolsPolygonFlags = NULL;
		m_psMaterialIDs = 0;
		m_puMaterialResponseHashIDs = 0;
		m_bValid = true;


		// Set up normals if needed
		if (!pxAsuraMesh->AreNormalsValid())
		{
			pxAsuraMesh->CreateNormals();
		}

		// Set up BB if needed.
		if (!pxAsuraMesh->BoundingBoxDataValid)
		{
			pxAsuraMesh->CalculateBoundingBox();
		}

		Asura_Assert(pxAsuraMesh->VertexDataValid, "Asura mesh not set up yet");
		Asura_Assert(pxAsuraMesh->AreNormalsValid(), "Asura mesh not set up yet");


		// Bounding volumes
		m_xBoundingBox = pxAsuraMesh->BoundingBox;
		m_fBoundingRadius = pxAsuraMesh->BoundingRadius;
		ResetBB();

		// Allocate memory
		m_uNumberOfPolys	= pxAsuraMesh->NumberOfPolygons;
		Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys * 2);	// * 2 for every polygon being (potentially) double-sided.


		int* piVertConv = new int[pxAsuraMesh->NumberOfVertices];
		for(int i=0;i<pxAsuraMesh->NumberOfVertices;i++)
		{
			piVertConv[i] = -1;
		}
		m_uNumberOfVertices = 0;


		const bool bHasPolygonData = pxAsuraMesh->PolygonFlagDataValid;
		const bool bHasTextureData = pxAsuraMesh->TextureDataValid;

		// TODO set up m_iOverallFlags from somewhere!
		// Needs to be setable in tools, so that overall flags can be set.

		if(m_uNumberOfPolys != 0)
		{
			// MC - 03/12/03: About time something was implemented for this!
			// For now, predict the overall flags based on the first poly, and check 
			// all subsequent polys agree with it in the loop, later.

			if (bHasPolygonData)
			{
				m_usOverallFlags = (unsigned short)pxAsuraMesh->PolygonFlagDataPtr[0].Flags;
                m_uToolsOverallFlags = static_cast< u_int >( pxAsuraMesh->PolygonFlagDataPtr[ 0 ].Flags );
			}
			else
			{
				// (AW - 11.02.2004) - If there is no polygon flag data whatsoever then it the
				// overall flags need to be reset accordingly.
				m_usOverallFlags = 0;
                m_uToolsOverallFlags = 0;
			}

			// AW - 24/03/03 - Same situation for material IDs.
			// MC - These seem to be handled by the call to SetAllMaterialIDs in LoadShape?

		}

		if (bHasPolygonData)
		{
			Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys * 2);
            Asura_ResourceAllocateArray( m_puToolsPolygonFlags, m_uNumberOfPolys * 2 );
		}

		if (bHasTextureData)
		{
			Asura_ResourceAllocateArray(m_psMaterialIDs, m_uNumberOfPolys * 2);
			Asura_ResourceAllocateArray(m_puMaterialResponseHashIDs, m_uNumberOfPolys * 2);
		}

		// Copy data

		bool bCanDiscardFlags = true;
		bool bCanDiscardMaterialIDs = true;
		u_int uCollPoly = 0;

		u_int uFlagsToIgnore = AsuraPolygonFlag_Water;
		if(!s_bIncludeDontExportPolys)
		{
			uFlagsToIgnore |= AsuraPolygonFlag_DontExport;
		}


		for (u_int uMeshPoly = 0; uMeshPoly < m_uNumberOfPolys; uMeshPoly++)
		{
			// Check to skip polygon based on its flags
			if(bHasPolygonData)
			{
				if(pxAsuraMesh->PolygonFlagDataPtr[uMeshPoly].Flags & uFlagsToIgnore)
				{
					//ignore this polygon
					continue;
				}
				else if((s_pfnProjectExcludePolygonFromExport != NULL) &&
					s_pfnProjectExcludePolygonFromExport(*pxAsuraMesh, uMeshPoly))
				{
					//ignore this polygon
					continue;
				}
			}

			//I've disabled removal of alpha decal polygons for projects using the new physics , to keep
			//some consistency with the old behaviour ,since mesh has been built that relies on these
			//being collidable.
			//RAR 09/06/2006

			#if 0
			// Check to skip polygon based on its material 
			if (bHasTextureData)
			{
				//Disabling these checks for max exports since the material system isn't set
				//up appropriately for getting the flags. Shouldn't be an issue , 
				//since the collision mesh should be re-done once in tools. 
				//RAR 20/05/2005

#ifndef ASURAIMPORT_EXPORTS
				int iMaterialID = pxAsuraMesh->TextureDataPtr[uMeshPoly].m_iMaterialID;
                
				if (Asura_Material_System::IsMaterialAlphaDecal(iMaterialID))
				{		
					//ignore this polygon
					continue;
				}
#endif
			}
			#endif	

			//skip merged object polygons (their collision meshes are merged in separately)
			if(pxAsuraMesh->m_pxPolygonGuids)
			{
				if(pxAsuraMesh->m_pxPolygonGuids[uMeshPoly] != ASURA_GUID_UNREGISTERED)
				{
					continue;
				}
			}

			// Update the current polygon (and the double-sided ones if necessary)...
			UpdatePolygonFromMesh(pxAsuraMesh, uMeshPoly, uCollPoly++, false, piVertConv, bCanDiscardFlags, bCanDiscardMaterialIDs);

			if( bHasPolygonData && (pxAsuraMesh->PolygonFlagDataPtr[uMeshPoly].Flags & AsuraPolygonFlag_DoubleSided) != 0)
			{
				UpdatePolygonFromMesh(pxAsuraMesh, uMeshPoly, uCollPoly++, true, piVertConv, bCanDiscardFlags, bCanDiscardMaterialIDs);
			}
		}

		Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
		for (int i = 0; i < pxAsuraMesh->NumberOfVertices; i++)
		{
			if(piVertConv[i] != -1)
			{
				m_pxVertices[piVertConv[i]] = pxAsuraMesh->VertexDataPtr[i].Position;
			}
		}
		delete [] piVertConv;


		//correct the number of polygons
		m_uNumberOfPolys = uCollPoly;

		// Discard the poly flags if they aren't needed.
		if ( bCanDiscardFlags )
		{
            if ( m_pusPolygonFlags )
            {
			    Asura_ResourceDeallocateArray(m_pusPolygonFlags);
			    m_pusPolygonFlags = NULL;
            }
            if ( m_puToolsPolygonFlags )
            {
                Asura_ResourceDeallocateArray( m_puToolsPolygonFlags );
                m_puToolsPolygonFlags = NULL;
            }
		}
		// Discard the material IDs if they aren't needed.
		if (m_psMaterialIDs && 
			bCanDiscardMaterialIDs)
		{
			Asura_ResourceDeallocateArray(m_psMaterialIDs);
			m_psMaterialIDs = NULL;
		}
		if (m_puMaterialResponseHashIDs &&
			bCanDiscardMaterialIDs)
		{
			Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);
			m_puMaterialResponseHashIDs = NULL;
		}

		//attach collision mesh for entities that are merged to this
		if(pxAsuraMesh->m_pxMergedEntityCollisionMesh)
		{
			AddCollisionMesh(pxAsuraMesh->m_pxMergedEntityCollisionMesh,Asura_Maths::IdentityMatrix3x3,Asura_Maths::ZeroVector3);
		}
	}
	Asura_ResourcePopType();
}

#endif // #ifdef ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_CollisionMesh::RemoveNonCollisionPolys( void )
{
    // If we don't have polygon flag data available then we're not going to be changing much.
    if ( !m_pusPolygonFlags ) { return; }

    u_int uNewNumPolys = 0;
    for( u_int uPoly = 0; uPoly < m_uNumberOfPolys; ++uPoly )
    {
        if( !(m_pusPolygonFlags[uPoly] & AsuraPolygonFlag_NoCollisions) )
        {
            ++uNewNumPolys;
        }
    }

	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		// Allocate new arrays for relevant data.
		Asura_CollisionMesh_Poly* pxNewPolys;
		u_short*                  pusNewPolyFlags;
        u_int*                    puNewToolsPolyFlags;
		short*                    psNewMaterialIDs;
		Asura_Hash_ID*            puNewMaterialResponseHashIDs;

		bool bCanDiscardFlags = true;

		Asura_ResourceAllocateArray(pxNewPolys, uNewNumPolys);
		Asura_ResourceAllocateArray(pusNewPolyFlags, uNewNumPolys);
        Asura_ResourceAllocateArray(puNewToolsPolyFlags, uNewNumPolys);
		if(m_psMaterialIDs == NULL)
		{
			psNewMaterialIDs = NULL;
		}
		else
		{
			Asura_ResourceAllocateArray(psNewMaterialIDs, uNewNumPolys);
		}
		if(m_puMaterialResponseHashIDs == NULL)
		{
			puNewMaterialResponseHashIDs = NULL;
		}
		else
		{
			Asura_ResourceAllocateArray(puNewMaterialResponseHashIDs, uNewNumPolys);
		}

		if( uNewNumPolys )
		{
            m_usOverallFlags     &= ~AsuraPolygonFlag_NoCollisions;
            m_uToolsOverallFlags &= ~AsuraPolygonFlag_NoCollisions;

			// Copy over relevant data.
			u_int uOut = 0;
			for( u_int uPoly = 0; uPoly < m_uNumberOfPolys; ++uPoly )
			{
				if( m_pusPolygonFlags[uPoly] & AsuraPolygonFlag_NoCollisions ) { continue; }

                pxNewPolys[uOut]            = m_pxPolys[uPoly];
                pusNewPolyFlags[uOut]       = m_pusPolygonFlags[uPoly];
                puNewToolsPolyFlags[ uOut ] = m_puToolsPolygonFlags ? m_puToolsPolygonFlags[ uPoly ] : 0;

				if( psNewMaterialIDs )
				{
					psNewMaterialIDs[uOut] = m_psMaterialIDs[uPoly];
				}
				if( puNewMaterialResponseHashIDs )
				{
					puNewMaterialResponseHashIDs[uOut] = m_puMaterialResponseHashIDs[uPoly];
				}

				if( pusNewPolyFlags[uOut] != m_usOverallFlags )
				{
					m_usOverallFlags &= pusNewPolyFlags[uOut];
					bCanDiscardFlags = false;
				}
                if ( puNewToolsPolyFlags[ uOut ] != m_uToolsOverallFlags )
                {
                    m_uToolsOverallFlags &= puNewToolsPolyFlags[ uOut ];
                    bCanDiscardFlags = false;
                }

				++uOut;
			}
		}
		else
		{
            m_usOverallFlags     = 0;
            m_uToolsOverallFlags = 0;
		}

		// Dump poly flags again if we've determined they're not required.
		if( bCanDiscardFlags )
		{
			Asura_ResourceDeallocateArray(pusNewPolyFlags);
			pusNewPolyFlags = NULL;

            Asura_ResourceDeallocateArray( puNewToolsPolyFlags );
            puNewToolsPolyFlags = NULL;
		}

		// Lose old data.
		Asura_ResourceDeallocateArray( m_puMaterialResponseHashIDs );
		Asura_ResourceDeallocateArray( m_psMaterialIDs );
		Asura_ResourceDeallocateArray( m_pusPolygonFlags );
        Asura_ResourceDeallocateArray( m_puToolsPolygonFlags );
		Asura_ResourceDeallocateArray( m_pxPolys );

		// Store new pointers and poly count.
        m_pxPolys                   = pxNewPolys;
        m_pusPolygonFlags           = pusNewPolyFlags;
        m_puToolsPolygonFlags       = puNewToolsPolyFlags;
		m_puMaterialResponseHashIDs = puNewMaterialResponseHashIDs;
        m_psMaterialIDs             = psNewMaterialIDs;
        m_uNumberOfPolys            = uNewNumPolys;
	}
	Asura_ResourcePopType();
}


// Reorders the collision mesh into an order convenient for LBAABB trees.
void Asura_CollisionMesh::ReorderCollisionMesh(const u_int* puConversionArray)
{
	if (m_uNumberOfPolys)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
		{
			Asura_CollisionMesh_Poly* pxReorderedPolys;
			unsigned short* pusReorderedPolygonFlags;
			u_int* puReorderedToolsPolygonFlags;
			short* psReorderedMaterialIDs;
			Asura_Hash_ID* puReorderedMaterialResponseHashIDs;

			Asura_ResourceAllocateArray(pxReorderedPolys, m_uNumberOfPolys);
			if(m_pusPolygonFlags == NULL)
			{
				pusReorderedPolygonFlags	= NULL;
			}
			else
			{
				Asura_ResourceAllocateArray(pusReorderedPolygonFlags, m_uNumberOfPolys);
			}
			if(m_puToolsPolygonFlags == NULL)
			{
				puReorderedToolsPolygonFlags	= NULL;
			}
			else
			{
				Asura_ResourceAllocateArray(puReorderedToolsPolygonFlags, m_uNumberOfPolys);
			}
			if(m_psMaterialIDs == NULL)
			{
				psReorderedMaterialIDs	= NULL;
			}
			else
			{
				Asura_ResourceAllocateArray(psReorderedMaterialIDs, m_uNumberOfPolys);
			}
			if(m_puMaterialResponseHashIDs == NULL)
			{
				puReorderedMaterialResponseHashIDs = NULL;
			}
			else
			{
				Asura_ResourceAllocateArray(puReorderedMaterialResponseHashIDs, m_uNumberOfPolys);
			}

			for (u_int i=0;i<m_uNumberOfPolys;i++)
			{
				pxReorderedPolys[i] = m_pxPolys[puConversionArray[i]];
				if (pusReorderedPolygonFlags)
				{
					pusReorderedPolygonFlags[i] = m_pusPolygonFlags[puConversionArray[i]];
				}
				if (puReorderedToolsPolygonFlags)
				{
					puReorderedToolsPolygonFlags[i] = m_puToolsPolygonFlags[puConversionArray[i]];
				}
				if (psReorderedMaterialIDs)
				{
					psReorderedMaterialIDs[i] = m_psMaterialIDs[puConversionArray[i]];
				}
				if (puReorderedMaterialResponseHashIDs)
				{
					puReorderedMaterialResponseHashIDs[i] = m_puMaterialResponseHashIDs[puConversionArray[i]];
				}
			}
			Asura_ResourceDeallocateArray(m_pxPolys);
			Asura_ResourceDeallocateArray(m_pusPolygonFlags);
			Asura_ResourceDeallocateArray(m_puToolsPolygonFlags);
			Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);
			Asura_ResourceDeallocateArray(m_psMaterialIDs);
			m_pxPolys = pxReorderedPolys;
			m_pusPolygonFlags = pusReorderedPolygonFlags;
			m_puToolsPolygonFlags = puReorderedToolsPolygonFlags;
			m_puMaterialResponseHashIDs = puReorderedMaterialResponseHashIDs;
			m_psMaterialIDs = psReorderedMaterialIDs;
		}
		Asura_ResourcePopType();
	}
}

#endif

////////////////////////////////////////////////////////////////////////////////
// Copy constructor
////////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_TOOLS
Asura_CollisionMesh::Asura_CollisionMesh(const Asura_CollisionMesh* pxCollisionMesh)
: Asura_Collision_Object()
, m_uNumberOfVertices(0)
, m_pxVertices(0)
, m_uNumberOfPolys(0)
, m_pxPolys(0)
, m_pusPolygonFlags(0)
, m_puToolsPolygonFlags( NULL )
, m_psMaterialIDs(0)
, m_puMaterialResponseHashIDs(0)
{
	Copy(pxCollisionMesh);
}
#endif

////////////////////////////////////////////////////////////////////////////////
// Copy from another collision mesh
////////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_TOOLS
void Asura_CollisionMesh::Copy(const Asura_CollisionMesh* pxCollisionMesh)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		//Delete any old collision mesh data. RAR 28/03/03
		Asura_ResourceDeallocateArray(m_pxVertices);
		Asura_ResourceDeallocateArray(m_pxPolys);
		Asura_ResourceDeallocateArray(m_pusPolygonFlags);
        Asura_ResourceDeallocateArray(m_puToolsPolygonFlags);
		Asura_ResourceDeallocateArray(m_psMaterialIDs);
		Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);

		m_uNumberOfVertices = 0;
		m_pxVertices = 0;
		m_uNumberOfPolys = 0;
		m_pxPolys = 0;
		m_pusPolygonFlags = 0;
        m_puToolsPolygonFlags = NULL;
		m_psMaterialIDs = 0;
		m_puMaterialResponseHashIDs = 0;

		// Bounding volumes
		m_xBoundingBox = pxCollisionMesh->m_xBoundingBox;
		m_fBoundingRadius = pxCollisionMesh->m_fBoundingRadius;
		ResetBB();

		// Allocate memory
		m_uNumberOfVertices = pxCollisionMesh->m_uNumberOfVertices;
		m_uNumberOfPolys = pxCollisionMesh->m_uNumberOfPolys;

		Asura_ResourceAllocateArray(m_pxVertices, m_uNumberOfVertices);
		Asura_ResourceAllocateArray(m_pxPolys, m_uNumberOfPolys);

		const bool bHasPolygonData = pxCollisionMesh->m_pusPolygonFlags!=0;
		if (bHasPolygonData)
		{
            Asura_ResourceAllocateArray(m_pusPolygonFlags, m_uNumberOfPolys);
		}
        const bool bHasToolsPolygonData = ( pxCollisionMesh->m_puToolsPolygonFlags != NULL );
        if ( bHasToolsPolygonData )
        {
            Asura_ResourceAllocateArray( m_puToolsPolygonFlags, m_uNumberOfPolys );
        }
		const bool bHasTextureData = pxCollisionMesh->m_psMaterialIDs!=0;
		if (bHasTextureData)
		{
			Asura_ResourceAllocateArray(m_psMaterialIDs, m_uNumberOfPolys);
		}
		const bool bHasMaterialResponseData = pxCollisionMesh->m_puMaterialResponseHashIDs!=0;
		if (bHasMaterialResponseData)
		{
			Asura_ResourceAllocateArray(m_puMaterialResponseHashIDs, m_uNumberOfPolys);
		}

		// Copy data
		for (u_int i = 0; i < m_uNumberOfVertices; i++)
		{
			m_pxVertices[i] = pxCollisionMesh->m_pxVertices[i];
		}

		for(u_int i=0; i < m_uNumberOfPolys; i++)
		{
			m_pxPolys[i] = pxCollisionMesh->m_pxPolys[i];
			if(m_pusPolygonFlags)
			{
				m_pusPolygonFlags[i] = pxCollisionMesh->m_pusPolygonFlags[i];
			}
            if ( m_puToolsPolygonFlags )
            {
                m_puToolsPolygonFlags[ i ] = pxCollisionMesh->m_puToolsPolygonFlags[ i ];
            }
			if(m_psMaterialIDs)
			{
				m_psMaterialIDs[i] = pxCollisionMesh->m_psMaterialIDs[i];
			}
			if(m_puMaterialResponseHashIDs)
			{
				m_puMaterialResponseHashIDs[i] = pxCollisionMesh->m_puMaterialResponseHashIDs[i];
			}
		}

		m_usOverallFlags = pxCollisionMesh->m_usOverallFlags;
		m_sOverallMaterial = pxCollisionMesh->m_sOverallMaterial;
		m_uOverallMaterialResponse = pxCollisionMesh->m_uOverallMaterialResponse;

		m_bValid = pxCollisionMesh->m_bValid;		
	}
	Asura_ResourcePopType();
}
#endif // ASURA_TOOLS


/////////////////////////////////////////
// LOSTestPolygons: per poly LOS test. // 
/////////////////////////////////////////
bool Asura_CollisionMesh::LOSTestPolygons(const Asura_Vector_3& xPosition, 
										  const Asura_Matrix_3x3& xOrient,
										  const Asura_LOS_InputData* pxInput,
										  Asura_LOS_OutputData* pxOutput) const
{
	// Check that overall LOS ignore isn't set.
    const u_int uFlagsToIgnore = pxInput->m_uPolyFlagsToIgnore;
	if( m_usOverallFlags & uFlagsToIgnore )
	{	
		return false;	
	}

	// First, test against the bounding sphere.
	float fDistance;
	Asura_Vector_3 xCentre = xPosition + (m_xBoundingBox.CalculateCentre() * xOrient);
	if (Asura_Maths::RayIntersectsSphere(pxInput->m_xOrigin, pxInput->m_xDirection, xCentre, m_fBoundingRadius, &fDistance) &&
		fDistance < pxOutput->m_fDistanceToIntersection)
	{
		// Next, do a LOS test against the rotated BB.
		// Convert LOS test from world space to object space
		Asura_Vector_3 xAlpha = pxInput->m_xOrigin;
		Asura_Vector_3 xBeta  = pxInput->m_xDirection;
		xAlpha -= xPosition;
		
		Asura_Matrix_3x3 xMat = xOrient;
		xMat.Transpose();

		xAlpha = xAlpha * xMat;
		xBeta = xBeta * xMat;
		if (Asura_Maths::RayIntersectsBoundingBox(xAlpha, xBeta, m_xBoundingBox, &fDistance))
		{
			// Hit the BB
			if (fDistance < pxOutput->m_fDistanceToIntersection)
			{
				// Closest so far...
				// Second, do a LOS test against each poly
				float fLowest = ASURA_LOS_MAX_DISTANCE;
				unsigned int uClosestPoly = 0xffffffff;

				for (unsigned int uPoly = 0; uPoly < m_uNumberOfPolys; uPoly++)
				{	
                    if( GetFlags(uPoly) & uFlagsToIgnore ) { continue; }

					const u_short v0 = m_pxPolys[uPoly].m_usVertexID[0];
					const u_short v1 = m_pxPolys[uPoly].m_usVertexID[1];
					const u_short v2 = m_pxPolys[uPoly].m_usVertexID[2];
					const u_short v3 = m_pxPolys[uPoly].m_usVertexID[3];

					bool bFacingToward;
					float fDist = Asura_Maths::RayIntersectsTri(xAlpha, xBeta, m_pxVertices[v0], m_pxVertices[v1], m_pxVertices[v2], bFacingToward);

					if (fDist>=0.0f && fDist<fLowest)
					{
						fLowest = fDist;
						uClosestPoly = uPoly;
					}
					else if (m_pxPolys[uPoly].IsQuad())
					{
						// This is a quad need to check the other triangle...
						fDist = Asura_Maths::RayIntersectsTri(xAlpha, xBeta, m_pxVertices[v2], m_pxVertices[v3], m_pxVertices[v0], bFacingToward);

						if (fDist>=0.0f && fDist<fLowest)
						{
							fLowest = fDist;
							uClosestPoly = uPoly;
						}
					}
				}

				if (uClosestPoly != 0xffffffff && fLowest < pxOutput->m_fDistanceToIntersection)
				{
					// HitEntity guid will be set by caller if needed.
					pxOutput->m_fDistanceToIntersection = fLowest;
					pxOutput->m_iHitBoneID = -1; // Clear this. Will be set by caller if needed.
					pxOutput->m_iHitAttachmentID = Asura_AnimationAttachment::INVALID_ID; // And clear this. RAR 28/02/03

					pxOutput->m_usPolyFlags = GetFlags(uClosestPoly);

					#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
						pxOutput->m_uMaterialResponseHashID = GetMaterialResponseHashID(uClosestPoly);
					#endif
					pxOutput->m_sMaterialID = GetMaterialID(uClosestPoly);

					// Get normal and rotate it.
					Asura_Vector_3 xTemp;
					GetNormal(uClosestPoly, xTemp);
					pxOutput->m_xNormal = xTemp * xOrient;

					return true;
				}
			}
		}
	}
	return false;
}


////////////////////////////////////////////////
// FillPolyArray : fill collision poly array. //
////////////////////////////////////////////////
int Asura_CollisionMesh::FillPolyArray( int iMaxNumber,
										Asura_Collision_Poly*& pxPolyPtr,
										const Asura_Vector_3& xPosition,
										const Asura_Matrix_3x3* pxOrient) const
{
	// This base class, just fills in the array with the BB polys.
	if (iMaxNumber<=0) return 0; // No free space
	if (!pxPolyPtr) return 0;

	// OK, just dump all the polys into the collision array.
	int iNumberAdded = 0;

	Asura_Assertf(m_uNumberOfPolys < 200, ("ARTIST WARNING: Collision mesh of object at (X: %f Y: %f Z: %f) has large number of polygons. This will cause huge slowdown in the dynamics.", Asura_Maths::FtoD(xPosition.x), Asura_Maths::FtoD(xPosition.y), Asura_Maths::FtoD(xPosition.z)));

	for (unsigned int uPoly=0; uPoly<m_uNumberOfPolys; uPoly++)
	{
		if ( iMaxNumber == iNumberAdded )
		{
			break; // Give up, list is full!
		}

		if (GetFlags(uPoly) & AsuraPolygonFlag_NoCollisions)
		{
			continue;
		}

		// Get the vertices
		Asura_Vector_3 axVertex[4];
		GetVertices(axVertex, uPoly);

		const int iVert = GetNumVerticesInPoly(uPoly);
		for (int i=0; i<iVert; i++)
		{
			if (pxOrient)
			{
				axVertex[i] = axVertex[i] * (*pxOrient);
			}
			axVertex[i] += xPosition;
		}

		// Copy data for first triangle.
		for (int i=0; i<3; i++)
		{
			pxPolyPtr->Vertex[i] = axVertex[i];
		}

		// Calculate the normal
	    pxPolyPtr->Normal = ((axVertex[1] - axVertex[0])^(axVertex[2] - axVertex[0]));
		pxPolyPtr->Normal.Normalise();

		const Asura_Vector_3& xFirstNormal = pxPolyPtr->Normal; // Save ref

		pxPolyPtr++;
		iNumberAdded++;

		// Add second triangle for quads.
		if (IsQuad(uPoly))
		{
			if ( iMaxNumber == iNumberAdded )
			{
				break; // give up list is full
			}

			pxPolyPtr->Vertex[0] = axVertex[2];
			pxPolyPtr->Vertex[1] = axVertex[3];
			pxPolyPtr->Vertex[2] = axVertex[0];

			pxPolyPtr->Normal = xFirstNormal;
			pxPolyPtr++;
			iNumberAdded++;
		}
	}

	return iNumberAdded;
}

#ifdef ASURA_TOOLS
int Asura_CollisionMesh::FillPolyArray( int iMaxNumber,
										Asura_Collision_Poly*& pxPolyPtr,
                                        u_short*& pusFlagsPtr,
										const Asura_Vector_3& xPosition,
										const Asura_Matrix_3x3* pxOrient) const
{
	// This base class, just fills in the array with the BB polys.
	if (iMaxNumber<=0) return 0; // No free space
	if (!pxPolyPtr) return 0;
    if (!pusFlagsPtr) return 0;

	// OK, just dump all the polys into the collision array.
	int iNumberAdded = 0;

	Asura_Assertf(m_uNumberOfPolys < 200, ("ARTIST WARNING: Collision mesh of object at (X: %f Y: %f Z: %f) has large number of polygons. This will cause huge slowdown in the dynamics.", Asura_Maths::FtoD(xPosition.x), Asura_Maths::FtoD(xPosition.y), Asura_Maths::FtoD(xPosition.z)));

	for (unsigned int uPoly=0; uPoly<m_uNumberOfPolys; uPoly++)
	{
		if ( iMaxNumber == iNumberAdded )
		{
			break; // Give up, list is full!
		}

		if (GetFlags(uPoly) & AsuraPolygonFlag_NoCollisions)
		{
			continue;
		}

		// Get the vertices
		Asura_Vector_3 axVertex[4];
		GetVertices(axVertex, uPoly);

		const int iVert = GetNumVerticesInPoly(uPoly);
		for (int i=0; i<iVert; i++)
		{
			if (pxOrient)
			{
				axVertex[i] = axVertex[i] * (*pxOrient);
			}
			axVertex[i] += xPosition;
		}

		// Copy data for first triangle.
		for (int i=0; i<3; i++)
		{
			pxPolyPtr->Vertex[i] = axVertex[i];
		}

		// Calculate the normal
	    pxPolyPtr->Normal = ((axVertex[1] - axVertex[0])^(axVertex[2] - axVertex[0]));
		pxPolyPtr->Normal.Normalise();

		const Asura_Vector_3& xFirstNormal = pxPolyPtr->Normal; // Save ref

        *pusFlagsPtr = GetFlags( uPoly );

		pxPolyPtr++;
        pusFlagsPtr++;
		iNumberAdded++;

		// Add second triangle for quads.
		if (IsQuad(uPoly))
		{
			if ( iMaxNumber == iNumberAdded )
			{
				break; // give up list is full
			}

			pxPolyPtr->Vertex[0] = axVertex[2];
			pxPolyPtr->Vertex[1] = axVertex[3];
			pxPolyPtr->Vertex[2] = axVertex[0];

			pxPolyPtr->Normal = xFirstNormal;

            *pusFlagsPtr = GetFlags( uPoly );

			pxPolyPtr++;
            pusFlagsPtr++;
			iNumberAdded++;
		}
	}

	return iNumberAdded;
}
#endif

void Asura_CollisionMesh::SetAllMaterialIDs(short iMaterialID)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		if (m_psMaterialIDs)
		{
			Asura_ResourceDeallocateArray(m_psMaterialIDs);
			m_psMaterialIDs = NULL;
		}
		m_sOverallMaterial = iMaterialID;
	}
	Asura_ResourcePopType();
}

void Asura_CollisionMesh::SetAllMaterialResponseHashIDs(Asura_Hash_ID uMaterialResponseHashID)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		if (m_puMaterialResponseHashIDs)
		{
			Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);
			m_puMaterialResponseHashIDs = NULL;
		}
		m_uOverallMaterialResponse = uMaterialResponseHashID;
	}
	Asura_ResourcePopType();
}

#ifdef ASURA_TOOLS
void Asura_CollisionMesh::SetMaterialID(u_int uPolyIndex, short sMaterialID)
{
	Asura_Assert(uPolyIndex<m_uNumberOfPolys, "Invalid index");
	if(!m_psMaterialIDs)
	{
		Asura_Assert(0,"Function only implemented for collision meshes that already have material IDs per polygon");
		return;
	}
	m_psMaterialIDs[uPolyIndex] = sMaterialID;
}
#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_CollisionMesh::ConvertMaterialIDsToIndeces(const int* piConversion)
{
	if(m_sOverallMaterial != ASURA_MATERIAL_NONE)
	{
		m_sOverallMaterial = (short) piConversion[m_sOverallMaterial - ASURA_MATERIAL_FIRSTID];
	}
	if(m_psMaterialIDs)
	{
		for(u_int i=0;i<m_uNumberOfPolys;i++)
		{
			if(m_psMaterialIDs[i] != ASURA_MATERIAL_NONE)
			{
				m_psMaterialIDs[i] = (short)piConversion[m_psMaterialIDs[i] - ASURA_MATERIAL_FIRSTID];

                // (MP - 08.06.2006) In theory that should never end up
                // mapping to ASURA_MATERIAL_NONE, since the conversion
                // list should contain entries for all materials in the mesh.
                // However, old collision mesh data that was saved out before
                // I fixed the material IDs not being converted to indices
                // will just have nonsense data and so we can't assert about
                // it, at least not until it's safe to assume we don't have
                // any such invalid data anymore.
			}
		}
	}
}

void Asura_CollisionMesh::ConvertMaterialIndecesToIDs(const int* piConversion)
{
	if(m_sOverallMaterial != ASURA_MATERIAL_NONE)
	{
		m_sOverallMaterial = (short) piConversion[m_sOverallMaterial];
	}
	if(m_psMaterialIDs)
	{
		for(u_int i=0;i<m_uNumberOfPolys;i++)
		{
			if(m_psMaterialIDs[i] != ASURA_MATERIAL_NONE)
			{
				m_psMaterialIDs[i] = (short)piConversion[m_psMaterialIDs[i]];
			}
		}
	}
}

void Asura_CollisionMesh::ResetMaterialIDs()
{
	m_sOverallMaterial = ASURA_MATERIAL_NONE;
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		Asura_ResourceDeallocateArray(m_psMaterialIDs);
		m_psMaterialIDs = 0;
	}
	Asura_ResourcePopType();

}


#endif // ASURA_TOOLS



#ifdef ASURA_TOOLS
void Asura_CollisionMesh::DeletePolygons(const bool* pbDeleteArray)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		int* piVertConv = new int [m_uNumberOfVertices];
		for(u_int i=0;i<m_uNumberOfVertices;i++)
		{
			piVertConv[i] = -1;
		}

		//remove deleted polygons , and determine used vertices
		u_int uNumPolys = 0;
		u_int uNumVerts = 0;
		for(u_int i=0;i<m_uNumberOfPolys;i++)
		{
			if(!pbDeleteArray[i])
			{
				if(i != uNumPolys)
				{
					m_pxPolys[uNumPolys] = m_pxPolys[i];
				}
				int iVerts = m_pxPolys[uNumPolys].IsQuad() ? 4 : 3;

				for(int j=0;j<iVerts;j++)
				{
					u_short usVertIndex = m_pxPolys[uNumPolys].m_usVertexID[j];
					if(piVertConv[usVertIndex] == -1)
					{
						piVertConv[usVertIndex] = uNumVerts++;
					}
					m_pxPolys[uNumPolys].m_usVertexID[j] = (u_short)piVertConv[usVertIndex];

				}
				uNumPolys++;
			}
		}

		//create a new vertex array ,and copy the vertices used
		Asura_Vector_3* pxVertices;
		Asura_ResourceAllocateArray(pxVertices, uNumVerts);
		for(u_int i=0;i<m_uNumberOfVertices;i++)
		{
			if(piVertConv[i] != -1)
			{
				pxVertices[piVertConv[i]] = m_pxVertices[i];
			}
		}
		delete [] piVertConv;

		//replace the vertex array , and update number of vertices
		Asura_ResourceDeallocateArray(m_pxVertices);
		m_pxVertices = pxVertices;
		m_uNumberOfVertices = uNumVerts;

		//update flags array
		if(m_pusPolygonFlags)
		{
			u_int uPos = 0;
			for(u_int i=0;i<m_uNumberOfPolys;i++)
			{
				if(!pbDeleteArray[i])
				{
					if(i != uPos)
					{
						m_pusPolygonFlags[uPos] = m_pusPolygonFlags[i];
					}
					uPos++;
				}
			}
		}

		//update tools flags array
		if(m_puToolsPolygonFlags)
		{
			u_int uPos = 0;
			for(u_int i=0;i<m_uNumberOfPolys;i++)
			{
				if(!pbDeleteArray[i])
				{
					if(i != uPos)
					{
						m_puToolsPolygonFlags[uPos] = m_puToolsPolygonFlags[i];
					}
					uPos++;
				}
			}
		}

		//update materials array
		if(m_psMaterialIDs)
		{
			u_int uPos = 0;
			for(u_int i=0;i<m_uNumberOfPolys;i++)
			{
				if(!pbDeleteArray[i])
				{
					if(i != uPos)
					{
						m_psMaterialIDs[uPos] = m_psMaterialIDs[i];
					}
					uPos++;
				}
			}
		}

		//update material response array
		if(m_puMaterialResponseHashIDs)
		{
			u_int uPos = 0;
			for(u_int i=0;i<m_uNumberOfPolys;i++)
			{
				if(!pbDeleteArray[i])
				{
					if(i != uPos)
					{
						m_puMaterialResponseHashIDs[uPos] = m_puMaterialResponseHashIDs[i];
					}
					uPos++;
				}
			}
		}

		m_uNumberOfPolys = uNumPolys;
	}
	Asura_ResourcePopType();
}

void Asura_CollisionMesh::AddCollisionMesh(const Asura_CollisionMesh* pxCollMesh,const Asura_Matrix_3x3& xMat,const Asura_Vector_3& xOffset,float fScale)
{
	AddCollisionMesh( pxCollMesh, xMat, xOffset, Asura_Vector_3(fScale,fScale,fScale),0 );
}

void Asura_CollisionMesh::AddCollisionMesh(const Asura_CollisionMesh* pxCollMesh,const Asura_Matrix_3x3& xMat,const Asura_Vector_3& xOffset,const Asura_Vector_3& xScale,u_int uPolygonFlagsToAdd)
{
	Asura_ResourcePushType(ASURA_RESOURCE_COLLISION_MESH);
	{
		u_int uOldNumVertices = m_uNumberOfVertices;
		u_int uNewNumVertices = m_uNumberOfVertices + pxCollMesh->m_uNumberOfVertices;
		Asura_Vector_3* pxVertices;
		Asura_ResourceAllocateArray(pxVertices, uNewNumVertices);

		//copy vertices from this mesh
		u_int uPos = 0;
		for(u_int i=0;i<m_uNumberOfVertices;i++,uPos++)
		{
			pxVertices[uPos] = m_pxVertices[i];
		}
		//and the other mesh
		for(u_int i=0;i<pxCollMesh->m_uNumberOfVertices;i++,uPos++)
		{

			pxVertices[uPos] = pxCollMesh->m_pxVertices[i];
			pxVertices[uPos].x *= xScale.x;
			pxVertices[uPos].y *= xScale.y;
			pxVertices[uPos].z *= xScale.z;
			pxVertices[uPos] = pxVertices[uPos]*xMat + xOffset;


		}

		Asura_ResourceDeallocateArray(m_pxVertices);
		m_pxVertices = pxVertices;
		m_uNumberOfVertices = uNewNumVertices;



		//allocate polygon array
		u_int uOldNumPolys = m_uNumberOfPolys;
		u_int uNewNumPolys = m_uNumberOfPolys + pxCollMesh->m_uNumberOfPolys;
		Asura_CollisionMesh_Poly* pxPolys;
		Asura_ResourceAllocateArray(pxPolys, uNewNumPolys);

		//copy polygons from this mesh
		uPos = 0;
		for(u_int i=0;i<m_uNumberOfPolys;i++,uPos++)
		{
			pxPolys[uPos] = m_pxPolys[i];
		}
		//and the other mesh
		for(u_int i=0;i<pxCollMesh->m_uNumberOfPolys;i++,uPos++)
		{
			pxPolys[uPos] = pxCollMesh->m_pxPolys[i];
			int iVerts = pxPolys[uPos].IsQuad() ? 4 : 3;
			//correct the vertex indeces
			for(int j=0;j<iVerts;j++)
			{
				pxPolys[uPos].m_usVertexID[j] = (u_short)(pxPolys[uPos].m_usVertexID[j] + uOldNumVertices);
			}
		}

		Asura_ResourceDeallocateArray(m_pxPolys);
		m_pxPolys = pxPolys;
		m_uNumberOfPolys = uNewNumPolys;


		//deal with polygon flags
		if(m_pusPolygonFlags || pxCollMesh->m_pusPolygonFlags || m_usOverallFlags != pxCollMesh->m_usOverallFlags || uPolygonFlagsToAdd)
		{
			u_short* pusNewPolyFlags;
			Asura_ResourceAllocateArray(pusNewPolyFlags, uNewNumPolys);

			//copy from this mesh
			uPos = 0;
			for(u_int i=0;i<uOldNumPolys;i++,uPos++)
			{
				pusNewPolyFlags[uPos] = m_pusPolygonFlags ? m_pusPolygonFlags[i] : m_usOverallFlags;
			}
			//and the other mesh
			for(u_int i=0;i<pxCollMesh->m_uNumberOfPolys;i++,uPos++)
			{																		
				pusNewPolyFlags[uPos] = pxCollMesh->m_pusPolygonFlags ? pxCollMesh->m_pusPolygonFlags[i] : pxCollMesh->m_usOverallFlags;
				pusNewPolyFlags[uPos] |= (u_short) uPolygonFlagsToAdd;
			}

			m_usOverallFlags &= (pxCollMesh->m_usOverallFlags | (u_short)uPolygonFlagsToAdd);

			Asura_ResourceDeallocateArray(m_pusPolygonFlags);
			m_pusPolygonFlags = pusNewPolyFlags;
		}

		//deal with tools polygon flags
		if(m_puToolsPolygonFlags || pxCollMesh->m_puToolsPolygonFlags || m_uToolsOverallFlags != pxCollMesh->m_uToolsOverallFlags || uPolygonFlagsToAdd)
		{
			u_int* puNewPolyFlags;
			Asura_ResourceAllocateArray(puNewPolyFlags, uNewNumPolys);

			//copy from this mesh
			uPos = 0;
			for(u_int i=0;i<uOldNumPolys;i++,uPos++)
			{
				puNewPolyFlags[uPos] = m_puToolsPolygonFlags ? m_puToolsPolygonFlags[i] : m_uToolsOverallFlags;
			}
			//and the other mesh
			for(u_int i=0;i<pxCollMesh->m_uNumberOfPolys;i++,uPos++)
			{																		
				puNewPolyFlags[uPos] = pxCollMesh->m_puToolsPolygonFlags ? pxCollMesh->m_puToolsPolygonFlags[i] : pxCollMesh->m_uToolsOverallFlags;
				puNewPolyFlags[uPos] |= uPolygonFlagsToAdd;
			}

			m_uToolsOverallFlags &= (pxCollMesh->m_uToolsOverallFlags | uPolygonFlagsToAdd);

			Asura_ResourceDeallocateArray(m_puToolsPolygonFlags);
			m_puToolsPolygonFlags = puNewPolyFlags;
		}

		//deal with materials
		if(m_psMaterialIDs || pxCollMesh->m_psMaterialIDs || m_sOverallMaterial != pxCollMesh->m_sOverallMaterial)
		{
			short* psNewMaterials;
			Asura_ResourceAllocateArray(psNewMaterials, uNewNumPolys);

			//copy from this mesh
			uPos = 0;
			for(u_int i=0;i<uOldNumPolys;i++,uPos++)
			{
				psNewMaterials[uPos] = m_psMaterialIDs ? m_psMaterialIDs[i] : m_sOverallMaterial;
			}
			//and the other mesh
			for(u_int i=0;i<pxCollMesh->m_uNumberOfPolys;i++,uPos++)
			{
				psNewMaterials[uPos] = pxCollMesh->m_psMaterialIDs ? pxCollMesh->m_psMaterialIDs[i] : pxCollMesh->m_sOverallMaterial;
			}

			Asura_ResourceDeallocateArray(m_psMaterialIDs);
			m_psMaterialIDs = psNewMaterials;
		}

		//deal with material responses
		if(m_puMaterialResponseHashIDs || pxCollMesh->m_puMaterialResponseHashIDs || m_uOverallMaterialResponse != pxCollMesh->m_uOverallMaterialResponse)
		{
			Asura_Hash_ID* puNewMaterialResponses;
			Asura_ResourceAllocateArray(puNewMaterialResponses, uNewNumPolys);

			//copy from this mesh
			uPos = 0;
			for(u_int i=0;i<uOldNumPolys;i++,uPos++)
			{
				puNewMaterialResponses[uPos] = m_puMaterialResponseHashIDs ? m_puMaterialResponseHashIDs[i] : m_uOverallMaterialResponse;
			}
			//and the other mesh
			for(u_int i=0;i<pxCollMesh->m_uNumberOfPolys;i++,uPos++)
			{
				puNewMaterialResponses[uPos] = pxCollMesh->m_puMaterialResponseHashIDs ? pxCollMesh->m_puMaterialResponseHashIDs[i] : pxCollMesh->m_uOverallMaterialResponse;
			}

			Asura_ResourceDeallocateArray(m_puMaterialResponseHashIDs);
			m_puMaterialResponseHashIDs = puNewMaterialResponses;
		}

		//Expand the collision mesh so that includes all current vertices.
		//Not resetting the box completely here , so that the bounding box doesn't get shrunk
		//from having lost loose vertices that were being used to expand the original Asura_Meshes
		//(These vertices won't have made it into the collision mesh).
		//RAR 20/03/2006
		CalculateBoundingBox(false);
	}
	Asura_ResourcePopType();
}

#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_CollisionMesh::CalculateBoundingBox(bool bResetExisting)
{
	
	if(bResetExisting)
	{
		m_xBoundingBox.Reset();
	}
			
	u_int j = m_uNumberOfVertices;
	while(j--)
	{
		m_xBoundingBox.Set(m_pxVertices[j]);
	}
		
	Asura_Vector_3 xCentre =	m_xBoundingBox.CalculateCentre();

	Asura_Vector_3 xCorner(m_xBoundingBox.MaxX,m_xBoundingBox.MaxY,m_xBoundingBox.MaxZ);
	xCorner -= xCentre;
	m_fBoundingRadius = xCorner.Magnitude();

	ResetBB();
}
#endif // ASURA_TOOLS


#ifdef ASURA_TOOLS
void Asura_CollisionMesh::Rotate(const Asura_Quat& xQuat)
{
	for(u_int i=0;i<m_uNumberOfVertices;i++)
	{
		m_pxVertices[i] = xQuat.RotateVector(m_pxVertices[i]);
	}
	CalculateBoundingBox();
}
#endif
