
//
// Asura_Xbox360_DeferredShading.cpp
//
// Created by DMR 13/02/07
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
// Handles deferred lighting passes
//

#include "Core/Asura.h"

#include "Rendering/Asura_DeferredShading.h"

#include "Core/Asura_Maths.h"
#include "Camera/Asura_Camera.h"
#include "Debugging/Asura_Logfile.h"
#include "Interface/Asura_CommandConsole.h"
#include "Light/Asura_Photon.h"
#include "Rendering/Asura_Font_System.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"

#include "Debugging/Asura_Platform_GPUProfile.h"
#include "Rendering/Asura_Xbox360_DeferredShading.h"
#include "Rendering/Asura_Xbox360_Graphics.h"
#include "Rendering/Asura_Xbox360_IndexBuffer.h"
#include "Rendering/Asura_Xbox360_MaterialResponse_System.h"
#include "Rendering/Asura_Xbox360_QuadOverlayRenderer.h"
#include "Rendering/Asura_Xbox360_TextureManagement.h"
#include "Rendering/Asura_Xbox360_VertexBuffer.h"

// Shader Include Files
#include "ApplyDeferredPhoton_MR.h"
#include "NormalBufferOverlayRenderer.h"


DVSVAR bool	dvs_bOptimiseLimitedPhotonRender	= true;
DVSVAR bool	dvs_bPhotonsUseIndexedSphere		= true;

enum
{
	// Effect Pass Indices
	DeferredPhoton_ClearDestAlpha_Photon	= 0,
	DeferredPhoton_ClearDestAlpha_IndexedPhoton,
	DeferredPhoton_ClearDestAlpha_LimitedPhoton,

	DeferredPhoton_Infinite,

	DeferredPhoton_Photon,
	DeferredPhoton_Photon_CubeMap,
	DeferredPhoton_Photon_DualCubeMaps,

	DeferredPhoton_IndexedPhoton,
	DeferredPhoton_IndexedPhoton_CubeMap,
	DeferredPhoton_IndexedPhoton_DualCubeMaps,

	DeferredPhoton_LimitedPhoton,
	DeferredPhoton_LimitedPhoton_CubeMap,
	DeferredPhoton_LimitedPhoton_DualCubeMaps,

	DeferredPhoton_OptimisedLimitedPhoton,
	DeferredPhoton_OptimisedLimitedPhoton_CubeMap,
	DeferredPhoton_OptimisedLimitedPhoton_DualCubeMaps,
};


// Redirect the Platform_ functions to our platform-specific class - as we can't add platform-specific
// members to Asura_DeferredShading, and we could end up with a load of globals again...

void Asura_DeferredShading::Platform_Initialise(void)			{Asura_Xbox360_DeferredShading::Initialise();}
void Asura_DeferredShading::Platform_Shutdown(void)				{Asura_Xbox360_DeferredShading::Shutdown();}
void Asura_DeferredShading::Platform_Reset(void)				{Asura_Xbox360_DeferredShading::Reset();}
void Asura_DeferredShading::Platform_RenderPhotons(bool bWithDarkeningShadows, bool bWithRealShadows, bool bWithNoShadows) {Asura_Xbox360_DeferredShading::RenderPhotons(bWithDarkeningShadows, bWithRealShadows, bWithNoShadows);}
void Asura_DeferredShading::Platform_RenderDebugOutput(void)	{Asura_Xbox360_DeferredShading::RenderDebugOutput();}

void Asura_DeferredShading::Platform_BeginRender(void)
{
	Asura_Xbox360_Graphics::BeginTiling2();

#if defined(ASURA_XBOX360_ENABLE_PIX)
	PIXBeginNamedEvent(D3DCOLOR_XRGB(0xFF, 0xFF, 0xFF), "Deferred Shading");
#endif	//defined(ASURA_XBOX360_ENABLE_PIX)
}

void Asura_DeferredShading::Platform_EndRender(void)
{
#if defined(ASURA_XBOX360_ENABLE_PIX)
	PIXEndNamedEvent();
#endif	//defined(ASURA_XBOX360_ENABLE_PIX)

	Asura_Xbox360_Graphics::EndTiling2();
}

Asura_Xbox360_VertexBuffer* Asura_Xbox360_DeferredShading::s_apxSphereVB[4]							= {NULL,NULL,NULL,NULL};
int							Asura_Xbox360_DeferredShading::s_aiSphereNumVerts[4]					= {0,0,0,0};

Asura_Xbox360_VertexBuffer*	Asura_Xbox360_DeferredShading::s_apxIndexedSphereVB[4]					= {NULL,NULL,NULL,NULL};
int							Asura_Xbox360_DeferredShading::s_aiIndexedSphereNumVerts[4]				= {0,0,0,0};
Asura_Xbox360_IndexBuffer*	Asura_Xbox360_DeferredShading::s_apxIndexedSphereIB[4]					= {NULL,NULL,NULL,NULL};
int							Asura_Xbox360_DeferredShading::s_aiIndexedSphereNumIndices[4]			= {0,0,0,0};

Asura_Xbox360_VertexBuffer*	Asura_Xbox360_DeferredShading::s_apxIndexedSphereSegmentVB[4]			= {NULL,NULL,NULL,NULL};
int							Asura_Xbox360_DeferredShading::s_aiIndexedSphereSegmentNumVerts[4]		= {0,0,0,0};
Asura_Xbox360_IndexBuffer*	Asura_Xbox360_DeferredShading::s_apxIndexedSphereSegmentIB[4]			= {NULL,NULL,NULL,NULL};
int							Asura_Xbox360_DeferredShading::s_aiIndexedSphereSegmentNumIndices[4]	= {0,0,0,0};

Asura_Xbox360_Effect*  	    Asura_Xbox360_DeferredShading::s_pxDeferredPhotonEffect					= NULL;
Asura_Xbox360_Effect*  	    Asura_Xbox360_DeferredShading::s_pxRenderNormalBufferEffect				= NULL;
Direct3D_VertexDeclaration* Asura_Xbox360_DeferredShading::s_pxDeferredPhotonVertexDeclaration		= NULL;

Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xMeshScaleAndPosHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xNormalBufferHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xDepthMtlBufferHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xAlbedoBufferHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xInvViewMatrixHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xMaterialsTextureHandle		= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xCameraTanHalfFOVHandle		= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xTwoOverScreenDimensionsHandle = NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xLightDirectionHandle		    = NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xLightPosScaleHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xLightColourHandle				= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xFalloffHandle					= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xLimitAnglesHandle				= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xUseSpecularMapHandle			= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xProjCubeTextureHandle		    = NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xProjCubeBlurredTextureHandle  = NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xProjCubeLerpDistHandle		= NULL;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_DeferredShading::s_xLightOrientationHandle	    = NULL;

const D3DVERTEXELEMENT9 axVERTEX_DECLARATION[] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },	// Vertex pos
	D3DDECL_END()
};




