//
// Asura_Xbox360_QuadOverlayRenderer.cpp
//
// Created by KJFL - 03 August 2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

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

#include "Core/Asura.h"

#include "Rendering/Asura_Xbox360_QuadOverlayRenderer.h"

// Asura Platform-independent
#include "Core/Asura_Maths.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"

// Asura Xbox 360
#include "Core/Asura_PlatformBasics.h"
#include "Rendering/Asura_Xbox360_Effect_System.h"
#include "Rendering/Asura_Xbox360_TextureManagement.h"

// Shader Include Files
#include "QuadOverlayRenderer.h"
// Misc
#include <fxl.h>
#include <xtl.h>

/******************************************************************************/
/* Classes                                                                    */
/******************************************************************************/

struct Asura_Xbox360_QuadOverlay_FlatColourVertex
{
	float m_fX, m_fY;
	float m_fU, m_fV;
};

struct Asura_Xbox360_QuadOverlay_GouraudColourVertex
{
	float m_fX, m_fY;
	float m_fU, m_fV;
	u_int m_uColour;
};

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

static const Direct3D_VertexElement g_axFlatColourVertexElements[] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0,  8, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	D3DDECL_END()
};

static const Direct3D_VertexElement g_axGouraudColourVertexElements[] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0,  8, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	{ 0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0 },
	D3DDECL_END()
};

/******************************************************************************/
/* Global Variables                                                           */
/******************************************************************************/

Direct3D_VertexElement const* const Asura_Xbox360_QuadOverlayRenderer::s_apxVertexElements[MAX_QUAD_TYPES] =
{
	g_axFlatColourVertexElements,
	g_axGouraudColourVertexElements,
};

Direct3D_VertexDeclaration*			Asura_Xbox360_QuadOverlayRenderer::s_apxVertexDeclarations[MAX_QUAD_TYPES];

Asura_Xbox360_Effect*				Asura_Xbox360_QuadOverlayRenderer::s_pxEffect;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_QuadOverlayRenderer::s_xSamplerHandle;
Asura_Xbox360_Effect_System::Handle Asura_Xbox360_QuadOverlayRenderer::s_xFlatColourHandle;


/******************************************************************************/
/* Asura_Xbox360_OverlayQuadRenderer Static Class Functions                   */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_QuadOverlayRenderer::Initialise()
//
// Initialises the overlay quad renderer on Xbox 360
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_QuadOverlayRenderer::Initialise()
{
	Direct3D_Device*	pxDevice	= Asura_Xbox360_Graphics::GetDevice();

	if(pxDevice != NULL)
	{
		for(u_int u = 0; u < MAX_QUAD_TYPES; u++)
		{
			pxDevice->CreateVertexDeclaration(s_apxVertexElements[u], &s_apxVertexDeclarations[u]);
			Asura_Assert(s_apxVertexDeclarations[u], "Failed to create overlay quad vertex declaration.");
		}
	}

	s_pxEffect			= Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auQuadOverlayRenderer);
	s_xSamplerHandle	= s_pxEffect->GetParameterHandle("xSampler");
	s_xFlatColourHandle	= s_pxEffect->GetParameterHandle("g_xFlatColour");
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_QuadOverlayRenderer::Shutdown()
//
// Shuts down the overlay quad renderer on Xbox 360
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_QuadOverlayRenderer::Shutdown()
{
	for(u_int u = 0; u < MAX_QUAD_TYPES; u++)
	{
		if(s_apxVertexDeclarations[u] != NULL)
		{
			s_apxVertexDeclarations[u]->Release();
			s_apxVertexDeclarations[u]	= NULL;
		}
	}

	if(s_pxEffect != NULL)
	{
		Asura_Xbox360_Effect_System::Destroy(s_pxEffect);
		s_pxEffect	= NULL;
	}

	s_xSamplerHandle	= 0;
	s_xFlatColourHandle	= 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_QuadOverlayRenderer::Render()
//
// Renders an overlay quad renderer on Xbox 360
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_QuadOverlayRenderer::Render( const Asura_Overlay_Quad& xQuad)
{
	Direct3D_Device*				pxDevice	= Asura_Xbox360_Graphics::GetDevice();
	Asura_Xbox360_Texture* const	pxTexture	= Asura_Xbox360_TextureManagement::GetTexture(xQuad.TextureIndex);

	Asura_Render::CurrentStates.RequestTranslucencyMode(xQuad.TranslucencyMode);
	pxDevice->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );
	pxDevice->SetRenderState_Inline( D3DRS_MULTISAMPLEANTIALIAS, FALSE );
	{
		RenderEx(xQuad, pxTexture->GetD3DTexture());
	}
	pxDevice->SetRenderState_Inline( D3DRS_MULTISAMPLEANTIALIAS, TRUE );
	pxDevice->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, FALSE );
}


