
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Platform_Decal_System.cpp
// Author : Andy Tate
// Date   : 29-Jun-2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "SpecialFX/Asura_Decal_System.h"

// Asura header-files...
#include "Core/Asura.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_HalfFloat.h"
#include "Core/Asura_TextureManagement.h"
#include "Light/Asura_GI_Solution.h"
#include "SpecialFX/Asura_Decal_System.h"
#include "SpecialFX/Asura_Decal_Template.h"
#include "Rendering/Asura_Colour.h"
#include "Camera/Asura_Camera.h"

#include "Rendering/Asura_PS3_Graphics.h"
#include "Rendering/Asura_PS3_FrameBuffer.h"
#include "Rendering/Asura_PS3_RenderingOptions.h"
#include "Rendering/Asura_PS3_AABBRenderer.h"
#include "Rendering/Asura_PS3_Graphics.h"
#include "Rendering/Asura_PS3_RenderingOptions.h"
#include "Rendering/Asura_PS3_TextureManagement.h"
#include "Rendering/Shaders/Asura_PS3_GCM_ShadersLibrary.h"
#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/Asura_PS3_MaterialResponse_System.h"
#include "Rendering/Asura_PS3_RenderingStates.h"
#include "Rendering/Asura_PS3_GCM_IndexBuffer.h"
#include "Rendering/Asura_PS3_GCM_VertexBuffer.h"

// Game header-files...


#define VECTOR_TO_UINT( xVector )						\
(														\
  (static_cast< u_int >( xVector.x * 511.0f ) <<  0)	\
+ (static_cast< u_int >( xVector.y * 511.0f ) << 11)	\
+ (static_cast< u_int >( xVector.z * 511.0f ) << 22)	\
)

////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////
#define DECAL_VERTEX_SIZE	60

class PS3_DecalVertex
{
public:

	float			fX,fY,fZ;
	float			fU,fV;	
	uint32			uNormal;
	uint32			uTangent;
	uint32		    uBinormal;
	Asura_HalfFloat afSHRed[4];
	Asura_HalfFloat afSHGreen[4];
	Asura_HalfFloat afSHBlue[4];
	float			fCreationTime;
};

static Asura_PS3_GCM_VertexShader*		g_pxVertexShader				= NULL;
static Asura_PS3_GCM_FragmentShader*	g_pxFragmentShader				= NULL;

extern uint32_t _binary_PS3_Decals_MR_VP_bin_start;
extern uint32_t _binary_PS3_Decals_MR_VP_bin_end;

extern uint32_t _binary_PS3_Decals_MR_FP_bin_start;
extern uint32_t _binary_PS3_Decals_MR_FP_bin_end;

static Asura_PS3_VertexBufferDeclaration g_axVertexDeclaration[] =
{
	//	m_szName, m_uOffset, m_uType, m_uNumberOfCoords, m_ucAttributeIndex
	{ "IN.xPosition",		 0, CELL_GCM_VERTEX_F,		3, 0 },
	{ "IN.axUV",			12, CELL_GCM_VERTEX_F,		2, 0 },
	{ "IN.xNormal",			20, CELL_GCM_VERTEX_CMP,	1, 0 },
	{ "IN.xTangent",		24, CELL_GCM_VERTEX_CMP,	1, 0 },
	{ "IN.xBinormal",		28, CELL_GCM_VERTEX_CMP,	1, 0 },
	{ "IN.xRedSH",			32, CELL_GCM_VERTEX_SF,		4, 0 },
	{ "IN.xGreenSH",		40, CELL_GCM_VERTEX_SF,		4, 0 },
	{ "IN.xBlueSH",			48, CELL_GCM_VERTEX_SF,		4, 0 },
	{ "IN.fCreationTime",	56, CELL_GCM_VERTEX_F,		1, 0 },
};

enum VERTEX_DECLARATION
{
	VD_Position,
	VD_UV,

	VD_Normal,
	VD_Tangent,
	VD_Binormal,

	VD_RedSH,
	VD_GreenSH,
	VD_BlueSH,

	VD_CreationTime,

	VD_ELEMENTS
};

