//////////////////////////////////////////////////////////////////////////////
//
// Asura_PlatformRendering.cpp
//
// Copyright (c) 2001-2003 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

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

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "Debugging/Asura_Assert.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"

// Engine platform-specific.
#include "Core/Asura_PS2_VU0.h"
#include "Rendering/Asura_PlatformTextureManagement.h"
#include "Rendering/Asura_PS2_2D_Graphics.h"
#include "Rendering/Asura_PS2_DeferredRendering.h"
#include "Rendering/Asura_PS2_GS.h"
#include "Rendering/Asura_PS2_GSMRenderer.h"
#include "Rendering/Asura_PS2_RenderingOptions.h"
#include "Rendering/Asura_PS2_VU1.h"
#include "SpecialFX/Asura_PS2_RenderParticle.h"
#include "Rendering/Asura_PS2_ShadowBuffer.h"

// D E F I N E S /////////////////////////////////////////////////////////////

#define	NO_SPLASH_SCREEN               (-2)

// G L O B A L S /////////////////////////////////////////////////////////////

static int sg_SplashScreenTextureID = NO_SPLASH_SCREEN; // Texture ID of loaded splashscreen

// C L A S S E S /////////////////////////////////////////////////////////////

//
// Asura_Render.
//

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::Platform_Initialise().
// PARAMETERS	None.
// RETURNS		None.
// DESCRIPTION	Initialises the PS2 rendering engine.
// AUTHOR		KJL.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_Initialise( void )
{
	Asura_PS2_GS::Initialise();
    Asura_PS2_VU0::Initialise();
    Asura_PS2_VU1::Initialise();
	Asura_PS2_DeferredRendering::Initialise();
    Asura_PS2_GSMRenderer::Initialise();
    Asura_PS2_2D_Graphics::Initialise();
	Asura_PS2_ShadowBufferSystem::Initialise();
	PlatformProject_Initialise();
}
// End function Asura_Render::Platform_Initialise().

void Asura_Render::Platform_EarlyShutdown( void )
{
    Asura_PS2_DeferredRendering::WaitForDeferredRendering();
}

void Asura_Render::Platform_LateShutdown( void )
{
	Asura_PS2_ShadowBufferSystem::Shutdown();
    Asura_PS2_2D_Graphics::Shutdown();
    Asura_PS2_ParticleSystem::Shutdown();
    Asura_PS2_GSMRenderer::Shutdown();
	Asura_PS2_DeferredRendering::Shutdown();
    Asura_PS2_VU1::Shutdown();
    Asura_PS2_VU0::Shutdown();
	Asura_PS2_GS::Shutdown();
}

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::Platform_Reset().
// PARAMETERS	None.
// RETURNS		None.
// DESCRIPTION	Resets the PS2 rendering engine.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_Reset( void )
{
    Asura_PS2_DeferredRendering::WaitForDeferredRendering();

    Asura_PS2_ParticleSystem::Reset();
    Asura_PS2_GSMRenderer::Reset();
    Asura_PS2_DeferredRendering::Reset();
}
// End function Asura_Render::Platform_Reset().

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::Platform_BeginFrame().
// PARAMETERS	None.
// RETURNS		None.
// DESCRIPTION	Called at the start of every new frame to be rendered.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_BeginFrame( void )
{
#if _DEBUG
    // (MP - 08.05.2002) At the moment, we only use SPR for temporary stuff
    // and almost exclusively for rendering so it makes sense to make the
    // following check at the beginning of every frame.
    // (MP - 11.12.2003) Once we've done that check, forget about all
    // outstanding allocations so we can pick up on any new transgressions in
    // this new frame.
    // (MP - 22.08.2007) Forcing everything to be deallocated was causing
    // issues in a vsync callback renderer on the Simpsons and doesn't really
    // have a massive advantage apart from ensuring that the same assert
    // doesn't keep firing. Given the goal is to avoid *any* SPR remaining
    // allocated in a new frame, this is ok.
    Asura_PS2_DebugSPR::VerifyAllDeallocated( false );
#endif

    Asura_PS2_Render::ResetForNewFrame();
	
	Asura_PS2_GS::BeginFrame();
    Asura_PS2_VU1::BeginFrame();

    Asura_PS2_TextureManagement::BeginFrame();
	Asura_PS2_ShadowBufferSystem::BeginNewFrame();
}
// End function Asura_Render::Platform_BeginFrame().

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::Platform_EndFrame().
// PARAMETERS	None.
// RETURNS		None.
// DESCRIPTION	Called at the end of every frame being rendered.
//
/////////////////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_EndFrame( void )
{
	FlushDisplay();
    Asura_PS2_2D_Graphics::CheckForTexDisplayRequest();
	Asura_PS2_GS::EndFrame();
    Asura_PS2_VU1::EndFrame();
    Asura_PS2_TextureManagement::EndFrame();
	Asura_PS2_ShadowBufferSystem::EndFrame();	
}
// End function Asura_Render::Platform_EndFrame().

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::Platform_FlushDisplay
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	N/A
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_FlushDisplay( void )
{
    Asura_PS2_2D_Graphics::Flush();
}
// End function Asura_Render::Platform_FlushDisplay().

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Render::SetVideoMode().
// PARAMETERS	Width, height and toggle for full screen anti-aliasing.
// RETURNS		None.
// DESCRIPTION	N/A.
//
//////////////////////////////////////////////////////////////////////////////
bool Asura_Render::Platform_SetVideoMode( int, int, bool )
{
    Asura_Assert( false, "Not supported on PS2." );
	return true;
}
// End function Asura_Render::SetVideoMode().

