//////////////////////////////////////////////////////////////////////////////
//
// Asura_Platform_CSM.cpp
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

#include "Core/Asura.h"

#if defined ASURA_USE_CSM

#include "Shadows/Asura_CSM.h"

// Asura Platform-independent
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Debugging/Asura_DebugLineRender.h"
#include "Interface/Asura_CommandConsole.h"
#include "Rendering/Asura_Viewport.h"

// Asura PS3
#include "Rendering/Asura_PS3_EnvironmentRenderer.h"
#include "Rendering/Asura_PS3_FrameBuffer.h"
#include "Rendering/Asura_PS3_QuadOverlayRenderer.h"
#include "Rendering/Asura_PS3_RSXProfiling.h"

// Shaders
#include "Rendering/ShadersDefinition/FP/CSM_FP.h"


/******************************************************************************/
/* Global Variables                                                           */
/******************************************************************************/

#if ASURA_CSM_MAX_MAPS > 5
	#error PS3 CSM only works with up to 5 maps
	// This is the limit the shader currently supports
#endif

#if ASURA_CSM_SIZE*ASURA_CSM_MAX_MAPS/2 > 4096
	#error PS3 CSM does not support this many maps of this size
	// We use an atlas texture to implement the multiple shadows, and this is currently stored with each texture one above the other.
	// 4096 is the largest a texture can be on any dimension, we can fit 16 1024x1024 textures in 4096x4096, but at present they're
	// arranged vertically which means a limit of 4 if we need more than this then change the atlas code to use a 2D arrangement.
#endif

const u_int ASURA_PS3_CSM_NUM_MAPS_Y	= (ASURA_CSM_MAX_MAPS+1)/2;

static Asura_AMatrix4x4					g_axCSMMatrices[ ASURA_CSM_MAX_MAPS ];

static Asura_PS3_FrameBuffer*			g_pxDepthBuffer = NULL;
static Asura_PS3_FrameBuffer*			g_pxCSMBuffer = NULL;

static bool g_bShowProjection = false;
static bool g_bShowCSMBuffers = false;
static bool g_bReadDepthBufferAsColour = false;
static float g_fDepthBias = 0.0f;
static bool g_bCulling = true;
static bool g_bStaggerUpdates = true;

// control the softness of the shadow
static float g_fPower = 1.0f; // falloff curve
static float g_fStretch = 1.0f; // increase the gradient of the falloff on lower res textures, to make them match up better.

static float g_fBlurScale = 1.0f;

#define MASK_RG (ASURA_PS3_COLOR_MASK_MRT0_RED | ASURA_PS3_COLOR_MASK_MRT0_GREEN)
#define MASK_BA (ASURA_PS3_COLOR_MASK_MRT0_BLUE | ASURA_PS3_COLOR_MASK_MRT0_ALPHA)

/******************************************************************************/
/* Functions                                                                  */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// CreateViewMatrix()
//
// Creates a matrix suitable for use as a view matrix, given translation and
// rotation components. The rotation is transposed in the function, but the
// translation is assumed to already be negated
//
////////////////////////////////////////////////////////////////////////////////

static void CreateViewMatrix( Asura_AMatrix4x4& xOut, const Asura_Vector_3& xAsuraTrans,
							 const Asura_Matrix_3x3& xAsuraRot )
{
	Asura_Matrix_3x3 xRot;
	xRot = xAsuraRot;

	// It's orthonormal, so just transpose it to invert it.
	xRot.e11 =  xAsuraRot.e11;
	xRot.e21 =  xAsuraRot.e12;
	xRot.e31 =  xAsuraRot.e13;
	xRot.e12 =  xAsuraRot.e21;
	xRot.e22 =  xAsuraRot.e22;
	xRot.e32 =  xAsuraRot.e23;
	xRot.e13 =  xAsuraRot.e31;
	xRot.e23 =  xAsuraRot.e32;
	xRot.e33 =  xAsuraRot.e33;

	xOut.FromMatrix( xRot );
	xOut.SetTranslation( xAsuraTrans * xRot );
}