static Asura_PS3_GCM_VertexShaderUniform g_axVertexShaderUniform[] =
{
	//m_szName, m_usArraySize, m_sizeInFloat4ForOneElement, m_usRegister
	{ "g_xViewProjectionMatrix",	1, 4, NULL },
	{ "g_xCameraPosition",			1, 1, NULL },
	{ "g_xFadeValues",				1, 1, NULL },
};

enum VERTEX_SHADER_UNIFORM
{
	VS_ViewProjectionMatrix,
	VS_CameraPosition,
	VS_FadeValues
};

static Asura_PS3_GCM_FragmentShaderUniform g_axFragmentShaderUniform[] =
{
	//m_szName, m_usArraySize, m_sizeInFloatsForOneElement, m_usaOffsets
	{ "g_bHasNormalMaps",			1, 1, NULL },
	{ "g_bParallaxMapping",			1, 1, NULL },
	{ "g_bUnlit",					1, 1, NULL },
	{ "g_bSpecularMapping",			1, 1, NULL },

	{ "g_fParallaxMapScale",		1, 1, NULL },
	{ "g_fParallaxMapBias",			1, 1, NULL },
	{ "g_fSpecularReflectionPower",	1, 1, NULL },
};

enum FRAGMENT_SHADER_UNIFORM
{
	FP_HasNormalMaps,
	FP_ParallaxMapping,
	FP_Unlit,
	FP_SpecularMapping,

	FP_ParallaxMapScale,
	FP_ParallaxMapBias,
	FP_SpecularReflectionPower,
};

static Asura_PS3_GCM_TextureUnitElement g_axTextureUnits[] = 
{
	//m_szName, m_ucArraySize, m_cTextureUnit
	{ "axSampler0",		1, NULL },
	{ "axSampler1",		1, NULL },
	{ "axSampler2",		1, NULL },
};

enum TEXTURE_UNITS
{
	FP_Sampler0,
	FP_Sampler1,
	FP_Sampler2,
};

class Asura_PS3_VP_Decals : public Asura_PS3_GCM_VertexShader
{
public:
	Asura_PS3_VP_Decals() : Asura_PS3_GCM_VertexShader( (unsigned char *)&_binary_PS3_Decals_MR_VP_bin_start, (unsigned char *)&_binary_PS3_Decals_MR_VP_bin_end, g_axVertexShaderUniform, sizeof ( g_axVertexShaderUniform ) / sizeof ( Asura_PS3_GCM_VertexShaderUniform ) )
	{
	}
};

class Asura_PS3_FP_Decals : public Asura_PS3_GCM_FragmentShader
{
public:
	Asura_PS3_FP_Decals() : Asura_PS3_GCM_FragmentShader( (unsigned char *)&_binary_PS3_Decals_MR_FP_bin_start, (unsigned char *)&_binary_PS3_Decals_MR_FP_bin_end, g_axFragmentShaderUniform, sizeof( g_axFragmentShaderUniform ) / sizeof( Asura_PS3_GCM_FragmentShaderUniform), g_axTextureUnits, sizeof( g_axTextureUnits) / sizeof( Asura_PS3_GCM_TextureUnitElement ) )
	{
	}
};

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////
// Copied straight from Tools_Tangentspace.
static void Swap( float& xValue1, float& xValue2 ) { float xTemp = xValue1; xValue1 = xValue2; xValue2 = xTemp; }
static void Swap( Asura_Vector_3& xValue1, Asura_Vector_3& xValue2 ) { Asura_Vector_3 xTemp = xValue1; xValue1 = xValue2; xValue2 = xTemp; }

