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

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

#include "Core/Asura.h"

#include "Core/Asura_MaterialResponse_System.h"
#include "Rendering/Asura_PS3_MaterialResponse_System.h"

// Asura Platform-independent
#include "Animation/Asura_TextureAnimation.h"
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_HalfFloat.h"
#include "Rendering/Asura_Render.h"
#include "Core/Asura_Maths.h"
#include "Light/Asura_GI_Solution.h"
#include "Animation/Asura_Animation.h"
#include "Rendering/Asura_DeferredShading.h"
// Asura PS3
#include "ChunkSystem/asura_chunk_resourcefile_ps3.h"
#include "Rendering/Shaders/Asura_PS3_GCM_Shader.h"
#include "Rendering/Asura_PS3_Graphics.h"
#include "Rendering/Asura_PS3_RenderingStates.h"
#include "Rendering/Asura_PS3_Fog.h"


class PS3MaterialResponse
{

public:

	u_int m_uMaterialHashID;
	u_int m_uShaderHashID;

    // For storage in Asura_HashTable.
    Asura_Hash_ID GetHashID( void ) const { return m_uMaterialHashID; }

};

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

////////////////////////////////////////////////////////////////////////////////
//
// Convert()
//
// Reads a float from a chunk stream and stores it as a half
//
// Parameters:		xStream - Chunk stream from which to read
//					xStore - Half in which to store the value
//
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

//#define ASURA_PS3_USE_32_BIT_MATERIAL

#ifdef ASURA_PS3_USE_32_BIT_MATERIAL

static void ConvertDevide2( Asura_Chunk_Stream& xStream, u_char& xStore )
{
	float fValue;
	xStream >> fValue;

	fValue *= 127.0f;

	xStore = static_cast<u_char>( fValue );
}

static void Convert( Asura_Chunk_Stream& xStream, u_char& xStore )
{
	float fValue;
	xStream >> fValue;

	fValue *= 255.0f;

	xStore = static_cast<u_char>( fValue );
}

#else

static void Convert( Asura_Chunk_Stream& xStream, Asura_HalfFloat& xStore )
{
	float fValue;
	xStream >> fValue;

	xStore = (Asura_HalfFloat)fValue;
}

#endif
/******************************************************************************/
/* Asura_Xbox360_MaterialResponse_System Static Class Data                    */
/******************************************************************************/

#ifdef ASURA_PS3_USE_32_BIT_MATERIAL
#define  s_eMaterialsTextureFormat	ASURA_PS3_TEXTUREFORMAT_8888_RGBA
#else
#define  s_eMaterialsTextureFormat	ASURA_PS3_TEXTUREFORMAT_ABGR16F
#endif

Asura_PS3_Texture					Asura_PS3_MaterialResponse_System::s_xMaterialsTexture;
float								Asura_PS3_MaterialResponse_System::s_fMaterialsTextureScale = 1.0f;

static Asura_HashTable<PS3MaterialResponse> g_xPS3MaterialHashTable( ASURA_MATERIAL_RESPONSE_HASH_TABLE_SIZE );

#if defined( ASURA_SUPPORT_COMMAND_CONSOLE )
	static bool g_bPaintItWhite = false;
	static bool g_bFlattenNormals = false;
	static bool g_bMattify = false;
	static int g_iPaintMaterialIndex = 0; // try it with a few different response curves
#else
	const bool g_bPaintItWhite = false;
	const bool g_bFlattenNormals = false;
	const bool g_bMattify = false;
	const int g_iPaintMaterialIndex = 0;
#endif