// Blit a texture to the screen, very simplistic function useful for debugging.
// fScale - if this is 1.0 texture will be drawn actual size, 2.0 means each texel will be drawn as 2x2 pixels on screen.
// Textures are trilinear filtered if shrunk, not filtered if enlarged (point sampled)
void ShowTexture( u_int uLeft, u_int uTop, float fScale, Asura_PS3_Texture* pxTexture )
{
	float fW = pxTexture->GetTexture()->width;
	float fH = pxTexture->GetTexture()->height;
	fW = (int)(fW * fScale);
	fH = (int)(fH * fScale);

	Asura_Overlay_Quad xQuad;
	xQuad.SetRectXY(uLeft, uTop, fW, fH);
	xQuad.SetRectUV(0,0,1,1);//pxTexture->GetTexture()->width,pxTexture->GetTexture()->height);
	xQuad.Colour = 0xffffffff;

	Asura_PS3_RenderingStates::RequestAlphaTest( false );

	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	xQuad.TranslucencyMode = ASURA_TRANSLUCENCY_OFF;  //cheat! we use some wierd blend modes here so do it without telling the quad system or render states system.
	Asura_PS3_RenderingStates::SetAlphaBlending( false );

	Asura_PS3_QuadOverlayRenderer::RenderEx( xQuad, pxTexture );
}


#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
static bool ReloadShaders( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf("recompile & reload shaders");
		return true;
	}

	static const char* aszArgs[] = {	// disable fast precision
		"-mcgb",
		"-texformat d FLOAT_RGBA16",
		"-fuse-nrmh",
		"-nobcolor",
		"-fno-max-psize",
		"-po OutColorPrec=fp16",
		"-fastmath",
		"-nofastprecision",
		"-inline all",
		"-unroll all",
		"-nofloatbindings",
		"-ifcvt none",
		NULL
	};

	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/ApplyShadows/CSM_Apply_FP.fpx",
		ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_CSM_APPLY, aszArgs, NULL, new Asura_PS3_CSM_Apply_FP::Handles );

	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/ApplyShadows/ComputeVariance_FP.fpx",
		ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_COMPUTE_VSM, aszArgs, NULL, new Asura_PS3_ComputeVariance_FP::Handles );

	// doesn't need full precision, use default args (NULL)
	Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader( "~/Asura/PS3/Shaders/FragmentPrograms/ApplyShadows/BlurVariance_FP.fpx",
		ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_BLUR_VSM, NULL, NULL, new Asura_PS3_BlurVariance_FP::Handles );

	return true;
}
#endif


void ConvertToVariance ( Asura_PS3_FrameBuffer* pxDepthBuffer, Asura_PS3_FrameBuffer* pxBufferTo, u_int uBuffer )
{
	// target
	pxBufferTo->Bind();
	Asura_PS3_RenderingStates::SetViewport( 0, (uBuffer%ASURA_PS3_CSM_NUM_MAPS_Y)*ASURA_CSM_SIZE, ASURA_CSM_SIZE, ASURA_CSM_SIZE );
	Asura_Viewport::Set( ASURA_CSM_SIZE, ASURA_CSM_SIZE );

	// shader
	Asura_PS3_FragmentShaderCopy* pxFragmentProgram = Asura_PS3_ComputeVariance_FP::GetInstance();
	//jiggle format to rgb
	Asura_PS3_Texture* pxTexture = pxDepthBuffer->GetZBufferAsTexture();
	uint8_t uFormat = pxTexture->GetTexture()->format;
	pxTexture->GetTexture()->format = (pxTexture->GetTexture()->format & 0x60) | CELL_GCM_TEXTURE_A8R8G8B8;
	pxFragmentProgram->SetTexture( Asura_PS3_ComputeVariance_FP::FP_DepthBufferSampler, pxTexture, false );
	pxTexture->GetTexture()->format = uFormat;
	pxFragmentProgram->Bind();

	// states
	Asura_PS3_RenderingStates::RequestAlphaTest( false );
	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_PS3_RenderingStates::SetAlphaBlending( false );
	Asura_PS3_RenderingStates::RequestZBufferEnabled( false );
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( false );
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );
	
	if ( uBuffer < ASURA_PS3_CSM_NUM_MAPS_Y )
	{
		Asura_PS3_RenderingStates::SetColorMaskMrt0( MASK_RG );
	}
	else
	{
		Asura_PS3_RenderingStates::SetColorMaskMrt0( MASK_BA );
	}

	// quad
	Asura_Overlay_Quad xQuad;
	xQuad.Colour = 0xffffffff; // ignored by FP
	xQuad.m_bNonRotated = true;
	xQuad.m_bGouraudShaded=false;
	xQuad.SetRectXY( 0, 0, ASURA_CSM_SIZE, ASURA_CSM_SIZE );
	xQuad.SetRectUV( 0, 0, ASURA_CSM_SIZE, ASURA_CSM_SIZE ); // these are the positions the shader reads from, it gets messed up by viewports if we use WPOS
										
	// blit
	Asura_PS3_QuadOverlayRenderer::RenderEx( &xQuad );
	
	Asura_PS3_RenderingStates::SetColorMaskMrt0( 0xf );
	
	// todo: combine this with first stage of blur (assuming that 5x the maths in this is faster than the extra read/write step
}