/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Asura_Render::Platform_InitialiseSplashScreen
// PARAMETERS	Filename of the splash screen
// RETURNS		None
// DESCRIPTION	Loads a splashscreen ready to be displayed
// AUTHOR		Richard M. Smith
/////////////////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_InitialiseSplashScreen( const char* const szName )
{
// RMS: This is temporary - it only allows one splash screen ever
// RMS: This will change when texture swapping is in

// Load the splash screen
	if( NO_SPLASH_SCREEN != sg_SplashScreenTextureID )
	{
        Asura_Texture_Management::DestroyTexture(sg_SplashScreenTextureID);
    }

    sg_SplashScreenTextureID = Asura_Texture_Management::AddTexture( szName );
	Asura_Texture_Management::LoadTextures();

} // End Function 'Asura_Render::InitialiseSplashScreen'


/////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Asura_Render::Platform_ShutdownSplashScreen
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Frees a splashscreen previously loaded by InitialiseSplashScreen
// AUTHOR		Richard M. Smith
/////////////////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_ShutdownSplashScreen(void)
{
// RMS: This will free the loaded splash screen file
//extern void DeleteLoadedTexture( int index );
//DeleteLoadedTexture( sg_SplashScreenTextureID );

    Asura_Texture_Management::DestroyTexture(sg_SplashScreenTextureID);
    // MH -- flag it as unloaded
    sg_SplashScreenTextureID = NO_SPLASH_SCREEN;

} // End Function 'Asura_Render::ShutdownSplashScreen'


////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION		Asura_Render::Platform_SplashScreenRender
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Renders a previously loaded Splash Screen to the frame buffer
// AUTHOR		Richard M. Smith
/////////////////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_SplashScreenRender(void)
{
	// Make sure there is a splash screen
	Asura_Assert( sg_SplashScreenTextureID != NO_SPLASH_SCREEN, "Asura_Render::SplashScreenRender - No splash screen present." );
    if (sg_SplashScreenTextureID != NO_SPLASH_SCREEN) 
    {
        Overlay.TextureIndex = sg_SplashScreenTextureID;
    }
    else
    {
        Overlay.TextureIndex = ASURA_TEXTURE_NONE;
    }
    if (Overlay.TextureIndex != ASURA_TEXTURE_NONE)
    {
        Overlay.Colour = 0xffffffff;
        Overlay.SetRectUV(0.0f, 0.0f, 1.0f, 1.0f);
    }
    else
    {
        Overlay.Colour = 0xff000000;
    }
    float fScreenWidth = static_cast<float>( Asura_Viewport::GetWidth() );
    float fScreenHeight = static_cast<float>( Asura_Viewport::GetHeight() );
    Overlay.SetRectXY(0.0f, 0.0f, fScreenWidth, fScreenHeight);
    Overlay.TranslucencyMode = ASURA_TRANSLUCENCY_NORMAL;
    Overlay.Render();

} // End Function 'Asura_Render::SplashScreenRender'


