//*******************************************************************
//
// File: Asura_PlatformFSFX_GaussianBlur.cpp 
//
// Created: 28 Feb 2006
//
// Description: 
//
//*******************************************************************

// system libraries
#include "Core/Asura.h"

#if defined( ASURA_USE_FSFX)

#include "Core/Asura_HeapArray.h"

#include "FSFX/Asura_FSFX_System.h"
#include "FSFX/Asura_FSFX_Template.h"
#include "FSFX/Asura_FSFX_Component.h"

#include "FSFX/Asura_Direct3D_FSFX.h"

#include "Rendering/Asura_Direct3D_Caps.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Direct3D_RenderTargetManager.h"
#include "Rendering/Asura_Direct3D_BackBufferCapture.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Direct3D_UserOptions.h"

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

static Direct3D_VertexElement g_axVertexDeclFSFXBlur[] =
{
	{ 0,  0,	D3DDECLTYPE_FLOAT3,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_POSITION,	0 },
	{ 0, 12,	D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_TEXCOORD,	0 },
	{ 0, 20,	D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_TEXCOORD,	1 },
	{ 0, 28,	D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_TEXCOORD,	2 },
	{ 0, 36,	D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_TEXCOORD,	3 },
	{ 0, 44,	D3DDECLTYPE_FLOAT4,	D3DDECLMETHOD_DEFAULT,	D3DDECLUSAGE_TEXCOORD,	4 },
	D3DDECL_END()
};

const char* const g_szVertexShader_FSFXBlur_Code =

"#line 1 \" "__FILE__"\" \n"
"vs.1.1 \n"
"dcl_position0 v0 \n"
"dcl_texcoord0 v1 \n"
"dcl_texcoord1 v2 \n"
"dcl_texcoord2 v3 \n"
"dcl_texcoord3 v4 \n"
"dcl_texcoord4 v5 \n"

"mov oPos, v0 \n"
"mov oT0, v1 \n"
"mov oT1, v2 \n"
"mov oT2, v3 \n"
"mov oT3, v4 \n"
"mov oD0, v5.xxxy \n"
"mov oD1, v5.zzzw \n"
;

//register the shader with Asura
const Asura_Direct3D_VertexShader g_xVertexShader_FSFXBlurShader(ASURA_D3D_VS_FSFX_BLUR, g_szVertexShader_FSFXBlur_Code, g_axVertexDeclFSFXBlur);

static const char* const g_szPixelShader_FSFXBlur_Code =

"#line 1 \" "__FILE__"\" \n"
"ps.1.1 \n"

"tex t0 \n"
"tex t1 \n"
"tex t2 \n"
"tex t3 \n"

"mul r0, t0, v0 \n"
"mad r0, t1, v0.a, r0 \n"
"mad r0, t2, v1, r0 \n"
"mad r0, t3, v1.a, r0 \n"
"mov r0.a, c0.a \n"
;

// Register the shader with Asura

const Asura_Direct3D_PixelShader g_xPixelShader_FSFXBlurShader(ASURA_D3D_PS_FSFX_BLUR, g_szPixelShader_FSFXBlur_Code);


//*******************************************************************
// Local Classes
//*******************************************************************

//*******************************************************************
// Static data
//*******************************************************************

static const float afHBLUR_TEXCOORD_OFFSETS[4][4] =
{
	{ -3.0f / 4.0f, 0.5f, 0.0f, 0.0f },
	{ -1.0f / 4.0f, 0.5f, 0.0f, 0.0f },
	{  1.0f / 4.0f, 0.5f, 0.0f, 0.0f },
	{  3.0f / 4.0f, 0.5f, 0.0f, 0.0f },
};

static const float afVBLUR_TEXCOORD_OFFSETS[4][4] =
{
	{ 0.5f, -3.0f / 4.0f, 0.0f, 0.0f },
	{ 0.5f, -1.0f / 4.0f, 0.0f, 0.0f },
	{ 0.5f,  1.0f / 4.0f, 0.0f, 0.0f },
	{ 0.5f,  3.0f / 4.0f, 0.0f, 0.0f },
};

