//
// Asura_PS3_EdgeAntiAliasing.h
//
// Ben Weston (18/9/2008)
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_EDGEANTIALIASING_H__
#define __ASURA_EDGEANTIALIASING_H__

#ifdef ASURA_EDGE_ANTIALIASING

#include "Core/Asura_Maths.h"
#include "Rendering/Asura_PS3_GCM_IndexBuffer.h"
#include "Rendering/Asura_PS3_GCM_VertexBuffer.h"

#define MAX_ANTIALIAS_ENTITIES	(4096) //maximum number that will be shown at any one time (on environments each strip is a seperate AA entity because they are individually culled)

//
// This class holds a list of edges that need anti-aliasing.
// Other renderers fill this list with the silouhette edges of the objects they're drawing.
// Then, when the Render function is called, the edges are re-drawn using a special directional blur that gives the appearance of anti-aliasing.
// For our deferred rendering, the anti-aliasing pass is performed after all the deferred lighting.
//
class Asura_PS3_EdgeAntiAliasing
{
public:
	static void Initialise( void );
	static void Shutdown( void );
	static void BeginFrame( void );
	static void EndFrame( void );
	static void Render( void );

	// Calling function is responsible for ensuring index buffer & vertex buffer do not change for at least 1 frame (i.e. until RSX has finished reading them)
	//  i.e. skinned objects will need to double buffer the vertices they pass to this function, and any dynamically created index lists (e.g. silhouette edges)
	// The projection matrix can change straight after calling this function
	// The vertex format is assumed to begin with 3 floats, representing x,y,z vertex position.
	static void AddEdgeList( const Asura_PS3_GCM_IndexBuffer* pxIndices, const Asura_PS3_GCM_VertexBuffer* pxVertices, const Asura_AMatrix4x4* pxProjectionMatrix, int iStartOffset = 0, int iNumVerts = -1 )
	{
		if ( iNumVerts == 0 ) return;

		Asura_Assert( !pxIndices->Is32BitIndex(), "Asura_PS3_EdgeAntiAliasing::AddEdgeList only supports 16-bit indices" );
		Asura_Assert( s_uNumAntiAliasEntities < MAX_ANTIALIAS_ENTITIES, "Trying to anti-alias too many things, discarding the excess." );
		if ( s_uNumAntiAliasEntities >= MAX_ANTIALIAS_ENTITIES ) return; // alternatively we could check which is closest (using projection matrix)
		
		// append it to my array
		AntiAliasEntity* pxEnt = &s_axAntiAliasEntities[s_uNumAntiAliasEntities];
		pxEnt->m_xProjectionMatrix	= *pxProjectionMatrix;
		pxEnt->m_pxIndexBuffer		= pxIndices;
		pxEnt->m_pxVertexBuffer		= pxVertices;
		pxEnt->m_iStartOffset		= iStartOffset;

		if ( iNumVerts == -1 )
		{
			pxEnt->m_iNumVerts = pxIndices->GetIndexBufferSize()/2 - iStartOffset; // all of them
		}
		else
		{
			pxEnt->m_iNumVerts = iNumVerts;
		}
		
		if ( pxEnt->m_iNumVerts > 0xfffff || pxEnt->m_iNumVerts <= 0 )
		{
			// no single object should have such a huge number of anti-alias indices (there's apparently a hardware limit of 0xfffff, according to GCM_IMPLEMENTATION_SUB.H)
			// in extreme cases this can cause a command buffer overflow crash because there's 1 command for every 0x7FF00 vertices
			// this has only fired when some kind of data corruption occurs, and it prevents a very hard-to-debug crash
			Asura_Assertf( 0, ("Asura_PS3_EdgeAntiAliasing::AddEdgeList - this object has %d anti-alias verts, this is probably a bad thing\n", pxEnt->m_iNumVerts) );
			return; // don't actually add this to the list.
		}

		s_uNumAntiAliasEntities++;
	}
	
	static bool Enabled() { return s_iDebugMode != AA_DISABLED; }

protected:
	enum AA_DEBUG_MODE {
		AA_DISABLED,
		AA_ENABLED,
		AA_DEBUG,
		NUM_AA_DEBUG_MODES,
	};

	static int s_iDebugMode;

#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
	static bool SwitchAAModes ( bool bInc, bool bInterrogate, bool bHelp );
#endif

	struct AntiAliasEntity {
		Asura_AMatrix4x4					m_xProjectionMatrix; //BW todo - remove this from this struct, it's pointless having it for every env strip. => maybe 2 lists of entities, ones with matrices and ones in world space?
		const Asura_PS3_GCM_IndexBuffer*	m_pxIndexBuffer;
		const Asura_PS3_GCM_VertexBuffer*	m_pxVertexBuffer;
		int m_iStartOffset;
		int m_iNumVerts;
	};

	static AntiAliasEntity		s_axAntiAliasEntities[MAX_ANTIALIAS_ENTITIES];
	static u_int				s_uNumAntiAliasEntities;
};

#endif

#endif // #ifdef ASURA_EDGE_ANTIALIASING