void Asura_Render::Platform_RenderLine( const Asura_Vector_3& xPoint1, const Asura_Vector_3& xPoint2, u_int uARGB )
{
#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
    for( int i = 0; i < Asura_Camera::GetNumberOfCameras(); ++i )
    {
        Asura_Camera::SetCameraIndex( i );
        if( !Asura_Camera::IsCameraActive() ) { continue; }

        Asura_PS2_GS::SetupViewport( Asura_Camera::GetCameraIndex() );

//        // Transform end points to screenspace.
//        Asura_Vector_3 xSSPoint1( xPoint1 );
//        Asura_Camera::ProjectWorldCoordsToScreenSpace( xSSPoint1, true );
//        Asura_Vector_3 xSSPoint2( xPoint2 );
//        Asura_Camera::ProjectWorldCoordsToScreenSpace( xSSPoint2, true );

        sceVu0FVECTOR xSSPoint1 = { xPoint1.x, xPoint1.y, xPoint1.z, 1.0f };
        sceVu0FVECTOR xSSPoint2 = { xPoint2.x, xPoint2.y, xPoint2.z, 1.0f };

        {
            sceVu0FMATRIX& xW2SMat = Asura_PS2_VU1::GetWorldToScreenMat();
            sceVu0ApplyMatrix( xSSPoint1, xW2SMat, xSSPoint1 );
            sceVu0ApplyMatrix( xSSPoint2, xW2SMat, xSSPoint2 );
        }

        sceVu0ScaleVector( xSSPoint1, xSSPoint1, 1.0f / xSSPoint1[3] );
        sceVu0ScaleVector( xSSPoint2, xSSPoint2, 1.0f / xSSPoint2[3] );

        {
            sceVu0FVECTOR xTrans = { ASURA_PS2_SCREEN_CENTRE_X - (Asura_PS2_GS::GetScreenWidth() >> 1),
                                     ASURA_PS2_SCREEN_CENTRE_Y - (Asura_PS2_GS::GetScreenHeight()>> 1),
                                     0.0f,
                                     0.0f };

            sceVu0SubVector( xSSPoint1, xSSPoint1, xTrans );
            sceVu0SubVector( xSSPoint2, xSSPoint2, xTrans );
        }

		const float fScreenWidth  = Asura_PS2_GS::GetScreenWidth();
		const float fScreenHeight = Asura_PS2_GS::GetScreenHeight();

		// Check if line is off screen - (Z clipping needs to be done in worldspace... so it's not implemented ATM... this is supposed to be a debugging tool)
        if( xSSPoint1[2] < 0.0f
            ||
            xSSPoint2[2] < 0.0f
            ||
            (xSSPoint1[0] < 0.0f && xSSPoint2[0] < 0.0f) || (xSSPoint1[0] > fScreenWidth  && xSSPoint2[0] > fScreenWidth)
            ||
            (xSSPoint1[1] < 0.0f && xSSPoint2[1] < 0.0f) || (xSSPoint1[1] > fScreenHeight && xSSPoint2[1] > fScreenHeight) )
		{
			// Offscreen !
			continue;
		}

		// Clip line
		if ( xSSPoint1[0] < 0.0f )
		{
			const float fClipRatio = (0.0f-xSSPoint1[0]) / (xSSPoint2[0]-xSSPoint1[0]);
			xSSPoint1[0] = 0.0f;
			xSSPoint1[1] += (xSSPoint2[1]-xSSPoint1[1])*fClipRatio;
			xSSPoint1[2] += (xSSPoint2[2]-xSSPoint1[2])*fClipRatio;
		}
		if ( xSSPoint1[0]>fScreenWidth )
		{
			const float fClipRatio = (fScreenWidth-xSSPoint1[0]) / (xSSPoint2[0]-xSSPoint1[0]);
			xSSPoint1[0] = fScreenWidth;
			xSSPoint1[1] += (xSSPoint2[1]-xSSPoint1[1])*fClipRatio;
			xSSPoint1[2] += (xSSPoint2[2]-xSSPoint1[2])*fClipRatio;
		}
		if ( xSSPoint1[1]<0.0f )
		{
			const float fClipRatio = (0.0f-xSSPoint1[1]) / (xSSPoint2[1]-xSSPoint1[1]);
			xSSPoint1[0] += (xSSPoint2[0]-xSSPoint1[0])*fClipRatio;
			xSSPoint1[1] = 0.0f;
			xSSPoint1[2] += (xSSPoint2[2]-xSSPoint1[2])*fClipRatio;
		}
		if ( xSSPoint1[1]>fScreenHeight )
		{
			const float fClipRatio = (fScreenHeight-xSSPoint1[1]) / (xSSPoint2[1]-xSSPoint1[1]);
			xSSPoint1[0] += (xSSPoint2[0]-xSSPoint1[0])*fClipRatio;
			xSSPoint1[1] = fScreenHeight;
			xSSPoint1[2] += (xSSPoint2[2]-xSSPoint1[2])*fClipRatio;
		}

		if ( xSSPoint2[0]<0.0f )
		{
			const float fClipRatio = (0.0f-xSSPoint2[0]) / (xSSPoint1[0]-xSSPoint2[0]);
			xSSPoint2[0] = 0.0f;
			xSSPoint2[1] += (xSSPoint1[1]-xSSPoint2[1])*fClipRatio;
			xSSPoint2[2] += (xSSPoint1[2]-xSSPoint2[2])*fClipRatio;
		}
		if ( xSSPoint2[0]>fScreenWidth )
		{
			const float fClipRatio = (fScreenWidth-xSSPoint2[0]) / (xSSPoint1[0]-xSSPoint2[0]);
			xSSPoint2[0] = fScreenWidth;
			xSSPoint2[1] += (xSSPoint1[1]-xSSPoint2[1])*fClipRatio;
			xSSPoint2[2] += (xSSPoint1[2]-xSSPoint2[2])*fClipRatio;
		}
		if ( xSSPoint2[1]<0.0f )
		{
			const float fClipRatio = (0.0f-xSSPoint2[1]) / (xSSPoint1[1]-xSSPoint2[1]);
			xSSPoint2[0] += (xSSPoint1[0]-xSSPoint2[0])*fClipRatio;
			xSSPoint2[1] = 0.0f;
			xSSPoint2[2] += (xSSPoint1[2]-xSSPoint2[2])*fClipRatio;
		}
		if ( xSSPoint2[1]>fScreenHeight )
		{
			const float fClipRatio = (fScreenHeight-xSSPoint2[1]) / (xSSPoint1[1]-xSSPoint2[1]);
			xSSPoint2[0] += (xSSPoint1[0]-xSSPoint2[0])*fClipRatio;
			xSSPoint2[1] = fScreenHeight;
			xSSPoint2[2] += (xSSPoint1[2]-xSSPoint2[2])*fClipRatio;
		}

		Asura_PS2_2D_Graphics::Line2D* pxLine = Asura_PS2_2D_Graphics::GetFreeLine();
	    if( !pxLine )
		{
			break;
		}

	    pxLine->uARGB  = uARGB;
	    pxLine->afX[0] = xSSPoint1[0];
	    pxLine->afY[0] = xSSPoint1[1];
	    pxLine->afX[1] = xSSPoint2[0];
	    pxLine->afY[1] = xSSPoint2[1];
    }
#else
    Asura_Assert( false, "Your project needs to support Asura_Render::Render2DLine() if you want Asura_Render::RenderLine()." );
#endif
}