void BlurVariance ( Asura_PS3_FrameBuffer* pxBufferFrom, u_int uMaskFrom, Asura_PS3_FrameBuffer* pxBufferTo, bool bVertical )
{
//try blurring in 1 step, with less blur. Straight from z-buff without intermediate step.

	// target
	pxBufferTo->Bind();
 	Asura_PS3_RenderingStates::SetViewport( 0, 0, ASURA_CSM_SIZE, ASURA_CSM_SIZE*ASURA_PS3_CSM_NUM_MAPS_Y );
 	Asura_Viewport::Set( ASURA_CSM_SIZE, ASURA_CSM_SIZE*ASURA_PS3_CSM_NUM_MAPS_Y );

	// shader
	Asura_PS3_FragmentShaderCopy* pxFragmentProgram = Asura_PS3_BlurVariance_FP::GetInstance();

	float afBlurDirection[2] = { bVertical?0:g_fBlurScale, bVertical?g_fBlurScale:0 };
	pxFragmentProgram->SetFloats( Asura_PS3_BlurVariance_FP::FP_BlurDirection, 2, afBlurDirection );
	pxFragmentProgram->SetTexture( Asura_PS3_BlurVariance_FP::FP_VarianceSampler, pxBufferFrom->GetRTAsTexture(0), false );
	pxFragmentProgram->Bind();

 	// states
	//these haven't changed
// 	Asura_PS3_RenderingStates::RequestAlphaTest( false );
// 	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
// 	Asura_PS3_RenderingStates::SetAlphaBlending( false );
// 	Asura_PS3_RenderingStates::RequestZBufferEnabled( false );
// 	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( false );
// 	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );

	// quad
	Asura_Overlay_Quad xQuad;
	xQuad.Colour = 0xffffffff; // ignored by FP
	xQuad.m_bNonRotated = true;
	xQuad.m_bGouraudShaded=false;
	xQuad.SetRectXY( 0, 0, ASURA_CSM_SIZE, ASURA_CSM_SIZE*ASURA_PS3_CSM_NUM_MAPS_Y );
	xQuad.SetRectUV( 0, 0, 1, 1 ); // ignored by FP

	// blit
	Asura_PS3_QuadOverlayRenderer::RenderEx( &xQuad );
}




/******************************************************************************/
/* Asura_CSM Static Class Function                                            */
/******************************************************************************/

