//
// CSM_FP.h
//
// Ben Weston 14/11/08
//
// Declarations for Cascaded Shadow Map shaders.
// This is a non-standard way of declaring a shader, see my notes in EdgeAntiAlias_FP.h
// Bw - addendum: this file has 3 shader declarations in it, which are working well and beginning to look like a standardized way to declare shaders. Perhaps this could be a macro/template.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_PS3_CSM_APPLY_FP_H__
#define __ASURA_PS3_CSM_APPLY_FP_H__

#include "Shadows/Asura_CSM.h"

// The number of ambient occlusion blurred levels, not counting sampling direct from the depth buffer for the first (sharpest) level (if we're doing that)
// i.e. this is the number of textures that need to be generated by pre-processing before the actual SSAO pass.
#define NUM_AMBIENT_OCCLUSION_LEVELS	(3)	// the shader has a corresponding value, hard-coded so it can unroll loops


#define ASURA_SHADOW_SHADER_CSM_APPLY			(0x1000) // shadows use hashes up to 1111 (decimal), but they're not declared anywhere
#define ASURA_SHADOW_SHADER_COMPUTE_VSM			(0x1001)
#define ASURA_SHADOW_SHADER_BLUR_VSM			(0x1002)


extern u_int _binary_PS3_CSM_Apply_FP_bin_start;
extern u_int _binary_PS3_CSM_Apply_FP_bin_end;

class Asura_PS3_CSM_Apply_FP : public Asura_PS3_FragmentShader
{
public:
	enum {
		FP_ShadowViewProj,
		FP_InvViewMatrix = FP_ShadowViewProj + ASURA_CSM_MAX_MAPS,
		FP_TwoOverScreen,
		FP_CameraTanHalfFOV,
		FP_DepthAdjustment,
		FP_Splits,
		FP_Strength,
		FP_Power,
		FP_Stretch,
		FP_ELEMENTS
	};

	enum {
		FP_DepthBufferSampler,
		FP_ShadowBufferSampler,
		TU_ELEMENTS
	};

	class Handles : public Asura_PS3_FP_Handles
	{
	public:
		Handles() : Asura_PS3_FP_Handles( FP_ELEMENTS, TU_ELEMENTS )
		{}

		void InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
		{
			//Uniform parameters
			for ( int i=0; i < ASURA_CSM_MAX_MAPS; i++ )
			{
				char szName[64];
				sprintf( szName, "g_axShadowViewProj[%d]", i );
				
				pxShader->InitUniformElement( FP_ShadowViewProj+i, szName, 4 );
			}
			pxShader->InitUniformElement( FP_InvViewMatrix, "g_xInvViewMatrix", 4 );
			pxShader->InitUniformElement( FP_TwoOverScreen, "g_xTwoOverScreen", 1 );
			pxShader->InitUniformElement( FP_CameraTanHalfFOV, "g_xCameraTanHalfFOV", 1 );
			pxShader->InitUniformElement( FP_DepthAdjustment, "g_xDepthAdjustment", 1 );
			pxShader->InitUniformElement( FP_Splits, "g_xSplits", 1 );
			pxShader->InitUniformElement( FP_Strength, "g_afStrengths", 1 );
			pxShader->InitUniformElement( FP_Power, "g_fPower", 1 );
			pxShader->InitUniformElement( FP_Stretch, "g_fStretch", 1 );
			

			//Texture units
			pxShader->InitTextureUnit( FP_DepthBufferSampler, "g_xDepthSampler", CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
			pxShader->InitTextureUnit( FP_ShadowBufferSampler, "g_xShadowMap", CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR );
		}
	};

//don't allow creation using new, this shader has some parameters that will vary from frame to frame so you need should use GetInstance (which stores each instance in scratch memory)
// 	Asura_PS3_CSM_Apply_FP() :
// 		Asura_PS3_FragmentShader( (unsigned char *)&_binary_PS3_CSM_Apply_FP_bin_start,
// 								  (unsigned char *)&_binary_PS3_CSM_Apply_FP_bin_end,
// 								  new Handles ) // create handles
// 	{
// 	}
// 
// 	~Asura_PS3_CSM_Apply_FP()
// 	{
// 		delete m_pxHandles;	// delete the handles we created in the constructor (though it's not obvious thanks to C++'s obfuscated syntax)
// 	}