// Filter weights
static const float afFILTER_WEIGHTS[4][4] =
{
	{ 0.1f, 0.1f, 0.1f, 0.1f },
	{ 0.4f, 0.4f, 0.4f, 0.4f },
	{ 0.4f, 0.4f, 0.4f, 0.4f },
	{ 0.1f, 0.1f, 0.1f, 0.1f },
};

struct SimpleVertex
{
	float m_fX, m_fY, m_fZ;
	float m_fU, m_fV;
};

static SimpleVertex g_axQUAD_VERTEX_DATA[4] =
{
	{ -1.0f,  1.0f,  0.0f,  0.0f,  0.0f },
	{  1.0f,  1.0f,  0.0f,  1.0f,  0.0f },
	{  1.0f, -1.0f,  0.0f,  1.0f,  1.0f },
	{ -1.0f, -1.0f,  0.0f,  0.0f,  1.0f },
};


// DLH - 18 July 2006 11:22:42 - I would have made this a member of the class so in-game it is set to the required size once (in-game at least) and left
// DLH - 18 July 2006 11:24:58 - But the person that designed the FSFX system did not allow for platform data or platform initalisations. 
// DLH - 18 July 2006 11:25:21 - So the array is resized if ever more coeffs are needed
// MGH 22/1/2008: The code is now clearing these arrays between levels to avoid memory fragmentation.
static Asura_HeapArray< Asura_Direct3D_FSFX_BlurKernalCoeff> g_xArrayKernelCoeffs;

static Asura_HeapArray< Asura_Direct3D_FSFX_Blur_Vertex> g_xArrayPolygonVertices;
static Asura_HeapArray< uint16> g_xArrayPolygonIndices;


//*******************************************************************
// Function Definitions
//*******************************************************************

void Asura_FSFX_Component_GaussianBlur_Platform_Reset()
{
	g_xArrayKernelCoeffs.Clear();
	g_xArrayPolygonVertices.Clear();
	g_xArrayPolygonIndices.Clear();
}