void Asura_Xbox360_DeferredShading::Initialise(void)
{
	Asura_Xbox360_Graphics::GetDevice()->CreateVertexDeclaration( axVERTEX_DECLARATION, &s_pxDeferredPhotonVertexDeclaration );
	Asura_Assert( s_pxDeferredPhotonVertexDeclaration, "Failed to create deferred photon vertex declaration." );

	s_pxDeferredPhotonEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auApplyDeferredPhoton_MR);
	Asura_Assert( s_pxDeferredPhotonEffect, "Failed to create deferred photon shader" );

	s_pxRenderNormalBufferEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auNormalBufferOverlayRenderer);
	Asura_Assert( s_pxRenderNormalBufferEffect, "Failed to create normal map debug shader" );

	s_xMeshScaleAndPosHandle        = s_pxDeferredPhotonEffect->GetParameterHandle("g_xMeshScaleAndPos");
	s_xInvViewMatrixHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xInvViewMatrix");
	s_xCameraTanHalfFOVHandle		= s_pxDeferredPhotonEffect->GetParameterHandle("g_xCameraTanHalfFOV" );
	s_xTwoOverScreenDimensionsHandle= s_pxDeferredPhotonEffect->GetParameterHandle("g_xTwoOverScreen" );
	s_xNormalBufferHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xNormalBufferSampler");
	s_xDepthMtlBufferHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xDepthMtlBufferSampler");
	s_xAlbedoBufferHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xAlbedoBufferSampler");
	s_xMaterialsTextureHandle		= s_pxDeferredPhotonEffect->GetParameterHandle("g_xMaterialsTextureSampler" );
	s_xLightDirectionHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xLightDirection" );
	s_xLightPosScaleHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xLightPosScale" );
	s_xLightColourHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xLightColour" );
	s_xFalloffHandle				= s_pxDeferredPhotonEffect->GetParameterHandle("g_xFalloff" );
	s_xLimitAnglesHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_xLimitAngles" );
	s_xUseSpecularMapHandle			= s_pxDeferredPhotonEffect->GetParameterHandle("g_bUseSpecularMap" );	
	s_xProjCubeTextureHandle		= s_pxDeferredPhotonEffect->GetParameterHandle("g_xProjCubeMapSampler" );
	s_xProjCubeBlurredTextureHandle = s_pxDeferredPhotonEffect->GetParameterHandle("g_xProjCubeMapBlurredSampler" );
	s_xProjCubeLerpDistHandle		= s_pxDeferredPhotonEffect->GetParameterHandle("g_fProjCubeLerpDist" );
	s_xLightOrientationHandle		= s_pxDeferredPhotonEffect->GetParameterHandle("g_xLightOrientation" );



	// Create VBs for a sphere, used to render the deferred passes, at 4 different LOD levels
	for (int iLod=0; iLod<4; iLod++)
	{
		int   iSphereVerticalSegments = 10;
		int   iSphereRadialSegments   = 20;
		float fSphereRadiusScale      = 1.0f;
		
		switch(iLod)
		{
			case 3:
				fSphereRadiusScale		= 1.05f;
				iSphereVerticalSegments = 6;
				iSphereRadialSegments	= 12;				
				break;
			case 2:
				fSphereRadiusScale		= 1.03f;
				iSphereVerticalSegments = 8;
				iSphereRadialSegments	= 16;
				break;
			case 1:
				fSphereRadiusScale		= 1.025f;
				iSphereVerticalSegments = 10;
				iSphereRadialSegments   = 20;
				break;
			case 0:
				fSphereRadiusScale		= 1.01f;
				iSphereVerticalSegments = 16;
				iSphereRadialSegments   = 32;
				break;
		}

		// Create our sphere LODs...
		{
			int	iNumVerts;

			Asura_DeferredShading::CalculateSphereData(iSphereRadialSegments, iSphereVerticalSegments, iNumVerts);

			s_apxSphereVB[iLod]	= new Asura_Xbox360_VertexBuffer;
			s_apxSphereVB[iLod]->CreateStatic(sizeof(Asura_Vector_3), iNumVerts);

			Asura_Vector_3 *pxVerts = static_cast<Asura_Vector_3*>(s_apxSphereVB[iLod]->Lock());		
			{
				Asura_DeferredShading::CreateSphere(pxVerts, iNumVerts, iSphereRadialSegments, iSphereVerticalSegments, fSphereRadiusScale);		
				s_aiSphereNumVerts[iLod]	= iNumVerts;
			}
			s_apxSphereVB[iLod]->Unlock();
		}

		// Create our indexed sphere LODs...
		{
			int	iNumVerts;
			int	iNumIndices;

			Asura_DeferredShading::CalculateIndexedSphereData(iSphereRadialSegments, iSphereVerticalSegments, iNumVerts, iNumIndices);

			s_apxIndexedSphereVB[iLod]	= new Asura_Xbox360_VertexBuffer;
			s_apxIndexedSphereVB[iLod]->CreateStatic(sizeof(Asura_Vector_2), iNumVerts);

			s_apxIndexedSphereIB[iLod]	= new Asura_Xbox360_IndexBuffer;
			s_apxIndexedSphereIB[iLod]->CreateStatic(sizeof(u_short) * iNumIndices);

			Asura_Vector_2*	pxVerts		= static_cast<Asura_Vector_2*>(s_apxIndexedSphereVB[iLod]->Lock());
			u_short*		puIndices	= static_cast<u_short*>(s_apxIndexedSphereIB[iLod]->Lock());
			{
				Asura_DeferredShading::CreateIndexedSphere(pxVerts, iNumVerts, puIndices, iNumIndices, iSphereRadialSegments, iSphereVerticalSegments);
				s_aiIndexedSphereNumVerts[iLod]		= iNumVerts;
				s_aiIndexedSphereNumIndices[iLod]	= iNumIndices;
			}
			s_apxIndexedSphereVB[iLod]->Unlock();
			s_apxIndexedSphereIB[iLod]->Unlock();
		}

		// Create our indexed sphere-segment LODs...
		{
			int	iNumVerts;
			int	iNumIndices;

			Asura_DeferredShading::CalculateIndexedSphereSegmentData(iSphereRadialSegments, iSphereVerticalSegments, iNumVerts, iNumIndices);

			s_apxIndexedSphereSegmentVB[iLod]	= new Asura_Xbox360_VertexBuffer;
			s_apxIndexedSphereSegmentVB[iLod]->CreateStatic(sizeof(Asura_Vector_3), iNumVerts);

			s_apxIndexedSphereSegmentIB[iLod]	= new Asura_Xbox360_IndexBuffer;
			s_apxIndexedSphereSegmentIB[iLod]->CreateStatic(sizeof(u_short) * iNumIndices);

			Asura_Vector_3*	pxVerts		= static_cast<Asura_Vector_3*>(s_apxIndexedSphereSegmentVB[iLod]->Lock());
			u_short*		puIndices	= static_cast<u_short*>(s_apxIndexedSphereSegmentIB[iLod]->Lock());
			{
				Asura_DeferredShading::CreateIndexedSphereSegment(pxVerts, iNumVerts, puIndices, iNumIndices, iSphereRadialSegments, iSphereVerticalSegments);
				s_aiIndexedSphereSegmentNumVerts[iLod]		= iNumVerts;
				s_aiIndexedSphereSegmentNumIndices[iLod]	= iNumIndices;
			}
			s_apxIndexedSphereSegmentVB[iLod]->Unlock();
			s_apxIndexedSphereSegmentIB[iLod]->Unlock();
		}
	}