////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_QuadOverlayRenderer::RenderEx()
//
// Renders an overlay quad on Xbox 360
//
// Special version that takes a D3D texture - used only from 360 platform code,
// e.g. to display deferred shading targets
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_QuadOverlayRenderer::RenderEx( const Asura_Overlay_Quad& xQuad , Direct3D_BaseTexture* pxD3DTexture, Asura_Xbox360_Effect *pxAltEffect)
{
	PIXBeginNamedEvent( D3DCOLOR_XRGB( 0, 0x80, 0x80 ), "Overlay Quad" );
	{
		const float							fOneOverScreenWidth		= 1.0f / static_cast<float>(Asura_Viewport::GetWidth());
		const float							fOneOverScreenHeight	= 1.0f / static_cast<float>(Asura_Viewport::GetHeight());

		const QuadType						eQuadType				= (xQuad.m_bGouraudShaded) ? QT_GOURAUD_COLOUR : QT_FLAT_COLOUR;
		Direct3D_Device*					pxDevice				= Asura_Xbox360_Graphics::GetDevice();

		Asura_Xbox360_Effect*				pxEffect;
		Asura_Xbox360_Effect_System::Handle	xSamplerHandle;
		Asura_Xbox360_Effect_System::Handle	xFlatColourHandle;

		if(pxAltEffect == NULL)
		{
			pxEffect		= s_pxEffect;
			xSamplerHandle	= s_xSamplerHandle;
			xFlatColourHandle = s_xFlatColourHandle;
		}
		else
		{
			// Support for specifying an alternative effect. Used for the debug rendering of the
			// normal buffer (for the deferred shading), as we need to unpack+scale the normal
			pxEffect		= pxAltEffect;
			xSamplerHandle	= pxAltEffect->GetParameterHandle("xSampler");
			xFlatColourHandle = pxAltEffect->GetParameterHandle("g_xFlatColour");;
		}	

		pxDevice->SetVertexDeclaration(s_apxVertexDeclarations[eQuadType]);
		pxEffect->GetInterface()->SetSampler(xSamplerHandle, pxD3DTexture);

		Direct3D_Surface* pxOldRT = NULL;
		Asura_Xbox360_Graphics::GetDevice()->GetRenderTarget( 0, &pxOldRT );

		if( pxOldRT == Asura_Xbox360_Graphics::GetPostToneMapRT() )
		{
			// We decided to disable sRGB writes for overlay quads on PC to preserve the Tools
			// interface and GUIMenu system etc. Need to do the same on 360, by switching render
			// target format. Only do this if we were writing to the post-tone-map sRGB RT
			// though...

			Direct3D_Surface* const pxRT = Asura_Xbox360_Graphics::GetPostToneMapLinearRT();
			Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, pxRT );
		}

		Asura_Render::CurrentStates.RequestWireFrameMode( false );

		switch(eQuadType)
		{
			case QT_FLAT_COLOUR:
			{
				const float fAlpha		= static_cast<float>((xQuad.Colour & 0xFF000000) >> 24) / 255.0f;
				const float fRed		= static_cast<float>((xQuad.Colour & 0x00FF0000) >> 16) / 255.0f;
				const float fGreen		= static_cast<float>((xQuad.Colour & 0x0000FF00) >>  8) / 255.0f;
				const float fBlue		= static_cast<float>((xQuad.Colour & 0x000000FF)      ) / 255.0f;
				const float afColour[4] = {fRed, fGreen, fBlue, fAlpha};

				pxEffect->SetVector(xFlatColourHandle, afColour);
				pxEffect->Begin(QT_FLAT_COLOUR);
				pxEffect->Commit();
				{
					Asura_Xbox360_QuadOverlay_FlatColourVertex*	pxBuffer	= NULL;
					const HRESULT								hResult		= pxDevice->BeginVertices(D3DPT_QUADLIST, 4, sizeof(Asura_Xbox360_QuadOverlay_FlatColourVertex), reinterpret_cast<void**>(&pxBuffer));

					if(SUCCEEDED(hResult)) 
					{
						for( u_int u = 0; u < 4; ++u )
						{
							pxBuffer[u].m_fX 	= 2.0f * xQuad.X[u] * fOneOverScreenWidth - 1.0f;
							pxBuffer[u].m_fY 	= 1.0f - 2.0f * xQuad.Y[u] * fOneOverScreenHeight;
							pxBuffer[u].m_fU 	= xQuad.U[u];
							pxBuffer[u].m_fV 	= xQuad.V[u];
						}

						pxDevice->EndVertices();
					}
				}
				pxEffect->End();
				break;
			}

			case QT_GOURAUD_COLOUR:
			{
				pxEffect->Begin(QT_GOURAUD_COLOUR);
				pxEffect->Commit();
				{
					Asura_Xbox360_QuadOverlay_GouraudColourVertex*	pxBuffer	= NULL;
					const HRESULT									hResult		= pxDevice->BeginVertices(D3DPT_QUADLIST, 4, sizeof(Asura_Xbox360_QuadOverlay_GouraudColourVertex), reinterpret_cast<void**>(&pxBuffer));

					if(SUCCEEDED(hResult)) 
					{
						for( u_int u = 0; u < 4; ++u )
						{
							pxBuffer[u].m_fX 		= 2.0f * xQuad.X[u] * fOneOverScreenWidth - 1.0f;
							pxBuffer[u].m_fY 		= 1.0f - 2.0f * xQuad.Y[u] * fOneOverScreenHeight;
							pxBuffer[u].m_fU 		= xQuad.U[u];
							pxBuffer[u].m_fV 		= xQuad.V[u];
							pxBuffer[u].m_uColour	= xQuad.GouraudColour[u];
						}

						pxDevice->EndVertices();
					}
				}
				pxEffect->End();
				break;
			}

			default:
			{
				break;
			}
		}		// End of swtich.

		Asura_Render::CurrentStates.RequestWireFrameMode( Asura_Render::GetWireframe() );

		Asura_Xbox360_Graphics::GetDevice()->SetRenderTarget( 0, pxOldRT );
		SAFE_RELEASE( pxOldRT );
	}
	PIXEndNamedEvent(); // Overlay Quad
}

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_QuadOverlayRenderer::Flush()
//
// Flushes pending overlay quads. Doesn't actually do anything at the moment
// since we render immediately on 360
//
// Parameters:		None
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Xbox360_QuadOverlayRenderer::Flush()
{
}
