//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Fog.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Fog.h"

// Engine platform-specific.
#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/Asura_PS3_GCM_VertexBuffer.h"
#include "Rendering/Asura_PS3_FrameBuffer.h"
#include "Rendering/Asura_PS3_RSXProfiling.h"
#include "Rendering/Asura_PS3_MaterialResponse_System.h"

#include "Rendering/ShadersDefinition/VD/Fog_VD.h"
#include "Rendering/ShadersDefinition/VP/Fog_VP.h"
#include "Rendering/ShadersDefinition/FP/Fog_FP.h"

/******************************************************************************/
/* Constants                                                                  */
/******************************************************************************/

const u_int uFOG_TEXTURE_SIZE = 128;

/******************************************************************************/
/* Global Variables                                                           */
/******************************************************************************/
static Asura_PS3_GCM_VertexShader*			g_pxVertexShader	= NULL;
static Asura_PS3_FragmentShaderCopy*		g_pxFragmentShader	= NULL;
static Asura_PS3_GCM_VertexBuffer			g_xVertexBuffer;
static Asura_PS3_Texture					g_xFogTexture;
static bool									g_bParamsUpdated	= false;

/******************************************************************************/
/* Asura_Fog Static Class Functions                                           */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Fog::Platform_UpdateForNewParameters()
//
// Updates platform fog data when parameters change. Called on level load
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Fog::Platform_UpdateForNewParameters()
{
	Asura_PS3_Fog::UpdateForNewParameters();
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Fog::Platform_Render()
//
// Renders the fog as a full-screen post-processing pass
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Fog::Platform_Render()
{
	Asura_PS3_Fog::RenderFog();
}

/******************************************************************************/
/* Asura_PS3_Fog Static Class Functions                                   */
/******************************************************************************/

Asura_PS3_FogParameters Asura_PS3_Fog::s_xParams;


////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Fog::Initialise()
//
// Initialises the PS3 fog system
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_Fog::Initialise()
{
	g_pxVertexShader	= new Asura_PS3_VP_Fog();
	Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_FOG, 1, &_binary_PS3_Fog_FP_bin_start, ( (unsigned char *)&_binary_PS3_Fog_FP_bin_end - (unsigned char *)&_binary_PS3_Fog_FP_bin_start ), new Asura_PS3_Fog_Handles, 1, true );
	g_pxVertexShader->InitVertexBufferDeclaration( g_axVertexDeclaration, VD_ELEMENTS );

	g_xFogTexture.CreateTexture( uFOG_TEXTURE_SIZE, 1, ASURA_PS3_TEXTUREFORMAT_L8 );
//	g_xFogTexture.CreateTexture( uFOG_TEXTURE_SIZE, uFOG_TEXTURE_SIZE, ASURA_PS3_TEXTUREFORMAT_L8 );
//	g_xFogTexture.CreateTexture( uFOG_TEXTURE_SIZE, 256, ASURA_PS3_TEXTUREFORMAT_8888_RGBA );
	g_xVertexBuffer.Initialise( FOG_VERTEX_SIZE, 4, CELL_GCM_LOCATION_MAIN );

	float* pfVertexData = ( float* )g_xVertexBuffer.LockForWriting();
	pfVertexData[ 0 ] = -1; pfVertexData[ 1 ] =  1; //Top left
	pfVertexData[ 2 ] =  1; pfVertexData[ 3 ] =  1; //Top right
	pfVertexData[ 4 ] =  1; pfVertexData[ 5 ] = -1; //Bottom right
	pfVertexData[ 6 ] = -1; pfVertexData[ 7 ] = -1; //Bottom left

	s_xParams.m_pxFogTexture	= &g_xFogTexture;
	s_xParams.m_bFogEnabled		= false;
	g_bParamsUpdated			= true;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Fog::Shutdown()
//
// Shuts the PS3 fog system down
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_Fog::Shutdown()
{
	g_xVertexBuffer.Destroy();
	g_xFogTexture.Destroy();

	delete g_pxVertexShader;
	g_pxVertexShader = NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Fog::UpdateForNewParameters()
//
// Updates platform fog data when parameters change. Called on level load
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_Fog::UpdateForNewParameters()
{
	if( Asura_Fog::IsFogEnabled() )
	{
		s_xParams.m_bFogEnabled = true;		
	}
	else
	{
		s_xParams.m_bFogEnabled = false;
	}

	g_bParamsUpdated = true;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Fog::RenderFog()
//
// Renders the fog pass on PS3
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_Fog::RenderFog()
{
	if(g_bParamsUpdated)
	{
		// Ensure the fog texture is not still bound

		const float fFogNearPlane		= Asura_Fog::GetFogNearPlane();
		const float fFogFarPlane		= Asura_Fog::GetFogFarPlane();

		s_xParams.m_fFogNearPlane		= fFogNearPlane;

		const float fDelta				= ( fFogFarPlane - fFogNearPlane ) / static_cast< float >( uFOG_TEXTURE_SIZE - 1 );

		u_char* pucTexels				= ( u_char* ) g_xFogTexture.GetTextureData();
		int pos = 0;

//		for( int i = 0; i < uFOG_TEXTURE_SIZE; ++i )
		{
			float fDistance				= fFogNearPlane;
			for( u_int u = 0; u < uFOG_TEXTURE_SIZE; ++u )
			{
				float fTemp = Asura_Maths::ClampToRange( Asura_Fog::GetFogDensityAtDistance( fDistance ), 0.0f, 1.0f ) * 255.0f;
				u_char ucTemp = ( u_char )fTemp;
//				for( int i = 0; i < 4; ++i )
				{
					pucTexels[ pos ] = ucTemp;
					++pos;
				}
				fDistance += fDelta;
			}
		}

		s_xParams.m_fOneOverFogDepth = Asura_Fog::GetOneOverFogPlaneSeparation();
		const Asura_RGBA& xFogColour = Asura_Fog::GetFogColour();
		s_xParams.m_afFogColour[0] = xFogColour.m_fR;
		s_xParams.m_afFogColour[1] = xFogColour.m_fG;
		s_xParams.m_afFogColour[2] = xFogColour.m_fB;

		g_bParamsUpdated = false;
	}

    ASURA_PS3_RSX_PROFILE__BEGIN( ASURA_PS3_RSX_PROFILE__FOG, "Fog" );

	// Pixel shader will output fog colour with fog factor as alpha
	Asura_PS3_DeferredRendering::BindAsSingleRT();
	Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
	Asura_PS3_RenderingStates::SetAlphaBlending( true );
	Asura_PS3_RenderingStates::RequestAlphaTest( false );
	Asura_PS3_RenderingStates::RequestCullMode( ASURA_CULLMODE_NONE );

	// Taken these settings out of the effect, as they were screwing up the cached states
	Asura_PS3_RenderingStates::RequestZBufferEnabled(false);
	Asura_PS3_RenderingStates::RequestZBufferWriteEnabled(false);

	g_pxVertexShader->Bind();

	const Asura_RGBA& xFogColour	= Asura_Fog::GetFogColour();
	float afFogColour[ 3 ]			= { xFogColour.m_fR, xFogColour.m_fG, xFogColour.m_fB };
	float xFog[ 3 ]					= { Asura_Fog::GetFogNearPlane(), Asura_Fog::GetOneOverFogPlaneSeparation(), Asura_Camera::GetFarClip() }; //g_fFogNearPlane - x, g_fOneOverFogVolumeDepth - y, g_fFarClipPlane - z

	g_pxFragmentShader = Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_FOG, 1 );
	g_pxFragmentShader->SetFloats ( FP_FogColour, 3, afFogColour );
	g_pxFragmentShader->SetFloats ( FP_Fog, 3, xFog );


	Asura_PS3_MaterialResponse_System::SetDepthParameters( g_pxFragmentShader, FP_DepthAdjustment );

	g_pxFragmentShader->SetTexture( FP_FogTexture, s_xParams.m_pxFogTexture, true );
	g_pxFragmentShader->SetTexture( FP_DepthTexture, Asura_PS3_DeferredRendering::GetDeferredFrameBuffer()->GetZBufferAsTexture(), false );

	g_pxFragmentShader->Bind();

	g_xVertexBuffer.Bind( g_axVertexDeclaration, VD_ELEMENTS );
	g_xVertexBuffer.RenderQuads( 4 );

    ASURA_PS3_RSX_PROFILE__END( ASURA_PS3_RSX_PROFILE__FOG );
}
