//
// Asura_Platform_Decal.cpp
//
// Created by KJFL - Tuesday, March 30, 2004
// Renderer implemented by Andrew Weinkove - 15/04/2004, based heavily on PC implementation.
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "SpecialFX/Asura_Platform_Decal.h"


#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "Rendering/Asura_Render.h"
#include "SpecialFX/Asura_Decal_Template.h"
#include "Rendering/Asura_PSP_Fog.h" 
#include "Rendering/Asura_PSP_Graphics.h"
#include "Rendering/Asura_PSP_RenderStates.h"
#include "Rendering/Asura_PSP_TextureManagement.h"

#include "Debugging/Asura_PSP_Profiling.h"

#include <kernel.h>


// MGH 28/6/2006: Allow projects to override the depth offset.
#ifndef ASURA_PSP_DECAL_DEPTH_OFFSET
	#define ASURA_PSP_DECAL_DEPTH_OFFSET 10
#endif //ASURA_PSP_DECAL_DEPTH_OFFSET

DEBUGVAR int dbg_iPSPDecalDepthOffset = -ASURA_PSP_DECAL_DEPTH_OFFSET;

///////////////////////////
// Asura_PSP_DecalBuffer //
///////////////////////////

Asura_PSP_DecalBuffer::Asura_PSP_DecalBuffer(const Asura_Decal_Template* pxDecalTemplate)
{
	m_uMaxNumberOfIndices = ASURA_PSP_NUM_DECAL_VERTICES_PER_BUFFER;
	m_uNumberOfVerticesToDraw = 0;
	
	m_uCurrentIndex = 0;
	m_uMaxIndex = 0;

	m_iTextureIndex = pxDecalTemplate->m_iTextureIndex;
	m_pxNextDecalBuffer = 0;
}

Asura_PSP_DecalBuffer::~Asura_PSP_DecalBuffer()
{
	if (m_pxNextDecalBuffer)
	{
		delete m_pxNextDecalBuffer;
	}
}

void Asura_PSP_DecalBuffer::AddStrip(const Asura_Decal_Vertex* pxStrip, const u_int uNumberOfVertices)
{
	if (m_uCurrentIndex + uNumberOfVertices+2 >= m_uMaxNumberOfIndices)
	{
		m_uCurrentIndex = 0;
	}

	unsigned uFirstVertexIndex = m_uCurrentIndex;
	m_uCurrentIndex++;
	// Copy all of the vertices.
	for(unsigned i=0; i<uNumberOfVertices; i++)
	{
		unsigned v = i + m_uCurrentIndex;
		m_axVertices[v].m_afPosition[0] = pxStrip[i].m_xPosition.x;
		m_axVertices[v].m_afPosition[1] = pxStrip[i].m_xPosition.y;
		m_axVertices[v].m_afPosition[2] = pxStrip[i].m_xPosition.z;
		m_axVertices[v].m_fU = pxStrip[i].m_fU;
		m_axVertices[v].m_fV = pxStrip[i].m_fV;
	}
	m_uCurrentIndex += uNumberOfVertices;
	// First vertex is copied twice to allow separate decals to be drawn as a single strip.
	m_axVertices[uFirstVertexIndex] = m_axVertices[uFirstVertexIndex + 1];
	// Last vertex is copied twice to allow separate decals to be drawn as a single strip.
	m_axVertices[m_uCurrentIndex] = m_axVertices[m_uCurrentIndex - 1];
	m_uCurrentIndex++;
			
	// MGH 28/6/2006: Because we are culling CCW polygons (ASURA_CULLMODE_CCW)
	// we need to be careful when adding to the triangle strip that we don't
	// accidentally flip the orientation.
	if (uNumberOfVertices%2 != 0)
	{
		// Odd number of vertices. Add an extra vertex to maintain the orientation.
		if (m_uCurrentIndex+1 < m_uMaxNumberOfIndices)
		{
			m_axVertices[m_uCurrentIndex] = m_axVertices[m_uCurrentIndex - 1];
			++m_uCurrentIndex;
		}
	}

	// KJL - 29/04/2003 10:32:10 - if wrapped we have to tidy up any decals we've half-overwritten
	if (m_uCurrentIndex<m_uMaxIndex)
	{
		unsigned uTemp = m_uCurrentIndex;
		while (m_axVertices[uTemp] != m_axVertices[uTemp+1] && 
				uTemp < m_uMaxIndex - 1)
		{
			m_axVertices[uTemp] = m_axVertices[uTemp-1];
			uTemp++;
		}
	}
	else
	{
		m_uMaxIndex = m_uCurrentIndex;
	}
		
	if (m_uCurrentIndex>m_uNumberOfVerticesToDraw)
	{
		m_uNumberOfVerticesToDraw = m_uCurrentIndex;
	}
}

