///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Platform_LightShaft_System.cpp
// Author : Jake Turner
// Date   : 23-July-2007
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//
// Just stub function to make projects link
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

#include "Camera/Asura_Camera.h"
#include "Core/Memory/Asura_Mem.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"
#include "Core/Asura_Timers.h"

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



//Shaders definition
#include "Rendering/ShadersDefinition/VD/LightShaft_VD.h"
#include "Rendering/ShadersDefinition/VP/LightShaft_VP.h"
#include "Rendering/ShadersDefinition/FP/LightShaft_FP.h"
#include "Rendering/ShadersDefinition/VD/LightShaftCorona_VD.h"
#include "Rendering/ShadersDefinition/VP/LightShaftCorona_VP.h"
#include "Rendering/ShadersDefinition/FP/LightShaftCorona_FP.h"

#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
#include "Interface/Asura_CommandConsole.h"
#endif


static Asura_PS3_GCM_VertexShader*		g_pxVertexShaderShaft		= NULL;
static Asura_PS3_LightShaft_Handles*	g_pxLightShaftHandles		= NULL;
static Asura_PS3_LightShaftC_Handles*	g_pxLightShaftCHandles		= NULL;
static Asura_PS3_FragmentShader*		g_pxFragmentShaderShaft		= NULL;
static Asura_PS3_FragmentShader*		g_pxFragmentShaderCorona	= NULL;

static Asura_PS3_GCM_VertexShader*		g_pxVertexShaderCorona		= NULL;

static Asura_PS3_GCM_VertexBuffer		g_axVertexBuffers[ 4 ];
static int								g_aiNumVertices[ 4 ];
static Asura_PS3_GCM_VertexBuffer		g_xVertexBuffer;

static Asura_PS3_GCM_VertexBuffer		g_xVertexBuffersCorona;

void Asura_LightShaft_System::Platform_Initialise(void)
{
	g_pxVertexShaderShaft	= new Asura_PS3_VP_LightShaft();
	g_pxVertexShaderShaft->InitVertexBufferDeclaration( g_axVertexDeclarationShaft, VDS_ELEMENTS );
	g_pxVertexShaderCorona	= new Asura_PS3_VP_LightShaft_Corona();
	g_pxVertexShaderCorona->InitVertexBufferDeclaration( g_axVertexDeclarationCorona, VDC_ELEMENTS );

	g_pxLightShaftHandles	= new Asura_PS3_LightShaft_Handles();
	g_pxLightShaftCHandles	= new Asura_PS3_LightShaftC_Handles();

	g_pxFragmentShaderShaft	= new Asura_PS3_FragmentShader( (unsigned char *)&_binary_PS3_LightShaft_FP_bin_start, (unsigned char *)&_binary_PS3_LightShaft_FP_bin_end, g_pxLightShaftHandles );
	g_pxFragmentShaderCorona= new Asura_PS3_FragmentShader( (unsigned char *)&_binary_PS3_LightShaft_Corona_FP_bin_start, (unsigned char *)&_binary_PS3_LightShaft_Corona_FP_bin_end, g_pxLightShaftCHandles );

	for ( int iLOD = 0; iLOD < 4; ++iLOD )
	{	
		int iRadialSegments = 32;
		int iLateralSegments = 16;

		switch (iLOD)
		{
		case 1:
			iRadialSegments = 28;
			iLateralSegments = 12;
			break;
		case 2:
			iRadialSegments = 24;
			iLateralSegments = 8;
			break;
		case 3:
			iRadialSegments = 16;
			iLateralSegments = 2;
			break;
		default:
			break;
		}

		int iNumVerts = ((iRadialSegments+2) * 2) * iLateralSegments;
		g_aiNumVertices[iLOD] = iNumVerts;
		g_axVertexBuffers[iLOD].Initialise( sizeof(Asura_LightShaftVertex), iNumVerts, CELL_GCM_LOCATION_MAIN );
		Asura_LightShaftVertex *pxVerts = (Asura_LightShaftVertex*)g_axVertexBuffers[iLOD].LockForWriting();
		Asura_LightShaft_System::CreateConeVertices(iRadialSegments, iLateralSegments, pxVerts, iNumVerts);
		g_axVertexBuffers[iLOD].Unlock();
	}
	g_xVertexBuffer.Initialise( sizeof( Asura_LightShaftCoronaVertex ), 4, CELL_GCM_LOCATION_MAIN );

#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
	Asura_CommandConsole::AddCmd(L"Render.ReloadShaders.LightShaftAndCorona", ReloadShaders, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
#endif
}

void Asura_LightShaft_System::Platform_Shutdown(void)
{
	delete g_pxVertexShaderShaft;
	delete g_pxVertexShaderCorona;

	delete g_pxFragmentShaderCorona;
	delete g_pxFragmentShaderShaft;

	delete g_pxLightShaftHandles;
	delete g_pxLightShaftCHandles;

	g_pxLightShaftHandles		= NULL;
	g_pxLightShaftCHandles		= NULL;

	g_pxVertexShaderShaft		= NULL;
	g_pxFragmentShaderShaft		= NULL;
	g_pxVertexShaderCorona		= NULL;
	g_pxFragmentShaderCorona	= NULL;

	g_xVertexBuffer.Shutdown();
	for ( int iLOD = 0; iLOD < 4; ++iLOD )
	{
		g_axVertexBuffers[ iLOD ].Destroy();
	}
}

void Asura_LightShaft_System::Platform_Reset(void)
{
}

void Asura_LightShaft_System::Platform_RenderStart(void)
{
	ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__MISC, "Light Shaft System" );

	Asura_PS3_DeferredRendering::BindAsSingleRT();
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_CCW );		
	Asura_PS3_RenderingStates::RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);		
	Asura_PS3_RenderingStates::RequestZBufferEnabled(true);
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled(false);	
	Asura_PS3_RenderingStates::RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
	Asura_PS3_RenderingStates::RequestAlphaTest(false);
	Asura_PS3_RenderingStates::SetAlphaBlending( true );
}

