//*******************************************************************
// File: Asura_PS3_Carpet.cpp
//
// Created: 26 January 2007 DLH
//
// Description: 
//
//*******************************************************************

// system libraries
#include "Core/Asura.h"

#if defined( ASURA_USE_CARPET)

// main header
#include "Carpet/Asura_PS3_Carpet.h"

#include <stdlib.h>

// other libraries
#include "Camera/Asura_Camera.h"
#include "Carpet/Asura_Carpet_MeshManager.h"
#include "Core/Asura_Mempool.h"
#include "Core/Asura_TextureFlags.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Light/Asura_Photon.h"
#include "Rendering/Asura_DIPCache.h"
#include "Rendering/Asura_Render.h"


#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/Asura_PS3_FrameBuffer.h"
#include "Rendering/Asura_PS3_GCM_IndexBuffer.h"
#include "Rendering/Asura_PS3_GCM_VertexBuffer.h"
#include "Rendering/Asura_PS3_Graphics.h"
#include "Rendering/Asura_PS3_RSXProfiling.h"
#include "Rendering/Asura_PS3_TextureManagement.h"

#include "Rendering/ShadersDefinition/VD/Carpet_VD.h"
#include "Rendering/ShadersDefinition/VP/Carpet_VP.h"
#include "Rendering/ShadersDefinition/FP/Carpet_FP.h"

static Asura_PS3_GCM_VertexShader*	g_pxVPCarpet			= NULL;
static Asura_PS3_FragmentShader*	g_pxFragmentShader		= NULL;
static Asura_PS3_Carpet_Handles*	g_pxHandles				= NULL;
// internal dependencies

//*******************************************************************
// Constants
//*******************************************************************

#if !defined( ASURA_CGR_MESH_VB_SIZE)
	#define ASURA_CGR_MESH_VB_SIZE 0x20000
#endif

#define SAFE_DELETE_ARRAY( X) { if( NULL != (X)){ delete[] (X); (X) = NULL;} }

//*******************************************************************
// Local Classes
//*******************************************************************

class Asura_PS3_CGR_VSConstants_Wave
{
public:
	float m_afWave[4];
};

class Asura_PS3_CGR_VSConstants_Base
{
public:
	float m_afTransform[4][4];
	float m_afBillboardRight[4];
	float m_afBillboardUp[4];
//	float m_afConstants1[4];
	float m_afCameraPlane[4];
	float m_afConstants2[4];
	float m_afCorners[4][4];
	
	Asura_PS3_CGR_VSConstants_Wave m_axWaves[16];
#if 0	//Not in use in the shader
	union
	{
		struct 
		{
			float m_afFogLinearPlane[4];
			float m_afFogLinearColour[4];
		};
		struct 
		{
			float m_afFogGraphPlane[4];
			float m_afFogGraphScale[2][4];
			float m_afFogGraphCoeffs[2][4];
		};
	};
	
	float m_afDynamicLightX[4];
	float m_afDynamicLightY[4];
	float m_afDynamicLightZ[4];
#endif
};

class Asura_PS3_CGR_VSConstants_Style
{
public:
	float m_afWidth, m_afHeight, m_afFadeStart, m_afFadeScale;
	float m_afUV[4];
};


static bool Asura_PS3_DIPCache_Render( const Asura_DIPCache_DIPData& xDIPData, const uint32 uCallbackData);

//*******************************************************************
// Local Data
//*******************************************************************
const uint32 Asura_PS3_CGR_PolygonStyle::uiInvalidStyleIndex = uint32( -1);

uint32 Asura_PS3_Carpet_Game_Render::s_uMeshesCount = 0;
Asura_PS3_CGR_Mesh* Asura_PS3_Carpet_Game_Render::s_pxMeshes = NULL;

uint32 Asura_PS3_Carpet_Game_Render::s_uTimeStampLast = 0;

int Asura_PS3_Carpet_Game_Render::s_iDrawCacheTextureID = ASURA_TEXTURE_INVALID;
int Asura_PS3_Carpet_Game_Render::s_iAttenuationMap = ASURA_TEXTURE_INVALID;

uint32 Asura_PS3_Carpet_Game_Render::s_uBatchIndexBufferSize = 6 * u_int(0x10000 / 6);
Asura_PS3_GCM_IndexBuffer Asura_PS3_Carpet_Game_Render::s_xBatchIndexBuffer;
Asura_PS3_GCM_VertexBuffer* Asura_PS3_Carpet_Game_Render::s_pxBatchVertexBuffer	 = NULL;

Asura_DIPCache_Static< 100> s_xDIPCache( Asura_PS3_DIPCache_Render, 0);

//*******************************************************************
// Function Definitions
//*******************************************************************