bool Asura_Direct3D_FSFX::Component_GaussianBlur_CalcCoeffs( const float fBlurRadius, Asura_HeapArray< Asura_Direct3D_FSFX_BlurKernalCoeff>& xArrayKernalCoeffs, const u_int uKernalCoeffsPerPolygon, u_int& uUsedKernalWidth)
{
	// DLH - 18 July 2006 14:52:02 - Describes the position and count of the actual used coeffs wrt the ideal coeffs
	static const u_int uKernalDescriptionDataMax = 2;
	static const u_int uKernalDescriptionDataOffsetsMax = 3;
	struct KernalDescriptionData
	{
		u_int m_uKernalRadiusMax;
		// DLH - 18 July 2006 14:51:57 - How many coeffs to loose in the translation
		u_int m_uKernalCoeffSub;
		// DLH - 18 July 2006 14:53:08 - How to scale the coeff count
		u_int m_uKernalCoeffDivide;
		// DLH - 18 July 2006 15:06:08 - The textel offset from the previous texture. Negative is a double check value
		float m_fOffset[ uKernalDescriptionDataOffsetsMax];
	} axKernalDescriptionData[ uKernalDescriptionDataMax] =
	{
		{ 7, 0, 1, { 0.0f, 1.0f, 1.0f}}, // 0-7 Radius
		{ static_cast<u_int>(~0l), 1, 2, { 0.5f, 2.0f, 2.0f}}, // 8+ Radius
	}
	;
	
	const u_int uBlurRadius = static_cast< u_int>( fBlurRadius);
	// DLH - 18 July 2006 14:55:23 - Get the Index of the Kernal Description to use
//	const u_int uKernalDescription = Asura_Maths::Min( uBlurRadius, (uKernalDescriptionDataMax - 1));
	u_int uKernalDescription = 0;
	for( ; uKernalDescription < uKernalDescriptionDataMax; uKernalDescription += 1)
	{
		const KernalDescriptionData& xKernalDescription = axKernalDescriptionData[ uKernalDescription];
		if( uBlurRadius <= xKernalDescription.m_uKernalRadiusMax)
		{
			break;
		}
	}
	Asura_Assertf( uKernalDescription < uKernalDescriptionDataMax, ("Kernal Description Index %d is too large. Max %d", uKernalDescription, uKernalDescriptionDataMax - 1));
	const KernalDescriptionData& xKernalDescription = axKernalDescriptionData[ uKernalDescription];

	const u_int uRequestedBlurRadiusSub = uBlurRadius - xKernalDescription.m_uKernalCoeffSub;
	const u_int uRequestedBlurRadiusDivide = (uRequestedBlurRadiusSub) / xKernalDescription.m_uKernalCoeffDivide;
	u_int uRequestedBlurWidth = (xKernalDescription.m_uKernalCoeffDivide * uRequestedBlurRadiusDivide) + 1;
	// DLH - 18 July 2006 11:11:32 - The kernal is not big enough to change the scene image
	if( uRequestedBlurWidth < 1)
	{
		return( false);
	}
	
	uRequestedBlurWidth += (0 == (uRequestedBlurWidth % 2)) ? 1 : 0;
	
	Asura_Assertf( 1 == (uRequestedBlurWidth % 2), ("The Requested Blur Width should always be odd. Requested %d.", uRequestedBlurWidth));
	
	uUsedKernalWidth = uRequestedBlurWidth;
	
	{
		const u_int uAllocatedBlurWidth = uRequestedBlurWidth + uKernalCoeffsPerPolygon;
		if( xArrayKernalCoeffs.Size() < uAllocatedBlurWidth)
		{
			xArrayKernalCoeffs.Clear();
			xArrayKernalCoeffs.SetSize( uAllocatedBlurWidth);
		}
	}
	
	{
		const u_int uKernalCoeffIndexCenter = (uUsedKernalWidth / 2);
		float fKernalCoeffOffset0 = xKernalDescription.m_fOffset[ 0];
		float fKernalCoeffOffset1 = fKernalCoeffOffset0;
		const float fKernalCoeffScaleGradient = -1.0f / (fBlurRadius);
		float fKernalCoeffScale = 1.0f;
		
		// DLH - 18 July 2006 16:17:36 - Calc the Coeffs and their positions
		for( u_int uKernalCoeffIndexOffset = 0; uKernalCoeffIndexOffset <= uKernalCoeffIndexCenter; uKernalCoeffIndexOffset += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xKernalCoeff0 = xArrayKernalCoeffs[ uKernalCoeffIndexCenter - uKernalCoeffIndexOffset];
			Asura_Direct3D_FSFX_BlurKernalCoeff& xKernalCoeff1 = xArrayKernalCoeffs[ uKernalCoeffIndexCenter + uKernalCoeffIndexOffset];

			xKernalCoeff0.m_fOffset = fKernalCoeffOffset0;
			xKernalCoeff1.m_fOffset = fKernalCoeffOffset1;
			
			Asura_Assertf( -0.0 <= fKernalCoeffScale, ("Kernal Coeff %f too small", fKernalCoeffScale));
			xKernalCoeff0.m_fScale = fKernalCoeffScale;
			xKernalCoeff1.m_fScale = fKernalCoeffScale;
			
			{
				const u_int uOffsetIndex = Asura_Maths::Min( (uKernalCoeffIndexOffset + 1), uKernalDescriptionDataOffsetsMax - 1);
				const float fCoeffOffset = xKernalDescription.m_fOffset[ uOffsetIndex];
				Asura_Assertf( -0.0f < fCoeffOffset, ("Coeff offset %f is too small for center offset %d", fCoeffOffset, uKernalCoeffIndexOffset));
				fKernalCoeffOffset0 -= fCoeffOffset;
				fKernalCoeffOffset1 += fCoeffOffset;
				
				fKernalCoeffScale += fKernalCoeffScaleGradient * fCoeffOffset;
			}
		}
		
		// DLH - 18 July 2006 16:17:57 - Calc the total height of all the coeffs
		float fKernalCoeffScaleTotal = 0.0f;
		for( u_int uKernalCoeffIndex = 0; uKernalCoeffIndex < uUsedKernalWidth; uKernalCoeffIndex += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xKernalCoeff = xArrayKernalCoeffs[ uKernalCoeffIndex];
			
			fKernalCoeffScaleTotal += xKernalCoeff.m_fScale;
		}
		
		// DLH - 18 July 2006 16:18:11 - Normalise all the Coeffs so they total 1.0f
		#if defined( ASURA_ASSERT)
			float fKernalCoeffScaleNormalisedTotal = 0.0f;
		#endif
		float fKernalCoeffScaleNormaliseScale = 1.0f / fKernalCoeffScaleTotal;
		for( u_int uKernalCoeffIndex = 0; uKernalCoeffIndex < uUsedKernalWidth; uKernalCoeffIndex += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xKernalCoeff = xArrayKernalCoeffs[ uKernalCoeffIndex];
			
			xKernalCoeff.m_fScale *= fKernalCoeffScaleNormaliseScale;
			#if defined( ASURA_ASSERT)
				fKernalCoeffScaleNormalisedTotal += xKernalCoeff.m_fScale;
			#endif
		}
		
		Asura_Assertf( Asura_Maths::Abs( 1.0f - fKernalCoeffScaleNormalisedTotal) < 0.1f, ("The normalised Coeffs (%f) are not close enought to 1.0f", fKernalCoeffScaleNormalisedTotal));
		
		const u_int uKernalCoeffBlankCount = xArrayKernalCoeffs.Size();
		for( u_int uKernalCoeffIndex = uUsedKernalWidth; uKernalCoeffIndex < uKernalCoeffBlankCount; uKernalCoeffIndex += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xKernalCoeff = xArrayKernalCoeffs[ uKernalCoeffIndex];
			
			xKernalCoeff.m_fScale = 0.0f;
			xKernalCoeff.m_fOffset = 0.0f;
		}
		
	}

	return( true);	
}