/******************************************************************************/
/* Asura_Xbox360_MaterialResponse_System Static Class Functions               */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_System::Initialise()
//
// Initialises the material response system on PS3
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_MaterialResponse_System::Initialise()
{
	//(AAM - 30.01.2007) Used to use D3DXCreateTexture() here, but it simply can't create most
	// 360 extension formats. No need to use it on a fixed platform anyway
	s_xMaterialsTexture.CreateTexture( ASURA_PS3_MATERIALS_TEXTURE_WIDTH, ASURA_PS3_MATERIALS_TEXTURE_HEIGHT, s_eMaterialsTextureFormat );

	Asura_Assert( s_xMaterialsTexture.GetTextureData(), "Failed to create material response data texture." );
	
#if defined( ASURA_SUPPORT_COMMAND_CONSOLE  )
	Asura_CommandConsole::AddVar( L"Render.Materials.PaintItWhite", &g_bPaintItWhite, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.Materials.PaintResponseIndex", &g_iPaintMaterialIndex, 0, 256, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.Materials.FlattenNormals", &g_bFlattenNormals, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.Materials.RemoveReflections", &g_bMattify, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
}


void Asura_PS3_MaterialResponse_System::Reset( const bool bFullReset )
{
    for( Asura_HashTable_It<PS3MaterialResponse> xIt(&g_xPS3MaterialHashTable); !xIt.Done(); )
    {
    	const Asura_MaterialResponse* pxMR = Asura_MaterialResponse_System::GetMaterialByHashID( xIt.GetCurrentHashID() );
        if( bFullReset || !pxMR || pxMR->GetRemoveOnReset() )
        {
            delete xIt.GetCurrent();
            xIt.DeleteCurrent();
        }
        else
        {
            xIt.Next();
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_System::Shutdown()
//
// Shuts the material response system down on PS3
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_MaterialResponse_System::Shutdown()
{
    // Reset(true) will already have been called by this point so we expect
    // the hash table to be empty.
    Asura_Assert( !g_xPS3MaterialHashTable.GetNumEntries(), "" );

	s_xMaterialsTexture.Destroy();
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_System::UpdateDataTexture()
//
// Adds material response curves from a chunk stream to the data texture
//
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_MaterialResponse_System::UpdateDataTexture( Asura_Chunk_Stream& xStream, const int iVersion, u_int	uNumberOfMaterials )
{
	// JAT 16/05/07 this version number is common to PC, 360, PS3 exports
	const int iCURRENT_VERSION = Asura_MaterialResponse_System::EXPORTED_TEXTURE_VERSION;
	if ( iVersion < iCURRENT_VERSION )
	{
		Asura_Assert( false, "Bad version re-export the level to fix this" );
		return;
	}

	if( !s_xMaterialsTexture.GetTextureData() )
	{
		Asura_Assert( false, "Trying to update material response texture, but it doesn't exist." );
		return;
	}

#ifdef ASURA_PS3_USE_32_BIT_MATERIAL
	u_int	uPitch = ( ASURA_PS3_MATERIALS_TEXTURE_WIDTH * 4 ) / sizeof( u_char );
#else
	u_int	uPitch = ( ASURA_PS3_MATERIALS_TEXTURE_WIDTH * 8 ) / sizeof( Asura_HalfFloat );
#endif

	Asura_Assert( uNumberOfMaterials <= ASURA_PS3_MATERIALS_TEXTURE_HEIGHT, "Too many material responses. Data texture needs to be enlarged." );

	// Format is ABGR, but we'll write little-endian floats
#ifdef ASURA_PS3_USE_32_BIT_MATERIAL
	u_char*	pxData	= static_cast< u_char* >( s_xMaterialsTexture.GetTextureData() );
#else
	Asura_HalfFloat*	pxData	= static_cast< Asura_HalfFloat* >( s_xMaterialsTexture.GetTextureData() );
#endif

	// Start at startMaterialIndex place in the texture
	// Pitch is specified in bytes for non-DXT formats
//	const int iFirstMaterialIndex = Asura_MaterialResponse_System::GetNumGlobalMaterials();
//	pxData += iFirstMaterialIndex * ( uPitch );
	u_int uiNumMaterials = Asura_MaterialResponse_System::GetNumMaterialsTextureLines();
	pxData += uiNumMaterials * (uPitch );
	Asura_MaterialResponse_System::SetNumMaterialsTextureLines( uiNumMaterials + uNumberOfMaterials );

	for ( u_int uMaterial = 0; uMaterial < uNumberOfMaterials; ++uMaterial )
	{
		// First we'll have the graphs, one in each colour channel

		u_int uElement = 0;

		for ( ; uElement<ASURA_MATERIAL_RESPONSE_GRAPH_WIDTH * 4;  )
		{
#ifdef ASURA_PS3_USE_32_BIT_MATERIAL
			// m_xDiffuseGraph.GetValueAtTime( fTime ) / 100.0f;
			ConvertDevide2( xStream, pxData[ uElement++ ] );
			// m_xSpecularGraph.GetValueAtTime( fTime ) / 100.0f;
			ConvertDevide2( xStream, pxData[ uElement++ ] );
			// m_xTransparencyGraph.GetValueAtTime( fTime ) / 100.0f;
			ConvertDevide2( xStream, pxData[ uElement++ ] );
			// 0.0f;
			ConvertDevide2( xStream, pxData[ uElement++ ] );
#else
			// m_xDiffuseGraph.GetValueAtTime( fTime ) / 100.0f;
			Convert( xStream, pxData[ uElement++ ] );
			// m_xSpecularGraph.GetValueAtTime( fTime ) / 100.0f;
			Convert( xStream, pxData[ uElement++ ] );
			// m_xTransparencyGraph.GetValueAtTime( fTime ) / 100.0f;
			Convert( xStream, pxData[ uElement++ ] );
			// 0.0f;
			Convert( xStream, pxData[ uElement++ ] );
#endif
		}

		// Then the rest of the material's properties

		// m_xAmbientColour.m_fR;
		// m_xAmbientColour.m_fG;
		// m_xAmbientColour.m_fB;
		// m_fColouriseAmbient / 100.0f;
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );

		// m_xDiffuseColour.m_fR;
		// m_xDiffuseColour.m_fG;
		// m_xDiffuseColour.m_fB;
		// m_fColouriseDiffuse / 100.0f;
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );

		// m_xSpecularColour.m_fR;
		// m_xSpecularColour.m_fG;
		// m_xSpecularColour.m_fB;
		// m_fColouriseSpecular / 100.0f;
		Convert(xStream, pxData[ uElement++ ]);
		Convert(xStream, pxData[ uElement++ ]);
		Convert(xStream, pxData[ uElement++ ]);
		Convert(xStream, pxData[ uElement++ ]);

		// m_fAmbientContribution / 100.0f;
		// m_fMetalicity / 100.0f;
		// 0.0f;
		// 0.0f;
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );
		Convert( xStream, pxData[ uElement++ ] );

		// Pitch is specified in bytes for non-DXT formats
		pxData += uPitch;
	}
}


void Asura_PS3_MaterialResponse_System::ReadPS3Material( Asura_Chunk_Stream& xStream, u_int uNumberOfMaterials )
{
	for ( u_int i=0; i < uNumberOfMaterials; i++ )
	{
		//it's pretty sucky to have to store & read the same material hash as in the platform independent stuff, but there's no other way to get this info
		u_int uMaterialHash, uShaderHash;
		xStream >> uMaterialHash;
		xStream >> uShaderHash;

        // If we've already encountered this material hash then just verify
        // that the shader matches.
        PS3MaterialResponse* pxPS3MR = g_xPS3MaterialHashTable.FindObjectByHashID( uMaterialHash );
        if( pxPS3MR )
        {
			Asura_Assert( uShaderHash == pxPS3MR->m_uShaderHashID, "The same material seems to have different shaders." );
            continue;
        }

		pxPS3MR = new PS3MaterialResponse();
        pxPS3MR->m_uMaterialHashID = uMaterialHash;
        pxPS3MR->m_uShaderHashID = uShaderHash;

        g_xPS3MaterialHashTable.AddObject( pxPS3MR );
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_System::SetBlendStates()
//
// Sets alpha blend state on the GPU appropriate for the given material response
//
// Parameters:		pxMaterial - Material to set. May be NULL for default values
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PS3_MaterialResponse_System::SetBlendStates( const Asura_MaterialResponse* const pxMaterial )
{
#ifdef ASURA_USE_NEW_VISIBILITY_SYSTEM
	bool	bXRayMode = Asura_Visibility_System::ShouldRenderInXRayMode();
	const	Asura_MaterialResponse::BLEND_MODE eBlendMode = bXRayMode ? Asura_MaterialResponse::BLEND_MODE_ADDITIVE :
															pxMaterial ? pxMaterial->GetBlendMode() : Asura_MaterialResponse::BLEND_MODE_OPAQUE;
#else
	const Asura_MaterialResponse::BLEND_MODE eBlendMode = pxMaterial ? pxMaterial->GetBlendMode() : Asura_MaterialResponse::BLEND_MODE_OPAQUE;
#endif
	switch( eBlendMode )
	{
		default:
		case Asura_MaterialResponse::BLEND_MODE_OPAQUE:
		{
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
			Asura_PS3_RenderingStates::SetBlendEnableMrt( false, false, false );
			Asura_PS3_RenderingStates::SetAlphaBlending(false);
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0123_ALL );
			Asura_PS3_RenderingStates::SetUseDepthBias( false );
			break;
		}
		case Asura_MaterialResponse::BLEND_MODE_ADDITIVE:
		{		
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING );
			Asura_PS3_RenderingStates::SetBlendEnableMrt( false, true, false );
			Asura_PS3_RenderingStates::SetAlphaBlending(true);

			// Need to disable writes to the normal-material buffer and specular channel				
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL_MRT13_NONE_MRT2_RGB );

			// Apply depth bias so these can be used a bit like decals
			Asura_PS3_RenderingStates::SetUseDepthBias( true );
			Asura_PS3_RenderingStates::SetDepthBias( -3.0f, 0.0f );
			break;
		}
		case Asura_MaterialResponse::BLEND_MODE_BACKGROUNDBLEND:
		{
			Asura_PS3_RenderingStates::RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
			Asura_PS3_RenderingStates::SetBlendEnableMrt( false, true, false );
			Asura_PS3_RenderingStates::SetAlphaBlending(true);
			// Need to disable writes to the normal-material buffer and specular channel				
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL_MRT13_NONE_MRT2_RGB );
			Asura_PS3_RenderingStates::SetUseDepthBias( false );
			break;
		}
	}

	if( pxMaterial && pxMaterial->GetIsDecal() )
	{
		// Apply depth bias so these can display above coplanar geometry
		Asura_PS3_RenderingStates::SetUseDepthBias( true );
		Asura_PS3_RenderingStates::SetDepthBias( 3.0f, 0.0f );

		if( pxMaterial->GetBlendMode() != Asura_MaterialResponse::BLEND_MODE_OPAQUE )
		{
			// Need to disable writes to the normal-depth buffer and specular channel
			Asura_PS3_RenderingStates::SetBlendEnableMrt( false, true, false );
			Asura_PS3_RenderingStates::SetAlphaBlending( true );
			// Need to disable writes to the normal-depth buffer and specular channel				
			Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0_ALL_MRT13_NONE_MRT2_RGB );
		}
	}
	else
	{
		Asura_PS3_RenderingStates::SetUseDepthBias( false );
		Asura_PS3_RenderingStates::SetColorMaskMrtState( ASURA_PS3_MRT_MRT0123_ALL );
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse::SetSHParameters()
//
// Sets effect parameters relating to an SH, including brightest light stuff
// for transparent strip rendering post-deferred
//
// Parameters:		pxMaterial - Material to use. May be NULL for default values
//					xHandles - Bundle of handles to the params in this effect
//					xEffect - Effect in which to set the parameters
//
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MaterialResponse_System::SetSHParameters(const Asura_MaterialResponse* const				pxMaterial,
														Asura_PS3_FragmentShaderCopy*					pxShader )
{
	Asura_SphericalHarmonics_Order2_RGB xSH;

	if( pxMaterial )
	{
		// Set SH lighting by considering static and dynamic components separately

		xSH = Asura_GI_Solution::s_xStaticSHForCurrentRender;

		Asura_RGBA xColour = pxMaterial->GetAmbientColour();

		const float fPrelightingColourise = pxMaterial->GetPrelightingColourise();
		xSH.Colourise( xColour.m_fR, xColour.m_fG, xColour.m_fB, fPrelightingColourise );

		const float fAmbientContribution = pxMaterial->GetAmbientContribution();
		xSH.Scale( fAmbientContribution );

		if( pxMaterial->IsTransparent() )
		{	
			xSH += Asura_GI_Solution::s_xDynamicSHForCurrentRender;
		}
	}
	else
	{
		// send through basic SH
		xSH = Asura_GI_Solution::s_xSHForCurrentRender;
	}

	// Set effect parameters
	Asura_AVector4 xRedSH( xSH.m_afRedCoefficients[1], xSH.m_afRedCoefficients[2], xSH.m_afRedCoefficients[3], xSH.m_afRedCoefficients[0] );

	Asura_AVector4 xGreenSH( xSH.m_afGreenCoefficients[1], xSH.m_afGreenCoefficients[2], xSH.m_afGreenCoefficients[3], xSH.m_afGreenCoefficients[0] );

	Asura_AVector4  xBlueSH( xSH.m_afBlueCoefficients[1], xSH.m_afBlueCoefficients[2], xSH.m_afBlueCoefficients[3], xSH.m_afBlueCoefficients[0] );

	pxShader->SetFloats( UE_MR_RedSH,	4, xRedSH.GetElements()   );
	pxShader->SetFloats( UE_MR_GreenSH,	4, xGreenSH.GetElements() );
	pxShader->SetFloats( UE_MR_BlueSH,	4, xBlueSH.GetElements()  );

	// Now we've scaled the SH, calculate the related light properties for transparent render
	if( pxMaterial && pxMaterial->IsTransparent() )
	{
		// Find the brightest direction

		Asura_Vector_3 xLightDirection_Asura;
		xSH.GetBrightestDirection( xLightDirection_Asura );

		if( xLightDirection_Asura.ApproximatelyEquals( Asura_Maths::ZeroVector3 ) )
		{
			// If there are no lights, we'll get a zero direction. Not much we can do here,
			// might as well consider the light as coming from above
			xLightDirection_Asura.y = -1.0f;
		}
		Asura_AVector4 xLightDirection_FXL( xLightDirection_Asura.x, xLightDirection_Asura.y, xLightDirection_Asura.z, 1.0f );

		// Evaluate the colour in that direction

		float x, y, z, w = 0.0f;

		x = xLightDirection_FXL.DotProduct4( xRedSH );
		y = xLightDirection_FXL.DotProduct4( xGreenSH );
		z = xLightDirection_FXL.DotProduct4( xBlueSH );
		Asura_AVector4 xLightColourTemp( x, y, z, w ), xLightColour;
		xLightColour.Max( xLightColourTemp, Asura_Maths::ZeroAVector4W0 );
		Asura_AVector4 xAveragingVector( 0.333f, 0.333f, 0.333f, 0.0f );
		float fLightBrightness = xLightColour.DotProduct3( xAveragingVector );

		pxShader->SetFloats( UE_MR_SHLightDirection,	4, xLightDirection_FXL.GetElements() );
		pxShader->SetFloats( UE_MR_SHLightColour,		4, xLightColour.GetElements() );
		pxShader->SetFloats( UE_MR_SHLightBrightness,	1, &fLightBrightness );
	}
}

//
// SetDepthParameters
//
// Call this if your shader uses Deferred_GetDepth in MaterialResponse_Shared.ps3FXH
//
void Asura_PS3_MaterialResponse_System::SetDepthParameters(	Asura_PS3_FragmentShaderCopy* pxShader, u_int uParameter )
{
	// Set parameters for converting depth buffer vals into distance from camera
	float f = Asura_Camera::GetFarClip(), n = Asura_Camera::GetNearClip();
	const float afDepthAdjustment[2] = { (f*n)/(f-n), f/(f-n) };
	pxShader->SetFloats( uParameter, 2, afDepthAdjustment );
}



void Asura_PS3_MaterialResponse_System::SetProjectContext( const Asura_MaterialResponse* const pxMaterial1, const Asura_MaterialResponse* const pxMaterial2 )
{
	if (Asura_MaterialResponse_System::GetMaterialProjectContextCallback())
	{
		Asura_MaterialResponse_System::GetMaterialProjectContextCallback() (pxMaterial1->GetProjectContext(),
			pxMaterial2 ? pxMaterial2->GetProjectContext() : ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID);
	}
}


void Asura_PS3_MaterialResponse_System::SetEffectParameters(	const Asura_MaterialResponse* const			pxMaterial1,
																const Asura_MaterialResponse* const			pxMaterial2,
																const Asura_Animation* const				pxAnim,
																Asura_PS3_FragmentShaderCopy*				pxShader )
{
	if( !pxShader ) return;
	pxShader->SetTexture( TU_MR_MaterialsTexture, GetDataTexture(), false );

	const Asura_TextureAnimationController* const pxTextureAnimationController = (pxAnim ? pxAnim->GetTextureAnimController() : NULL);
	
	SetDepthParameters( pxShader, UE_MR_DepthAdjustment );

	if( pxMaterial1 )
	{
		Asura_PS3_FogParameters &xFogParams = Asura_PS3_Fog::GetFogParameters();
		if( xFogParams.m_bFogEnabled )
		{
			pxShader->SetTexture( TU_MR_FogTexture,	xFogParams.m_pxFogTexture, true );

			const float afFogProperties [ 2 ] = { xFogParams.m_fFogNearPlane, xFogParams.m_fOneOverFogDepth };

			pxShader->SetFloats( UE_MR_FogColour,				3, xFogParams.m_afFogColour );
			pxShader->SetFloats( UE_MR_FogProperties,			2, afFogProperties );
		}
		// Set texture usage flags and textures
		//No longer needed to set the boolean values

		pxShader->SetBool(UE_MR_UseAlbedo, pxMaterial1->GetTexture( Asura_MaterialResponse::ALBEDO_TEXTURE ) != -1);
		pxShader->SetBool(UE_MR_UseNormalMap, pxMaterial1->GetTexture( Asura_MaterialResponse::NORMAL_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseAlbedoDetailMap, pxMaterial1->GetTexture( Asura_MaterialResponse::DETAIL_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseNormalDetailMap, pxMaterial1->GetTexture( Asura_MaterialResponse::DETAIL_NORMAL_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseSpecularMap, pxMaterial1->GetTexture( Asura_MaterialResponse::SPECULAR_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseTransparencyMap, pxMaterial1->GetTexture( Asura_MaterialResponse::TRANSPARENCY_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseSelfIlluminationMap, pxMaterial1->GetTexture( Asura_MaterialResponse::SELFILLUMINATION_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseEnvironmentMap, pxMaterial1->GetTexture( Asura_MaterialResponse::ENVIRONMENT_MAP ) != -1);
		pxShader->SetBool(UE_MR_UseReflectivityMask, pxMaterial1->GetTexture( Asura_MaterialResponse::REFLECTIVITY_MASK ) != -1);
		pxShader->SetBool(UE_MR_UseParallaxMap, pxMaterial1->GetTexture( Asura_MaterialResponse::PARALLAX_MAP ) != -1);

		pxShader->SetTexture( TU_MR_Albedo,					pxMaterial1->GetTexture( Asura_MaterialResponse::ALBEDO_TEXTURE ) );
		pxShader->SetTexture( TU_MR_NormalMap,				pxMaterial1->GetTexture( Asura_MaterialResponse::NORMAL_MAP ) );
		pxShader->SetTexture( TU_MR_AlbedoDetailMap,		pxMaterial1->GetTexture( Asura_MaterialResponse::DETAIL_MAP ) );
		pxShader->SetTexture( TU_MR_NormalDetailMap,		pxMaterial1->GetTexture( Asura_MaterialResponse::DETAIL_NORMAL_MAP ) );
		pxShader->SetTexture( TU_MR_SpecularMap,			pxMaterial1->GetTexture( Asura_MaterialResponse::SPECULAR_MAP ) );
		pxShader->SetTexture( TU_MR_TransparencyMap,		pxMaterial1->GetTexture( Asura_MaterialResponse::TRANSPARENCY_MAP ) );
		pxShader->SetTexture( TU_MR_SelfIlluminationMap,	pxMaterial1->GetTexture( Asura_MaterialResponse::SELFILLUMINATION_MAP ) );
		pxShader->SetTexture( TU_MR_EnvironmentMap,			pxMaterial1->GetTexture( Asura_MaterialResponse::ENVIRONMENT_MAP ) );
		pxShader->SetTexture( TU_MR_ReflectivityMask,		pxMaterial1->GetTexture( Asura_MaterialResponse::REFLECTIVITY_MASK ) );
		pxShader->SetTexture( TU_MR_ParallaxMap,			pxMaterial1->GetTexture( Asura_MaterialResponse::PARALLAX_MAP ) );

		if( pxMaterial2 )
		{
			// Set texture usage flags
			//No longer needed to set the boolean values

			pxShader->SetTexture( TU_MR_Albedo2,				pxMaterial2->GetTexture( Asura_MaterialResponse::ALBEDO_TEXTURE ) );
			pxShader->SetTexture( TU_MR_NormalMap2,				pxMaterial2->GetTexture( Asura_MaterialResponse::NORMAL_MAP ) );
			pxShader->SetTexture( TU_MR_AlbedoDetailMap2,		pxMaterial2->GetTexture( Asura_MaterialResponse::DETAIL_MAP ) );
			pxShader->SetTexture( TU_MR_NormalDetailMap2,		pxMaterial2->GetTexture( Asura_MaterialResponse::DETAIL_NORMAL_MAP ) );
			pxShader->SetTexture( TU_MR_SpecularMap2,			pxMaterial2->GetTexture( Asura_MaterialResponse::SPECULAR_MAP ) );
			pxShader->SetTexture( TU_MR_TransparencyMap2,		pxMaterial2->GetTexture( Asura_MaterialResponse::TRANSPARENCY_MAP ) );
			pxShader->SetTexture( TU_MR_SelfIlluminationMap2,	pxMaterial2->GetTexture( Asura_MaterialResponse::SELFILLUMINATION_MAP ) );
			pxShader->SetTexture( TU_MR_ParallaxMap2,			pxMaterial2->GetTexture( Asura_MaterialResponse::PARALLAX_MAP ) );


			const float afDetailMapParameters2[ 4 ] = { pxMaterial2->GetAlbedoDetailMapUVScale(), pxMaterial2->GetAlbedoDetailMapValueScale(), pxMaterial2->GetNormalDetailMapUVScale(), pxMaterial2->GetNormalDetailMapValueScale() };
			pxShader->SetFloats( UE_MR_DetailMapParameters2,	4, afDetailMapParameters2 );
		}

		const float afMaterialProperties[ 4 ] = { pxMaterial1->GetMaterialIndex(), pxMaterial1->GetReflectivity(), pxMaterial1->GetParallaxScale(), -pxMaterial1->GetParallaxScale() * 0.5f };
		const float afDetailMapParameters[ 4 ] = { pxMaterial1->GetAlbedoDetailMapUVScale(), pxMaterial1->GetAlbedoDetailMapValueScale(), pxMaterial1->GetNormalDetailMapUVScale(), pxMaterial1->GetNormalDetailMapValueScale() };

		pxShader->SetFloats( UE_MR_MaterialProperties,		4, afMaterialProperties );
		pxShader->SetFloats( UE_MR_DetailMapParameters,		4, afDetailMapParameters );

		if ( g_bPaintItWhite )
		{
			// Override things to give a white material with no specular or reflection, but with the normal map, transparency & self-illumination of the original material.
			// Basically it's as if we cover everything with a coat of matt white paint.
			pxShader->SetTexture( TU_MR_Albedo,				Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_NOTEXTURE );
			pxShader->SetTexture( TU_MR_AlbedoDetailMap,	Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_NOTEXTURE );
			pxShader->SetTexture( TU_MR_SpecularMap,		Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );
			pxShader->SetTexture( TU_MR_ReflectivityMask,	Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );

			if ( pxMaterial2 )
			{
				pxShader->SetTexture( TU_MR_Albedo2,			Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_NOTEXTURE );
				pxShader->SetTexture( TU_MR_AlbedoDetailMap2,	Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_NOTEXTURE );
				pxShader->SetTexture( TU_MR_SpecularMap2,		Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );
			}

			const float afWhiteMaterialProperties[ 4 ] = { g_iPaintMaterialIndex, 0, afMaterialProperties[2], afMaterialProperties[3] };
			pxShader->SetFloats( UE_MR_MaterialProperties,		4, afWhiteMaterialProperties );
		}
		
		if ( g_bFlattenNormals )
		{
			// replace normal maps with a flat texture
			pxShader->SetTexture( TU_MR_NormalMap,				Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_FLATNORMALMAP );
			if ( pxMaterial2 ) pxShader->SetTexture( TU_MR_NormalMap2,	Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_FLATNORMALMAP );
		}
		
		if ( g_bMattify )
		{
			// replace reflectivity & specular maps with black texture
			pxShader->SetTexture( TU_MR_SpecularMap,			Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );
			pxShader->SetTexture( TU_MR_ReflectivityMask,		Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );
			if ( pxMaterial2 ) pxShader->SetTexture( TU_MR_SpecularMap2,			Asura_PS3_TextureManagement::SPECIALTEXTURE_ID_BLACKTEXTURE );
		}

		// Set self-illumination parameters
		float fSelfIlluminationAmount;
		float fSelfIlluminationColourise;
		Asura_RGBA xSelfIlluminationColour;
		pxMaterial1->GetSelfIllumination(pxTextureAnimationController, fSelfIlluminationAmount, fSelfIlluminationColourise, xSelfIlluminationColour);

		Asura_AVector4 xSelfIlluminationParameters( xSelfIlluminationColour.m_fR, xSelfIlluminationColour.m_fG, xSelfIlluminationColour.m_fB, 0.0f );
		xSelfIlluminationParameters *= fSelfIlluminationAmount * fSelfIlluminationColourise;
		xSelfIlluminationParameters.SetW( fSelfIlluminationAmount * (1.0f - fSelfIlluminationColourise) );

		pxShader->SetFloats( UE_MR_SelfIlluminationParameters, 4, xSelfIlluminationParameters.GetElements() );


		// Albedo modifier
		{
			Asura_RGBA xAlbedoModifier( 1.0f, 1.0f, 1.0f, 1.0f );

			if( Asura_MaterialResponse_System::GetAlbedoModifierCallback() )
			{
				Asura_MaterialResponse_System::GetAlbedoModifierCallback()( pxAnim,
					pxMaterial1->GetProjectContext(), xAlbedoModifier );
			}

			pxShader->SetFloats( UE_MR_AlbedoModifier, 3, &xAlbedoModifier.m_fR );
		}


	}
	else
	{
		// Set self-illumination parameters
		{
			const float xSelfIlluminationParameters[ 4 ] = { 0.0f, 0.0f, 0.0f, 0.0f };
			pxShader->SetFloats( UE_MR_SelfIlluminationParameters, 4, xSelfIlluminationParameters );
		}

		// Misc
		{
			const float afMaterialProperties[ 4 ]	= { 0.0f, 0.0f, 0.0f, 0.0f };
			const float afDetailMapParameters[ 4 ]	= { 0.0f, 0.0f, 0.0f, 0.0f };

			// Just use the first material's responses
			pxShader->SetFloats( UE_MR_MaterialProperties,	4, afMaterialProperties );
			pxShader->SetFloats( UE_MR_DetailMapParameters,	4, afDetailMapParameters );
		}
	}
}
/******************************************************************************/
/* Asura_PS3_MaterialResponse_Handles Class Functions                     */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_Handles::Asura_PS3_MaterialResponse_Handles()
//
// Constructor
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

Asura_PS3_MaterialResponse_Handles::Asura_PS3_MaterialResponse_Handles()
: Asura_PS3_FP_Handles ( UE_MR_ELEMENTS, TU_MR_ELEMENTS )
{
}

Asura_PS3_MaterialResponse_Handles::~Asura_PS3_MaterialResponse_Handles()
{
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MaterialResponse_Handles::InitialiseFromShader()
//
// Initialises a bundle of MR parameter handles from a given effect
//
// Parameters:		pxShader - Fragment shader from which to grab the handles
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MaterialResponse_Handles::InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
{
	//Uniform parameters
	pxShader->InitUniformElement( UE_MR_FogColour,					"g_xFogColour",					1 );
	pxShader->InitUniformElement( UE_MR_FogProperties,				"g_xFogProperties",				1 );

	pxShader->InitUniformElement( UE_MR_RedSH,						"g_xRedSH",						1 );
	pxShader->InitUniformElement( UE_MR_GreenSH,					"g_xGreenSH",					1 );
	pxShader->InitUniformElement( UE_MR_BlueSH,						"g_xBlueSH",					1 );

	pxShader->InitUniformElement( UE_MR_SHLightDirection,			"g_xSHLightDirection",			1 );
	pxShader->InitUniformElement( UE_MR_SHLightColour,				"g_xSHLightColour",				1 );
	pxShader->InitUniformElement( UE_MR_SHLightBrightness,			"g_fSHLightBrightness",			1 );

	pxShader->InitUniformElement( UE_MR_MaterialProperties,			"g_xMaterialProperties",		1 );
	pxShader->InitUniformElement( UE_MR_DetailMapParameters,		"g_xDetailMapParameters",		1 );
	pxShader->InitUniformElement( UE_MR_DetailMapParameters2,		"g_xDetailMapParameters2",		1 );

	pxShader->InitUniformElement( UE_MR_SelfIlluminationParameters,	"g_xSelfIlluminationParameters",1 );
	pxShader->InitUniformElement( UE_MR_AlbedoModifier,				"g_xAlbedoModifier",1 );

	pxShader->InitUniformElement( UE_MR_DepthAdjustment,			"g_xDepthAdjustment",1 );

	pxShader->InitUniformElement( UE_MR_UseAlbedo,	"g_bUseAlbedo",1 );
	pxShader->InitUniformElement( UE_MR_UseNormalMap,	"g_bUseNormalMap",1 );
	pxShader->InitUniformElement( UE_MR_UseSpecularMap,	"g_bUseSpecularMap",1 );
	pxShader->InitUniformElement( UE_MR_UseTransparencyMap,	"g_bUseTransparencyMap",1 );
	pxShader->InitUniformElement( UE_MR_UseSelfIlluminationMap,	"g_bUseSelfIlluminationMap",1 );
	pxShader->InitUniformElement( UE_MR_UseParallaxMap,	"g_bUseParallaxMap",1 );
	pxShader->InitUniformElement( UE_MR_UseEnvironmentMap,	"g_bUseEnvironmentMap",1 );
	pxShader->InitUniformElement( UE_MR_UseReflectivityMask,	"g_bUseReflectivityMask",1 );
	pxShader->InitUniformElement( UE_MR_UseAlbedoDetailMap,	"g_bUseAlbedoDetailMap",1 );
	pxShader->InitUniformElement( UE_MR_UseNormalDetailMap,	"g_bUseNormalDetailMap",1 );

	//Texture units
	pxShader->InitTextureUnit( TU_MR_MaterialsTexture,		"g_xMaterialsTextureSampler",		CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR );

	pxShader->InitTextureUnit( TU_MR_Albedo,				"g_xAlbedoSampler",					CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR
		, true
		);
	pxShader->InitTextureUnit( TU_MR_NormalMap,				"g_xNormalMapSampler",				CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_AlbedoDetailMap,		"g_xAlbedoDetailMapSampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR
		, true
		);
	pxShader->InitTextureUnit( TU_MR_NormalDetailMap,		"g_xNormalDetailMapSampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_SpecularMap,			"g_xSpecularMapSampler",			CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_TransparencyMap,		"g_xTransparencyMapSampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_SelfIlluminationMap,	"g_xSelfIlluminationMapSampler",	CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_ParallaxMap,			"g_xParallaxMapSampler",			CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_ReflectivityMask,		"g_xReflectivityMaskSampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );

	pxShader->InitTextureUnit( TU_MR_Albedo2,				"g_xAlbedo2Sampler",				CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR
		, true
		);
	pxShader->InitTextureUnit( TU_MR_NormalMap2,			"g_xNormalMap2Sampler",				CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_AlbedoDetailMap2,		"g_xAlbedoDetailMap2Sampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR
		, true
		);
	pxShader->InitTextureUnit( TU_MR_NormalDetailMap2,		"g_xNormalDetailMap2Sampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_SpecularMap2,			"g_xSpecularMap2Sampler",			CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_TransparencyMap2,		"g_xTransparencyMap2Sampler",		CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_SelfIlluminationMap2,	"g_xSelfIlluminationMap2Sampler",	CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );
	pxShader->InitTextureUnit( TU_MR_ParallaxMap2,			"g_xParallaxMap2Sampler",			CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR );

	pxShader->InitTextureUnit( TU_MR_EnvironmentMap,		"g_xEnvironmentMapSampler",			CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_CLAMP_TO_EDGE, CELL_GCM_TEXTURE_LINEAR_LINEAR, CELL_GCM_TEXTURE_LINEAR
		, true
		);

	pxShader->InitTextureUnit( TU_MR_FogTexture,			"g_xFogSampler",					CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
}

/******************************************************************************/
/* Asura_MaterialResponse_System Static Class Functions                       */
/******************************************************************************/

void Asura_MaterialResponse_System::Platform_Initialise()
{
	Asura_PS3_MaterialResponse_System::Initialise();
}

void Asura_MaterialResponse_System::Platform_Reset( const bool bFullReset )
{
	Asura_PS3_MaterialResponse_System::Reset( bFullReset );
}

void Asura_MaterialResponse_System::Platform_Shutdown()
{
	Asura_PS3_MaterialResponse_System::Shutdown();
}

void Asura_MaterialResponse_System::Platform_InitialiseMaterial( const Asura_Hash_ID uNameHash,
	const Asura_MaterialResponse& xMaterial )
{
}

//
// Compute the hash for the shader that can draw the combination of these 2 materials.
// This assumes the tools have exported shaders for every combination that will be used in-game.
//
// Note: this duplicates the functionality of Tools_PS3_ShaderExport::CombineHashIDs, and the two functions must be kept in sync.
//
Asura_Hash_ID Asura_PS3_MaterialResponse_System::CreateShaderHashID( Asura_Hash_ID uMaterialHash1, Asura_Hash_ID uMaterialHash2 )
{
	// Allow for overridden material hashes
	uMaterialHash1 = Asura_MaterialResponse_System::GetOverriddenHash(uMaterialHash1);
	uMaterialHash2 = Asura_MaterialResponse_System::GetOverriddenHash(uMaterialHash2);

	// Generate shader hash from the partial hashes in these 2 materials.
	Asura_Hash_ID uHash	= ASURA_HASH_ID_UNSET;

	const PS3MaterialResponse* pxMaterial1 = g_xPS3MaterialHashTable.FindObjectByHashID( uMaterialHash1 );
	const PS3MaterialResponse* pxMaterial2 = g_xPS3MaterialHashTable.FindObjectByHashID( uMaterialHash2 );

	if ( !pxMaterial1 )
	{
		Asura_Assert( uMaterialHash1 == ASURA_HASH_ID_UNSET, "Failed to find PS3 info for material" ); //this is only a failure if the material seems to have a valid hash.
		return ASURA_HASH_ID_UNSET;
	}
	
	uHash |= pxMaterial1->m_uShaderHashID;
	
	if ( pxMaterial2 )
	{
		// modify flags for material 2's textures
		u_int uTextureFlags = pxMaterial2->m_uShaderHashID & ((1<<Asura_MaterialResponse::NUMBER_OF_TEXTURE_SLOTS) - 1);
		uHash |= uTextureFlags<<1;
		
		// combine flags for other things 
		uHash |= pxMaterial2->m_uShaderHashID & ~((1<<Asura_MaterialResponse::NUMBER_OF_TEXTURE_SLOTS) - 1); 
	}

	return uHash;
}