#if defined(ASURA_DEBUG_VARIABLE_SYSTEM)
	Asura_CommandConsole::AddVar(L"Render.Deferred.PhotonsUseIndexedSphere", &dvs_bPhotonsUseIndexedSphere, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddVar(L"Render.Deferred.FastLimitedAnglePhotons", &dvs_bOptimiseLimitedPhotonRender, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
#endif	//defined(ASURA_DEBUG_VARIABLE_SYSTEM)
}


void Asura_Xbox360_DeferredShading::Shutdown(void)
{
	Asura_Xbox360_Effect_System::Destroy( s_pxDeferredPhotonEffect );
	s_pxDeferredPhotonEffect = NULL;
	s_pxDeferredPhotonVertexDeclaration->Release();
	s_pxDeferredPhotonVertexDeclaration = NULL;

	for(int i=0;i<4;i++)
	{
		delete s_apxSphereVB[i];
		s_apxSphereVB[i]	= NULL;

		delete s_apxIndexedSphereIB[i];
		s_apxIndexedSphereIB[i]	= NULL;
		delete s_apxIndexedSphereVB[i];
		s_apxIndexedSphereVB[i]	= NULL;

		delete s_apxIndexedSphereSegmentIB[i];
		s_apxIndexedSphereSegmentIB[i]	= NULL;
		delete s_apxIndexedSphereSegmentVB[i];
		s_apxIndexedSphereSegmentVB[i]	= NULL;
	}	
}

void Asura_Xbox360_DeferredShading::Reset(void)
{

}





void Asura_Xbox360_DeferredShading::RenderPhotonOutlines_Callback( Asura_Photon* pxPhoton, void* pData )
{

	// Photons now support a distance fade-out, like the projectors
	float fDistanceFade = 1.0f;
	if (pxPhoton->GetDistanceFadeEnable())
	{	
		Asura_Vector_3 xCameraPos = *Asura_Camera::GetPosition();
		Asura_Vector_3 xOfs = pxPhoton->GetPosition()-xCameraPos;
		float fDist = xOfs.Magnitude();
		if (fDist> pxPhoton->GetDistanceFadeStart())
		{
			fDistanceFade = 1.0f - ((fDist - pxPhoton->GetDistanceFadeStart())/pxPhoton->GetDistanceFadeDist());
		}

		if (fDistanceFade<=0.0f)
		{
			return;
		}
	}
#if defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	if ( !pxPhoton->IsVisible() )
	{
		return;
	}
#else// !defined(ASURA_USE_NEW_VISIBILITY_SYSTEM )

	Asura_Vector_3 xPos = pxPhoton->GetPosition();			
	float fRadius = pxPhoton->GetRange();

	const bool bIsPhotonInFrustrum = Asura_Camera::IsSphereInFrustum( xPos, fRadius);
	if( false == bIsPhotonInFrustrum)
	{
		return;
	}


	Asura_Bounding_Box xBB;
	xBB.Reset();
	xBB.Set(xPos + Asura_Vector_3(fRadius,fRadius,fRadius));
	xBB.Set(xPos - Asura_Vector_3(fRadius,fRadius,fRadius));
	if (!Asura_Module_System::DoesBBIntersectVisibleModule(xBB, &pxPhoton->m_iLastKnownModule))
	{
		return;
	}
#endif//!defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)

	pxPhoton->RenderLightVolume();
}




void SetZBufferStates(const Asura_Vector_3 &xPos, bool bInside)
{	
	
	// The shadow code changes some states directly, so the Asura state may be invalid
	// Changing the state twice here is a workaround for that.
	Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_NONE);
	Asura_Render::CurrentStates.RequestZBufferCompareMode( ASURA_ZBUFFERCMP_ALWAYS);

	if (bInside)
	{
		Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_CW );
		Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_GREATER);		
	}
	else
	{	
		Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_CCW );		
		Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
	}	
}
 




class Asura_Xbox360_DeferredShading_RenderPhotons_Callback_Data
{
public:
	Asura_Vector_3     m_xCameraPos;		
#if !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	Asura_Bounding_Box m_xBoundingBox;
#endif
	bool			   m_bWithDarkeningShadows;
	bool			   m_bWithRealShadows;
	bool			   m_bWithNoShadows;
};