void Asura_LightShaft_System::Platform_RenderEnd(void)
{
	ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__MISC );

}

void Asura_LightShaft_System::Platform_RenderLightShaft( Asura_LightShaft* pxLightShaft )
{
	Asura_Vector_3 xOfs = *Asura_Camera::GetPosition() - pxLightShaft->GetPosition();
	float fDist = xOfs.Magnitude();

	float fDistanceFade = 1.0f;
	if (fDist < pxLightShaft->GetCloseFadeStart())
	{
		fDistanceFade = 1.0f - ((pxLightShaft->GetCloseFadeStart() - fDist)/pxLightShaft->GetCloseFadeDist());
	}
	else if (fDist > pxLightShaft->GetDistanceFadeStart())
	{
		fDistanceFade = 1.0f - ((fDist - pxLightShaft->GetDistanceFadeStart())/pxLightShaft->GetDistanceFadeDist());
	}

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

	g_pxVertexShaderShaft->Bind();

	const Asura_Matrix_3x3& xOri = pxLightShaft->GetOrient();
	Asura_Vector_3 xDir = Asura_Vector_3(0,0,1)*pxLightShaft->GetOrient();
	Asura_Vector_3 xPos = pxLightShaft->GetPosition();

	Asura_AMatrix4x4 xPosOrient;
	xPosOrient.SetColumn0(Asura_AVector4( xOri.e11, xOri.e21, xOri.e31, 0.0f ) );
	xPosOrient.SetColumn1(Asura_AVector4( xOri.e12, xOri.e22, xOri.e32, 0.0f ) );
	xPosOrient.SetColumn2(Asura_AVector4( xOri.e13, xOri.e23, xOri.e33, 0.0f ) );
	xPosOrient.SetColumn3(Asura_AVector4( xPos.x, xPos.y, xPos.z, 1.0f ) );
	g_pxVertexShaderShaft->SetUniformParameter( VPS_PosOrient, xPosOrient.GetElements() );

	const Asura_AMatrix4x4& xViewProjectionMatrix = Asura_PS3_Graphics::GetViewProjectionMatrix();
	g_pxVertexShaderShaft->SetUniformParameter( VPS_ViewProjectionMatrix, xViewProjectionMatrix.GetElements() );

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

	g_pxVertexShaderShaft->SetUniformParameter( VPS_CameraPosition, afCamPos );

	// Near/Far colours
	Asura_RGBA xInheritedColour = pxLightShaft->GetInheritedColour();
	{
		// Cone geometry
		float xConeParams[ 4 ] = { pxLightShaft->GetNearRadius(), pxLightShaft->GetFarRadius(), pxLightShaft->GetNearDistance(), pxLightShaft->GetFarDistance() };
		g_pxVertexShaderShaft->SetUniformParameter( VPS_ConeGeometry, xConeParams );

		Asura_RGBA  xRGBA = pxLightShaft->GetNearColour();
		float xColour[ 4 ] = { xRGBA.m_fR*fDistanceFade*xInheritedColour.m_fR, xRGBA.m_fG*fDistanceFade*xInheritedColour.m_fG, xRGBA.m_fB*fDistanceFade*xInheritedColour.m_fB, xRGBA.m_fA*fDistanceFade*xInheritedColour.m_fA };
		g_pxVertexShaderShaft->SetUniformParameter( VPS_NearColour, xColour );

		xRGBA = pxLightShaft->GetFarColour();
		float xColourFar[ 4 ] = { xRGBA.m_fR*fDistanceFade*xInheritedColour.m_fR, xRGBA.m_fG*fDistanceFade*xInheritedColour.m_fG, xRGBA.m_fB*fDistanceFade*xInheritedColour.m_fB, xRGBA.m_fA*fDistanceFade*xInheritedColour.m_fA };
		g_pxVertexShaderShaft->SetUniformParameter( VPS_FarColour, xColourFar );

		// Texture params
		float xTextureParams[ 4 ] = { pxLightShaft->GetUScale(0), pxLightShaft->GetUScale(1), Asura_Maths::FloatMod(pxLightShaft->GetUScrollRate(0) * Asura_Timers::GetLocalScaledTime(), 1.0f), Asura_Maths::FloatMod(pxLightShaft->GetUScrollRate(1) * Asura_Timers::GetLocalScaledTime(), 1.0f) };
		g_pxVertexShaderShaft->SetUniformParameter( VPS_TextureParams, xTextureParams);

// 		Asura_PS3_MaterialResponse_System::SetDepthParameters( g_pxFragmentShaderShaft, FPS_DepthAdjustment );
		// can't call that function because this isn't a copy, which is going to cause problems if we want to draw 2 shafts with different textures (though this parameter will be fine since it's not instance-dependent)
		float f = Asura_Camera::GetFarClip(), n = Asura_Camera::GetNearClip();
		const float afDepthAdjustment[2] = { (f*n)/(f-n), f/(f-n) };
		g_pxFragmentShaderShaft->SetFloats( FPS_DepthAdjustment, 2, afDepthAdjustment );

		g_pxFragmentShaderShaft->SetTexture( FPS_DepthBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetZBufferAsTexture(), false );
		g_pxFragmentShaderShaft->SetTexture( FPS_TextureSampler, pxLightShaft->GetTextureIndex( 0 ) );
		g_pxFragmentShaderShaft->SetTexture( FPS_TextureSampler2, pxLightShaft->GetTextureIndex( 1 ) );
		// Draw the shaft
		// --------------------------------------------------
		// Pick a LOD
		float fSize = Asura_Maths::Max(pxLightShaft->GetFarDistance(), 2.0f*pxLightShaft->GetFarRadius());
		int iLOD = (int)(fDist*0.25f/fSize);
		iLOD = Asura_Maths::ClampToRange(iLOD,0,3);

		g_pxFragmentShaderShaft->Bind();
		g_axVertexBuffers[iLOD].Bind( g_axVertexDeclarationShaft, VDS_ELEMENTS );
		g_axVertexBuffers[iLOD].RenderTriangleStrip( g_aiNumVertices[iLOD] );
	}


	// Now draw the corona
	// --------------------------------------------------
	int iTex = pxLightShaft->GetCoronaTextureIndex();

	if (iTex>=0)
	{	
		Asura_LightShaftCoronaVertex*	xVerts;

		xVerts = ( Asura_LightShaftCoronaVertex* ) g_xVertexBuffer.LockForWriting();

		g_pxVertexShaderCorona->Bind();
		g_pxVertexShaderCorona->SetUniformParameter( VPC_PosOrient, xPosOrient.GetElements() );
		g_pxVertexShaderCorona->SetUniformParameter( VPC_ViewProjectionMatrix, xViewProjectionMatrix.GetElements() );

		float fSize;
		float fForwards;

		Asura_Vector_3 xFwd;
		xOfs.Normalise();
		float fDot = xDir*xOfs;

		float fAngleRange = pxLightShaft->GetCoronaAngleRange();
		float fAmount = fDistanceFade * ((fDot - (1.0f-fAngleRange))/fAngleRange);

		// Special feature to use these coronas for fog planes... set angle range to max,
		// and it's 'always on' with no fade up/down
		if (fAngleRange>=0.99f)
		{
			fAmount = 1.0f;
		}

		if (fAmount>0)
		{
//			Asura_PS3_MaterialResponse_System::SetDepthParameters( g_pxFragmentShaderCorona, FPC_DepthAdjustment );
			// can't call that function because this isn't a copy, which is going to cause problems if we want to draw 2 shafts with different textures (though this parameter will be fine since it's not instance-dependent)
			float f = Asura_Camera::GetFarClip(), n = Asura_Camera::GetNearClip();
			const float afDepthAdjustment[2] = { (f*n)/(f-n), f/(f-n) };
			g_pxFragmentShaderCorona->SetFloats( FPC_DepthAdjustment, 2, afDepthAdjustment );
		
			g_pxFragmentShaderCorona->SetTexture( FPC_DepthBufferSampler, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetZBufferAsTexture(), false );
			g_pxFragmentShaderCorona->SetTexture( FPC_TextureSampler, iTex );

			fSize = pxLightShaft->GetCoronaSize();
			fForwards = pxLightShaft->GetCoronaDepth();

			fSize *= (0.666f * (0.5f+fAmount));
			fForwards *= fAmount;

			Asura_RGBA xCol = pxLightShaft->GetCoronaColour();
			xCol.m_fR *= xInheritedColour.m_fR;
			xCol.m_fG *= xInheritedColour.m_fG;
			xCol.m_fB *= xInheritedColour.m_fB;
			xCol.m_fA = fAmount;

			for( int i = 0; i < 4; ++i )
			{
				const float afLeft[ 4 ]	= { -1, -1, 1,  1 };
				const float afUp[ 4 ]	= { -1,  1, 1, -1 };
				const float afU[ 4 ]	= {  0,  0, 1,  1 };
				const float afV[ 4 ]	= {  0,  1, 1,  0 };

				xVerts[ i ].xPos.x	= afLeft[ i ] * fSize;
				xVerts[ i ].xPos.y	= afUp[ i ] * fSize;
				xVerts[ i ].xPos.z	= fForwards;
				xVerts[ i ].fU		= afU[ i ];
				xVerts[ i ].fV		= afV[ i ];
				xVerts[ i ].fDepth	= fForwards;
				xVerts[ i ].fR		= xCol.m_fR;
				xVerts[ i ].fG		= xCol.m_fG;
				xVerts[ i ].fB		= xCol.m_fB;
				xVerts[ i ].fA		= xCol.m_fA;
			}

			g_pxFragmentShaderCorona->Bind();
			g_xVertexBuffer.Bind( g_axVertexDeclarationCorona, VDC_ELEMENTS );
			g_xVertexBuffer.RenderTriangleFanInline( 4, xVerts );
			Asura_PS3_Graphics::Flush();
		}
	}
}