void Asura_CSM::Platform_Initialise()
{
	// There's no support for array textures on PS3, so create a texture atlas

	// depth buffer, for faster z-only render
	g_pxDepthBuffer = Asura_PS3_DeferredRendering::GetCSMDepthBuffer();
	
	// Variance buffer (depth, depth squared)
	g_pxCSMBuffer = Asura_PS3_DeferredRendering::GetCSMVarianceBuffer();
//BW todo: in theory we only need 2xFP16 of this size, at present we have 4xFP16 + depth buffer. Instead we could do a rendertarget the size of 1 texture then blit it into the atlas, though there's still no 2xFP16 surface format.

	Asura_Assert( g_pxDepthBuffer, "Failed to create Cascaded Shadow Map Buffer, it's probably too big." );
	Asura_Assert( g_pxCSMBuffer, "Failed to create Cascaded Shadow Map Buffer, it's probably too big." );


	// Initialise Shaders
	Asura_PS3_CSM_Apply_FP::AddToShaderLibrary();
	Asura_PS3_ComputeVariance_FP::AddToShaderLibrary();
	Asura_PS3_BlurVariance_FP::AddToShaderLibrary();


#if defined(ASURA_DEBUG_VARIABLE_SYSTEM)
#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
	Asura_CommandConsole::AddCmd( L"Render.CSM.ReloadShaders", ReloadShaders, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
	Asura_CommandConsole::AddVar( L"Render.CSM.ShowBuffer", &g_bShowCSMBuffers, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.ShowProjection", &g_bShowProjection, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.ReadBufferAsColour", &g_bReadDepthBufferAsColour, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.EnableCull", &g_bCulling, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.StaggerUpdates", &g_bStaggerUpdates, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.DepthBias", &g_fDepthBias, -FLT_MAX, FLT_MAX, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.ShadowPower", &g_fPower, 0, FLT_MAX, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.ShadowStretch", &g_fStretch, 0, FLT_MAX, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.CSM.BlurScale", &g_fBlurScale, -FLT_MAX, FLT_MAX, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
}

void Asura_CSM::Platform_Reset()
{
}

void Asura_CSM::Platform_Shutdown()
{
}

void Asura_CSM::Platform_CreateShadowMaps()
{
	if ( s_afBSRadii[0] <= 0 ) return; // something isn't initialised.


	if( Asura_PS3_EnvironmentRenderer::GetNumberOfStrips() == 0 ) return;


	ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__CREATESHADOWS, "Create CSM" );
	ASURA_CPU_PROFILE__START_LABEL(ASURA_CPU_PROFILE__ENGINE_CYAN, "Create CSM");


	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( true );
	Asura_PS3_RenderingStates::RequestZBufferEnabled( true );
	Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_PS3_RenderingStates::SetBlendEnableMrt( false, false, false );
	Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL );
// 	Asura_PS3_RenderingStates::SetUseDepthBias( false );
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CW );


 	// Create each cascaded shadow texture
	for( u_int u = 0; u < s_uNumMaps; ++u )
	{
        if( g_bStaggerUpdates )
        {
            // This code currently assumes 4 levels of CSM.
            Asura_Assert( s_uNumMaps == 4, "" );

            // Update maps 0 and 3 on even frames and update maps 1 and 2
            // on odd frames. (Hopefully this scheme should balance the load
            // somewhat, due to the amount of geometry that's likely to be
            // rendered into each one.)
            if( Asura_Timers::GetFrameNumber() & 1 )
            {
                if( u == 1 || u == 2 ) { continue; }
            }
            else
            {
                if( u == 0 || u == 3 ) { continue; }
            }
        }

		// setup & clear the z-buffer
		g_pxDepthBuffer->BindAsZPassOnly();

		Asura_PS3_RenderingStates::SetViewport( ASURA_CSM_SIZE, ASURA_CSM_SIZE );
		Asura_Viewport::Set( ASURA_CSM_SIZE, ASURA_CSM_SIZE );

		cellGcmSetZcullStatsEnableUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_FALSE );
		cellGcmSetClearDepthStencilUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), 0xffffff00 );
		cellGcmSetClearSurfaceUnsafeInline( Asura_PS3_Graphics::GetGcmCurrentContext(), CELL_GCM_CLEAR_Z | CELL_GCM_CLEAR_S );



		// setup the orthogonal view matrix
		CreateViewMatrix( g_axCSMMatrices[u], s_axShadowViewMatrixTrans[u], s_axShadowViewMatrixOrients[u] );

		Asura_AMatrix4x4 xProj;
		const float fDiameter = s_afBSRadii[u] * 2.0f;

		float fNear = -s_fExtraFrustumDepth;
		float fFar = fDiameter;
		xProj.SetOrthographicForLH( fDiameter, fDiameter, fNear, fFar );

		g_axCSMMatrices[u] = g_axCSMMatrices[u] * xProj;

		// Find out where the origin will project to in the shadow map
		Asura_AVector4 xOrigin( 0.0f, 0.0f, 0.0f, 1.0f );
		xOrigin = g_axCSMMatrices[u].Apply( xOrigin );

		float fOriginX = (xOrigin.GetX() * 0.5f) * ASURA_CSM_SIZE;
		float fOriginY = (xOrigin.GetY() * 0.5f) * ASURA_CSM_SIZE;

		float fPixelDiffX = floorf( fOriginX ) - fOriginX;
		float fPixelDiffY = floorf( fOriginY ) - fOriginY;

		float fRoundingX = (fPixelDiffX / ASURA_CSM_SIZE) * 2.0f;
		float fRoundingY = (fPixelDiffY / ASURA_CSM_SIZE) * 2.0f;

		Asura_AMatrix4x4 xRounding;
		xRounding.Identity();
		xRounding.SetTranslation( Asura_AVector3(fRoundingX, fRoundingY, 0.0f) );

 		g_axCSMMatrices[u] = g_axCSMMatrices[u] * xRounding;


		// set pixel shader, this doesn't have to do anything clever.
		Asura_PS3_FragmentShaderCopy* pxFragmentShader = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_GLOBAL, ASURA_GLOBAL_SHADER_BLANK );
		pxFragmentShader->Bind();


		Asura_PS3_RenderingStates::SetUseDepthBias( true );

		// surface acne will occur in proportion to dx/dz in the buffer, so we adjust our depth bias to compensate for both, to give consistent quality on the different shadow maps
		// this formula converts a bias in metres into a depth value (note that in orthogonal projection depth buffer is linear, so this is just a multiply by depth buffer range / depth range in metres)
		// and adjusts this bias according to the size of the texels relative to level 0
		Asura_PS3_RenderingStates::SetDepthBias( g_fDepthBias * (s_afBSRadii[u]/s_afBSRadii[0]) * ((float)(1<<23))/(fFar-fNear), 0.0f );