void Asura_Xbox360_DeferredShading::RenderPhotons_Callback( Asura_Photon* pxPhoton, void* pData )
{		
	Asura_Xbox360_DeferredShading_RenderPhotons_Callback_Data* pxData = static_cast< Asura_Xbox360_DeferredShading_RenderPhotons_Callback_Data* >(pData);
	const bool				bUseAngleLimits	= pxPhoton->GetLimitAnglesEnabled();
	const Asura_Vector_3*	pxCuboidVerts	= NULL;
	bool					bInside			= false;
	int						iSphereLOD		= 0;

	if (!pxPhoton->IsActive())
	{
		return;
	}

	
	float fRadius = pxPhoton->GetRange();
	if( fRadius <= 0.0f)
	{
		return;
	}


	// Check this light has one of the types of shadows that we're rendering this pass
	bool bAccept = false;
	uint32 uFlags =  pxPhoton->GetShadowParameters().m_uFlags;

	if (pxData->m_bWithRealShadows)
	{
		if ((uFlags & (ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS|ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS)) && ((uFlags & ASURA_SHADOWFLAG_IS_DARKENING_PASS) == 0))
		{
			if (uFlags & ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS)
			{
				if ((uFlags & ASURA_SHADOWFLAG_CONTRIBUTES_TO_AVERAGE_SHADOW) == 0)
				{
					bAccept = true;
				}
			} else {
				bAccept = true;
			}
		}
	}

	if (pxData->m_bWithDarkeningShadows)
	{
		if (((uFlags & (ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS|ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS)) && (uFlags & ASURA_SHADOWFLAG_IS_DARKENING_PASS)) || (uFlags & ASURA_SHADOWFLAG_CONTRIBUTES_TO_AVERAGE_SHADOW))
		{
			bAccept = true;
		}			
	}

	if (pxData->m_bWithNoShadows)
	{
		if (0 == (uFlags & ((ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS|ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS) | ASURA_SHADOWFLAG_CONTRIBUTES_TO_AVERAGE_SHADOW)))
		{
			bAccept = true;
		}			
	}

	if (!bAccept)
	{		
		return;
	}

	// Photons now support a distance fade-out, like the projectors
	float fDistanceFade = 1.0f;
	if (pxPhoton->GetDistanceFadeEnable())
	{	
		Asura_Vector_3 xOfs = pxPhoton->GetPosition()-pxData->m_xCameraPos;
		float fDist = xOfs.Magnitude();
		if (fDist> pxPhoton->GetDistanceFadeStart())
		{
			fDistanceFade = 1.0f - ((fDist - pxPhoton->GetDistanceFadeStart())/pxPhoton->GetDistanceFadeDist());
		}

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

#if defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	if ( !pxPhoton->IsVisible() )
	{
		return;
	}
	
	const Asura_Vector_3& xPos = pxPhoton->GetPosition();		

#else// !defined(ASURA_USE_NEW_VISIBILITY_SYSTEM )

	Asura_Vector_3 xPos = pxPhoton->GetPosition();		
	const bool bIsPhotonInBoundingBox = pxData->m_xBoundingBox.IntersectsSphere( xPos, fRadius);	
	if( false == bIsPhotonInBoundingBox)
	{
		return;
	}

	const bool bIsPhotonInFrustrum = Asura_Camera::IsSphereInFrustum( xPos, fRadius);
	if( false == bIsPhotonInFrustrum)
	{
		return;
	}


	Asura_Bounding_Box xBB;
	xBB.Reset();
	xBB.Set(xPos + Asura_Vector_3(fRadius,fRadius,fRadius));
	xBB.Set(xPos - Asura_Vector_3(fRadius,fRadius,fRadius));
	if (!Asura_Module_System::DoesBBIntersectVisibleModule(xBB))
	{
		return;
	}
#endif// !defined(ASURA_USE_NEW_VISIBILITY_SYSTEM)

	PIXBeginNamedEvent( D3DCOLOR_XRGB( 0xC0,  0xC0, 0 ), "Deferred Photon @ %.1f %.1f %.1f", xPos.GetX(), xPos.GetY(), xPos.GetZ());	

	Asura_Vector_3 xUp, xRight;
	Asura_Camera::GetUpVector(xUp);
	Asura_Camera::GetRightVector(xRight);
	Asura_Vector_3 xVerts[8];

	float		   fMeshScale;
	Asura_Vector_3 xMeshPos;

	if (pxPhoton->GetCustomLightVolumeEnable())
	{
		// Rendering a cuboid in world-space
		pxCuboidVerts	= xVerts;
		xMeshPos.x = 0;
		xMeshPos.y = 0;
		xMeshPos.z = 0;
		fMeshScale = 1;

		// Custom bounding box
		const Asura_Bounding_Box& xBB = pxPhoton->GetCustomLightVolume();
		const Asura_Quat& xOrient = pxPhoton->GetCustomLightVolumeOrient();

		xVerts[0].x = xBB.MinX;		xVerts[0].y = xBB.MinY;		xVerts[0].z = xBB.MinZ;
		xVerts[1].x = xBB.MaxX;		xVerts[1].y = xBB.MinY;		xVerts[1].z = xBB.MinZ;
		xVerts[2].x = xBB.MaxX;		xVerts[2].y = xBB.MaxY;		xVerts[2].z = xBB.MinZ;
		xVerts[3].x = xBB.MinX;		xVerts[3].y = xBB.MaxY;		xVerts[3].z = xBB.MinZ;
		xVerts[4].x = xBB.MinX;		xVerts[4].y = xBB.MinY;		xVerts[4].z = xBB.MaxZ;
		xVerts[5].x = xBB.MaxX;		xVerts[5].y = xBB.MinY;		xVerts[5].z = xBB.MaxZ;
		xVerts[6].x = xBB.MaxX;		xVerts[6].y = xBB.MaxY;		xVerts[6].z = xBB.MaxZ;
		xVerts[7].x = xBB.MinX;		xVerts[7].y = xBB.MaxY;		xVerts[7].z = xBB.MaxZ;

		// It's an oriented box, so rotate the verts	
		Asura_Vector_3 xCentre = xBB.CalculateCentre();		
		for(int i=0;i<8;i++)
		{
			xVerts[i] -= xCentre;
			xVerts[i] = xOrient.RotateVector(xVerts[i]);
			xVerts[i] += xCentre;
		}

		// Are we inside the volume?

		//need to get position relative to volume		
		Asura_Vector_3 xRelative = pxData->m_xCameraPos - xCentre;

		//and rotate into the space of the volume
		Asura_Quat xOrientInverse = xOrient;
		xOrientInverse.Invert();
		xRelative = xOrientInverse.RotateVector(xRelative);		
		Asura_Vector_3 xTestPos = xRelative+xCentre;

		float fAdjust = 0.1f;
		bInside = ((xTestPos.x>=xBB.MinX-fAdjust && xTestPos.x<=xBB.MaxX+fAdjust) &&
			(xTestPos.y>=xBB.MinY-fAdjust && xTestPos.y<=xBB.MaxY+fAdjust) &&
			(xTestPos.z>=xBB.MinZ-fAdjust && xTestPos.z<=xBB.MaxZ+fAdjust));

	}
	else
	{	
		if (Asura_DeferredShading::GetUseCubesForPhotons())
		{
			// Rendering a cube in world-space
			pxCuboidVerts	= xVerts;
			xMeshPos.x = 0;
			xMeshPos.y = 0;
			xMeshPos.z = 0;
			fMeshScale = 1;

			// Standard axis-aligned cube
			for(int i=0;i<8;i++)
			{
				static const float afUp[8]    = {-1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f, 1.0f,  1.0f};
				static const float afRight[8] = {-1.0f,  1.0f,  1.0f, -1.0f, -1.0f,  1.0f, 1.0f, -1.0f};
				static const float afFwd[8]   = {-1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  1.0f, 1.0f,  1.0f};				

				xVerts[i].x = xPos.x + (afRight[i]*fRadius);
				xVerts[i].y = xPos.y + (afUp[i]*fRadius);
				xVerts[i].z = xPos.z + (afFwd[i]*fRadius);	
			}

			// Are we inside the volume?
			const Asura_Vector_3 &xCameraPos = pxData->m_xCameraPos;
			float fRadiusAdjusted = fRadius+0.1f;
			bInside = ((xCameraPos.x>=xPos.x-fRadiusAdjusted && xCameraPos.x<=xPos.x+fRadiusAdjusted) &&
				(xCameraPos.y>=xPos.y-fRadiusAdjusted && xCameraPos.y<=xPos.y+fRadiusAdjusted) &&
				(xCameraPos.z>=xPos.z-fRadiusAdjusted && xCameraPos.z<=xPos.z+fRadiusAdjusted));
		}
		else
		{
			// Rendering a sphere - use the offset+scale
			pxCuboidVerts	= NULL;
			xMeshPos.x = xPos.x;
			xMeshPos.y = xPos.y;
			xMeshPos.z = xPos.z;
			fMeshScale = fRadius;

			// Are we inside the volume?
			const Asura_Vector_3 &xCameraPos = pxData->m_xCameraPos;
			float fRadiusAdjusted = (fRadius*1.025f)+0.1f;
			Asura_Vector_3 xOfs = xCameraPos - xPos;
			float fDist = xOfs.Normalise();
			bInside = ( fDist<fRadiusAdjusted );
			if( bInside && bUseAngleLimits && dvs_bOptimiseLimitedPhotonRender )
			{
				// light direction is apparently the opposite from what you would expect and goes towards the light
				const Asura_Vector_3	xLightDirection(pxPhoton->GetLightDirection());
				const float				fLimitAngleOuter	= pxPhoton->GetLimitAngleOuter();

				if (fLimitAngleOuter >= 0.0f)
				{
					const float				fCosDifference		= -(xLightDirection * xOfs);
//					Asura_OutputToDebugger_Retail(("%.3f, %.3f\n", fCosDifference, fLimitAngleOuter));
					bInside	 = fCosDifference > (fLimitAngleOuter - 0.05f);
				} 
				else 
				{
					float fDist = Asura_Maths::Cosine(-fLimitAngleOuter) * fRadius;
					// distance to plane
					float fDistBack = xLightDirection * (xCameraPos - xPos);

//					Asura_OutputToDebugger_Retail(("%.3f, %.3f\n", fDist, fDistBack));
					bInside = fDistBack < fDist;
				}
			}
			
			// Pick a sphere LOD. Switch down quickly to the low LOD, as we only need a more round
			// sphere when the camera is about to move inside it, for a more accurate inside-outside test
			iSphereLOD = (int)((fDist-fRadius)/(fRadius*0.25f));
			if (iSphereLOD>3)
			{
				iSphereLOD = 3;
			}		
			else if (iSphereLOD<0)
			{
				iSphereLOD = 0;
			}
		}
	}

	int iEffectPass;

	if (pxPhoton->GetIsDirectional())
	{
		iEffectPass = DeferredPhoton_Infinite;
	}
	else
	{
		if(bUseAngleLimits)
		{
			if(pxPhoton->GetProjCubeTextureIndex() == ASURA_TEXTURE_NONE)
			{
				iEffectPass	= (dvs_bOptimiseLimitedPhotonRender)
							? DeferredPhoton_OptimisedLimitedPhoton
							: DeferredPhoton_LimitedPhoton;
			}
			else
			{
				iEffectPass	= (dvs_bOptimiseLimitedPhotonRender)
							? DeferredPhoton_OptimisedLimitedPhoton_CubeMap
							: DeferredPhoton_LimitedPhoton_CubeMap;
			}
		}
		else
		{
			if(pxPhoton->GetProjCubeTextureIndex() == ASURA_TEXTURE_NONE)
			{
				iEffectPass	= (dvs_bPhotonsUseIndexedSphere)
							? DeferredPhoton_IndexedPhoton
							: DeferredPhoton_Photon;
			}
			else
			{
				iEffectPass	= (dvs_bPhotonsUseIndexedSphere)
							? DeferredPhoton_IndexedPhoton_CubeMap
							: DeferredPhoton_Photon_CubeMap;
			}
		}
	}

	// -----------------------------------------------------------------------------------------------------------------------
	// 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();

	if (bUseStencil)
	{		
		Asura_Render::CurrentStates.RequestAlphaTest( false );
		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
		Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_NONE );
		Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_ALWAYS);		
		// need to make sure the last bound RT was 32bpp as the hardware wont do fast z writes with a 64bit RT!
		Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, Asura_Xbox360_Graphics::GetPostToneMapRT());
		Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, NULL);

		SetShaderParameters(pxPhoton, xMeshPos, fMeshScale, fDistanceFade);

		for (int iPass=0; iPass<!bInside + 1; iPass++)
		{
			float afTemp[4];

			s_pxDeferredPhotonEffect->Begin(0, iEffectPass);	

			afTemp[0] = xMeshPos.x;
			afTemp[1] = xMeshPos.y;
			afTemp[2] = xMeshPos.z;
			afTemp[3] = fMeshScale;
			s_pxDeferredPhotonEffect->SetVector( s_xMeshScaleAndPosHandle, afTemp);

			s_pxDeferredPhotonEffect->Commit();

			switch(iPass)
			{		
			case 0:		
				// Back faces
				Asura_Render::CurrentStates.RequestStencilEnabled(true);
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILFUNC,			D3DCMP_ALWAYS );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILREF,			ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILWRITEMASK,		ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILMASK,			ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILPASS,			D3DSTENCILOP_ZERO );				
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILZFAIL,			D3DSTENCILOP_REPLACE);
				if (bInside)
				{
					Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILWRITEENABLE, TRUE );
					Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILFUNC,		D3DHSCMP_EQUAL );
					Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILREF,			0x0 );
				} else {
					Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILWRITEENABLE, FALSE );
				}

				Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_CW );
				Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		

				break;

			case 1:
				// Front faces
				Asura_Render::CurrentStates.RequestStencilEnabled(true);
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILFUNC,			D3DCMP_ALWAYS );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILREF,			ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILWRITEMASK,		ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILMASK,			ASURA_STENCIL_MASK_PHOTON );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILPASS,			D3DSTENCILOP_DECR );				
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILZFAIL,			D3DSTENCILOP_REPLACE );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILWRITEENABLE, TRUE );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILFUNC,		D3DHSCMP_NOTEQUAL );
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILREF,			0 );


				Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_CCW );		
				Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		

				break;
			}			

			Asura_Xbox360_Graphics::GetDevice()->SetPixelShader(NULL);
			RenderPhotonPrimitivies(pxCuboidVerts, iSphereLOD, bUseAngleLimits);
			s_pxDeferredPhotonEffect->End();				
		}
		Asura_Xbox360_Graphics::GetDevice()->FlushHiZStencil( D3DFHZS_ASYNCHRONOUS );


		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALL);
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILENABLE, FALSE );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILWRITEENABLE,	FALSE );
		Asura_Render::CurrentStates.RequestStencilEnabled(false);
		Asura_Render::CurrentStates.RequestAlphaTest( false );
		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, Asura_Xbox360_Graphics::GetFinalRT());
	}


	// -----------------------------------------------------------------------------------------------------------------------
	// DMR - 03/04/07 - Shadow support.
	// If this light is a shadow caster, we need to build the shadow mask now (in the destination alpha channel), and set the
	// appropriate blending mode.
	//
	// Before projecting the shadows, we need to clear the area of the shadow mask that's used by this light - we do that by
	// rendering the light volume, with colour writes only enabled to the alpha channel
	// -----------------------------------------------------------------------------------------------------------------------
	bool bShadows = false;

	if (pxPhoton->GetShadowParameters().m_uFlags & (ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS|ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS))
	{
		if (Asura_Shadow_System::LightHasActiveShadows(ASURA_SHADOW_LIGHTSOURCE_PHOTON,pxPhoton->GetHandle()))
		{
			bShadows = true;
		}
	}

	if (bShadows)
	{	
		float afTemp[4];

		// Are there any active shadows for this light?
		ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_DEFERRED_PHOTONS);
		ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_APPLYSHADOWS);

		// First, clear the destination alpha to 0 for the area affected by our shadows
		// (note - Might be worth moving this into one of the stencil-filling passes?)
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF); 			
		SetZBufferStates(xPos, bInside);


		int iPass = bUseAngleLimits && dvs_bOptimiseLimitedPhotonRender ? DeferredPhoton_ClearDestAlpha_LimitedPhoton : (dvs_bPhotonsUseIndexedSphere ? DeferredPhoton_ClearDestAlpha_IndexedPhoton : DeferredPhoton_ClearDestAlpha_Photon);
		s_pxDeferredPhotonEffect->Begin(0, iPass);	
		{
			SetShaderParameters(pxPhoton, xMeshPos, fMeshScale, fDistanceFade);

			afTemp[0] = xMeshPos.x;
			afTemp[1] = xMeshPos.y;
			afTemp[2] = xMeshPos.z;
			afTemp[3] = fMeshScale;
			s_pxDeferredPhotonEffect->SetVector( s_xMeshScaleAndPosHandle, afTemp);
			s_pxDeferredPhotonEffect->Commit();


			if (bUseStencil)
			{		
				// Set up the stencil states, if we're using the stencil buffer
				Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILENABLE,	TRUE );
				Asura_Xbox360_Graphics::GetDevice()->FlushHiZStencil( D3DFHZS_ASYNCHRONOUS );
			}

			RenderPhotonPrimitivies(pxCuboidVerts, iSphereLOD, bUseAngleLimits);
		}
		s_pxDeferredPhotonEffect->End();

		// Then render the shadow mask into the destination alpha
		Asura_Shadow_System::BuildShadowMask(ASURA_SHADOW_LIGHTSOURCE_PHOTON, pxPhoton->GetHandle());
			
		if (bUseStencil)
		{		
			// Disable stencil tests, if we're using stencil
			Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILENABLE,	FALSE );		
		}

		// Re-enable RGB writes
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState( D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA|D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_BLUE);

		// Restore the renderstates we need for the deferred photon pass
		Asura_Xbox360_Graphics::GetDevice()->SetVertexDeclaration( s_pxDeferredPhotonVertexDeclaration );			

		Asura_Render::CurrentStates.RequestAlphaTest( false );	
		Asura_Render::CurrentStates.RequestZBufferEnabled( true );		
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled( false );
		Asura_Render::CurrentStates.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			
		D3DBLENDSTATE xBlendState;
		xBlendState.BlendOp = xBlendState.BlendOpAlpha = D3DBLENDOP_ADD;
		xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_INVDESTALPHA;
		xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ONE;
		Asura_Xbox360_Graphics::GetDevice()->SetBlendState( 0, xBlendState );	

		ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_APPLYSHADOWS);
		ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_DEFERRED_PHOTONS);		
	}

	
	// Set up the stencil states, if we're using the stencil buffer
	if (bUseStencil)
	{
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );					
		Asura_Render::CurrentStates.RequestStencilEnabled(true);
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILFUNC,				bInside ? D3DCMP_NOTEQUAL : D3DCMP_EQUAL );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILREF,				0x00 );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILWRITEMASK,		ASURA_STENCIL_MASK_PHOTON );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_STENCILMASK,			ASURA_STENCIL_MASK_PHOTON );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILWRITEENABLE,		FALSE );
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILENABLE,			TRUE );		
		Asura_Xbox360_Graphics::GetDevice()->FlushHiZStencil( D3DFHZS_ASYNCHRONOUS );
	}


	// Actually render the light
	s_pxDeferredPhotonEffect->Begin(0, iEffectPass);
	{
		if (!bUseStencil)
		{
			SetShaderParameters(pxPhoton, xMeshPos, fMeshScale, fDistanceFade);
		}

		for (int iTiling = 0; iTiling < Asura_Xbox360_Graphics::GetNumberOfPredicatedTilePasses(); ++iTiling)
		{
			if (Asura_Xbox360_Graphics::IsTiling2())
			{
				Asura_Xbox360_Graphics::GetDevice()->SetPredication(D3DPRED_TILE(iTiling));
				Asura_Xbox360_Graphics::CorrectVPOSForTiling(iTiling);
			}

			s_pxDeferredPhotonEffect->Commit();
			SetZBufferStates(xPos, bInside);
			Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );
			RenderPhotonPrimitivies(pxCuboidVerts, iSphereLOD, bUseAngleLimits);
		}
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, FALSE );		

		if (Asura_Xbox360_Graphics::IsTiling2())
		{
			Asura_Xbox360_Graphics::GetDevice()->SetPredication(0);
		}
	}
 	s_pxDeferredPhotonEffect->End();

	if (bUseStencil)
	{
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HISTENCILENABLE, FALSE );
		Asura_Render::CurrentStates.RequestStencilEnabled(false);
	}
	

	if (bShadows)
	{
		// If we've been rendering with shadows, restore states, so we aren't doing the multiply by the dest alpha	
		D3DBLENDSTATE xBlendState;
		xBlendState.BlendOp = xBlendState.BlendOpAlpha = D3DBLENDOP_ADD;
		xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_ONE;
		xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_ONE;
		Asura_Xbox360_Graphics::GetDevice()->SetBlendState( 0, xBlendState );
	}

	PIXEndNamedEvent(); 
	
}