void Asura_Decal_System_CalculateTangentAndBinormal( const Asura_Vector_3* pxPositions,
													const Asura_Vector_2* pxTextureCoords,
													Asura_Vector_3& rxTangent,
													Asura_Vector_3& rxBinormal )
{
	float			afU [3];
	float			afV [3];
	Asura_Vector_3	axPositions [3];

	// Initialise the local data...
	for (int i = 0; i < 3; i++)
	{
		axPositions [i]	= pxPositions [i];
		afU [i]			= pxTextureCoords [i].x;
		afV [i]			= pxTextureCoords [i].y;
	}

	// *** calc the tangent vector ***
	{
		// first sort vertices based on v coord
		if (afV [0] < afV [1])
		{
			Swap	(afU [0],			afU [1]);
			Swap	(afV [0],			afV [1]);
			Swap	(axPositions [0],	axPositions [1]);
		}
		if (afV [0] < afV [2])
		{
			Swap	(afU [0],			afU [2]);
			Swap	(afV [0],			afV [2]);
			Swap	(axPositions [0],	axPositions [2]);
		}
		if (afV [1] < afV [2])
		{
			Swap	(afU [1],			afU [2]);
			Swap	(afV [1],			afV [2]);
			Swap	(axPositions [1],	axPositions [2]);
		}

		// find offset along edge2 to the 'middle' v coord
		float fLambda;
		float fDivisor = (afV [2] - afV [0]);

		if (Asura_Maths::Abs(fDivisor) < 0.0001f)
		{
			fLambda = 1.0f;
		}
		else
		{
			fLambda = (afV [1] - afV [0]) / fDivisor;
		}

		// find the vertex position associated with this
		Asura_Vector_3 xPos = (axPositions [2] - axPositions [0]) * fLambda + axPositions [0];

		// tangent is the ray from the middle vertex to the lerped vertex
		rxTangent = xPos - axPositions [1];

		// make sure tangent points in the right direction
		float fInterpolatedU = (afU [2] - afU [0]) * fLambda + afU [0];

		if (fInterpolatedU < afU [1])
		{
			rxTangent = -rxTangent;
		}
	}

	// *** calc the binormal vector ***
	{
		// first sort vertices based on u coord
		if (afU [0] < afU [1])
		{
			Swap	(afU [0],			afU [1]);
			Swap	(afV [0],			afV [1]);
			Swap	(axPositions [0],	axPositions [1]);
		}
		if (afU [0] < afU [2])
		{
			Swap	(afU [0],			afU [2]);
			Swap	(afV [0],			afV [2]);
			Swap	(axPositions [0],	axPositions [2]);
		}
		if (afU [1] < afU [2])
		{
			Swap	(afU [1],			afU [2]);
			Swap	(afV [1],			afV [2]);
			Swap	(axPositions [1],	axPositions [2]);
		}

		// find offset along edge2 to the 'middle' v coord
		float fLambda;
		float fDivisor = (afU [2] - afU [0]);

		if (Asura_Maths::Abs(fDivisor) < 0.0001f)
		{
			fLambda = 1.0f;
		}
		else
		{
			fLambda = (afU [1] - afU [0]) / fDivisor;
		}

		// find the vertex position associated with this
		Asura_Vector_3 xPos = (axPositions [2] - axPositions [0]) * fLambda + axPositions [0];

		// binormal is the ray form the middle vertex to the lerped vertex
		rxBinormal = xPos - axPositions [1];

		// make sure binormal points in the right direction
		float fInterpolatedV = (afV [2] - afV [0]) * fLambda + afV [0];

		if (fInterpolatedV > afV [1])
		{
			rxBinormal = -rxBinormal;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////


// ------------------------------------------------------------------------------------
// Asura_PS3_DecalBuffer 
// This stores all decals for one material type (which may be more than one decal template)
// Has a circular buffer of verts, and an index buffer that gets shifted down when full
// ------------------------------------------------------------------------------------
class Asura_PS3_DecalBuffer : public Asura_DecalBuffer
{
public:

	// These are created the first time a decal of this material type is created
	// This isn't ideal - but we can't pre-alloc them as we don't know how many 
	// decal templates there are.
	Asura_PS3_DecalBuffer(const Asura_Decal_Template* pxDecalTemplate)
	{
		const Asura_Decal_Material *pxMtl = Asura_Decal_System::Project_GetDecalMaterial(pxDecalTemplate->m_iDecalMaterialIndex);

		m_iDecalMaterialIndex	= pxDecalTemplate->m_iDecalMaterialIndex;
		m_iMaxDecals			= pxMtl->m_iMaxDecals;		
		m_apxDecals				= new Decal[ m_iMaxDecals ];
		m_iNumVerts				= m_iMaxDecals * 8;			// Worst-case is 8 verts in a decal
		m_iNumIndices			= m_iMaxDecals * 18;		// Worst-case is 18 indices for a decal (if all 8 verts are used)
		m_xVertexBuffer.Initialise( DECAL_VERTEX_SIZE, m_iNumVerts, CELL_GCM_LOCATION_LOCAL );
		m_xIndexBuffer.Initialise( m_iNumIndices );
		m_apxVerts				= ( PS3_DecalVertex* )m_xVertexBuffer.LockForWriting();		
		m_apxIndices			= ( uint16* )m_xIndexBuffer.LockForWriting();
		m_iFirstDecal			= 0;
		m_iNumDecals			= 0;
		m_iNextVertex			= 0;
		m_iNumDecalTris			= 0;									 
		m_pxNextDecalBuffer		= NULL;		
	}

	~Asura_PS3_DecalBuffer()
	{
		delete m_pxNextDecalBuffer;

		delete [] m_apxDecals;

		m_xVertexBuffer.Shutdown();
		m_xIndexBuffer.Shutdown();
	}

	class Decal
	{
	public:
		int		iFirstIndex;
		int		iNumIndices;		
		float	fCreationTime;
	};

	int							m_iMaxDecals;				// Max decals in this buffer
	int							m_iDecalMaterialIndex;		// Material used by this buffer	
	Decal*						m_apxDecals;				// Decal structs (circular buffer)
	int							m_iNextVertex;				// Next vertex to use (verts = another circular buffer)
	int							m_iNumVerts;
	int							m_iNumIndices;
	int							m_iFirstDecal;				// First decal struct in use
	int							m_iNumDecals;				// Num decals active	
	int							m_iNumDecalTris;			// Num decal triangles active
	PS3_DecalVertex*			m_apxVerts;					// Verts  
	uint16*						m_apxIndices;				// Indices
	Asura_PS3_GCM_VertexBuffer	m_xVertexBuffer;
	Asura_PS3_GCM_IndexBuffer	m_xIndexBuffer;
	Asura_PS3_DecalBuffer*		m_pxNextDecalBuffer;

	void AddDecal(const Asura_Decal_Vertex* pxStrip, const u_int uNumberOfVertices, const Asura_Decal_Template* pxDecalTemplate )
	{
		if (m_iNumDecals>=m_iMaxDecals)
		{
			// No room - flush the first one
			m_iNumDecalTris -= (m_apxDecals[m_iFirstDecal].iNumIndices/3);
			m_iFirstDecal++;
			if (m_iFirstDecal>=m_iMaxDecals)
			{
				m_iFirstDecal=0;
			}
			m_iNumDecals--;				
		}		

		// Is there space in the index buffer?
		int iFirstIndex = 0;
		if (m_iNumDecals>0)
		{		
			int iLastDecal = (m_iFirstDecal+m_iNumDecals-1);
			if (iLastDecal>=m_iMaxDecals)
			{
				iLastDecal-=m_iMaxDecals;
			}

			iFirstIndex = m_apxDecals[iLastDecal].iFirstIndex+
				m_apxDecals[iLastDecal].iNumIndices;

			// De we need to shift the index buffer down
			if (iFirstIndex + ((int)((uNumberOfVertices-2)*3)) >= m_iNumIndices)
			{					
				int iShiftDown = m_apxDecals[m_iFirstDecal].iFirstIndex;			
				int iSrc	   = iShiftDown;
				int iDst	   = 0;
				int iDecal	   = m_iFirstDecal;

				for(int i=0;i<m_iNumDecalTris*3;i++)
				{				
					m_apxIndices[iDst++]=m_apxIndices[iSrc++];
				}

				for(int i=0; i<m_iNumDecals; i++)
				{										
					m_apxDecals[iDecal].iFirstIndex-=iShiftDown;										
					iDecal++;
					if (iDecal>=m_iMaxDecals)
					{
						iDecal-=m_iMaxDecals;
					}
				}	

				iFirstIndex = m_apxDecals[iLastDecal].iFirstIndex+
					m_apxDecals[iLastDecal].iNumIndices;
			}
		}

		// Fill in the decal struct
		int iDecal = m_iFirstDecal+m_iNumDecals;			
		if (iDecal>=m_iMaxDecals)
		{
			iDecal-=m_iMaxDecals;
		}
		m_apxDecals[iDecal].iFirstIndex		= iFirstIndex;
		m_apxDecals[iDecal].iNumIndices		= (uNumberOfVertices-2)*3;
		m_apxDecals[iDecal].fCreationTime	= Asura_Timers::GetLocalScaledTime();
		m_iNumDecals++;


		// Fill in the verts+indices, include degenerates to join the strips		
		if ((m_iNextVertex+(int)uNumberOfVertices)>m_iNumVerts)
		{
			m_iNextVertex = 0;
		}

		// Compute the tangent/binormal
		// We take a single normal for the decal now, to avoid a discontinuity when the
		// decal is 'bent' over a polygon edge.
		Asura_Vector_3 xNormal,xTangent,xBinormal;
		xNormal = Asura_Decal_System::GetCurrentDecalNormal();
		xNormal.Normalise();
		{		
			// There's room for some optimization here...
			Asura_Vector_3 axPositions[3];
			Asura_Vector_2 axUVs[3];
			for(int i=0;i<3;i++)
			{			
				axPositions[i] = pxStrip[i].m_xPosition;
				axUVs[i].x	   = pxStrip[i].m_fU;
				axUVs[i].y     = 1.0f - pxStrip[i].m_fV;
			}
			Asura_Decal_System_CalculateTangentAndBinormal(axPositions, axUVs, xTangent, xBinormal);

			// The calculation above is incomplete, as it doesn't take the normal into account... so finish the job:
			xBinormal.Normalise();
			xTangent  = xBinormal ^  xNormal;
			xTangent.Normalise();
			xBinormal = xNormal ^ xTangent;
			xBinormal.Normalise();
		}


		// Converting from a strip to an indexed list for now - avoids messing with degens, and
		// may actually be faster?
		for(u_int i=2; i<uNumberOfVertices; i++)
		{	
			m_apxIndices[iFirstIndex++] = (uint16)(m_iNextVertex+i-2);			
			m_apxIndices[iFirstIndex++] = (uint16)(m_iNextVertex+i-1);			
			m_apxIndices[iFirstIndex++] = (uint16)(m_iNextVertex+i);			
		}

		// Get a GI sample
		Asura_SphericalHarmonics_Order2_RGB xSH;
		Asura_GI_Solution::GetStaticIrradiance(xSH,pxStrip[0].m_xPosition);

		// Fill in these clinically obese vertices...
		for(u_int i=0; i<uNumberOfVertices; i++)
		{						
			m_apxVerts[m_iNextVertex].fX = pxStrip[i].m_xPosition.x;
			m_apxVerts[m_iNextVertex].fY = pxStrip[i].m_xPosition.y;
			m_apxVerts[m_iNextVertex].fZ = pxStrip[i].m_xPosition.z;
			m_apxVerts[m_iNextVertex].fU = pxStrip[i].m_fU;
			m_apxVerts[m_iNextVertex].fV = pxStrip[i].m_fV;

			// (packing some of these values would be a good idea - at least on 360 - as PC support for packed verts seems poor)
			// Tangent frame
			m_apxVerts[m_iNextVertex].uNormal		= VECTOR_TO_UINT(xNormal);
			m_apxVerts[m_iNextVertex].uTangent		= VECTOR_TO_UINT(xTangent);
			m_apxVerts[m_iNextVertex].uBinormal		= VECTOR_TO_UINT(xBinormal);

			// Lighting
			m_apxVerts[m_iNextVertex].afSHRed[0]	= xSH.m_afRedCoefficients[1];			
			m_apxVerts[m_iNextVertex].afSHRed[1]	= xSH.m_afRedCoefficients[2];
			m_apxVerts[m_iNextVertex].afSHRed[2]	= xSH.m_afRedCoefficients[3];
			m_apxVerts[m_iNextVertex].afSHRed[3]	= xSH.m_afRedCoefficients[0];
			m_apxVerts[m_iNextVertex].afSHGreen[0]	= xSH.m_afGreenCoefficients[1];
			m_apxVerts[m_iNextVertex].afSHGreen[1]	= xSH.m_afGreenCoefficients[2];
			m_apxVerts[m_iNextVertex].afSHGreen[2]	= xSH.m_afGreenCoefficients[3];
			m_apxVerts[m_iNextVertex].afSHGreen[3]	= xSH.m_afGreenCoefficients[0];
			m_apxVerts[m_iNextVertex].afSHBlue[0]   = xSH.m_afBlueCoefficients[1];
			m_apxVerts[m_iNextVertex].afSHBlue[1]   = xSH.m_afBlueCoefficients[2];
			m_apxVerts[m_iNextVertex].afSHBlue[2]   = xSH.m_afBlueCoefficients[3];
			m_apxVerts[m_iNextVertex].afSHBlue[3]   = xSH.m_afBlueCoefficients[0];

			m_apxVerts[m_iNextVertex].fCreationTime = m_apxDecals[iDecal].fCreationTime;
			m_iNextVertex++;						
			if (m_iNextVertex>=m_iNumVerts) m_iNextVertex = 0;
		}				

		m_iNumDecalTris+= (uNumberOfVertices-2);
	}

	void Render(void)
	{
		const Asura_Decal_Material *pxMtl = Asura_Decal_System::Project_GetDecalMaterial(m_iDecalMaterialIndex);				
		float fCurrentTime = Asura_Timers::GetLocalScaledTime();
		float afFadeValues[4] = {fCurrentTime, pxMtl->m_fFadeInTime,pxMtl->m_fFadeOutTime, pxMtl->m_fDecalStayTime};
		if (pxMtl->m_fDecalStayTime<0)
		{
			// Not 'forever', but should be long enough?
			// (AAM - 17.01.2007) If this value is too large, PC cards
			// don't render properly. Some register overflow perhaps?
			afFadeValues[3] = 100000.0f;
		}

		// Remove faded-out decals here - so it doesn't keep rendering them invisibly.
		while (m_iNumDecals>0 && (fCurrentTime > m_apxDecals[m_iFirstDecal].fCreationTime + (afFadeValues[3]+pxMtl->m_fFadeOutTime)))
		{
			// Decal is faded out, remove it
			m_iNumDecalTris -= (m_apxDecals[m_iFirstDecal].iNumIndices/3);
			m_iFirstDecal++;
			if (m_iFirstDecal>=m_iMaxDecals)
			{
				m_iFirstDecal=0;
			}
			m_iNumDecals--;				
		}			

		g_pxVertexShader->Bind();

		Asura_PS3_Graphics::GetDeferredFrameBuffer()->RestoreBind();

		if( m_iNumDecals > 0 )
		{
			if (pxMtl->m_eBlendMode == ASURA_DECAL_BLENDMODE_MULTIPLYX2)
			{
				g_pxFragmentShader->SetBool( g_axFragmentShaderUniform, FP_Unlit, 0, true );
//				Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_DOUBLECOLOUR);
			}
			else
			{
				g_pxFragmentShader->SetBool( g_axFragmentShaderUniform, FP_Unlit, 0, false );
//				Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);								
			}

			Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);								
			g_pxVertexShader->SetUniformParameter( VS_FadeValues, afFadeValues );

			// Diffuse texture
			int iTextureChannel = g_axTextureUnits[ FP_Sampler0 ].m_cTextureUnit[ 0 ];
			Asura_Render::CurrentStates.RequestTextureIndex(iTextureChannel, pxMtl->m_iTextureIndex);

			// Normal map
			iTextureChannel = g_axTextureUnits[ FP_Sampler2 ].m_cTextureUnit[ 0 ];
			Asura_Render::CurrentStates.RequestTextureIndex(iTextureChannel, pxMtl->m_iNormalMapIndex);
			g_pxFragmentShader->SetBool( g_axFragmentShaderUniform, FP_HasNormalMaps, 0, (pxMtl->m_iNormalMapIndex!=ASURA_TEXTURE_NONE) );

			// Parallax?
			g_pxFragmentShader->SetBool( g_axFragmentShaderUniform, FP_ParallaxMapping, 0, pxMtl->m_bUseParallaxMapping );

			// Specular map
			iTextureChannel = g_axTextureUnits[ FP_Sampler1 ].m_cTextureUnit[ 0 ];
			Asura_Render::CurrentStates.RequestTextureIndex(iTextureChannel, pxMtl->m_iSpecularMapIndex);
			g_pxFragmentShader->SetBool( g_axFragmentShaderUniform, FP_SpecularMapping, 0, (pxMtl->m_iSpecularMapIndex!=ASURA_TEXTURE_NONE) );
			float xCameraPosition[3];
			xCameraPosition[0] = (( float* )Asura_Camera::GetPosition())[0];
			xCameraPosition[1] = (( float* )Asura_Camera::GetPosition())[1];
			xCameraPosition[2] = (( float* )Asura_Camera::GetPosition())[2];

			g_pxVertexShader->SetUniformParameter( VS_ViewProjectionMatrix, ( float* ) Asura_PS3_Graphics::GetViewProjectionMatrix() );
			g_pxVertexShader->SetUniformParameter( VS_CameraPosition, ( float* ) ( xCameraPosition ) );

			// Render them
			for(int i=0; i<Asura_Camera::GetNumberOfCameras(); i++)
			{
				Asura_Camera::SetCameraIndex(i);

				if (Asura_Camera::IsCameraActive())
				{
					Asura_Render::ApplyCameraSettings();
					int iFirstIndex = m_apxDecals[m_iFirstDecal].iFirstIndex;

					g_pxFragmentShader->Bind();
					m_xVertexBuffer.BindFromVertex( g_axVertexDeclaration, VD_ELEMENTS, iFirstIndex );
					m_xIndexBuffer.RenderTriangles( m_iNumDecalTris * 3 );
					Asura_PS3_Graphics::Flush();
				}
			}
		}

		if (m_pxNextDecalBuffer)
		{
			m_pxNextDecalBuffer->Render();
		}
	}
};


void Asura_Decal_System::Platform_Initialise( void )
{
	g_pxVertexShader	= new Asura_PS3_VP_Decals();
	g_pxFragmentShader	= new Asura_PS3_FP_Decals();

	g_pxVertexShader->InitVertexBufferDeclaration( g_axVertexDeclaration, VD_ELEMENTS );
}

void Asura_Decal_System::Platform_Reset( void )
{
}

void Asura_Decal_System::Platform_Shutdown( void )
{
	delete g_pxVertexShader;
	delete g_pxFragmentShader;
}

void Asura_Decal_Handler::Platform_AddStrip(const Asura_Decal_Vertex* pxStrip,
											u_int uNumVertices,
											const Asura_RGBA& xDiffuseRGBA,
											const Asura_Decal_Template* pxDecalTemplate )
{
	Asura_PS3_DecalBuffer* pxDecalBuffer = static_cast<Asura_PS3_DecalBuffer*>(m_pxFirstDecalBuffer);

	// Do we have a decal buffer for the current decal material?
	while (pxDecalBuffer)
	{
		if (pxDecalBuffer->m_iDecalMaterialIndex == pxDecalTemplate->m_iDecalMaterialIndex)
		{			
			pxDecalBuffer->AddDecal(pxStrip, uNumVertices, pxDecalTemplate);
			return;
		}
		pxDecalBuffer = pxDecalBuffer->m_pxNextDecalBuffer;
	}

	// Need to create a new buffer
	pxDecalBuffer = new Asura_PS3_DecalBuffer(pxDecalTemplate);
	pxDecalBuffer->m_pxNextDecalBuffer = static_cast<Asura_PS3_DecalBuffer*>(m_pxFirstDecalBuffer);
	m_pxFirstDecalBuffer = pxDecalBuffer;
	pxDecalBuffer->AddDecal(pxStrip,uNumVertices,pxDecalTemplate);
}

void Asura_Decal_Handler::Platform_Render (void)
{
	return;
	if (m_pxFirstDecalBuffer == 0) return;

	Asura_Render::CurrentStates.RequestAlphaTest( true, 1 );

	// Blend to the albedo, don't touch normal, depth or specular
	Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL_MRT13_NONE_MRT2_RGB );

	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );		
	Asura_Render::CurrentStates.RequestTextureOpMode( ASURA_TEXTUREOP_SINGLE_PASS );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( false );
	Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_NONE );
	Asura_Render::CurrentStates.RequestZBufferCompareMode( ASURA_ZBUFFERCMP_LESSEQUAL );		

	Asura_PS3_RenderingStates::SetUseDepthBias( true );
	Asura_PS3_RenderingStates::SetDepthBias( -0.01f, -1.0f );

	static_cast<Asura_PS3_DecalBuffer*>(m_pxFirstDecalBuffer)->Render();

	Asura_PS3_RenderingStates::SetUseDepthBias( false );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
	Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0123_ALL );
	Asura_PS3_Graphics::Flush();
}

/// End of file. ///