// 		Asura_Bounding_Box xClip;
// 		xClip.Zero();
// 		xClip.Grow(1);
// 		xClip.Scale(Asura_Vector_3(fDiameter,fDiameter,fDiameter+fExtraDepth));	//must scale +ve? why'd it say that?!
// 
// //debug:
// xClip.Scale(.5f);
// 		
// 		Asura_Bounding_Box xAAClip;
// 		xAAClip.FromOBB(xClip, s_axShadowViewMatrixOrients[u]);
// 		xAAClip.Displace(s_axShadowViewMatrixTrans[u]);
// 
// axis alignedness isn't good enough, want actual.
// => planes (all points must be off the same side => not perfect but pretty good)
// but wait, we have a sphere, and only in z do we need more.
// try sphere-cull. then sphere cull ignore z.

// in truth we really just want the shape of the view frustum (truncated between the depths of this texture, projected into shadow space and extended as a prism)
// that is the minimal cull shape to achieve the same image result.

// 		Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW ); // back faces

		#ifdef ASURA_USE_GCM_REPLAY
			char szTemp[256];
			sprintf( szTemp, "Create CSM - Render Geom pass %d", u );
		#endif
		ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__CREATESHADOWS, szTemp );

		// Construct an OBB around the CSM split bounding sphere. Could get tighter by just surrounding
		// the view frustum split section, but don't have OBB functions for that right now
		Asura_OBB xShadowOBB;

		if( g_bCulling )
		{
			Asura_Matrix_3x3 xCSMOrient;
			Asura_Maths::OrientationFromDirection( xCSMOrient, Asura_CSM::GetShadowDir() );

			xShadowOBB.SetOrientation( Asura_AMatrix3x3( xCSMOrient ) );

			// Offset centre to account for "extra frustum depth"
			Asura_AVector3 xCentre( s_axBSCentres[u] );
			xCentre -= Asura_AVector3( GetShadowDir() ) * (s_fExtraFrustumDepth * 0.5f);
			xShadowOBB.SetCentre( xCentre );

			// Same for OBB depth
			const float fHalfExtentZ = s_afBSRadii[u] + s_fExtraFrustumDepth * 0.5f;
			const Asura_AVector3 xHalfExtents( s_afBSRadii[u], s_afBSRadii[u], fHalfExtentZ );
			xShadowOBB.SetHalfExtents( xHalfExtents );
		}

		Asura_Vector_3 xPosition = -s_axShadowViewMatrixTrans[u];
		Asura_PS3_EnvironmentRenderer::RenderGeometry(g_axCSMMatrices[u], xPosition, g_bCulling?(fDiameter+s_fExtraFrustumDepth):FLT_MAX, g_bCulling?&xShadowOBB:NULL);

		ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__CREATESHADOWS );
	
		// get depth & depth squared from depth buffer
		ConvertToVariance( g_pxDepthBuffer, g_pxCSMBuffer, u );
	}