bool Asura_Direct3D_FSFX::Component_Setup( const u_int uUsedKernalWidth, const u_int uKernalCoeffsPerPolygon, Asura_Direct3D_FSFX_Blur_Info& Info, Asura_HeapArray< Asura_Direct3D_FSFX_Blur_Vertex>& xArrayPolygonVertices, Asura_HeapArray< uint16>& xArrayPolygonIndices)
{
	const u_int uKernalPolygons = Info.uKernalPolygons = (uUsedKernalWidth / uKernalCoeffsPerPolygon) + ((0 != (uUsedKernalWidth % uKernalCoeffsPerPolygon)) ? 1 : 0);
	const u_int uKernalPolygonVertices = Info.uKernalPolygonVertices = 4 * uKernalPolygons;
	
	if( xArrayPolygonVertices.Size() < uKernalPolygonVertices)
	{
		xArrayPolygonVertices.Clear();
		xArrayPolygonVertices.SetSize( uKernalPolygonVertices);
	}

	const u_int uKernalPolygonIndices = Info.uKernalPolygonIndices = 6 * uKernalPolygons;
	
	if( xArrayPolygonIndices.Size() < uKernalPolygonIndices)
	{
		xArrayPolygonIndices.Clear();
		xArrayPolygonIndices.SetSize( uKernalPolygonIndices);
	}
	
	for( u_int uPolygonIndex = 0; uPolygonIndex < uKernalPolygons; uPolygonIndex += 1)
	{
		u_int uVertex0Index = 4 * uPolygonIndex;
		Asura_Direct3D_FSFX_Blur_Vertex* pxVertices = &(xArrayPolygonVertices[ uVertex0Index]);
		
		for( u_int uIndexCorner = 0; uIndexCorner < 4; uIndexCorner += 1)
		{
			pxVertices[ uIndexCorner].m_fPositionX = g_axQUAD_VERTEX_DATA[ uIndexCorner].m_fX;
			pxVertices[ uIndexCorner].m_fPositionY = g_axQUAD_VERTEX_DATA[ uIndexCorner].m_fY;
			pxVertices[ uIndexCorner].m_fPositionZ = g_axQUAD_VERTEX_DATA[ uIndexCorner].m_fZ;
		}
		
		u_int uIndex0Index = 6 * uPolygonIndex;
		uint16* pusIndices = &(xArrayPolygonIndices[ uIndex0Index]);
		
		Asura_Assert( uVertex0Index < (0xFFFF - 6), "Too many Coeff polygons");
		pusIndices[ 0] = static_cast< uint16>( uVertex0Index + 0);
		pusIndices[ 1] = static_cast< uint16>( uVertex0Index + 1);
		pusIndices[ 2] = static_cast< uint16>( uVertex0Index + 3);
		pusIndices[ 3] = static_cast< uint16>( uVertex0Index + 1);
		pusIndices[ 4] = static_cast< uint16>( uVertex0Index + 3);
		pusIndices[ 5] = static_cast< uint16>( uVertex0Index + 2);
	}
	
return( true);
}

