//////////////////////////////////////////////////////////////////////////////
//
// Asura_Platform_Projector_System.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_PS3_Projector_System.h"

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "Projectors/Asura_Projector_System.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_DeferredShading.h"
#include "Rendering/Asura_Viewport.h"

// Engine platform-specific.
#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_MaterialResponse_System.h"
#include "Rendering/Asura_PS3_RenderingStates.h"
#include "Rendering/Asura_PS3_RSXProfiling.h"

//Shaders definition
#include "Rendering/ShadersDefinition/VD/ProjectedTexture_VD.h"
#include "Rendering/ShadersDefinition/VP/ProjectedTexture_VP.h"
#include "Rendering/ShadersDefinition/FP/ProjectedTexture_DynamicLight_FP.h"
#include "Rendering/ShadersDefinition/FP/ProjectedTexture_FakeLight_FP.h"
#include "Rendering/ShadersDefinition/FP/ProjectedTexture_SpotLight_FP.h"
#include "Rendering/ShadersDefinition/FP/ProjectedTexture_Standard_FP.h"
#include "Rendering/ShadersDefinition/FP/ClearAlpha_FP.h"

enum
{
	Projector_Shader_DynamicLight			= 1,
	Projector_Shader_DynamicLight_Detail,
	Projector_Shader_DynamicSpotlight,
	Projector_Shader_FakeLight,
	Projector_Shader_FakeLight_Detail,
	Projector_Shader_Standard,
	Projector_Shader_Standard_Detail
};

#define PS3_PROJECTOR_MAXIMUM_LIGHTS		512

void Asura_Projector_System::Platform_Initialise(void)								{Asura_PS3_ProjectorSystem::Initialise();}
void Asura_Projector_System::Platform_Shutdown(void)								{Asura_PS3_ProjectorSystem::Shutdown();}
void Asura_Projector_System::Platform_Reset(void)									{Asura_PS3_ProjectorSystem::Reset();}
void Asura_Projector_System::Platform_RenderProjector(Asura_Projector *pxProjector)	{Asura_PS3_ProjectorSystem::RenderProjector(pxProjector);}
void Asura_Projector_System::Platform_BeginRender(void)
{
	// optimize stencil culling for the tests we're about to perform
	Asura_PS3_Graphics::SwitchStencilCullMode( ASURA_PS3_STENCIL_EQUAL, ASURA_STENCIL_MASK_PHOTON, ASURA_STENCIL_MASK_PHOTON );
}
void Asura_Projector_System::Platform_EndRender(void)								{}

static Asura_PS3_GCM_VertexShader*		g_pxVertexShader					= NULL;
static Asura_PS3_FragmentShader*		g_pxFragmentShaderClearAlpha		= NULL;

#define ASURA_PROJECTORS_BUFFERS	8

static Asura_PS3_GCM_IndexBuffer		g_xIndexBuffer;
static Asura_PS3_GCM_VertexBuffer		g_axVertexBuffer[ ASURA_PROJECTORS_BUFFERS ];

static int								g_iCurrentLight						= 0;
static int								g_iCurrentBuffer					= 0;

class Asura_PS3_ProjectorSystem_Handles : public Asura_PS3_FP_Handles
{
public:
	Asura_PS3_ProjectorSystem_Handles() : Asura_PS3_FP_Handles( UE_ELEMENTS, TU_ELEMENTS )
	{}