// 	// blur it for prettiness - //BW we don't presently have a spare buffer for this, could use the same buffer with a slight offset. 2-ply will double the speed of this.
// 	BlurVariance( g_pxCSMBuffer, MASK_RG, g_pxCSMBuffer, MASK_BA, false );
// 	BlurVariance( g_pxCSMBuffer, MASK_BA, g_pxCSMBuffer, MASK_RG, true );

	// restore render states
 	Asura_PS3_RenderingStates::SetUseDepthBias( false );

	Asura_PS3_DeferredRendering::Bind();
	Asura_Viewport::Set( Asura_PS3_Graphics::GetScreenWidth(), Asura_PS3_Graphics::GetScreenHeight() );
	Asura_PS3_Graphics::SetViewportToDefaults();

	ASURA_CPU_PROFILE__END(ASURA_CPU_PROFILE__ENGINE_CYAN);
	ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__CREATESHADOWS );
}




void Asura_CSM::Platform_ApplyToScene()
{
	ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__APPLYSHADOWS, "Apply CSM" );
	
	Asura_PS3_DeferredRendering::BindAsSingleRT();

	Asura_PS3_FragmentShaderCopy* pxFragmentShader = Asura_PS3_CSM_Apply_FP::GetInstance();

//	Asura_PS3_Graphics::SwitchStencilCullMode( ASURA_PS3_STENCIL_NOTEQUAL, ASURA_STENCIL_MASK_SKY, ASURA_STENCIL_MASK_SKY );	// Switch SCULL modes to enable fast-cull on things using this stencil
	Asura_PS3_RenderingStates::SetStencilTestEnabled( true );
	Asura_PS3_RenderingStates::SetStencilFunction( ASURA_PS3_STENCIL_NOTEQUAL, ASURA_STENCIL_MASK_SKY, ASURA_STENCIL_MASK_SKY );
	Asura_PS3_RenderingStates::SetStencilMask(ASURA_STENCIL_MASK_SKY);
	Asura_PS3_RenderingStates::SetStencilOperation(ASURA_PS3_STENCIL_OPERATION_KEEP,ASURA_PS3_STENCIL_OPERATION_KEEP,ASURA_PS3_STENCIL_OPERATION_KEEP);