bool Asura_Direct3D_FSFX::Component_Setup_Horizontal( const float fViewWidth, const float fViewHeight, const Asura_Direct3D_FSFX_Blur_Info& xBlurInfo, Asura_HeapArray< Asura_Direct3D_FSFX_BlurKernalCoeff>& xArrayKernalCoeffs, Asura_HeapArray< Asura_Direct3D_FSFX_Blur_Vertex>& xArrayPolygonVertices)
{
	for( u_int uPolygonIndex = 0; uPolygonIndex < xBlurInfo.uKernalPolygons; uPolygonIndex += 1)
	{
		u_int uVertex0Index = 4 * uPolygonIndex;
		Asura_Direct3D_FSFX_Blur_Vertex* pxVertices = &(xArrayPolygonVertices[ uVertex0Index]);
		
		u_int uCoeff0Index = 4 * uPolygonIndex;
		for( u_int uCoeffIndex = 0; uCoeffIndex < 4; uCoeffIndex += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xCoeff = xArrayKernalCoeffs[ uCoeff0Index + uCoeffIndex];
			
			const float fOffset = xCoeff.m_fOffset;
			
			const float fCoeffScale = xCoeff.m_fScale;
			
			for( u_int uIndexVertex = 0; uIndexVertex < 4; uIndexVertex += 1)
			{
				Asura_Direct3D_FSFX_Blur_Vertex& xVertex = pxVertices[ uIndexVertex];
				
				xVertex.m_afTextureOffset[ uCoeffIndex][ 0] = ((0.5f + fOffset) / fViewWidth) + g_axQUAD_VERTEX_DATA[ uIndexVertex].m_fU;
				xVertex.m_afTextureOffset[ uCoeffIndex][ 1] = ((0.5f) / fViewHeight) + g_axQUAD_VERTEX_DATA[ uIndexVertex].m_fV;
				xVertex.m_afCoeffs[ uCoeffIndex] = fCoeffScale;
			}
		}
		
	}
	return( true);
}