void Asura_Xbox360_DeferredShading::SetShaderParameters(Asura_Photon *pxPhoton, Asura_Vector_3 &xMeshPos, float fMeshScale, float fDistFade)
{
	D3DXMATRIX xViewMatrix;
	D3DXMATRIX xViewMatrixInv;
	xViewMatrix = Asura_Xbox360_Graphics::GetViewMatrix();	
	D3DXMatrixInverse(&xViewMatrixInv,NULL,&xViewMatrix);
	FXLEffect *pxFXLEffect = s_pxDeferredPhotonEffect->GetInterface();
	pxFXLEffect->SetMatrixF(s_xInvViewMatrixHandle,	xViewMatrixInv);	

	float afCameraTanHalfFOV[4];
	float fCameraTanHalfVertFOV	= Asura_Maths::Tangent( Asura_Camera::GetVerticalFieldOfView() * 0.5f );
	float fCameraTanHalfHorizFOV= fCameraTanHalfVertFOV * Asura_Camera::GetXOverYAspectRatio();
	afCameraTanHalfFOV[0] = fCameraTanHalfHorizFOV;
	afCameraTanHalfFOV[1] = fCameraTanHalfVertFOV;
	afCameraTanHalfFOV[2] = 0.0f;
	afCameraTanHalfFOV[3] = 0.0f;
	pxFXLEffect->SetVectorF(s_xCameraTanHalfFOVHandle, afCameraTanHalfFOV);

	float afTwoOverScreenDimensions[2];
	afTwoOverScreenDimensions[0] = 2.0f / Asura_Viewport::GetWidth();
	afTwoOverScreenDimensions[1] = 2.0f / Asura_Viewport::GetHeight();	
	pxFXLEffect->SetVectorF(s_xTwoOverScreenDimensionsHandle, afTwoOverScreenDimensions);

	pxFXLEffect->SetSampler(s_xNormalBufferHandle,		Asura_Xbox360_Graphics::GetNormalTexture());
	pxFXLEffect->SetSampler(s_xDepthMtlBufferHandle,	Asura_Xbox360_Graphics::GetDepthMtlTexture());
	pxFXLEffect->SetSampler(s_xAlbedoBufferHandle,		Asura_Xbox360_Graphics::GetAlbedoTexture());		

	pxFXLEffect->SetSampler( s_xMaterialsTextureHandle, Asura_Xbox360_MaterialResponse_System::GetDataTexture() );
	Asura_Vector_3 xCameraPos = *Asura_Camera::GetPosition();

	pxFXLEffect->SetBool(s_xUseSpecularMapHandle, true);



	
	bool bUseProjCube = (ASURA_TEXTURE_NONE != pxPhoton->GetProjCubeTextureIndex());

	if (bUseProjCube)
	{	
		s_pxDeferredPhotonEffect->SetTexture(s_xProjCubeTextureHandle, pxPhoton->GetProjCubeTextureIndex());

		bool bUseDualProjCube = (ASURA_TEXTURE_NONE != pxPhoton->GetProjCubeBlurredTextureIndex());

		if (bUseDualProjCube)
		{
			s_pxDeferredPhotonEffect->SetTexture(s_xProjCubeBlurredTextureHandle, pxPhoton->GetProjCubeBlurredTextureIndex());
			pxFXLEffect->SetFloat(s_xProjCubeLerpDistHandle, pxPhoton->GetProjCubeLerpDist());
		}


		const Asura_Matrix_3x3& xPhotonOrient = pxPhoton->GetLightOrientation();
		D3DXMATRIX xOrient;
		for(int i=0;i<3;i++)
		{
			for(int j=0;j<3;j++)
			{
				xOrient.m[i][j] = xPhotonOrient.Element[i][j];				
			}			
		}

		pxFXLEffect->SetMatrixF(s_xLightOrientationHandle,xOrient);
	}
	else
	{
		pxFXLEffect->SetSampler(s_xProjCubeTextureHandle, NULL);
		pxFXLEffect->SetSampler(s_xProjCubeBlurredTextureHandle, NULL);
		
	}



	float afTemp[4];

	afTemp[0]	= pxPhoton->GetFalloffScale();
	afTemp[1]	= pxPhoton->GetFalloffPower();
	pxFXLEffect->SetVectorF(s_xFalloffHandle, afTemp);

	if(pxPhoton->GetLimitAnglesEnabled())
	{
		const float	fCosInnerAngle	= pxPhoton->GetLimitAngleInner();
		const float	fCosOuterAngle	= pxPhoton->GetLimitAngleOuter();

		// Handle angular fade - same way as spotlights
		afTemp[0]	= -fCosOuterAngle;
		afTemp[1]	= -(fCosOuterAngle - fCosInnerAngle);
		afTemp[2]	= Asura_Maths::ArcCosine(fCosOuterAngle);

		if (afTemp[1] != 0.0f)
		{
			afTemp[1] = 1.0f / afTemp[1];
		}
		else
		{
			// Nasty case where Inner angle == Outer angle
			afTemp[1] = 1e15f;
		}

		pxFXLEffect->SetVectorF(s_xLimitAnglesHandle, afTemp);

		if (dvs_bOptimiseLimitedPhotonRender )
		{
			// beware the transpose...
			const Asura_Matrix_3x3& xPhotonOrient = pxPhoton->GetLightOrientation();
			D3DXMATRIX xOrient;
			for(int i=0;i<3;i++)
			{
				for(int j=0;j<3;j++)
				{
					xOrient.m[i][j] = xPhotonOrient.Element[j][i];				
				}			
			}

			pxFXLEffect->SetMatrixF(s_xLightOrientationHandle,xOrient);
		}

	}

	const Asura_Vector_3& xDir = pxPhoton->GetLightDirection();
	afTemp[0] = xDir.x;
	afTemp[1] = xDir.y;
	afTemp[2] = xDir.z;
	pxFXLEffect->SetVectorF(s_xLightDirectionHandle, afTemp);

	const Asura_Vector_3& xPosition = pxPhoton->GetPosition();
	afTemp[0] = xPosition.GetX();
	afTemp[1] = xPosition.GetY();
	afTemp[2] = xPosition.GetZ();
	afTemp[3] = 1.0f / pxPhoton->GetRange();
	s_pxDeferredPhotonEffect->SetVector( s_xLightPosScaleHandle, afTemp );

	const float fR = fDistFade * pxPhoton->GetRedComponent();
	const float fG = fDistFade * pxPhoton->GetGreenComponent();
	const float fB = fDistFade * pxPhoton->GetBlueComponent();
	afTemp[0] = fR;
	afTemp[1] = fG;
	afTemp[2] = fB;
	afTemp[3] = (fR + fG + fB) / 3.0f;
	afTemp[0] *= 2.0f;
	afTemp[1] *= 2.0f;
	afTemp[2] *= 2.0f;
	s_pxDeferredPhotonEffect->SetVector( s_xLightColourHandle, afTemp );

	afTemp[0] = xMeshPos.x;
	afTemp[1] = xMeshPos.y;
	afTemp[2] = xMeshPos.z;
	afTemp[3] = fMeshScale;
	s_pxDeferredPhotonEffect->SetVector( s_xMeshScaleAndPosHandle, afTemp);
}