//	Asura_PS3_Graphics::ReloadStencilCull(); // a lot faster than running the CSM shader on all those sky pixels.
//BW GCM HUD says SCULL isn't helping speed, I think this is a bug & will investigate but for the time being there's not point wasting time reloading SCULL.
	
	pxFragmentShader->Bind();
	
	for ( int i=0; i < ASURA_CSM_MAX_MAPS; i++ )
	{
		pxFragmentShader->SetArray( Asura_PS3_CSM_Apply_FP::FP_ShadowViewProj + i, 4, 4, g_axCSMMatrices[i].GetElements() );
	}
	
	const Asura_AMatrix4x4& xView = Asura_PS3_Graphics::GetViewMatrix();
	Asura_AMatrix4x4 xInvView;
	xView.Invert( xInvView );

	pxFragmentShader->SetArray( Asura_PS3_CSM_Apply_FP::FP_InvViewMatrix, 4, 4, xInvView.GetElements() );
	
	float afTwoOverScreenDimensions[2];
	afTwoOverScreenDimensions[0] = 2.0f / Asura_Viewport::GetWidth();
	afTwoOverScreenDimensions[1] = 2.0f / Asura_Viewport::GetHeight();	
	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_TwoOverScreen, 2, afTwoOverScreenDimensions );

	float afCameraTanHalfFOV[2];
	afCameraTanHalfFOV[1] = Asura_Maths::Tangent( Asura_Camera::GetVerticalFieldOfView() * 0.5f );
	afCameraTanHalfFOV[0] = afCameraTanHalfFOV[1] * Asura_PS3_Graphics::GetScreenAspectRatio();
	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_CameraTanHalfFOV, 2, afCameraTanHalfFOV );

	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_Splits, 4, &s_afSplits[1] ); //BW not sure why it's using [1], I'm just copying the 360 version

	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_Strength, 4, &s_afStrengths[0] );
	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_Power, 1, &g_fPower );
	pxFragmentShader->SetFloats( Asura_PS3_CSM_Apply_FP::FP_Stretch, 1, &g_fStretch);
	
	Asura_PS3_MaterialResponse_System::SetDepthParameters( pxFragmentShader, Asura_PS3_CSM_Apply_FP::FP_DepthAdjustment );

	pxFragmentShader->SetTexture( Asura_PS3_CSM_Apply_FP::FP_DepthBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetZBufferAsTexture(), false );

	if ( g_bReadDepthBufferAsColour )
	{
		// read texture as colours
		Asura_PS3_Texture* pxTexture = g_pxDepthBuffer->GetZBufferAsTexture();
		uint8_t uFormat = pxTexture->GetTexture()->format;
		pxTexture->GetTexture()->format = (pxTexture->GetTexture()->format & 0x60) | CELL_GCM_TEXTURE_A8R8G8B8;
		pxFragmentShader->SetTexture( Asura_PS3_CSM_Apply_FP::FP_ShadowBufferSampler, pxTexture, true );
		pxTexture->GetTexture()->format = uFormat;
	}
	else
	{
// 		// read texture as depth comparison (with implicit PCF)
//		pxFragmentShader->SetTexture( Asura_PS3_CSM_Apply_FP::FP_ShadowBufferSampler, g_pxDepthBuffer->GetZBufferAsTexture(), true );

		// read the variance texture
		pxFragmentShader->SetTexture( Asura_PS3_CSM_Apply_FP::FP_ShadowBufferSampler, g_pxCSMBuffer->GetRTAsTexture(0), true );
	}


	Asura_Overlay_Quad xQuad;
	xQuad.Colour = 0xffffffff; // ignored by FP
	xQuad.m_bNonRotated = true;
	xQuad.m_bGouraudShaded=false;
	xQuad.TranslucencyMode = ASURA_TRANSLUCENCY_NORMAL;
	xQuad.SetRectXY( 0, 0, Asura_PS3_Graphics::GetScreenWidth(), Asura_PS3_Graphics::GetScreenHeight() );
	xQuad.SetRectUV( 0, 0, 1, 1 ); // ignored by FP

	Asura_PS3_RenderingStates::SetColorMaskMrt0(0xf);
	Asura_PS3_RenderingStates::RequestAlphaTest( false );
	Asura_PS3_RenderingStates::SetAlphaBlending( true );
	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
	Asura_PS3_RenderingStates::RequestZBufferEnabled( false );
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled( false );
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );

	Asura_PS3_QuadOverlayRenderer::RenderEx( &xQuad );

	
	ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__APPLYSHADOWS );


	Asura_PS3_RenderingStates::SetStencilTestEnabled( false );


	Asura_PS3_DeferredRendering::BindAsSingleRT();

	if ( g_bShowCSMBuffers )
	{
		// debug, blit buffer onto the screen
// 		//masquerade as an RGBA8 (shadow buffers are depth texture format, so reading them just gives "yes" or "no")
// 		Asura_PS3_Texture* pxTexture = g_pxDepthBuffer->GetZBufferAsTexture();
// 		uint8_t uFormat = pxTexture->GetTexture()->format;
// 		pxTexture->GetTexture()->format = (pxTexture->GetTexture()->format & 0x60) | CELL_GCM_TEXTURE_A8R8G8B8;
// 
// 		ShowTexture(0,0,720.0f/(float)(ASURA_CSM_SIZE*ASURA_CSM_MAX_MAPS), pxTexture);
// 
// 		pxTexture->GetTexture()->format = uFormat;

		ShowTexture(0,0,720.0f/(float)(ASURA_CSM_SIZE*ASURA_PS3_CSM_NUM_MAPS_Y), g_pxCSMBuffer->GetRTAsTexture(0));
	}