void Asura_PSP_DecalBuffer::Render(void)
{
	// MGH 28/3/2006: Request the relevant texture for this buffer.
	Asura_PSP_TextureManagement::RequestTextureIndex(m_iTextureIndex);

	sceKernelDcacheWritebackRange( m_axVertices, m_uNumberOfVerticesToDraw * sizeof(Asura_PSP_RenderDecal_Vertex) );
	GUM_DrawArray(SCEGU_PRIM_TRIANGLE_STRIP, 
					iASURA_PSP_DECAL_VERTEX_FORMAT,
					m_uNumberOfVerticesToDraw, 
					NULL, 
					m_axVertices);

	#if defined ASURA_RENDER_TTYPES
	Asura_Render::AddToTrianglesRendered(m_uNumberOfVerticesToDraw - 2,1,
		ASURA_RENDER_TTYPE_OTHER);
	#else
	Asura_Render::AddToTrianglesRendered(m_uNumberOfVerticesToDraw - 2);
	#endif

	if (m_pxNextDecalBuffer)
	{
		m_pxNextDecalBuffer->Render();
	}
}

// JAT 24/05/06 - platform specific functions needed for PS2 optimization
void Asura_Decal_System::Platform_Initialise( void )
{
}

void Asura_Decal_System::Platform_Reset( void )
{
}

void Asura_Decal_System::Platform_Shutdown( void )
{
}

///////////////////////
// Platform_AddStrip //
///////////////////////
void Asura_Decal_Handler::Platform_AddStrip(const Asura_Decal_Vertex* pxStrip, u_int uNumberOfVertices, const Asura_RGBA& xRGBA, const Asura_Decal_Template* pxDecalTemplate)
{
	// Add the strip to a new decal buffer if the texture is different.
	Asura_PSP_DecalBuffer* pxDecalBuffer = static_cast<Asura_PSP_DecalBuffer*>(m_pxFirstDecalBuffer);
		
	while (pxDecalBuffer)
	{
		if (pxDecalBuffer->m_iTextureIndex == pxDecalTemplate->m_iTextureIndex)
		{
			pxDecalBuffer->AddStrip(pxStrip,uNumberOfVertices);
			return;
		}
		pxDecalBuffer = pxDecalBuffer->m_pxNextDecalBuffer;
	}

	pxDecalBuffer = new Asura_PSP_DecalBuffer(pxDecalTemplate);
	pxDecalBuffer->m_pxNextDecalBuffer = static_cast<Asura_PSP_DecalBuffer*>(m_pxFirstDecalBuffer);
	m_pxFirstDecalBuffer = pxDecalBuffer;

	pxDecalBuffer->AddStrip(pxStrip, uNumberOfVertices);
}

/////////////////////
// Platform_Render //
/////////////////////
DEBUGVAR bool dbg_bASURA_PSP_DEPTHOFFSETWORKS = true;

void Asura_Decal_Handler::Platform_Render()
{
#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_DECAL_START); 
#endif // #ifdef ASURA_PSP_PROFILING

	if (!m_pxFirstDecalBuffer) 
	{
		// KJFL - 28 February 2005 - enable vertex fog if it's on.
		Asura_PSP_Fog::TurnOnFogIfNecessary();
		return;
	}
	
	// KJFL - 28 February 2005 - disable vertex fog if it's on.
	Asura_PSP_Fog::TurnOffFogIfNecessary();

	if (dbg_bASURA_PSP_DEPTHOFFSETWORKS)
	{
		Asura_PSP_RenderStates::RequestDepthOffset(dbg_iPSPDecalDepthOffset);
	}
	else
	{
		Asura_PSP_Graphics::SetupPSPProjectionMatrix(0.010f);
	}
	
	Asura_Render::CurrentStates.RequestZBufferEnabled(true);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CCW);	
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_DOUBLECOLOUR );
	Asura_PSP_Graphics::SetUseColourDoubling(false);
	Asura_PSP_Graphics::SetUseLighting(false);
	Asura_PSP_Graphics::SetMaterialType( ASURA_PSP_GRAPHICS_MATERIALTYPE_ENTITY );

	// AJE - 21 June 2007
	//  - It is entirely possible that a preceding system (e.g. botanicals) may have altered the matrix stacks
	//    to accomodate their dynamic rendering needs - in which case we need to ensure, prior to rendering
	//    our decals, that our active world matrix is set to identity.

	// Set to the world matrix stack and push on an identity matrix
	GUM_MatrixMode(SCEGU_MATRIX_WORLD);
	GUM_PushMatrix();
	GUM_LoadIdentity();

	static_cast<Asura_PSP_DecalBuffer*>(m_pxFirstDecalBuffer)->Render();

	// AJE - 21 June 2007
	// Now pop our previously set identity world matrix
	GUM_PopMatrix();

	if (dbg_bASURA_PSP_DEPTHOFFSETWORKS)
	{
		Asura_PSP_RenderStates::RequestDepthOffset(0);	// Restore the depth offset.
	}
	else
	{
		Asura_PSP_Graphics::SetupPSPProjectionMatrix(0.000f);
	}
	// KJFL - 28 February 2005 - enable vertex fog if it's on.
	Asura_PSP_Fog::TurnOnFogIfNecessary();

#ifdef ASURA_PSP_PROFILING
	Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_DECAL_END ); 
#endif // #ifdef ASURA_PSP_PROFILING

}