void Asura_Xbox360_DeferredShading::RenderPhotons(bool bWithDarkeningShadows, bool bWithRealShadows, bool bWithNoShadows)
{
	ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_DEFERRED_PHOTONS);	

#if !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	Asura_Bounding_Box xBB;

	// Make a bounding box around all visible modules
	xBB.MinX=xBB.MinY=xBB.MinZ = 1e12f;
	xBB.MaxX=xBB.MaxY=xBB.MaxZ = -1e12f;	
	{
		const u_int uModuleCount = Asura_Module_System::GetNumberOfModules();
		for( u_int uModuleIndex = 0; uModuleIndex < uModuleCount; uModuleIndex += 1)
		{
			if( false == Asura_Module_System::IsModuleVisible( uModuleIndex))
			{
				continue;
			}
			Asura_Bounding_Box xModuleBoundingBox = Asura_Module_System::GetBoundingBoxOfModule( uModuleIndex);
			xModuleBoundingBox.Displace( Asura_Module_System::GetPositionOfModule(uModuleIndex) );

			xBB.Set( xModuleBoundingBox);
		}
	}	
#endif// !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	
	Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, Asura_Xbox360_Graphics::GetFinalRT() );
	Asura_Xbox360_Graphics::GetDevice()->SetDepthStencilSurface( Asura_Xbox360_Graphics::GetFinalDepth() );

	Asura_Xbox360_DeferredShading_RenderPhotons_Callback_Data xCallbackData;