	// alternative to creating an instance of this shader, hand it over to the library.
	static void AddToShaderLibrary()
	{
		Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_CSM_APPLY, (unsigned char *)&_binary_PS3_CSM_Apply_FP_bin_start, (unsigned char *)&_binary_PS3_CSM_Apply_FP_bin_end - (unsigned char *)&_binary_PS3_CSM_Apply_FP_bin_start,
											new Handles, 1, true );
	}
	
	static Asura_PS3_FragmentShaderCopy* GetInstance()
	{
		return Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_CSM_APPLY );
	}
};



extern u_int _binary_PS3_ComputeVariance_FP_bin_start;
extern u_int _binary_PS3_ComputeVariance_FP_bin_end;

class Asura_PS3_ComputeVariance_FP : public Asura_PS3_FragmentShader
{
public:
	enum {
		FP_ELEMENTS
	};

	enum {
		FP_DepthBufferSampler,
		TU_ELEMENTS
	};

	class Handles : public Asura_PS3_FP_Handles
	{
	public:
		Handles() : Asura_PS3_FP_Handles( FP_ELEMENTS, TU_ELEMENTS )
		{}

		void InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
		{
			pxShader->InitTextureUnit( FP_DepthBufferSampler, "g_xDepthSampler", CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_NEAREST, CELL_GCM_TEXTURE_NEAREST );
		}
	};

	// alternative to creating an instance of this shader, hand it over to the library.
	static void AddToShaderLibrary()
	{
		Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_COMPUTE_VSM, (unsigned char *)&_binary_PS3_ComputeVariance_FP_bin_start, (unsigned char *)&_binary_PS3_ComputeVariance_FP_bin_end - (unsigned char *)&_binary_PS3_ComputeVariance_FP_bin_start,
											new Handles, 1, true );
	}

	static Asura_PS3_FragmentShaderCopy* GetInstance ()
	{
		return Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_COMPUTE_VSM );
	}
};


extern u_int _binary_PS3_BlurVariance_FP_bin_start;
extern u_int _binary_PS3_BlurVariance_FP_bin_end;

class Asura_PS3_BlurVariance_FP : public Asura_PS3_FragmentShader
{
public:
	enum {
		FP_BlurDirection,
		FP_ELEMENTS
	};

	enum {
		FP_VarianceSampler,
		TU_ELEMENTS
	};

	class Handles : public Asura_PS3_FP_Handles
	{
	public:
		Handles() : Asura_PS3_FP_Handles( FP_ELEMENTS, TU_ELEMENTS )
		{}

		void InitialiseFromShader( Asura_PS3_FragmentShaderBase* pxShader )
		{
			// Uniform parameters
			pxShader->InitUniformElement( FP_BlurDirection, "g_xBlurDirection", 1 );

			// Texture units
			pxShader->InitTextureUnit( FP_VarianceSampler, "g_xVarianceSampler", CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_CLAMP, CELL_GCM_TEXTURE_LINEAR, CELL_GCM_TEXTURE_LINEAR );
		}
	};

	// alternative to creating an instance of this shader, hand it over to the library.
	static void AddToShaderLibrary()
	{
		Asura_PS3_GCM_ShadersLibrary::AddFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_BLUR_VSM, (unsigned char *)&_binary_PS3_BlurVariance_FP_bin_start, (unsigned char *)&_binary_PS3_BlurVariance_FP_bin_end - (unsigned char *)&_binary_PS3_BlurVariance_FP_bin_start,
			new Handles, 1, true );
	}

	static Asura_PS3_FragmentShaderCopy* GetInstance ()
	{
		return Asura_PS3_GCM_ShadersLibrary::GetPermanentFP( ASURA_SHADER_HASH_SHADOW, ASURA_SHADOW_SHADER_BLUR_VSM );
	}
};


#endif //#ifndef __ASURA_PS3_CSM_APPLY_FP_H__