#if defined(ASURA_PS3_ALLOW_SHADER_RELOAD)
bool Asura_LightShaft_System::ReloadShaders ( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf("Reload LightShaft_FP.fpx and LightShaft_Corona_FP.fpx");
		return true;
	}
	
	void *pTemp;
	pTemp = Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader(
		"~/Asura/PS3/Shaders/FragmentPrograms/LightShaft/LightShaft_FP.fpx",
		ASURA_HASH_ID_UNSET, ASURA_HASH_ID_UNSET, NULL, NULL, g_pxLightShaftHandles );
	if ( pTemp ) { delete g_pxFragmentShaderShaft; g_pxFragmentShaderShaft = static_cast<Asura_PS3_FragmentShader*>(pTemp); }
	
	pTemp = Asura_PS3_GCM_ShadersLibrary::LoadAndCompileShader(
		"~/Asura/PS3/Shaders/FragmentPrograms/LightShaft/LightShaft_Corona_FP.fpx",
		ASURA_HASH_ID_UNSET, ASURA_HASH_ID_UNSET, NULL, NULL, g_pxLightShaftCHandles );
	if ( pTemp ) { delete g_pxFragmentShaderCorona; g_pxFragmentShaderCorona = static_cast<Asura_PS3_FragmentShader*>(pTemp); }

	return true;
}
#endif

/// End of file. ///