bool Asura_PS3_Carpet_Game_Render::RenderSetShaderConstantsStyles( const uint32 uiStylesCounts, const Asura_PS3_CGR_Style* pxStyles )
{
	float xDetails[ 20 ][ 4 ];
	float xUVs[ 20 ][ 4 ];

	for( uint32 i = 0; i < uiStylesCounts; ++i )
	{
		xDetails[ i ][ 0 ] = pxStyles[ i ].m_fWidth;
		xDetails[ i ][ 1 ] = pxStyles[ i ].m_fHeight;
		xDetails[ i ][ 2 ] = -1.0f / pxStyles[ i ].m_fLodFadeDelta;
		xDetails[ i ][ 3 ] = pxStyles[ i ].m_fLodFadeFar / pxStyles[ i ].m_fLodFadeDelta;
		xUVs[ i ][ 0 ] = pxStyles[ i ].m_afU[0];
		xUVs[ i ][ 1 ] = pxStyles[ i ].m_afV[0];
		xUVs[ i ][ 2 ] = pxStyles[ i ].m_afU[1] - pxStyles[ i ].m_afU[0];
		xUVs[ i ][ 3 ] = pxStyles[ i ].m_afV[1] - pxStyles[ i ].m_afV[0];
	}

	g_pxVPCarpet->SetUniformParameter( VP_Details, xDetails[ 0 ], uiStylesCounts );
	g_pxVPCarpet->SetUniformParameter( VP_UV, xUVs[ 0 ], uiStylesCounts );

	float xMovementScale[4] = {pxStyles->m_fMovementScale,0,0,0};
	g_pxVPCarpet->SetUniformParameter( VP_MovementScale, xMovementScale);

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::RenderSetShaderConstantsStatic( const Asura_Carpet_Mesh_Data_Frame& xDataFrame, const Asura_Photon* const pxPhoton)
{
	Asura_PS3_CGR_VSConstants_Base xVSConstantsBase;
	
	const Asura_AMatrix4x4& xMat = Asura_PS3_Graphics::GetViewProjectionMatrix();
	Asura_AMatrix4x4 xTranspose( xMat );
	xTranspose.Transpose();

	xVSConstantsBase.m_afBillboardRight[0] = xDataFrame.m_xBillboardRight[0];
	xVSConstantsBase.m_afBillboardRight[1] = xDataFrame.m_xBillboardRight[1];
	xVSConstantsBase.m_afBillboardRight[2] = xDataFrame.m_xBillboardRight[2];
	xVSConstantsBase.m_afBillboardRight[3] = 0.5f;

	xVSConstantsBase.m_afBillboardUp[0] = xDataFrame.m_xBillboardUp[0];
	xVSConstantsBase.m_afBillboardUp[1] = xDataFrame.m_xBillboardUp[1];
	xVSConstantsBase.m_afBillboardUp[2] = xDataFrame.m_xBillboardUp[2];
	xVSConstantsBase.m_afBillboardUp[3] = 1.0f;

	xVSConstantsBase.m_afCameraPlane[0] = xDataFrame.m_xCameraForwards[0];
	xVSConstantsBase.m_afCameraPlane[1] = xDataFrame.m_xCameraForwards[1];
	xVSConstantsBase.m_afCameraPlane[2] = xDataFrame.m_xCameraForwards[2];
	xVSConstantsBase.m_afCameraPlane[3] = xDataFrame.m_fCameraPlaneDistance;

	xVSConstantsBase.m_afConstants2[0] = 0x100;
	xVSConstantsBase.m_afConstants2[1] = 2 * 0x100;
	xVSConstantsBase.m_afConstants2[2] = 0;
	xVSConstantsBase.m_afConstants2[3] = 0;

	xVSConstantsBase.m_afCorners[0][0] = -0.5;
	xVSConstantsBase.m_afCorners[0][1] = +1.0;
	xVSConstantsBase.m_afCorners[0][2] = +0.0;
	xVSConstantsBase.m_afCorners[0][3] = +0.0;

	xVSConstantsBase.m_afCorners[1][0] = +0.5;
	xVSConstantsBase.m_afCorners[1][1] = +1.0;
	xVSConstantsBase.m_afCorners[1][2] = +1.0;
	xVSConstantsBase.m_afCorners[1][3] = +0.0;

	xVSConstantsBase.m_afCorners[2][0] = +0.5;
	xVSConstantsBase.m_afCorners[2][1] = +0.0;
	xVSConstantsBase.m_afCorners[2][2] = +1.0;
	xVSConstantsBase.m_afCorners[2][3] = +1.0;

	xVSConstantsBase.m_afCorners[3][0] = -0.5;
	xVSConstantsBase.m_afCorners[3][1] = +0.0;
	xVSConstantsBase.m_afCorners[3][2] = +0.0;
	xVSConstantsBase.m_afCorners[3][3] = +1.0;

	const uint32 uiWavesCount = sizeof( xVSConstantsBase.m_axWaves) / sizeof( xVSConstantsBase.m_axWaves[ 0]);
	for( uint32 uiWaveIndex = 0; uiWaveIndex < uiWavesCount; ++uiWaveIndex )
	{
		Asura_PS3_CGR_VSConstants_Wave& xWave = xVSConstantsBase.m_axWaves[ uiWaveIndex];

		xWave.m_afWave[0] = xDataFrame.m_afWaveX[ uiWaveIndex];
		xWave.m_afWave[1] = xDataFrame.m_afWaveY[ uiWaveIndex];
		xWave.m_afWave[2] = xDataFrame.m_afWaveZ[ uiWaveIndex];
		xWave.m_afWave[3] = 0;
	}

    const Asura_Vector_3* pxCamPos = Asura_Camera::GetPosition();

	g_pxVPCarpet->SetUniformParameter( VP_CameraPosition, &pxCamPos->x );
	g_pxVPCarpet->SetUniformParameter( VP_Transform, xTranspose.GetElements(), 4 );
	g_pxVPCarpet->SetUniformParameter( VP_BillboardRight, xVSConstantsBase.m_afBillboardRight );
	g_pxVPCarpet->SetUniformParameter( VP_BillboardUp, xVSConstantsBase.m_afBillboardUp );
	g_pxVPCarpet->SetUniformParameter( VP_CameraPlane, xVSConstantsBase.m_afCameraPlane );
	g_pxVPCarpet->SetUniformParameter( VP_Constants2, xVSConstantsBase.m_afConstants2 );
	g_pxVPCarpet->SetUniformParameter( VP_Corners, xVSConstantsBase.m_afCorners[ 0 ], 4 );
	g_pxVPCarpet->SetUniformParameter( VP_Waves, xVSConstantsBase.m_axWaves->m_afWave, uiWavesCount );

#if 0 //Not in use by the shader
	{
		const float fStrenght = 0;
		const float fStart = 1;
		const float fEnd = 2;
		const float fScale = ( fStart != fEnd) ? (1.0f / (fEnd - fStart)) : 0;

		Asura_Vector_3 xFogPlane = xDataFrame.m_xCameraForwards;
		xFogPlane.Normalise();
		xFogPlane *= fScale;
		
		Asura_Vector_3 xFogPoint = (xDataFrame.m_xCameraForwards * fStart) + xDataFrame.m_xCameraPosition;
		const float fPlaneDistance = xFogPoint * xFogPlane;
		
		xVSConstantsBase.m_afFogLinearPlane[0] = xFogPlane.GetX();
		xVSConstantsBase.m_afFogLinearPlane[1] = xFogPlane.GetY();
		xVSConstantsBase.m_afFogLinearPlane[2] = xFogPlane.GetZ();
		xVSConstantsBase.m_afFogLinearPlane[3] = -fPlaneDistance;
		
		xVSConstantsBase.m_afFogLinearColour[0] = -(fStrenght);
		xVSConstantsBase.m_afFogLinearColour[1] = 1.0f;
		xVSConstantsBase.m_afFogLinearColour[2] = 0.75f;
		xVSConstantsBase.m_afFogLinearColour[3] = 0.5f;
	}
	
	if( NULL == pxPhoton)
	{
		xVSConstantsBase.m_afDynamicLightX[0] = 0;
		xVSConstantsBase.m_afDynamicLightX[1] = 0;
		xVSConstantsBase.m_afDynamicLightX[2] = 0;
		xVSConstantsBase.m_afDynamicLightX[3] = 0;
		
		xVSConstantsBase.m_afDynamicLightY[0] = 0;
		xVSConstantsBase.m_afDynamicLightY[1] = 0;
		xVSConstantsBase.m_afDynamicLightY[2] = 0;
		xVSConstantsBase.m_afDynamicLightY[3] = 0;
		
		xVSConstantsBase.m_afDynamicLightZ[0] = 0;
		xVSConstantsBase.m_afDynamicLightZ[1] = 0;
		xVSConstantsBase.m_afDynamicLightZ[2] = 0;
		xVSConstantsBase.m_afDynamicLightZ[3] = 0;
	}
	else
	{
		const Asura_Photon& xPhoton = *pxPhoton;
		
		Asura_Vector_3 xPosition = xPhoton.GetPosition();
		const float fRange = xPhoton.GetRange();
		const float fScale = 1.0f / fRange;
		const float fScale2 = 0.5f * fScale;
		
		xVSConstantsBase.m_afDynamicLightX[0] = fScale2;
		xVSConstantsBase.m_afDynamicLightX[1] = 0;
		xVSConstantsBase.m_afDynamicLightX[2] = 0;
		xVSConstantsBase.m_afDynamicLightX[3] = 0.5f - (fScale2 * xPosition.GetX());
		
		xVSConstantsBase.m_afDynamicLightZ[0] = 0;
		xVSConstantsBase.m_afDynamicLightZ[1] = fScale2;
		xVSConstantsBase.m_afDynamicLightZ[2] = 0;
		xVSConstantsBase.m_afDynamicLightZ[3] = 0.5f - (fScale2 * xPosition.GetY());
		
		xVSConstantsBase.m_afDynamicLightY[0] = 0;
		xVSConstantsBase.m_afDynamicLightY[1] = 0;
		xVSConstantsBase.m_afDynamicLightY[2] = fScale2;
		xVSConstantsBase.m_afDynamicLightY[3] = 0.5f - (fScale2 * xPosition.GetZ());
		
	
		{
			const float fBrightnessScale = 1.0f / 4.0f;
			float afColour[4];
			
			afColour[0] = fBrightnessScale * xPhoton.GetRedComponent();
			afColour[1] = fBrightnessScale * xPhoton.GetGreenComponent();
			afColour[2] = fBrightnessScale * xPhoton.GetBlueComponent();
			afColour[3] = 1.0f;

			xResult = Asura_PS3_Graphics::GetDevice()->SetPixelShaderConstantF( 0, afColour, 1);
		}
	}
#endif

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::CreateVertexFormat()
{
	return( true);
}

bool Asura_PS3_Carpet_Game_Render::CreateVertexBufferMesh( Asura_PS3_CGR_Mesh& xCGRMesh)
{
	Asura_Assert( !xCGRMesh.m_xBatchVertexBuffer.IsInitialized(), "Vertex Buffer already setup!" );
	const uint32 uiVertexSize = sizeof( Asura_PS3_CGR_StreamVertexSimple );

	if( xCGRMesh.m_xBatchVertexBuffer.IsInitialized() )
	{
		return( false );
	}
	xCGRMesh.m_xBatchVertexBuffer.Initialise( uiVertexSize, xCGRMesh.m_uBatchVertexBufferSize, CELL_GCM_LOCATION_LOCAL, true );
	xCGRMesh.m_xVBRangeManager.SetMaxRange( xCGRMesh.m_uBatchVertexBufferSize );

	return( true );
}

bool Asura_PS3_Carpet_Game_Render::CreateVertexBuffer()
{
	for( uint32 uiMeshIndex = 0; uiMeshIndex < s_uMeshesCount; uiMeshIndex += 1)
	{
		Asura_PS3_CGR_Mesh& xCGRMesh = s_pxMeshes[ uiMeshIndex];
		
		CreateVertexBufferMesh( xCGRMesh);
		
	}
	return( true);
}

bool Asura_PS3_Carpet_Game_Render::CreateIndexBuffer()
{
	{
		if( !s_xBatchIndexBuffer.IsInitialized() )
		{
			s_xBatchIndexBuffer.Initialise( s_uBatchIndexBufferSize, CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32, CELL_GCM_LOCATION_LOCAL, true );
		}
		uint32* pauVertexIndexData = NULL;
		pauVertexIndexData = (uint32*)s_xBatchIndexBuffer.LockForWriting();
	
		// DLH - 12 January 2006 16:07:27 - This should really be in an index buffer
		// DLH - 12 January 2006 16:07:13 - If this array has not been initialise then fill it with indices
		const uint32 uQuadCount = s_uBatchIndexBufferSize / 6;
		Asura_Assert( (6 * uQuadCount) == s_uBatchIndexBufferSize, "CGR Number of Indices is not a multiple of 6");
		for( uint32 uIndexQuad = 0; uIndexQuad < uQuadCount; ++uIndexQuad )
		{
			const uint32 uIndexVertex = 4 * uIndexQuad;
			const uint32 uIndexIndex = 6 * uIndexQuad;			

			uint32* paxIndex = &pauVertexIndexData[ uIndexIndex];

			paxIndex[ 0] = uIndexVertex + 0;
			paxIndex[ 1] = uIndexVertex + 1;
			paxIndex[ 2] = uIndexVertex + 3;
			paxIndex[ 3] = uIndexVertex + 1;
			paxIndex[ 4] = uIndexVertex + 3;
			paxIndex[ 5] = uIndexVertex + 2;
		}
		s_xBatchIndexBuffer.Unlock();
	}

	return( true);
}


bool Asura_PS3_Carpet_Game_Render::CreateDirect3DData()
{
	ClearDirect3DData();
	
	CreateVertexFormat();
	CreateIndexBuffer();
	CreateVertexBuffer();

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::ClearDirect3DData_AllBuffers()
{
	for( uint32 uiMeshIndex = 0; uiMeshIndex < s_uMeshesCount; uiMeshIndex += 1)
	{
		Asura_PS3_CGR_Mesh& xCGRMesh = s_pxMeshes[ uiMeshIndex];

		const uint32 uBatchCount = xCGRMesh.m_uPolygonBatchCount;
		for( uint32 uBatchIndex = 0; uBatchIndex < uBatchCount; uBatchIndex += 1)
		{
			Asura_PS3_CGR_PolygonBatch& xBatch = xCGRMesh.m_pxPolygonBatches[ uBatchIndex];

			BatchRemoveFromBuffers( xCGRMesh, xBatch);
		}

		Asura_Assert( 0 == xCGRMesh.m_xVBRangeManager.m_uRangeUsedCount, "Not all the vertex Batchs where deallocated");
		Asura_Assert( 0 == xCGRMesh.m_xVBRangeManager.m_uBlockUsedCount, "Not all the Batchs where deallocated");
	}

	return( true);	
}

bool Asura_PS3_Carpet_Game_Render::ClearDirect3DData()
{
	ClearDirect3DData_AllBuffers();

	for( uint32 uiMeshIndex = 0; uiMeshIndex < s_uMeshesCount; ++uiMeshIndex )
	{
		Asura_PS3_CGR_Mesh& xCGRMesh = s_pxMeshes[ uiMeshIndex ];
		xCGRMesh.m_xBatchVertexBuffer.Destroy();
	}

	s_xBatchIndexBuffer.Shutdown();
	s_iAttenuationMap = ASURA_TEXTURE_INVALID;
	return( true );
}

bool Asura_PS3_Carpet_Game_Render::ClearDataSet()
{
	Asura_PS3_Carpet_Game_Render::ClearDirect3DData();

	SAFE_DELETE_ARRAY( s_pxMeshes );
	s_uMeshesCount = 0;

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::BatchGetRequiredBufferSize( const Asura_PS3_CGR_Mesh& xCGRMesh, const Asura_PS3_CGR_PolygonBatch& xCGRBatch, uint32& uIndexCount, uint32& uVertexCount)
{
	uIndexCount = 0;	
	uVertexCount = 0;	

	const uint32 uPolygonFinish = xCGRBatch.m_uDataOffset + xCGRBatch.m_uDataSize;
	for( uint32 uPolygonIndex = xCGRBatch.m_uDataOffset; uPolygonIndex < uPolygonFinish; uPolygonIndex += 1)
	{
		Asura_Assert( uPolygonIndex <= xCGRMesh.m_uPolygonCount, "CGRMesh Polygon Index not in range");
		const Asura_PS3_CGR_Polygon& xPolygon = xCGRMesh.m_pxPolygons[ uPolygonIndex];

		const uint32 uInstanceCount = xPolygon.m_uInstanceCount;
		uIndexCount += 6 * uInstanceCount;
		uVertexCount += 4 * uInstanceCount;
	}

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::BatchCopyToBuffers( Asura_PS3_CGR_Mesh& xCGRMesh, const Asura_PS3_CGR_PolygonBatch& xCGRBatch)
{
	const uint32 uLockSize = xCGRBatch.m_uVertexBufferSize;
	if( 0 == uLockSize )
	{
		return( true);
	}

	if( !xCGRMesh.m_xBatchVertexBuffer.IsInitialized() )
	{
		CreateVertexBufferMesh( xCGRMesh);
	}

	Asura_PS3_GCM_VertexBuffer* const pxVertexBuffer = &xCGRMesh.m_xBatchVertexBuffer;
	Asura_PS3_CGR_StreamVertexSimple* pxVertex00 = NULL;
	pxVertex00 = (Asura_PS3_CGR_StreamVertexSimple*) pxVertexBuffer->LockForWriting();
	if( !pxVertex00 )
	{
		return( false);
	}

	const uint32 uLockOffset = xCGRBatch.m_uVertexBufferOffset;
	Asura_PS3_CGR_StreamVertexSimple* pxVertex0 = pxVertex00 + uLockOffset;

	uint32 uVertexSpace = xCGRBatch.m_uVertexBufferSize;
	Asura_PS3_CGR_StreamVertexSimple* pxVertex = pxVertex0;
	const uint32 uPolygonCount = xCGRBatch.m_uDataSize;
	Asura_PS3_CGR_Polygon* pxCGRPolygon = &xCGRMesh.m_pxPolygons[ xCGRBatch.m_uDataOffset];
	for( uint32 uPolygonIndex = 0; uPolygonIndex < uPolygonCount; uPolygonIndex += 1, pxCGRPolygon += 1)
	{
		uint32 uVertexCount = uVertexSpace;

		CopyMeshPolygonToVertex( xCGRMesh, *pxCGRPolygon, pxVertex, uVertexCount);

		Asura_Assert( uVertexCount <= uVertexSpace, "CGR Too many vertices have been used");
		pxVertex += uVertexCount;
		uVertexSpace -= uVertexCount;
	}
	Asura_Assert( (pxVertex0 + xCGRBatch.m_uVertexBufferSize) == pxVertex, "CGR Vertex pointer anommily");
	Asura_Assert( 0 == uVertexSpace, "CGR Vertex count anommily!");

	pxVertexBuffer->Unlock();

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::BatchCopyAllToBuffers()
{
	const uint32 uMeshCount = s_uMeshesCount;
	for( uint32 uMeshIndex = 0; uMeshIndex < uMeshCount; uMeshIndex += 1)
	{
		Asura_PS3_CGR_Mesh& xCGRMesh	= s_pxMeshes[ uMeshIndex];
		const uint32 uBatchCount = xCGRMesh.m_uPolygonBatchCount;
		for( uint32 uBatchIndex = 0; uBatchIndex < uBatchCount; uBatchIndex += 1)	
		{
			const Asura_PS3_CGR_PolygonBatch& xCGRBatch = xCGRMesh.m_pxPolygonBatches[ uBatchIndex];

			BatchCopyToBuffers( xCGRMesh, xCGRBatch);
		}
	}
	return( true);
}

bool Asura_PS3_Carpet_Game_Render::BatchAddToBuffers( Asura_PS3_CGR_Mesh& xCGRMesh, Asura_PS3_CGR_PolygonBatch& xCGRBatch)
{
	xCGRBatch.m_uTimeStampLastUsed = s_uTimeStampLast;
	if( 0 != xCGRBatch.m_uVertexBufferSize)
	{
		return( true);
	}

	// DLH - 03 February 2006 12:38:54 - Find Required Buffer Sizes
	if( false == xCGRMesh.m_xVBRangeManager.AddBatch( s_uTimeStampLast, xCGRMesh, xCGRBatch))
	{
		return( false);
	}

	// DLH - 03 February 2006 12:39:16 - Copy Data To Buffers

	BatchCopyToBuffers( xCGRMesh, xCGRBatch);

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::BatchRemoveFromBuffers( Asura_PS3_CGR_Mesh& xCGRMesh, Asura_PS3_CGR_PolygonBatch& xCGRBatch)
{
	xCGRMesh.m_xVBRangeManager.RemoveBatch( xCGRBatch);

	return( true);
}

bool Asura_PS3_CGR_RangeManager::AddBatch( const uint32 uTimeStamp, Asura_PS3_CGR_Mesh& xCGRMesh, Asura_PS3_CGR_PolygonBatch& xCGRBatch)
{
	// DLH - 03 February 2006 12:38:54 - Find Required Buffer Sizes
	uint32 uIndexCount = 0;
	uint32 uVertexCount = 0;

	Asura_PS3_Carpet_Game_Render::BatchGetRequiredBufferSize( xCGRMesh, xCGRBatch, uIndexCount, uVertexCount);

	if( 0 == uVertexCount)
	{
		return( true);
	}

	// DLH - 03 February 2006 12:39:07 - Allocate Space
	uint32 uVertexOffset;

	bool bFoundSpace = FindFreeRange( xCGRMesh, &xCGRBatch, uVertexCount, uVertexOffset);
	if( false == bFoundSpace)
	{
		DeleteOldBatches( uTimeStamp);

		bFoundSpace = FindFreeRange( xCGRMesh, &xCGRBatch, uVertexCount, uVertexOffset);
		if( false == bFoundSpace)
		{
			Asura_Assert( false, "Failed to find room for Carpet Batch in Vertex Buffer. This will cause slown down and render problems. Increase the Size of ASURA_CGR_MESH_VB_SIZE.");
			return( false);
		}
	}

	xCGRBatch.m_uVertexBufferSize = uVertexCount;
	xCGRBatch.m_uVertexBufferOffset = uVertexOffset;
	xCGRBatch.m_uTimeStampLastUsed = uTimeStamp;

	this->m_uRangeUsedCount += xCGRBatch.m_uVertexBufferSize;
	this->m_uBlockUsedCount += 1;

	return( true);
}

bool Asura_PS3_CGR_RangeManager::RemoveBatch( Asura_PS3_CGR_PolygonBatch& xCGRBatch)
{
	if( 0 != xCGRBatch.m_uVertexBufferSize)
	{
		Asura_ListElt< Asura_PS3_CGR_PolygonBatch* >* pxIteratorBatch = this->m_xListBatches.Find( &xCGRBatch);
		Asura_Assert( NULL != pxIteratorBatch, "Missing Batch from Range Manager");
		if( NULL != pxIteratorBatch)
		{
			this->m_xListBatches.Remove( pxIteratorBatch);
		}

		Asura_Assert( 0 < this->m_uBlockUsedCount, "Trying to delete too many blocks");
		Asura_Assert( xCGRBatch.m_uVertexBufferSize <= this->m_uRangeUsedCount, "Trying to delete too many ranges");
		this->m_uBlockUsedCount -= 1;
		this->m_uRangeUsedCount -= xCGRBatch.m_uVertexBufferSize;

	}

	xCGRBatch.m_uVertexBufferSize = 0;
	xCGRBatch.m_uVertexBufferOffset = 0;


	return( true);
}

bool Asura_PS3_CGR_RangeManager::FindFreeRange( const Asura_PS3_CGR_Mesh& xCGRMesh, Asura_PS3_CGR_PolygonBatch* pxCGRBatch, const uint32 uRequiredSize, uint32& uFounfOffset)
{
	uint32 uOffsetLast = 0;

	for (Asura_ListElt< Asura_PS3_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
	{
		Asura_PS3_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		
		const uint32 uOffsetPresent = pxBatchSearch->m_uVertexBufferOffset;

		const uint32 uPresentSized = uOffsetPresent - uOffsetLast;
		if( uRequiredSize <= uPresentSized)
		{
			uFounfOffset = uOffsetLast;
			this->m_xListBatches.Insert_Before( pxIteratorBatch, pxCGRBatch);
			return( true);
		}

		uOffsetLast = uOffsetPresent + pxBatchSearch->m_uVertexBufferSize;
	}

	{
		const uint32 uPresentSized = this->m_uRangeMaxCount - uOffsetLast;
		if( uRequiredSize <= uPresentSized)
		{
			uFounfOffset = uOffsetLast;
			this->m_xListBatches.Push_Back( pxCGRBatch);
			return( true);
		}
	}

	return( false);
}

bool Asura_PS3_CGR_RangeManager::DeleteOldBatches( const uint32 uTimeStampLast)
{
	uint32 const uTimeStampDelete = uTimeStampLast - 2;

	Asura_LinkList< Asura_PS3_CGR_PolygonBatch*> xListDeleteBatches;

	{
		for (Asura_ListElt< Asura_PS3_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
		{
			Asura_PS3_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		

			if( 0 == pxBatchSearch->m_uVertexBufferSize)
			{
				continue;
			}

			const int32 uTimeStampDelta = pxBatchSearch->m_uTimeStampLastUsed - uTimeStampDelete;
			if( uTimeStampDelta < 0)
			{
				xListDeleteBatches.Push_Front( pxBatchSearch);
			}
		}
	}

	{
		for (Asura_ListElt< Asura_PS3_CGR_PolygonBatch*>* pxIteratorBatch = xListDeleteBatches.Front(); pxIteratorBatch; pxIteratorBatch = pxIteratorBatch->Next())
		{
			Asura_PS3_CGR_PolygonBatch* pxBatchSearch = pxIteratorBatch->Data();		

			RemoveBatch( *pxBatchSearch);
		}
	}

	return( true);
}

bool Asura_PS3_CGR_RangeManager::DeleteAllBatches()
{
	for (Asura_ListElt< Asura_PS3_CGR_PolygonBatch*>* pxIteratorBatch = this->m_xListBatches.Front(); NULL != pxIteratorBatch; pxIteratorBatch = this->m_xListBatches.Front())
	{
		Asura_PS3_CGR_PolygonBatch* pxBatch = pxIteratorBatch->Data();
		RemoveBatch( *pxBatch);
	}

	return( true);
}

static bool Asura_PS3_DIPCache_Render( const Asura_DIPCache_DIPData& xDIPData, const uint32 uCallbackData)
{
	const static uint32 uIndexMaxCount = Asura_PS3_Carpet_Game_Render::GetBatchIndexBufferSize();
	
	uint32 uIndexTotalCount = xDIPData.GetIndexBufferStop() - xDIPData.GetIndexBufferStart();
	uint32 uVertexBufferOffset = 0;
	while( 0 < uIndexTotalCount)
	{
		const uint32 uIndexCount = Asura_Maths::Min( uIndexMaxCount, uIndexTotalCount);
		const uint32 uiCountPrimitives = uIndexCount / 3;
		const uint32 uVertexCount = 2 * uiCountPrimitives;
		Asura_PS3_Carpet_Game_Render::GetVertexBuffer()->BindFromVertex( g_axVertexBufferDeclaration, VD_ELEMENTS, xDIPData.GetVertexBufferStart() + uVertexBufferOffset );
		Asura_PS3_Carpet_Game_Render::GetIndexBuffer()->RenderTriangles( uIndexCount );//xDIPData.GetIndexBufferStart() );
		Asura_PS3_Graphics::Flush();
		
		uIndexTotalCount -= uIndexCount;
		uVertexBufferOffset += uVertexCount;
	}
	
	return( true);
}

bool Asura_PS3_CGR_Camera::GetCurrentCamera( const float fFarPlane)
{
	const Asura_CameraData& xCurrentCamera = Asura_Camera::GetCameraData( 0 );
	
	this->m_fFarPlane			= fFarPlane;
	this->m_xOrientation		= xCurrentCamera.m_xOrientation;
	this->m_xPosition			= xCurrentCamera.m_xPosition;
	this->m_fOneOverCosHalfFovX	= xCurrentCamera.m_fOneOverCosHalfFovX;
	this->m_fOneOverCosHalfFovY	= xCurrentCamera.m_fOneOverCosHalfFovY;
	this->m_fTanHalfFovX		= xCurrentCamera.m_fTanHalfFovX;
	this->m_fYOverXAspectRatio	= xCurrentCamera.m_fYOverXAspectRatio;
	
	return( true);
}

Asura_PS3_CGR_RangeManager::Asura_PS3_CGR_RangeManager()
{
	this->m_uRangeMaxCount	= 0;
	this->m_uRangeUsedCount	= 0;
	this->m_uBlockUsedCount	= 0;
	//this->m_uBlockMaxCount= 0;
}

void Asura_PS3_CGR_RangeManager::Clear()
{
	this->m_uRangeUsedCount = 0;
	this->m_uBlockUsedCount = 0;
}

void Asura_PS3_CGR_RangeManager::SetMaxRange( const uint32 uMaxRange)
{
	this->m_uRangeMaxCount = uMaxRange;
}

Asura_PS3_CGR_RTreeNode::Asura_PS3_CGR_RTreeNode()
{
	this->m_xBoundingBox.Zero();
	this->m_pxChild		= NULL;
	this->m_pxSibling	= NULL;
	this->m_uData		= 0;
}

Asura_PS3_CGR_Vertex::Asura_PS3_CGR_Vertex()
{
	//this->m_xPosition;
	//this->m_aucColourARGB[ 4];
};

Asura_PS3_CGR_Polygon::Asura_PS3_CGR_Polygon()
{
	this->m_fLodFadeFar		= 0;
	this->m_fDenisty		= 0;
	this->m_uInstanceCount	= 0;
	this->m_uRandomSeed		= 0;
	
	this->m_uVertexIndex[0]	= 0;
	this->m_uVertexIndex[1]	= 0;
	this->m_uVertexIndex[2]	= 0;
};

Asura_PS3_CGR_PolygonBatch::Asura_PS3_CGR_PolygonBatch()
{
	this->m_uDataOffset			= 0;
	this->m_uDataSize			= 0;
	this->m_uVertexBufferOffset	= 0;
	this->m_uVertexBufferSize	= 0;
	this->m_uTimeStampLastUsed	= 0;
}

Asura_PS3_CGR_PolygonStyle::Asura_PS3_CGR_PolygonStyle()
{
};

Asura_PS3_CGR_Style::Asura_PS3_CGR_Style()
{
	this->m_fWidth			= 0.0f;
	this->m_fHeight			= 0.0f;
	this->m_afU[0]			= 0.0f;
	this->m_afV[0]			= 0.0f;
	this->m_afU[1]			= 0.0f;
	this->m_afV[1]			= 0.0f;
	this->m_fScaleMin		= 0.0f; // DLH - 0-255 encoded 0.0f-1.0f
	this->m_fLodFadeFar		= 0.0f;
	this->m_fLodFadeNear	= 0.0f;
	this->m_fLodFadeDelta	= 1e-5f;
}

Asura_PS3_CGR_Mesh::Asura_PS3_CGR_Mesh()
{
	this->m_xTextureHash		= 0;
	
	this->m_uVertexCount		= 0;
	this->m_uPolygonCount		= 0;
	this->m_uPolygonStyleCount	= 0;
	this->m_uStyleCount			= 0;
	this->m_uRTreeCount			= 0;
	this->m_uPolygonBatchCount	= 0;
	
	this->m_pxVertices			= NULL;
	this->m_pxPolygons			= NULL;
	this->m_pxPolygonStyles		= NULL;
	this->m_pxStyles			= NULL;
	this->m_pxRTree				= NULL;
	this->m_pxPolygonBatches	= NULL;
	
	this->m_uBatchVertexBufferSize = ASURA_CGR_MESH_VB_SIZE;
}

Asura_PS3_CGR_Mesh::~Asura_PS3_CGR_Mesh()
{
	this->m_xBatchVertexBuffer.Destroy();
	
	SAFE_DELETE_ARRAY( this->m_pxVertices);
	SAFE_DELETE_ARRAY( this->m_pxPolygons);
	SAFE_DELETE_ARRAY( this->m_pxPolygonStyles);
	SAFE_DELETE_ARRAY( this->m_pxStyles);
	SAFE_DELETE_ARRAY( this->m_pxRTree);
	SAFE_DELETE_ARRAY( this->m_pxPolygonBatches);
	
	this->m_uVertexCount			= 0;
	this->m_uPolygonCount			= 0;
	this->m_uPolygonStyleCount		= 0;
	this->m_uStyleCount				= 0;
	this->m_uRTreeCount				= 0;
	this->m_uPolygonBatchCount		= 0;
	this->m_uBatchVertexBufferSize	= 0;
//	this->m_uBatchVertexBufferPresentSize = 0;
}

//-------------------------------------------------------------------
// Function:    Asura_PS3_Carpet_Game_Render::GetCullStateOfSphereInFrustum
//
// Description: 
//
// Parameters:  xCentre         - 
//              fRadius         - 
//
// Returns:     Inside, outside or Touching the view frustum extents
//-------------------------------------------------------------------
Asura_PS3_Carpet_Game_Render::CullState Asura_PS3_Carpet_Game_Render::GetCullStateOfSphereInFrustum( const Asura_PS3_CGR_Camera& xCamera, const Asura_Vector_3& xCentre, const float fRadius)
{
	//const Asura_CameraData* pxCurrentCamera = Asura_Camera::GetCameraData();
	const Asura_Matrix_3x3& xOrientation = xCamera.m_xOrientation;

	const Asura_Vector_3 xViewSpacePos = xOrientation.ApplyTranspose( xCentre - xCamera.m_xPosition );

	const float fZFar = xViewSpacePos.z + fRadius;
	// is the sphere outside of the view frustum?
	if( fZFar < 0)
	{
		return( CULL_OUTSIDE);
	}

	const float fZNear = xViewSpacePos.z - fRadius;
	if( xCamera.m_fFarPlane < fZNear)
	{
		return( CULL_OUTSIDE);
	}

	const float fRX = fRadius * xCamera.m_fOneOverCosHalfFovX;
	const float fZX = xViewSpacePos.z * xCamera.m_fTanHalfFovX;

	// is the sphere outside of the left and right planes?
	if( fRX < (xViewSpacePos.x - fZX) || fRX < (-xViewSpacePos.x - fZX))
	{
		return( CULL_OUTSIDE);
	}

	const float fZY = fZX * xCamera.m_fYOverXAspectRatio;
	const float fRY = fRadius * xCamera.m_fOneOverCosHalfFovY;

	// is the sphere outside of the top and bottom planes?
	if ( fRY < (xViewSpacePos.y-fZY) || fRY < (-xViewSpacePos.y-fZY))
	{
		return( CULL_OUTSIDE);
	}

	// is the sphere inside of all the planes?
	if( 
		0 <= fZNear
		&&	
		fRX <= (fZX - xViewSpacePos.x)
		&& 
		fRX <= (xViewSpacePos.x - (-fZX))
		&&	
		fRY <= (fZY - xViewSpacePos.y)
		&& 
		fRY <= (xViewSpacePos.y - (-fZY))
		&&
		fZFar < xCamera.m_fFarPlane
		)
	{
		return( CULL_INSIDE);
	}


	return( CULL_TOUCHING);
}

Asura_PS3_Carpet_Game_Render::CullState Asura_PS3_Carpet_Game_Render::GetCullStateOfAABBInFrustum( const Asura_PS3_CGR_Camera& xCamera, const Asura_Bounding_Box& xBB )
{
	// KJFL - 09 May 2005 - overly conservative placeholder test 
	return( GetCullStateOfSphereInFrustum( xCamera, xBB.CalculateCentre(), xBB.CalculateRadius() ));
}

Asura_PS3_Carpet_Game_Render::CullState Asura_PS3_Carpet_Game_Render::GetCullStateOfAABBInFrustum( const Asura_PS3_CGR_Camera_And_Box& xCamera, const Asura_Bounding_Box& xBB )
{
	const bool bBoxIntersects = xCamera.m_xBoundingBox.Intersects( xBB);
	if( false == bBoxIntersects)
	{
		return( CULL_OUTSIDE);
	}

	const Asura_PS3_Carpet_Game_Render::CullState eCullStateFrustrum = GetCullStateOfSphereInFrustum( xCamera, xBB.CalculateCentre(), xBB.CalculateRadius() );
	if( CULL_OUTSIDE == eCullStateFrustrum)
	{
		return( CULL_OUTSIDE);
	}

	const bool bBoxContains = xCamera.m_xBoundingBox.Contains( xBB);

	if( CULL_INSIDE == eCullStateFrustrum)
	{
		if( true == bBoxContains)
		{
			return( CULL_INSIDE);
		}
	}

	return( CULL_TOUCHING);
}

//-------------------------------------------------------------------
// Function:    Asura_PS3_Carpet_Game_Render::CalcVisibleBatches
//
// Description: Fill in a list of uint32 values that are with in the view frustum
//
// Parameters:  
//				xDataFrame      - Not really used
//              pxRTree         - Root of RTree to search
//              puiBatchIndices - Pointer of array to place values into
//              uiBatchCount    - in: Size of Pointer array
//								- out: Count of uint32 values in the array
//
// Returns:     sucees == true
//				fail == false
//-------------------------------------------------------------------
template< class TvisibleBounds, bool bCheckVisability > bool Asura_PS3_Carpet_Game_Render::CalcVisibleBatches( const TvisibleBounds& xCamera, const Asura_PS3_CGR_RTreeNode* pxRTree, uint32* puiBatchIndices, uint32& uiBatchCount )
{
	const uint32 uiBatchMaxCount = uiBatchCount;

	uiBatchCount = 0;

	if( NULL == pxRTree)
	{
		return( true);
	}

	// DLH - 01 February 2006 16:27:49 - A stack of nodes that have to be processed
	const uint32 uiRTreeNodeToProcessMaxCount = 50;
	const Asura_PS3_CGR_RTreeNode* apxRTreeNodesToProcess[ 1 + uiRTreeNodeToProcessMaxCount];

#if defined ASURA_ASSERT || defined ASURA_TOOLS
	const Asura_PS3_CGR_RTreeNode** ppxRTreeNodeToProcessMax = &apxRTreeNodesToProcess[ uiRTreeNodeToProcessMaxCount];
#endif

	const Asura_PS3_CGR_RTreeNode** ppxRTreeNodeToProcess = apxRTreeNodesToProcess;

	// DLH - 01 February 2006 16:28:16 - Insert the root node for processing
	(*ppxRTreeNodeToProcess) = pxRTree;

	while( apxRTreeNodesToProcess <= ppxRTreeNodeToProcess)
	{
		// DLH - 01 February 2006 16:28:34 - Pop top of stack
		const Asura_PS3_CGR_RTreeNode* pxRTreeNodeProcess = *(ppxRTreeNodeToProcess);
		ppxRTreeNodeToProcess -= 1;

		if( NULL == pxRTreeNodeProcess)
		{
			continue;
		}

		Asura_Assert( (2 + ppxRTreeNodeToProcess) <= ppxRTreeNodeToProcessMax, "CGR Batch Node Buffer Overrun");
#if defined( ASURA_TOOLS)
		if( ppxRTreeNodeToProcessMax < (2 + ppxRTreeNodeToProcess))
		{
			continue;
		}
#endif

		// DLH - 01 February 2006 16:29:48 - Add the Next sibling for processing.
		ppxRTreeNodeToProcess += 1;
		(*ppxRTreeNodeToProcess) = pxRTreeNodeProcess->m_pxSibling;

		// DLH - 01 February 2006 16:30:06 - If this node is not visible then stop processing
		const CullState eIsAABBInFrustum = (true == bCheckVisability) ? GetCullStateOfAABBInFrustum( xCamera, pxRTreeNodeProcess->m_xBoundingBox) : CULL_TOUCHING;
		if( true == bCheckVisability)
		{
			if( CULL_OUTSIDE == eIsAABBInFrustum)
			{
				continue;
			}
			if( CULL_INSIDE == eIsAABBInFrustum)
			{
#if 1
				uint32 uBatchNewCount = uiBatchMaxCount - uiBatchCount;
				uint32* puiBatchNewIndices = puiBatchIndices + uiBatchCount;
				CalcVisibleBatches< TvisibleBounds, false>( xCamera, pxRTreeNodeProcess->m_pxChild, puiBatchNewIndices, uBatchNewCount);

				uiBatchCount += uBatchNewCount;
#endif
			}
		}

		if( false == bCheckVisability || CULL_TOUCHING == eIsAABBInFrustum)
		{
			ppxRTreeNodeToProcess += 1;
			(*ppxRTreeNodeToProcess) = pxRTreeNodeProcess->m_pxChild;
		}

		const uint32 uiMeshBatchIndex = pxRTreeNodeProcess->m_uData;
		if( uint32(-1) == uiMeshBatchIndex)
		{
			continue;
		}

		// DLH - 01 February 2006 16:30:25 - This node has data to batch it
		Asura_Assert( uiBatchCount < uiBatchMaxCount, "CGR Batch List Overrun");
#if defined( ASURA_TOOLS)
		if( uiBatchMaxCount <= uiBatchCount)
		{
			return( false);
		}
#endif
		puiBatchIndices[ uiBatchCount] = uiMeshBatchIndex;
		uiBatchCount += 1;
	}

	return( true);	
}

bool Asura_PS3_Carpet_Game_Render::CopyMeshPolygonToVertex( const Asura_PS3_CGR_Mesh& xCGRMesh, const Asura_PS3_CGR_Polygon& xCGRPolygon, Asura_PS3_CGR_StreamVertexSimple* pxVertex0, uint32& uVertexOriginalMaxCount )
{
	uVertexOriginalMaxCount = 0;

	Asura_PS3_CGR_StreamVertexSimple* pxVerts = pxVertex0;
	const uint32 uiCGRPolygonStyle = xCGRPolygon.m_uPolygonStyle;
	Asura_Assertf( uiCGRPolygonStyle < xCGRMesh.m_uPolygonStyleCount, ("Polygon Style index (%d) is too high", uiCGRPolygonStyle));
	const Asura_PS3_CGR_PolygonStyle* pxCGRPolygonStyle = &xCGRMesh.m_pxPolygonStyles[ uiCGRPolygonStyle];

	// Reseed the random number generator with any value that's constant for this triangle.
	srand( xCGRPolygon.m_uRandomSeed);

	const uint32 uiCGRInstanceCount = xCGRPolygon.m_uInstanceCount;

	for( uint32 uiCGRInstanceIndex = 0; uiCGRInstanceIndex < uiCGRInstanceCount; uiCGRInstanceIndex += 1)
	{
		const uint32 uiStyleLookupIndex = uiCGRInstanceIndex % ASURA_PS3_CGR_POLYGONSTYLE_LOOKUP_LENGTH;
		const uint32 uiStyleIndex = pxCGRPolygonStyle->m_aucStyleLookup[ uiStyleLookupIndex];
		Asura_Assertf( uiStyleIndex < xCGRMesh.m_uStyleCount, ("Style index (%d) is out of range", uiStyleIndex));
		const Asura_PS3_CGR_Style* pxStyle = &xCGRMesh.m_pxStyles[ uiStyleIndex];

		// Pick a random point within the triangle (barycentric coords)
		float fEdgeA = ( (rand() & 255) / 255.0f);
		float fEdgeB = ( (rand() & 255) / 255.0f);

		if( (fEdgeA + fEdgeB) > 1.0f)
		{
			fEdgeA = 1.0f - fEdgeA;
			fEdgeB = 1.0f - fEdgeB;
		}

		float fEdgeC = 1.0f - (fEdgeA + fEdgeB);

		const uint32 uiCGRVertexIndexA = xCGRPolygon.m_uVertexIndex[ 0];
		const uint32 uiCGRVertexIndexB = xCGRPolygon.m_uVertexIndex[ 1];
		const uint32 uiCGRVertexIndexC = xCGRPolygon.m_uVertexIndex[ 2];

		const Asura_PS3_CGR_Vertex* pxVertices = xCGRMesh.m_pxVertices;
		const Asura_PS3_CGR_Vertex& xVertexA = pxVertices[ uiCGRVertexIndexA];
		const Asura_PS3_CGR_Vertex& xVertexB = pxVertices[ uiCGRVertexIndexB];
		const Asura_PS3_CGR_Vertex& xVertexC = pxVertices[ uiCGRVertexIndexC];
		Asura_Vector_3 xPositionCenter( (xVertexA.m_xPosition * fEdgeA) + (xVertexB.m_xPosition * fEdgeB) + (xVertexC.m_xPosition * fEdgeC));		


		{				
			// Add some motion
			const uint32 uiWaveIndex = (uiCGRInstanceIndex + xCGRPolygon.m_uRandomSeed) % ASURA_CARPET_MESH_DATA_FRAME_SIZE_WAVEX;

			Asura_Vector_3 xColour = (xVertexA.m_xColourRGB * fEdgeA) + (xVertexB.m_xColourRGB * fEdgeB) + (xVertexC.m_xColourRGB * fEdgeC);

			// Calc the scale for this 
			float fScale = (xVertexA.m_fDensity * fEdgeA) + (xVertexB.m_fDensity * fEdgeB) + (xVertexC.m_fDensity * fEdgeC);
			// We only scale then down to fMinScale, not 0
			fScale = ((1.0f - fScale) * pxStyle->m_fScaleMin) + fScale;
			const uint32 uiScale = uint32( 255.0f * fScale);

			// Position and colour
			for( uint32 uiCornerIndex = 0; uiCornerIndex < 4; uiCornerIndex += 1, pxVerts += 1, uVertexOriginalMaxCount += 1)
			{		
				Asura_PS3_CGR_StreamVertexSimple& xVertex = *pxVerts;

				xVertex.m_fX = xPositionCenter[0];
				xVertex.m_fY = xPositionCenter[1];
				xVertex.m_fZ = xPositionCenter[2];

				xVertex.m_afDiffuse[0] = xColour.GetX();
				xVertex.m_afDiffuse[1] = xColour.GetY();
				xVertex.m_afDiffuse[2] = xColour.GetZ();
				xVertex.m_afDiffuse[3] = 1.0f;
				xVertex.m_ucScale = static_cast<uint8>( uiScale );
				xVertex.m_ucCornerIndex = static_cast<uint8>( uiCornerIndex );
				xVertex.m_ucWaveTableIndex = static_cast<uint8>( uiWaveIndex );
				xVertex.m_ucStyleIndex = static_cast<uint8>( uiStyleIndex );
			}
		}

	}

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::RenderMeshBatch( const Asura_PS3_CGR_Mesh& xCGRMesh, const Asura_PS3_CGR_PolygonBatch& xCGRBatch )
{
	if( 0 == xCGRBatch.m_uVertexBufferSize)
	{
		return( true);
	}

	{
		uint32 uVertexBufferStart = xCGRBatch.m_uVertexBufferOffset;
		uint32 uVertexBufferStop = xCGRBatch.m_uVertexBufferOffset + xCGRBatch.m_uVertexBufferSize;
		uint32 uIndexBufferStart = (6 * (xCGRBatch.m_uVertexBufferOffset / 4));
		uint32 uIndexBufferStop = uIndexBufferStart + (6 * (xCGRBatch.m_uVertexBufferSize / 4));

		Asura_DIPCache_DIPData xData;

		xData.Set( uIndexBufferStart, uIndexBufferStop, uVertexBufferStart, uVertexBufferStop);

		s_xDIPCache.AddDIP( xData);
	}

	return( true);
}

template< class Tcamera> bool Asura_PS3_Carpet_Game_Render::RenderMesh( const Asura_Carpet_Mesh_Data_Frame& xDataFrame, const Tcamera& tCamera, Asura_PS3_CGR_Mesh& xCGRMesh )
{
	// DLH - 03 February 2006 15:25:06 - Draw a batch at a time using the tree culling and Batching
#if defined( ASURA_CARPET_MESHMANAGER_PROFILE)	
	int iOverallTimer	= Asura_Profiling_CreateTimer();

	Asura_Profiling_StartTiming( iOverallTimer );
#endif

	const uint32 uiBatchMaxCount = 0x200;
	uint32 uiBatchCount = uiBatchMaxCount;
	uint32 auiBatchIndices[ uiBatchMaxCount];

	{
		CalcVisibleBatches< Tcamera, true>( tCamera, xCGRMesh.m_pxRTree, auiBatchIndices, uiBatchCount);

#if defined( ASURA_CARPET_MESHMANAGER_PROFILE)	
		{
			const float fOverallTime = Asura_Timers::TicksToSeconds( Asura_Profiling_StopTiming( iOverallTimer ) );

			TOOLS_CARPET_STATISTICS_SET( s_fTimeRTree, fOverallTime);

			Asura_Profiling_DestroyTimer(iOverallTimer);
		}
#endif
		if( 0 == uiBatchCount)
		{
			return( true);
		}

		Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0123_ALL );
		SetTextureID( Asura_Texture_Management::GetIndexFromHashID( xCGRMesh.m_xTextureHash ) );

		if( !xCGRMesh.m_xBatchVertexBuffer.IsInitialized() )
		{
			CreateVertexBufferMesh( xCGRMesh);
		}

		s_xDIPCache.Clear();
		s_pxBatchVertexBuffer = &xCGRMesh.m_xBatchVertexBuffer;

		for( uint32 uiBatchIndicesIndex = 0; uiBatchIndicesIndex < uiBatchCount; uiBatchIndicesIndex += 1)
		{
			const uint32 uiBatchIndex = auiBatchIndices[ uiBatchIndicesIndex];
			Asura_Assert( uiBatchIndex < xCGRMesh.m_uPolygonBatchCount, "CGR Polygon Batch out of range");

			Asura_PS3_CGR_PolygonBatch& xCGRBatch = xCGRMesh.m_pxPolygonBatches[ uiBatchIndex];

			if( false == BatchAddToBuffers( xCGRMesh, xCGRBatch))
			{
				// DLH - Batch not empty, assume that there is not enough room in the VB so clear it out and continue
				s_xDIPCache.Render();
				s_xDIPCache.Clear();

				xCGRMesh.m_xVBRangeManager.DeleteAllBatches();

				if( false == BatchAddToBuffers( xCGRMesh, xCGRBatch))
				{
					continue;
				}
			}

			RenderMeshBatch( xCGRMesh, xCGRBatch);
		}

		s_xDIPCache.Render();
	}

	return( true);
}

bool Asura_PS3_Carpet_Game_Render::Render()
{
   ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__CARPET, "Carpet" );

	const Asura_Carpet_Mesh_Data_Frame xDataFrame( Asura_Timers::GetLocalScaledTime() );
	bool bResetRenderStates = false;

	{
		bool bRenderStatesInitialised = false;

		for( uint32 uiMeshIndex = 0; uiMeshIndex < s_uMeshesCount; ++uiMeshIndex )
		{
			Asura_PS3_CGR_Mesh& xCGRMesh = s_pxMeshes[ uiMeshIndex ];

			const bool bIsMeshVisible = Asura_Camera::IsAABBInFrustum( xCGRMesh.m_xBoundingBox );
			if( false == bIsMeshVisible )
			{
				continue;
			}

			if( false == bRenderStatesInitialised )
			{
				if( false == BeginRenderStatic( xDataFrame ) )
				{
					continue;
				}
				bResetRenderStates = true;
				bRenderStatesInitialised = true;
			}

			RenderSetShaderConstantsStyles( xCGRMesh.m_uStyleCount, xCGRMesh.m_pxStyles );

			Asura_PS3_CGR_Camera xCamera;

			xCamera.GetCurrentCamera( xCGRMesh.m_fLodFadeFar );

			RenderMesh( xDataFrame, xCamera, xCGRMesh );
		}

		EndRenderStatic();
	}

	if( true == bResetRenderStates )
	{
		EndRender();
	}

    ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__CARPET );

	return( true );
}

void Asura_PS3_Carpet_Game_Render::EndRenderStatic()
{
}

bool Asura_PS3_Carpet_Game_Render::BeginRenderStatic( const Asura_Carpet_Mesh_Data_Frame& xDataFrame)
{
	// Set some renderstates
	Asura_PS3_DeferredRendering::Bind();
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );
	Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
	Asura_PS3_RenderingStates::RequestAlphaTest( false ); //Not supported in multiple render target
	Asura_PS3_RenderingStates::RequestZBufferEnabled(true);
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled(true);
	g_pxVPCarpet->Bind();

	RenderSetShaderConstantsStatic( xDataFrame, NULL);

	s_iDrawCacheTextureID = ASURA_TEXTURE_INVALID;
	s_uTimeStampLast += 1;

	return( true);
}

void Asura_PS3_Carpet_Game_Render::EndRender()
{
	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_PS3_RenderingStates::RequestAlphaTest( false );
	Asura_PS3_RenderingStates::RequestZBufferEnabled( true );
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( true );
}

void Asura_PS3_Carpet_Game_Render::SetTextureID( const int iTextureID)
{
	if( iTextureID != s_iDrawCacheTextureID)
	{
		g_pxFragmentShader->SetTexture( FP_Texture, iTextureID );
		g_pxFragmentShader->Bind();
		s_iDrawCacheTextureID = iTextureID;
	}
}

Asura_Carpet_Mesh_Data_Frame::Asura_Carpet_Mesh_Data_Frame( const float fTime) :
m_fTime( fTime)
{	
	Asura_Vector_3 xRight;
	Asura_Camera::GetRightVector(xRight);	
	xRight.y = 0;
	xRight.Normalise();

	float fScaledTime = fTime * Asura_Carpet_MeshManager::GetMovementSpeed();

	const u_int uiSizeWaveX = ASURA_CARPET_MESH_DATA_FRAME_SIZE_WAVEX;
	for ( u_int i = 0; i < uiSizeWaveX; i++)
	{		
		float fS = (float)i;
		// DLH - 01 February 2006 16:21:02 - Added .0f to all constant values, requested by JGP
		// DLH - 01 February 2006 16:21:27 - Changed sin to Asura_Maths::Sine(), requested by JGP
		float fAmount = 0.1f  * Asura_Maths::Sine( (1.0f * fS) + fScaledTime * (1.0f + (fS * 0.05f)) )
			+ 0.05f * Asura_Maths::Sine( (2.0f * fS) + fScaledTime * (1.0f + ((16.0f - fS) * 0.05f)));		

		this->m_afWaveX[i] = fAmount * xRight.x;
		this->m_afWaveY[i] = fAmount * xRight.y;
		this->m_afWaveZ[i] = fAmount * xRight.z;
	}

	Asura_Camera::GetForwardVector( this->m_xCameraForwards);
	this->m_xCameraPosition = *Asura_Camera::GetPosition();

	// Use camera pos/fwd to get plane eq, and use this to get distance to centre 
	// of triangle
	this->m_fCameraPlaneDistance = -(this->m_xCameraForwards * this->m_xCameraPosition);

	// JAT 13 Feb 2006 - you don't need to do all this maths to get a view horizontal vector
	/*
	Asura_Matrix_3x3 xMat = *Asura_Camera::GetOrientation();
	Asura_Euler_3 xOri;
	xOri.FromMatrix(xMat);
	xOri.z = xOri.x = 0.0f;
	xOri.ToMatrix(xMat);
	*/
	// JAT 13 Feb 2006 - get the view x vector from the view matrix
	// might go wrong if the camera has roll on it
	const Asura_Matrix_3x3* const xView = Asura_Camera::GetOrientation();
	this->m_xBillboardRight.x = xView->e11;
	this->m_xBillboardRight.y = xView->e21;
	this->m_xBillboardRight.z = xView->e31;

	// DLH - 01 February 2006 16:21:02 - Added .0f to all constant values, requested by JGP
	this->m_xBillboardUp = Asura_Vector_3(0.0f,-1.0f,0.0f);
}

void Asura_Carpet_MeshManager::Platform_Initialise( void)
{
	g_pxVPCarpet		= new Asura_PS3_VP_Carpet();
	g_pxHandles			= new Asura_PS3_Carpet_Handles();
	g_pxFragmentShader	= new Asura_PS3_FragmentShader( (unsigned char *)&_binary_PS3_Carpet_FP_bin_start, (unsigned char *)&_binary_PS3_Carpet_FP_bin_end, g_pxHandles );
	g_pxVPCarpet->InitVertexBufferDeclaration( g_axVertexBufferDeclaration, VD_ELEMENTS );
}

void Asura_Carpet_MeshManager::Platform_Shutdown( void)
{
	Asura_PS3_Carpet_Game_Render::ClearDataSet();

	delete g_pxVPCarpet;
	delete g_pxFragmentShader;
	delete g_pxHandles;

	g_pxVPCarpet		= NULL;
	g_pxFragmentShader	= NULL;
	g_pxHandles			= NULL;
}

void Asura_Carpet_MeshManager::Platform_Reset( void )
{
	Asura_PS3_Carpet_Game_Render::ClearDataSet();
}

void Asura_Carpet_MeshManager::Platform_Render( float)
{
	if( Asura_PS3_Carpet_Game_Render::HasDataSet())
	{
		Asura_PS3_Carpet_Game_Render::Render();
	}
}


//*******************************************************************
//
// (c) 2006 Rebellion
//
// Warning: This computer program is protected by copyright law and 
// international treaties. Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent 
// possible under the law.
//
//*******************************************************************

#endif //defined( ASURA_USE_CARPET)