	void InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
	{
		//Uniform parameters
		pxShader->InitUniformElement( UE_TwoOverScreen,					"g_xTwoOverScreen",					1 );
		pxShader->InitUniformElement( UE_CameraTanHalfFOV,				"g_xCameraTanHalfFOV",				1 );
		pxShader->InitUniformElement( UE_DepthAdjustment,				"g_xDepthAdjustment",				1 );
		pxShader->InitUniformElement( UE_NearPlane,						"g_xNearPlane",						1 );
		pxShader->InitUniformElement( UE_Falloff,						"g_xFalloff",						1 );
		pxShader->InitUniformElement( UE_DistanceFade,					"g_fDistanceFade",					1 );

		pxShader->InitUniformElement( UE_ProjectionMatrix,				"g_xProjectionMatrix",				4 );
		pxShader->InitUniformElement( UE_InvViewMatrix,					"g_xInvViewMatrix",					4 );
		pxShader->InitUniformElement( UE_DetailTextureScaleAndOffset,	"g_xDetailTextureScaleAndOffset",	1 );
		pxShader->InitUniformElement( UE_ProjectorPosition,				"g_xProjectorPosition",				1 );
		pxShader->InitUniformElement( UE_LightColour,					"g_xLightColour",					1 );
		pxShader->InitUniformElement( UE_ProjectionDirection,			"g_xProjectionDirection",			1 );
		pxShader->InitUniformElement( UE_NeutralColour,					"g_xNeutralColour",					1 );

		//Texture units
		pxShader->InitTextureUnit( TU_NormalMaterialBufferSampler,		"g_xNormalMaterialBufferSampler",	CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		pxShader->InitTextureUnit( TU_AlbedoSpecularBufferSampler,		"g_xAlbedoSpecularBufferSampler",	CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		pxShader->InitTextureUnit( TU_DepthBufferSampler,				"g_xDepthBufferSampler",			CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		pxShader->InitTextureUnit( TU_ProjectedTextureSampler,			"g_xProjectedTextureSampler",		CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR
			, true
			);
		pxShader->InitTextureUnit( TU_DetailTextureSampler,				"g_xDetailTextureSampler",			CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR
			, true
			);
		pxShader->InitTextureUnit( TU_MaterialsTexture,					"g_xMaterialsTextureSampler",		CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	}
};

void Asura_PS3_ProjectorSystem::BeginFrame(void)
{
}

void Asura_PS3_ProjectorSystem::EndFrame(void)
{
	g_iCurrentLight = 0;
	++g_iCurrentBuffer;
	g_iCurrentBuffer = g_iCurrentBuffer % ASURA_PROJECTORS_BUFFERS;
}

void Asura_PS3_ProjectorSystem::Initialise(void)
{
	g_pxVertexShader				= new Asura_PS3_VP_Projector();
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_DynamicLight,			(unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP0_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP0_bin_end - (unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP0_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_DynamicLight_Detail,	(unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP1_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP1_bin_end - (unsigned char *)&_binary_PS3_ProjTex_DynamicLight_FP1_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_DynamicSpotlight,		(unsigned char *)&_binary_PS3_ProjTex_SpotLight_FP_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_SpotLight_FP_bin_end - (unsigned char *)&_binary_PS3_ProjTex_SpotLight_FP_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_FakeLight,			(unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP0_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP0_bin_end - (unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP0_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_FakeLight_Detail,		(unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP1_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP1_bin_end - (unsigned char *)&_binary_PS3_ProjTex_FakeLight_FP1_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_Standard,				(unsigned char *)&_binary_PS3_ProjTex_Standard_FP0_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_Standard_FP0_bin_end - (unsigned char *)&_binary_PS3_ProjTex_Standard_FP0_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );
	Asura_PS3_GCM_ShadersLibrary::AddFP( static_cast<Asura_Hash_ID> ( ASURA_SHADER_HASH_PROJECTOR ), Projector_Shader_Standard_Detail,		(unsigned char *)&_binary_PS3_ProjTex_Standard_FP1_bin_start, ( (unsigned char *)&_binary_PS3_ProjTex_Standard_FP1_bin_end - (unsigned char *)&_binary_PS3_ProjTex_Standard_FP1_bin_start), new Asura_PS3_ProjectorSystem_Handles(), 1, true );

	g_pxFragmentShaderClearAlpha	= new Asura_PS3_FragmentShader( (unsigned char *)&_binary_PS3_DeferredPhoton_P0_FP_bin_start, (unsigned char *)&_binary_PS3_DeferredPhoton_P0_FP_bin_end, NULL );

	g_pxVertexShader->InitVertexBufferDeclaration( g_axVertexDeclaration, VD_ELEMENTS );


	const uint16 au16Indices[] =
	{
		0,1,3, 1,2,3, 
		0,4,1, 1,4,5,  
		1,5,6, 1,6,2,
		3,2,7, 2,6,7, 
		0,3,7, 0,7,4,
		4,7,5, 5,7,6
	};

	g_xIndexBuffer.Initialise( 36 );
	void* pBuffer = g_xIndexBuffer.LockForWriting();
	Asura_Mem::Copy( pBuffer, au16Indices, sizeof( au16Indices ) );
	g_xIndexBuffer.Unlock();

	const u_char uVtxSize = sizeof( Asura_Vector_3 );
	for( int i = 0; i < ASURA_PROJECTORS_BUFFERS; ++i )
	{
		g_axVertexBuffer[ i ].Initialise( uVtxSize, PS3_PROJECTOR_MAXIMUM_LIGHTS * 8, CELL_GCM_LOCATION_MAIN );
	}
}

void Asura_PS3_ProjectorSystem::Shutdown(void)
{
	Reset();

	for( int i = 0; i < ASURA_PROJECTORS_BUFFERS; ++i )
	{
		g_axVertexBuffer[ i ].Destroy();
	}
	g_xIndexBuffer.Shutdown();

	delete g_pxVertexShader;
	delete g_pxFragmentShaderClearAlpha;

	g_pxVertexShader				= NULL;
	g_pxFragmentShaderClearAlpha	= NULL;
}

void Asura_PS3_ProjectorSystem::Reset(void)
{	
}

bool g_bUseCorrectZTestForProjectors = true;

static void SetZTestAndCullMode( bool bInsideFrustum )
{
    if( g_bUseCorrectZTestForProjectors )
    {
	    if( bInsideFrustum )
	    {
		    Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW );
		    Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_GREATER);		
	    }
	    else
	    {	
		    Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );		
		    Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
	    }
    }
    else
    {
	    Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW );
	    Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_GREATER);
    }
}

void Asura_PS3_ProjectorSystem::RenderProjector( Asura_Projector *pxProjector )
{
	bool bUseDetail	= pxProjector->GetDetailTextureIndex() != ASURA_TEXTURE_NONE;
	Asura_PS3_FragmentShaderCopy*		pxFragmentShader	= NULL;

	if (Asura_DeferredShading::IsShowingProjectorFrustums())
	{	
		pxProjector->RenderFrustum();
	}

	// Is the projector in range?
	Asura_Vector_3 xVec = *Asura_Camera::GetPosition() - pxProjector->GetPosition();
	if( xVec.MagnitudeSquared() > ( ( pxProjector->GetDistFadeStart() + pxProjector->GetDistFadeRange() ) * ( pxProjector->GetDistFadeStart()+pxProjector->GetDistFadeRange() ) ) )
	{
		// out of range - don't draw anything
		return;
	}

	float fDistanceFade = 1.0f;
	float fDist = xVec.Magnitude();

	if ( fDist > pxProjector->GetDistFadeStart() )
	{ 
		fDistanceFade = 1.0f - ( ( fDist - pxProjector->GetDistFadeStart() ) / pxProjector->GetDistFadeRange() );
	}

	if (fDistanceFade<=0.0f)
	{
		return;
	}

//	u_int uiValue = Asura_PS3_RenderingStates::BlockRSX();
	Asura_PS3_DeferredRendering::BindAsSingleRT();
	Asura_PS3_RenderingStates::RequestZBufferEnabled( Asura_DeferredShading::IsUsingZBufferOptimization() );		
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( false );

	bool bCameraInFrustum = pxProjector->PointInFrustum( *Asura_Camera::GetPosition(), 1.0f ); //need to add a bit to avoid problems when near-plane intersects it.
	
	bool bShadows = false;

	const Asura_AMatrix4x4& xViewProjectionMatrix = Asura_PS3_Graphics::GetViewProjectionMatrix();
	const Asura_Vector_3* const pxCamPos = Asura_Camera::GetPosition();
	float afCamPos[4] = { pxCamPos->x, pxCamPos->y, pxCamPos->z, 1.0f };

	u_int uiVertSize = sizeof( Asura_Vector_3 );
	u_int uiCurrentVertex = ( g_iCurrentLight * 8 );
	char* xVerts = (char *) g_axVertexBuffer[ g_iCurrentBuffer ].LockForWriting();
	xVerts += uiCurrentVertex * uiVertSize;
	Asura_Mem::Copy( xVerts, pxProjector->GetVertices(), uiVertSize * 8 );

	//Move to the next light
	++g_iCurrentLight;
	if( g_iCurrentLight >= PS3_PROJECTOR_MAXIMUM_LIGHTS )
	{
		g_iCurrentLight = 0;
		++g_iCurrentBuffer;
		g_iCurrentBuffer = g_iCurrentBuffer % ASURA_PROJECTORS_BUFFERS;
	}

	// Set the neutral colour - this is the colour that will have no visible effect when
	// blended in the current blend mode - and set when outside the range of projection/falloff
	Asura_RGBA xNeutralColour( 0.0f, 0.0f, 0.0f, 0.0f );

	g_pxVertexShader->Bind();
	g_pxVertexShader->SetUniformParameter( VS_ViewProjectionMatrix, xViewProjectionMatrix.GetElements() );
	g_pxVertexShader->SetUniformParameter( VS_CameraPosition, afCamPos );

	g_axVertexBuffer[ g_iCurrentBuffer ].BindFromVertex( g_axVertexDeclaration, VD_ELEMENTS, uiCurrentVertex );

	//BW comment from the xbox360 version:
	// -----------------------------------------------------------------------------------------------------------------------
	// DMR - 06/08/07 - Stencil buffer optimization
	// The stencil mask is created before rendering shadows or the light itself, as it should help with both.
	// We only care about the Hi-Stencil, as the per-pixel stencil test is executed after the shader - so not an optimization
	//
	// It takes 2 passes to fill the Hi-Stencil - the first one draws back faces, setting the stencil to 1 on z-pass and 0 on
	// z-fail. The 2nd pass draws front faces, setting the hi-stencil if the stencil was previously set to 1 (i.e. if both
	// the front and back are visible => nothing to light)
	//
	// (The camera-inside-the-volume case needs a special-case in the 2nd pass - draws the back faces again, with z-testing off)
	//
	// -----------------------------------------------------------------------------------------------------------------------	
	bool bUseStencil = Asura_DeferredShading::GetUseStencilBuffer();

	Asura_PS3_FragmentShaderCopy *pxBlankFP = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_GLOBAL, ASURA_GLOBAL_SHADER_BLANK );

	if (bUseStencil)
	{		
		Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );
		Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_ALWAYS);		

		//BW still takes a while drawing this. mostly ROP utilization => vert shader? I think it should be quicker

		Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);

		Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_NONE );
		//Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);