void Asura_Render::Platform_Render2DLine( const Asura_Vector_2& xPoint1, const Asura_Vector_2& xPoint2, u_int uARGB )
{
#ifdef ASURA_PS2_MAX_2D_LINES_PER_FRAME
    Asura_PS2_2D_Graphics::Line2D* pxLine = Asura_PS2_2D_Graphics::GetFreeLine();
    if( pxLine )
    {
	    pxLine->uARGB  = uARGB;
	    pxLine->afX[0] = xPoint1.x;
	    pxLine->afY[0] = xPoint1.y;
	    pxLine->afX[1] = xPoint2.x;
	    pxLine->afY[1] = xPoint2.y;
    }
#else
	Asura_Assert(false, "Asura_Render::Render2DLine no support in this project");
	Asura_Assert(false, "Define ASURA_PS2_MAX_2D_LINES_PER_FRAME (N) in your engine config file");
#endif
}

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_Render::ApplyCameraSettings().
// PARAMETERS   None.
// RETURNS      None.
// DESCRIPTION  Sets up viewport for current camera.
// AUTHOR       Morgan Parry - 07.05.2002.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_ApplyCameraSettings( void )
{
	const u_int uCamera = Asura_Camera::GetCameraIndex();
	const u_int uCameraBit = 1 << uCamera;
    Asura_PS2_GS::SetupViewport( uCamera );
	Asura_PS2_GS::CacheViewportParams( uCameraBit );
	Asura_PS2_VU1::SetupMatrices( uCameraBit );
}
// End function Asura_Render::ApplyCameraSettings().