#if !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	xCallbackData.m_xBoundingBox		  = xBB;
#endif
	xCallbackData.m_bWithDarkeningShadows = bWithDarkeningShadows;
	xCallbackData.m_bWithRealShadows      = bWithRealShadows;
	xCallbackData.m_bWithNoShadows		  = bWithNoShadows;

	Asura_Render::CurrentStates.RequestAlphaTest( false );	

	// I've had some problems with Asura's cached renderstates being out-of-sync with D3D, so we change the
	// state twice to force the change...
	Asura_Render::CurrentStates.RequestZBufferEnabled( !Asura_DeferredShading::IsUsingZBufferOptimization());		
	Asura_Render::CurrentStates.RequestZBufferEnabled( Asura_DeferredShading::IsUsingZBufferOptimization());		

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( false );
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING); 

	Asura_Xbox360_Graphics::GetDevice()->SetVertexDeclaration( s_pxDeferredPhotonVertexDeclaration );
		
	PIXBeginNamedEvent( D3DCOLOR_XRGB( 0xC0,  0xC0, 0 ), "Deferred Photons");
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);

	// Traverse all photons, rendering the visible ones
	xCallbackData.m_xCameraPos = *Asura_Camera::GetPosition();
	Asura_Photon_System::TraverseAll( Asura_Xbox360_DeferredShading::RenderPhotons_Callback, static_cast< void*>( &xCallbackData), false, true);

	PIXEndNamedEvent(); 
	

	Asura_Render::CurrentStates.RequestZBufferEnabled( true );				
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );
	Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_CCW );
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF); 
	Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		

	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_DEFERRED_PHOTONS);
}





void Asura_Xbox360_DeferredShading::RenderDebugOutput(void)
{
	// Debug rendering
	if (Asura_DeferredShading::IsRenderingPhotonOutlines())
	{	
		Asura_Photon_System::TraverseAll( Asura_Xbox360_DeferredShading::RenderPhotonOutlines_Callback, NULL, false, true);
	}


	if (Asura_DeferredShading::IsShowingDeferredBuffers())
	{
		DebugRenderDeferredBuffers();
	}
}