bool Asura_Direct3D_FSFX::Component_Setup_Vertical( const float fViewWidth, const float fViewHeight, const float fBufferWidth, const float fBufferHeight, const Asura_Direct3D_FSFX_Blur_Info& xBlurInfo, Asura_HeapArray< Asura_Direct3D_FSFX_BlurKernalCoeff>& xArrayKernalCoeffs, Asura_HeapArray< Asura_Direct3D_FSFX_Blur_Vertex>& xArrayPolygonVertices)
{
	for( u_int uPolygonIndex = 0; uPolygonIndex < xBlurInfo.uKernalPolygons; uPolygonIndex += 1)
	{
		u_int uVertex0Index = 4 * uPolygonIndex;
		Asura_Direct3D_FSFX_Blur_Vertex* pxVertices = &(xArrayPolygonVertices[ uVertex0Index]);
		
		u_int uCoeff0Index = 4 * uPolygonIndex;
		for( u_int uCoeffIndex = 0; uCoeffIndex < 4; uCoeffIndex += 1)
		{
			Asura_Direct3D_FSFX_BlurKernalCoeff& xCoeff = xArrayKernalCoeffs[ uCoeff0Index + uCoeffIndex];
			
			const float fOffset = xCoeff.m_fOffset;
			
			for( u_int uIndexVertex = 0; uIndexVertex < 4; uIndexVertex += 1)
			{
				Asura_Direct3D_FSFX_Blur_Vertex& xVertex = pxVertices[ uIndexVertex];
				
				float &fTextureOffsetU = xVertex.m_afTextureOffset[ uCoeffIndex][ 0];
				float &fTextureOffsetV = xVertex.m_afTextureOffset[ uCoeffIndex][ 1];
				
				fTextureOffsetU = ((0.5f) + (fViewWidth * g_axQUAD_VERTEX_DATA[ uIndexVertex].m_fU)) / fBufferWidth;
				fTextureOffsetV = ((0.5f + fOffset) + (fViewHeight * g_axQUAD_VERTEX_DATA[ uIndexVertex].m_fV)) / fBufferHeight;
			}
		}
	}
	return( true);
}