		pxBlankFP->Bind();

		for (int iPass=0; iPass<2; iPass++)
		{
#ifdef ASURA_USE_GCM_REPLAY
			char szProfileName[256];
			sprintf( szProfileName, "Render Projector - stencil pass %d", iPass );
			ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, szProfileName );
#endif

			switch(iPass)
			{		
				//BW I do these in the opposite order to xbox, because it's a bit simpler - write 1 for front faces then overwrite 0 for backfaces.
			case 1:		
				// Back faces - the bits that should be excluded
				Asura_PS3_RenderingStates::SetStencilTestEnabled(true);
				Asura_PS3_RenderingStates::SetStencilFunction( ASURA_PS3_STENCIL_ALWAYS, ASURA_STENCIL_MASK_PHOTON, ASURA_STENCIL_MASK_PHOTON );
				Asura_PS3_RenderingStates::SetStencilMask( ASURA_STENCIL_MASK_PHOTON );
				Asura_PS3_RenderingStates::SetStencilOperation( ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_ZERO );

				Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW );
				Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
				break;

			case 0:
				// Front faces   - difference between this and backfaces is the area we need to mask.
				Asura_PS3_RenderingStates::SetStencilTestEnabled(true);
				Asura_PS3_RenderingStates::SetStencilFunction( ASURA_PS3_STENCIL_ALWAYS, ASURA_STENCIL_MASK_PHOTON, ASURA_STENCIL_MASK_PHOTON );
				Asura_PS3_RenderingStates::SetStencilMask( ASURA_STENCIL_MASK_PHOTON );