void Asura_Xbox360_DeferredShading::DebugRenderDeferredBuffers(void)
{
	SCOPEDPIXEVENT(( 0, "Show Deferred Buffers" ));

	// Blit to the screen:
	//
	// Base    Normal
	// Albedo  Specular

	// Account for half-texel offset
	const float fTexOffsX = 0.5f / static_cast< float >( Asura_Viewport::GetWidth() );
	const float fTexOffsY = 0.5f / static_cast< float >( Asura_Viewport::GetHeight() );

	Asura_Overlay_Quad xQuad;
	xQuad.SetRectUV(fTexOffsX, fTexOffsY, 1.0f+fTexOffsX, 1.0f+fTexOffsY);

	Direct3D_Device* const pxDevice = Asura_Xbox360_Graphics::GetDevice();

	float fW = (float)(Asura_Viewport::GetWidth()/2);
	float fH = (float)(Asura_Viewport::GetHeight()/2);

	// Resolve final view
	Asura_Xbox360_Graphics::Resolve( Asura_Xbox360_Graphics::GetPostToneMapTexture() );

	// Base
	{
		SCOPEDPIXEVENT(( 0, "Base" ));

		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
		xQuad.Colour = 0xffffffff;
		xQuad.SetRectXY(0,0,fW,fH);
		Asura_Xbox360_QuadOverlayRenderer::RenderEx(xQuad,
			Asura_Xbox360_Graphics::GetPostToneMapTexture(), NULL);

		Asura_Font_System::Print( 0.0f, 0.0f, 0.5f, 0xFFFFFFFF, 0, "Base" );
	}

	// Albedo
	{
		SCOPEDPIXEVENT(( 0, "Albedo" ));

		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
		xQuad.Colour = 0xffffffff;
		xQuad.SetRectXY(0,fH,fW,fH);

		Asura_Xbox360_QuadOverlayRenderer::RenderEx(xQuad,
			Asura_Xbox360_Graphics::GetAlbedoTexture(), NULL);

		Asura_Font_System::Print( 0.0f, fH, 0.5f, 0xFFFFFFFF, 0, "Albedo" );
	}

	// Normal
	{
		SCOPEDPIXEVENT(( 0, "Normal" ));

		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
		xQuad.Colour = 0xffffffff;
		xQuad.SetRectXY(fW,0,fW,fH);

		pxDevice->SetSamplerState_Inline( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
		pxDevice->SetSamplerState_Inline( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );

		Asura_Xbox360_QuadOverlayRenderer::RenderEx(xQuad,
			Asura_Xbox360_Graphics::GetNormalTexture(), s_pxRenderNormalBufferEffect);

		pxDevice->SetSamplerState_Inline( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		pxDevice->SetSamplerState_Inline( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );

		Asura_Font_System::Print( fW, 0.0f, 0.5f, 0xFF000000, 0, "Normals" );
	}

	// Specular
	{
		SCOPEDPIXEVENT(( 0, "Specular" ));

		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
		xQuad.Colour = 0xffffffff;
		xQuad.SetRectXY(fW,fH,fW,fH);

		Asura_Xbox360_QuadOverlayRenderer::RenderEx(xQuad,
			Asura_Xbox360_TextureManagement::GetTexture(ASURA_TEXTURE_NONE)->GetD3DTexture(), NULL);

		// We want dest col * src alpha + 0
		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
		D3DBLENDSTATE xBlendState;
		xBlendState.BlendOp = xBlendState.BlendOpAlpha = D3DBLENDOP_ADD;
		xBlendState.SrcBlendAlpha = D3DBLEND_SRCALPHA;
		xBlendState.DestBlendAlpha = D3DBLEND_INVSRCALPHA;
		xBlendState.SrcBlend = D3DBLEND_ZERO;
		xBlendState.DestBlend = D3DBLEND_SRCALPHA;
		pxDevice->SetBlendState(0,xBlendState);

		xQuad.Colour = 0xff000000;

		Asura_Xbox360_QuadOverlayRenderer::RenderEx(xQuad,
			Asura_Xbox360_Graphics::GetAlbedoTexture(), NULL);

		xBlendState.SrcBlend = xBlendState.SrcBlendAlpha = D3DBLEND_SRCALPHA;
		xBlendState.DestBlend = xBlendState.DestBlendAlpha = D3DBLEND_INVSRCALPHA;
		pxDevice->SetBlendState(0,xBlendState);

		Asura_Font_System::Print( fW, fH, 0.5f, 0xFFFF0000, 0, "Specular Mask" );
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Xbox360_DeferredShading::RenderPhotonPrimitivies
// Author       : Andy Tate
// Date         : 13-May-2009
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_DeferredShading::RenderPhotonPrimitivies(const Asura_Vector_3* pxCuboidVerts,
															const int iSphereLOD, const bool bUseAngleLimits)
{
	if(pxCuboidVerts == NULL)
	{		
		if(bUseAngleLimits)
		{
			if(dvs_bOptimiseLimitedPhotonRender)
			{
				const int	iNumIndices	= s_aiIndexedSphereSegmentNumIndices[iSphereLOD];
				const int	iNumVerts	= s_aiIndexedSphereSegmentNumVerts[iSphereLOD];

				s_apxIndexedSphereSegmentVB[iSphereLOD]->Bind();
				s_apxIndexedSphereSegmentIB[iSphereLOD]->Bind();
				Asura_Xbox360_Graphics::GetDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, iNumVerts, 0, iNumIndices - 2);
			}
			else
			{
				s_apxSphereVB[iSphereLOD]->Bind();
				Asura_Xbox360_Graphics::GetDevice()->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,s_aiSphereNumVerts[iSphereLOD]-2);
			}
		}
		else
		{
			if(dvs_bPhotonsUseIndexedSphere)
			{
				const int	iNumIndices	= s_aiIndexedSphereNumIndices[iSphereLOD];
				const int	iNumVerts	= s_aiIndexedSphereNumVerts[iSphereLOD];

				s_apxIndexedSphereVB[iSphereLOD]->Bind();
				s_apxIndexedSphereIB[iSphereLOD]->Bind();
				Asura_Xbox360_Graphics::GetDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, iNumVerts, 0, iNumIndices - 2);
			}
			else
			{
				s_apxSphereVB[iSphereLOD]->Bind();
				Asura_Xbox360_Graphics::GetDevice()->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,s_aiSphereNumVerts[iSphereLOD]-2);
			}
		}
	}
	else
	{
		static const u_short ausIndices[] =
		{			
			0,1,2 ,2,3,0,
			1,5,6 ,6,2,1,
			7,6,5 ,5,4,7,
			0,3,7 ,7,4,0,
			4,5,1 ,1,0,4,
			7,3,2 ,2,6,7,
		};

		Asura_Xbox360_Graphics::GetDevice()->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST,0,8,12, (void*)ausIndices, D3DFMT_INDEX16, (void *)pxCuboidVerts, sizeof(pxCuboidVerts[0]));
	}
}

/// End of function 'Asura_Xbox360_DeferredShading::RenderPhotonPrimitivies'. ///