void Asura_FSFX_Component_GaussianBlur::Platform_Render( const float fIntensity )
{
	SCOPEDPIXEVENT(( 0xFFE0D0C0, L"FSFX: Gaussian Blur" ));

	if ( fIntensity == 0.0f )
	{
		return;
	}

	Asura_Direct3D_RenderTarget* pxBlurBuffer = Asura_Direct3D_RenderTargetManager::FindUnusedRenderTarget( 1.0f, 1.0f, Asura_Direct3D_UserOptions::GetRenderTargetBufferFormat(), D3DFMT_UNKNOWN, true);
	if( NULL == pxBlurBuffer)
	{
		return;
	}

	const u_int uViewWidth = Asura_Direct3D::GetPresentParameters().BackBufferWidth;
	const u_int uViewHeight = Asura_Direct3D::GetPresentParameters().BackBufferHeight;
	const u_int uBufferWidth = pxBlurBuffer->GetWidth();
	const u_int uBufferHeight = pxBlurBuffer->GetHeight();
	const float fViewWidth = static_cast< float> ( uViewWidth);
	const float fViewHeight = static_cast< float> ( uViewHeight);
	const float fBufferWidth = static_cast< float> ( uBufferWidth);
	const float fBufferHeight = static_cast< float> ( uBufferHeight);
	
	// DLH - 18 July 2006 11:07:55 - Calculate the Scale to go from PS2's 512 wide screen to the present PC resolution 
	const float fResolutionScale = fBufferWidth / static_cast< float>( ASURA_FSFX_BLUR_PS2_REFERENCE_SCREEN_WIDTH);
	const float fBlurRadius = fResolutionScale * (0.5f * this->m_fSize) * (this->m_fStrength / 100.0f) * fIntensity;
	
	// DLH - 18 July 2006 11:22:42 - I would have made this a member of the class so in-game it is set to the required size once (in-game at least) and left
	// DLH - 18 July 2006 11:24:58 - But the person that designed the FSFX system did not allow for platform data or platform initalisations. 
	// DLH - 18 July 2006 11:25:21 - So the array is resized if ever more coeffs are needed
	u_int uUsedKernalWidth = 0;
	
	const u_int uKernalCoeffsPerPolygon = 4;
	if( false == Asura_Direct3D_FSFX::Component_GaussianBlur_CalcCoeffs( fBlurRadius, g_xArrayKernelCoeffs, uKernalCoeffsPerPolygon, uUsedKernalWidth))
	{
		pxBlurBuffer->MarkAsUnused();
		return;
	}
	
	if( uUsedKernalWidth <= 1)
	{
		pxBlurBuffer->MarkAsUnused();
		return;
	}
	
	Asura_Direct3D_FSFX_Blur_Info xBlurInfo;
	Asura_Direct3D_FSFX::Component_Setup( uUsedKernalWidth, uKernalCoeffsPerPolygon, xBlurInfo, g_xArrayPolygonVertices, g_xArrayPolygonIndices);
	
	Asura_Direct3D_FSFX::Component_Setup_Horizontal( fViewWidth, fViewHeight, xBlurInfo, g_xArrayKernelCoeffs, g_xArrayPolygonVertices);
	
	const u_int uDIPPrimitives = 2 * xBlurInfo.uKernalPolygons;
	
	HRESULT xResult;
	
	// set up some states that persist across the effect
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_OPAQUE);
	if( 1 < xBlurInfo.uKernalPolygons)
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_ADD);
	}
	else
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
	}
	Asura_Render::CurrentStates.RequestAlphaTest(false);
	Asura_Render::CurrentStates.RequestZBufferEnabled(false);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

	// Use clamping of tex co-ords

	DWORD uOldAddressU[4], uOldAddressV[4];
	Direct3D_BaseTexture* pxOldTexture[4];
	for (u_int u = 0; u < 4; ++u)
	{
		xResult = Asura_Direct3D::D3DDevice->GetSamplerState(u, D3DSAMP_ADDRESSU, &uOldAddressU[u]);
		ASURA_D3D_OK( xResult);
		xResult = Asura_Direct3D::D3DDevice->GetSamplerState(u, D3DSAMP_ADDRESSV, &uOldAddressV[u]);
		ASURA_D3D_OK( xResult);

		xResult = Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
		ASURA_D3D_OK( xResult);
		xResult = Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
		ASURA_D3D_OK( xResult);
		
		xResult = Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		ASURA_D3D_OK( xResult);
		xResult = Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		ASURA_D3D_OK( xResult);

		xResult = Asura_Direct3D::D3DDevice->GetTexture(u, &pxOldTexture[u]);
		ASURA_D3D_OK( xResult);
	}

	// ensure that we have a backbuffer capture knocking about
	if(!Asura_Direct3D_BackBufferCapture::HasCapturedThisFrame())
	{
		Asura_Direct3D_BackBufferCapture::CaptureCurrentBackBuffer();
	}
	Asura_Render::AllowFullScreenRendering();

	// get the current render target
	Direct3D_Surface* pxOldRenderTargetBuffer = NULL;
	Direct3D_Surface* pxOldRenderTargetDepth = NULL;
	xResult = Asura_Direct3D::D3DDevice->GetRenderTarget(0, &pxOldRenderTargetBuffer);
	ASURA_D3D_OK( xResult);
	xResult = Asura_Direct3D::D3DDevice->GetDepthStencilSurface(&pxOldRenderTargetDepth);
	ASURA_D3D_OK( xResult);
	
	// set up the blur shaders
	Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_FSFX_BLUR);
	Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_FSFX_BLUR);

	// Shader const - for the constant alpha to output 
	float afColour[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
	xResult = Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 0, afColour, 1);
	ASURA_D3D_OK( xResult);

	// set our blur buffer as the target
	xResult = Asura_Direct3D::D3DDevice->SetRenderTarget(0, pxBlurBuffer->GetSurface());
	ASURA_D3D_OK( xResult);
	{
		unsigned int u;
		for (u = 1; u < Asura_Direct3D_Caps::GetMaxNumberOfRenderTargets(); ++u)
		{
			Asura_Direct3D::D3DDevice->SetRenderTarget(u, NULL);
		}
	}
	xResult = Asura_Direct3D::D3DDevice->SetDepthStencilSurface( NULL);
	ASURA_D3D_OK( xResult);

	#if defined ASURA_TARGET_SM3
		// Might as well disable sRGB
		Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, FALSE );
	#endif

	// DLH - 17 July 2006 17:04:20 - Only use the area we want
	{
		D3DVIEWPORT9 xViewport;
		
		xViewport.Width = uViewWidth;
		xViewport.Height = uViewHeight;
		xViewport.X = 0;
		xViewport.Y = 0;
		xViewport.MinZ = 0.0f;
		xViewport.MaxZ = 1.0f;
		xResult = Asura_Direct3D::D3DDevice->SetViewport( &xViewport);
		ASURA_D3D_OK( xResult);
	}
	
	{
		const u_int uClearColour = 0x00000000;
		xResult = Asura_Direct3D::D3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET, uClearColour, 0, 0);
		ASURA_D3D_OK( xResult);
	}

	// set the backbuffer capture as the textures
	for(u_int u = 0; u < 4; ++u)
	{
		Asura_Direct3D::D3DDevice->SetTexture(u, Asura_Direct3D_BackBufferCapture::GetCaptureTexture());
	}

	{
		xResult = Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, xBlurInfo.uKernalPolygonVertices, uDIPPrimitives, &(g_xArrayPolygonIndices[0]), D3DFMT_INDEX16, &(g_xArrayPolygonVertices[0]), sizeof( g_xArrayPolygonVertices[0]));
		ASURA_D3D_OK( xResult);
	}