				Asura_PS3_RenderingStates::SetStencilOperation( ASURA_PS3_STENCIL_OPERATION_ZERO, ASURA_PS3_STENCIL_OPERATION_ZERO, ASURA_PS3_STENCIL_OPERATION_REPLACE );


				if (bCameraInFrustum)
				{
					//shouldn't we just draw a quad?
					Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW );
					Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_ALWAYS);
				}
				else
				{
					Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );
					Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
				}

				break;
			}			

			g_xIndexBuffer.RenderTriangles( 36, 0 );

#ifdef ASURA_USE_GCM_REPLAY
			ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );
#endif

		}

		Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
		Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );
		Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL );
	}



	if (bUseStencil)
	{		
		// read the stencil
		Asura_PS3_RenderingStates::SetStencilTestEnabled(true);
		Asura_PS3_RenderingStates::SetStencilFunction( ASURA_PS3_STENCIL_EQUAL, ASURA_STENCIL_MASK_PHOTON, ASURA_STENCIL_MASK_PHOTON );
		Asura_PS3_RenderingStates::SetStencilMask( ASURA_STENCIL_MASK_PHOTON );
		Asura_PS3_RenderingStates::SetStencilOperation( ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_KEEP, ASURA_PS3_STENCIL_OPERATION_KEEP );
	}

	SetZTestAndCullMode( bCameraInFrustum );


	Asura_PS3_FP_List_Tree* fpTree = Asura_PS3_GCM_ShadersLibrary::GetPermanentFPTree( ASURA_SHADER_HASH_PROJECTOR );


	if ( pxProjector->IsDynamicLight() || pxProjector->IsDynamicSpotlight() )
	{
		if ( pxProjector->IsDynamicLight() )
		{
			if( bUseDetail )
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_DynamicLight_Detail );
			}
			else
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_DynamicLight );
			}
		}
		else
		{
			pxFragmentShader = fpTree->GetShader( Projector_Shader_DynamicSpotlight );
		}


		if( pxProjector->GetShadowParameters().m_uFlags & ( ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS | ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS ) &&
			Asura_Shadow_System::LightHasActiveShadows( ASURA_SHADOW_LIGHTSOURCE_PROJECTOR, pxProjector->GetHandle() ) )
		{
			// Are there any active shadows for this light?
			// Set this flag, so we know to restore the renderstates
			bShadows = true;

			// First, clear the destination alpha to 0 for the area affected by our shadows
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALPHA );
			Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
			Asura_PS3_RenderingStates::RequestAlphaTest( false );
			SetZTestAndCullMode(bCameraInFrustum);

			g_pxFragmentShaderClearAlpha->Bind();
			g_axVertexBuffer[ g_iCurrentBuffer ].BindFromVertex( g_axVertexDeclaration, VD_ELEMENTS, uiCurrentVertex );
			g_xIndexBuffer.RenderTriangles( 36, 0 );

			// Then render the shadow mask into the destination alpha
			Asura_Shadow_System::BuildShadowMask(ASURA_SHADOW_LIGHTSOURCE_PROJECTOR, pxProjector->GetHandle());

			// Re-enable RGB writes
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL );
			SetZTestAndCullMode(bCameraInFrustum);
			g_pxVertexShader->Bind();
			g_pxVertexShader->SetUniformParameter( VS_ViewProjectionMatrix, xViewProjectionMatrix.GetElements() );
			g_pxVertexShader->SetUniformParameter( VS_CameraPosition, afCamPos );

			Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
			// Asura doesn't have a mode for this, so I'm changing it manually here, then restoring it when we're done
			cellGcmSetBlendFuncUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_ONE_MINUS_DST_ALPHA, CELL_GCM_ONE, CELL_GCM_ONE_MINUS_DST_ALPHA, CELL_GCM_ONE );
			Asura_PS3_RenderingStates::RequestAlphaTest( false );
        }
		else
		{
// somehow it's todo with this mode here.
// is this done after edge AA? reading back AA's alpha?
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING );
			xNeutralColour = Asura_RGBA( 0x000000, 0 );
		}
	}
	else
	{	
		if (pxProjector->IsFakeLight())
		{
			if( bUseDetail )
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_FakeLight_Detail );
			}
			else
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_FakeLight );
			}

			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING );
			xNeutralColour = Asura_RGBA(0x000000,0);
		}
		else
		{	
			if( bUseDetail )
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_Standard_Detail );
			}
			else
			{
				pxFragmentShader = fpTree->GetShader( Projector_Shader_Standard );
			}

			switch (pxProjector->GetBlendMode())
			{				
				case ASURA_PROJECTOR_BLEND_MULTIPLY:
					Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_COLOUR ); 
					xNeutralColour = Asura_RGBA( 0xffffff, 0 );
					break;

				case ASURA_PROJECTOR_BLEND_MULTIPLYX2:
					Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_DOUBLECOLOUR ); 
					xNeutralColour = Asura_RGBA( 0x808080, 0 );
					break;

				case ASURA_PROJECTOR_BLEND_ADD:
					Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING ); 
					xNeutralColour = Asura_RGBA( 0x000000, 0 );
					break;

				default:
					Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL ); 
					xNeutralColour = Asura_RGBA( 0x000000, 0 );
					break;
			}
		}
	}

	ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__PROJECTORS, "Render Projector" );

	float faFalloff[ 4 ] = { pxProjector->GetFalloffScale(), pxProjector->GetFalloffPower(), pxProjector->GetAngularFalloffOfs(), pxProjector->GetAngularFalloffScale() };

	pxFragmentShader->SetFloats( UE_Falloff,		4, faFalloff );
	pxFragmentShader->SetTexture( TU_NormalMaterialBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_NORMAL_MATERIAL ), false );
	if( pxProjector->IsFakeLight() )
	{
		pxFragmentShader->SetTexture( TU_AlbedoSpecularBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_CURRENT_FRONT_BUFFER ), false );
	}
	else
	{
		pxFragmentShader->SetTexture( TU_AlbedoSpecularBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetRTAsTexture( ASURA_DEFERRED_RENDER_TARGET_ALBEDO_SPECULAR ), false );
	}
	pxFragmentShader->SetTexture( TU_DepthBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetZBufferAsTexture(), false );

	pxFragmentShader->SetTexture( TU_ProjectedTextureSampler,	pxProjector->GetTextureIndex() );
	pxFragmentShader->SetTexture( TU_DetailTextureSampler,		pxProjector->GetDetailTextureIndex() );
	pxFragmentShader->SetTexture( TU_MaterialsTexture,			Asura_PS3_MaterialResponse_System::GetDataTexture(), false );

	// Near plane equation - used to calc distance for falloff
	// Needs to be in view space.
	Asura_Vector_3 xDir = Asura_Vector_3( 0.0f, 0.0f, 1.0f );
	xDir = xDir * pxProjector->GetOrient();
	Asura_Vector_3 xPoint = pxProjector->GetPosition() + ( xDir * pxProjector->GetMinRange() );

	float fRange = pxProjector->GetMaxRange() - pxProjector->GetMinRange();
	Asura_Matrix_3x3 xOri = pxProjector->GetOrient();
	Asura_Vector_3 xProjPos = pxProjector->GetPosition();

	const Asura_AMatrix4x4& xViewMatrix	= Asura_PS3_Graphics::GetViewMatrix();
	Asura_AMatrix4x4 xViewMatrixInv;
	Asura_AVector4 xNormal, xNormalWS( xDir.x, xDir.y, xDir.z, 0.0f );
	Asura_AVector4 xPointOnPlaneWS( xPoint.x, xPoint.y, xPoint.z, 1.0f ), xPointOnPlane;
	xViewMatrix.InvertOrthogonal( xViewMatrixInv );

	xPointOnPlane = xPointOnPlaneWS * xViewMatrix;
	xNormal = xNormalWS * xViewMatrix;

	float fD = - ( (xNormal.GetX() * xPointOnPlane.GetX() ) + ( xNormal.GetY() * xPointOnPlane.GetY() ) + ( xNormal.GetZ() * xPointOnPlane.GetZ() ) );

	Asura_AVector4 xPlane( xNormal.GetX() / fRange, xNormal.GetY() / fRange, xNormal.GetZ() / fRange, fD / fRange );

	Asura_AMatrix4x4 xProjMatrix, xOrient;
	Asura_AMatrix4x4 xScale, xOffset;

	xScale.Identity();
	xOffset.Identity();
	xScale.SetElement( 0, 0, 0.5f / pxProjector->GetUVAspect() );
	xScale.SetElement( 1, 1, 0.5f );
	xScale.SetElement( 2, 2, 0.0f );
	xOffset.SetTranslation( Asura_Vector_3( 0.5f, 0.5f, 0.0f ) );

	if( pxProjector->GetType() == ASURA_PROJECTOR_TYPE_PERSPECTIVE )
	{
		// Perspective projection
		xProjMatrix.SetPerspectiveForLH( pxProjector->GetFoV(), 1.0f, pxProjector->GetMinRange(), pxProjector->GetMaxRange() );
	}
	else
	{
		// Parallel projection
		float fInvScale = 1.0f / pxProjector->GetScale();
		xProjMatrix.Identity();
		xProjMatrix.SetElement( 0, 0, fInvScale  );
		xProjMatrix.SetElement( 1, 1, fInvScale );
		xProjMatrix.SetElement( 2, 2, 1.0f );
	}

	Asura_AMatrix4x4 xPos( xOri, xProjPos );
	xPos.InvertOrthogonal();
	xProjMatrix = xViewMatrixInv * xPos * xProjMatrix * xScale * xOffset;

	float afTwoOverScreenDimensions[ 4 ] = { 2.0f / Asura_Viewport::GetWidth(), 2.0f / Asura_Viewport::GetHeight(), 0.0f, 0.0f };
	float fCameraTanHalfVertFOV	= Asura_Maths::Tangent( Asura_Camera::GetVerticalFieldOfView() * 0.5f );
	float fCameraTanHalfHorizFOV= fCameraTanHalfVertFOV * Asura_PS3_Graphics::GetScreenAspectRatio();//Asura_Camera::GetXOverYAspectRatio();
	float afCameraTanHalfFOV[2] = { fCameraTanHalfHorizFOV, fCameraTanHalfVertFOV };
	float afPosition[4] = { xProjPos.x, xProjPos.y, xProjPos.z, 1.0f };
	float afDirection[4] = { xOri.e13, xOri.e23, xOri.e33, 1.0f };
	float afNeutralColour[4] = { xNeutralColour.m_fR, xNeutralColour.m_fG, xNeutralColour.m_fB, xNeutralColour.m_fA };
	float afLightColour[4] = { pxProjector->GetIntensity() * pxProjector->GetRGB()[0], pxProjector->GetIntensity() * pxProjector->GetRGB()[1], pxProjector->GetIntensity() * pxProjector->GetRGB()[2], 1.0f };
	float afDetailTextureScaleAndOffset[4] = { pxProjector->GetUScrollOfs(), pxProjector->GetVScrollOfs(), pxProjector->GetDetailTextureScale(), 1.0f };

	// (AAM - 07.01.2008) Roll a x2 from the pixel shader into here
	fDistanceFade *= 2.0f;
	pxFragmentShader->SetArray ( UE_ProjectionMatrix,			4,	4, xProjMatrix.GetElements() );
	pxFragmentShader->SetArray ( UE_InvViewMatrix,				4,	4, xViewMatrixInv.GetElements() );
	pxFragmentShader->SetFloats( UE_TwoOverScreen,					4, afTwoOverScreenDimensions );
	pxFragmentShader->SetFloats( UE_CameraTanHalfFOV,				2, afCameraTanHalfFOV );
	pxFragmentShader->SetFloats( UE_NearPlane,						4, xPlane.GetElements() );
	pxFragmentShader->SetFloats( UE_DistanceFade,					1, &fDistanceFade );
	pxFragmentShader->SetFloats( UE_ProjectorPosition,				4, afPosition );
	pxFragmentShader->SetFloats( UE_ProjectionDirection,			4, afDirection );
	pxFragmentShader->SetFloats( UE_NeutralColour,					4, afNeutralColour );
	pxFragmentShader->SetFloats( UE_LightColour,					4, afLightColour );
	pxFragmentShader->SetFloats( UE_DetailTextureScaleAndOffset,	4, afDetailTextureScaleAndOffset );
	
	Asura_PS3_MaterialResponse_System::SetDepthParameters( pxFragmentShader, UE_DepthAdjustment );

	pxFragmentShader->Bind();

	if ( Asura_DeferredShading::GetShowStencils() )
	{
		// highlight the stencilled area,
		// - draw same shape used when render lights
		// - draw translucent so can see each light (possibly colour code lights too)
		// ...so actually just draw light as a flat colour
		pxBlankFP->Bind();

		Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW ); //draw back faces, makes no difference unless inside where it ensures we can see them
		cellGcmSetBlendFuncUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_ONE, CELL_GCM_ONE, CELL_GCM_ONE, CELL_GCM_ONE );
		Asura_PS3_RenderingStates::InvalidateTranslucencyMode();
		Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_ALWAYS); //actually show the stencil, regardless of depth
	}

	g_axVertexBuffer[ g_iCurrentBuffer ].BindFromVertex( g_axVertexDeclaration, VD_ELEMENTS, uiCurrentVertex );
	g_xIndexBuffer.RenderTriangles( 36, 0 );

	if (bShadows)
	{
		// If we've been rendering with shadows, tell the render state system that it must force the next change
		Asura_PS3_RenderingStates::InvalidateTranslucencyMode();
	}

 	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );

	Asura_PS3_RenderingStates::SetStencilTestEnabled(false);

    ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__PROJECTORS );
}