#ifndef ASURA_NO_DEBUG_OUTPUT
	if ( g_bShowProjection )
	{
		// Debug: render the bounding box, from the projection matrix.
		for( u_int u = 0; u < s_uNumMaps; ++u )
		{
			// orthographic => invertable. wooh.
			Asura_AMatrix4x4 xMatrix;
			g_axCSMMatrices[u].Invert( xMatrix );
			
			//draw lines of a <-1,-1,0> to <1,1,1> box
			Asura_AVector3	axP[8];
			for ( int i=0; i < 8; i++ )
			{
				Asura_AVector4 xV( (i&1)*2-1, (i&2)-1, (i&4)/4, 1.0f ), xV2;
				xV2 = xMatrix.Apply( xV );
				axP[i].LoadAligned( xV2.GetElements() );
			}

			Asura_DebugLineRender::AddLine( axP[0], axP[1], 0xffff0000 );
			Asura_DebugLineRender::AddLine( axP[2], axP[3], 0xffff0000 );
			Asura_DebugLineRender::AddLine( axP[4], axP[5], 0xffff0000 );
			Asura_DebugLineRender::AddLine( axP[6], axP[7], 0xffff0000 );
			Asura_DebugLineRender::AddLine( axP[0], axP[2], 0xff00ff00 );
			Asura_DebugLineRender::AddLine( axP[1], axP[3], 0xff00ff00 );
			Asura_DebugLineRender::AddLine( axP[4], axP[6], 0xff00ff00 );
			Asura_DebugLineRender::AddLine( axP[5], axP[7], 0xff00ff00 );
			Asura_DebugLineRender::AddLine( axP[0], axP[4], 0xff0000ff );
			Asura_DebugLineRender::AddLine( axP[1], axP[5], 0xff0000ff );
			Asura_DebugLineRender::AddLine( axP[2], axP[6], 0xff0000ff );
			Asura_DebugLineRender::AddLine( axP[3], axP[7], 0xff0000ff );
			
			Asura_AVector4 xV(0,0,0,1);
			xV = xMatrix.Apply( xV );
			Asura_AVector3 xP(xV.GetElements());
			
			Asura_DebugLineRender::AddLine( xP, axP[0], 0xffff8000 );
			Asura_DebugLineRender::AddLine( xP, axP[1], 0xffff8000 );
			Asura_DebugLineRender::AddLine( xP, axP[2], 0xffff8000 );
			Asura_DebugLineRender::AddLine( xP, axP[3], 0xffff8000 );
		}
	}
#endif//ndef ASURA_NO_DEBUG_OUTPUT

	Asura_PS3_DeferredRendering::Bind();
}

#endif // defined ASURA_USE_CSM