////// Vertical blur passes

	// Restore the render target
	xResult = Asura_Direct3D::D3DDevice->SetRenderTarget(0, pxOldRenderTargetBuffer);
	pxOldRenderTargetBuffer->Release();
	ASURA_D3D_OK( xResult);
	if( NULL != pxOldRenderTargetDepth)
	{
		xResult = Asura_Direct3D::D3DDevice->SetDepthStencilSurface( pxOldRenderTargetDepth);
		pxOldRenderTargetDepth->Release();
		ASURA_D3D_OK( xResult);
	}

	{
		const u_int uClearColour = 0x00000000;
		xResult = Asura_Direct3D::D3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET, uClearColour, 0, 0);
		ASURA_D3D_OK( xResult);
	}
	
	// set our blur buffer as the textures
	for(u_int u = 0; u < 4; ++u)
	{
		Asura_Direct3D::D3DDevice->SetTexture(u, pxBlurBuffer->GetTexture());
	}

	Asura_Direct3D_FSFX::Component_Setup_Vertical( fViewWidth, fViewHeight, fBufferWidth, fBufferHeight, xBlurInfo, g_xArrayKernelCoeffs, g_xArrayPolygonVertices);

	
	{
		xResult = Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, xBlurInfo.uKernalPolygonVertices, uDIPPrimitives, &(g_xArrayPolygonIndices[0]), D3DFMT_INDEX16, &(g_xArrayPolygonVertices[0]), sizeof( g_xArrayPolygonVertices[0]));
		ASURA_D3D_OK( xResult);
	}

////// Finished

	// clean up
	Asura_Render::CurrentStates.RequestZBufferEnabled(true);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

	for(u_int u = 0; u < 4; ++u)
	{
		Asura_Direct3D::D3DDevice->SetTexture(u, pxOldTexture[u]);
		if(pxOldTexture[u]) pxOldTexture[u]->Release();

		Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_ADDRESSU, uOldAddressU[u]);
		Asura_Direct3D::D3DDevice->SetSamplerState(u, D3DSAMP_ADDRESSV, uOldAddressV[u]);
	}
	Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_NONE);
	Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);
	
	Asura_Direct3D_BackBufferCapture::AdviseNextFrame();
	
	pxBlurBuffer->MarkAsUnused();
	Asura_Render::ApplyCameraSettings();

	#if defined ASURA_TARGET_SM3
		const bool bSRGB = Asura_Direct3D::UseGammaCorrection();
		Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, bSRGB );
	#endif
}

#endif //defined( ASURA_USE_FSFX)