void Asura_Render::Platform_AllowFullScreenRendering()
{
    Asura_PS2_GS::AllowFullScreenRendering();
}

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_Render::Platform_ClearZBuffer().
// PARAMETERS   None.
// RETURNS      None.
// DESCRIPTION  
// AUTHOR       Mike Healey - 20.08.2002.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_ClearZBuffer()
{
    Asura_PS2_GS::ClearZBuffer();
}
// End function Asura_Render::Platform_ClearZBuffer().

//////////////////////////////////////////////////////////////////////////////
//
// FUNCTION     Asura_Render::Platform_RenderDynamicShadowsOntoEnvironment().
// PARAMETERS   None.
// RETURNS      None.
// DESCRIPTION  
// AUTHOR       Steve Humphreys - 17/11/2004.
//
//////////////////////////////////////////////////////////////////////////////
void Asura_Render::Platform_RenderDynamicShadowsOntoEnvironment()
{
	Asura_PS2_ShadowBufferSystem::FinishedCollectingShadows();
}
// End function Asura_Render::Platform_RenderDynamicShadowsOntoEnvironment().


void Asura_Render::Platform_PrepareToRenderFirstPersonMesh ()
{
	Asura_Assert(false,"Special rendering settings for the first person mesh are not supported on this platform");
}


void Asura_Render::Platform_FinishRenderFirstPersonMesh ()
{
	//Do nothing
}

//
// Asura_Overlay_Quad.
//

/////////////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION		Asura_Overlay_Quad::Platform_Render
// PARAMETERS	None
// RETURNS		None
// DESCRIPTION	Renders an Overlay Quad
//
/////////////////////////////////////////////////////////////////////////////////////////
void Asura_Overlay_Quad::Platform_Render()
{
    if( Asura_PS2_RenderingOptions::GetOption(Asura_PS2_RenderingOptions::OPTION_DRAW_OVERLAYS) )
    {
        Asura_PS2_2D_Graphics::AddQuad( this );
    }
}
// End Function 'Asura_Overlay_Quad::Render'

//
// Asura_Font_System.
//

#include "rendering/Asura_Font_System.h"

float Asura_Font_System::GetPlatformYScaleFactor(void)
{
	Asura_Assert(s_bUsePlatformYScaleFactor, "Shouldn't be calling GetPlatformYScaleFactor if set to not use that factor");

	// (EJD - 04.01.2005) We're assuming the Y scale factor is constant in each video mode here-
	//	this may well break if we start using different resolutions.
	// SNH: no longer true, we calculate it now.
	return Asura_Camera::GetPhysicalScreenAspectRatio() * Asura_PS2_GS::GetScreenHeight() / Asura_PS2_GS::GetScreenWidth();
}

//
// Asura_PS2_Render.
//

#if ASURA_PS2_COLLECT_RENDER_STATS
u_int Asura_PS2_Render::s_auTriPassCounts[Asura_PS2_Render::TRICOUNT_NUM_TYPES];
float Asura_PS2_Render::s_fNoClipModulesRatio;
#endif

#if ASURA_PS2_COLLECT_DETAILED_LIGHT_STATS
u_int Asura_PS2_Render::s_auTrisPerDirLightCount[3];
u_int Asura_PS2_Render::s_auTrisPerPointLightCount[8];
#endif

void Asura_PS2_Render::ResetForNewFrame( void )
{
#if ASURA_PS2_COLLECT_RENDER_STATS
    for( u_int u = 0; u < TRICOUNT_NUM_TYPES; ++u ) { s_auTriPassCounts[u] = 0; }
    s_fNoClipModulesRatio = 0.0f;
#endif

#if ASURA_PS2_COLLECT_DETAILED_LIGHT_STATS
    for( u_int u = 0; u < 3; ++u ) { s_auTrisPerDirLightCount[u] = 0; }
    for( u_int u = 0; u < 8; ++u ) { s_auTrisPerPointLightCount[u] = 0; }
#endif
}
