//
// Tools_BatchRenderPolygonOutline.cpp
//
// Created by KJFL - 12 September 2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

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

#include "Rendering/Asura_Direct3D_LineRendering.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Direct3D_StoreRenderTargets.h"
#include "Rendering/Asura_Module.h"
#include "Mesh/Asura_Mesh.h"
#include "Misc/Tools_Render.h"
#include "Misc/Tools_Render_Options.h"

DEBUGVAR float	dbg_fBatchRenderPolygonOutlineDepthBias	= -0.0002f;

struct Vertex
	{
		enum { FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE };
		float m_fX, m_fY, m_fZ;
		u_int m_uColour;
	};
static Vertex axVertices[1024];
static u_int uNumberOfVertices = 0;
static Asura_Direct3D_StoreRenderTargets xRenderTargetStore;

//					const Asura_Vector_3* cameraPosition = Asura_Camera::GetPosition();
	//				const Asura_Matrix_3x3* cameraOrientation = Asura_Camera::GetOrientation();

	//				Asura_Vector_3 zvec = Asura_Vector_3(cameraOrientation->e13,cameraOrientation->e23,cameraOrientation->e33);	

		//Values chosen so that single polygon outlines will take precedence over
						//entire mesh highlights
//						const float MIN_OFFSET   = 0.06f;
//						const float MAX_OFFSET   = 6.0f;
//						const float MIN_CAM_DIST = 10.0f;
//						const float MAX_CAM_DIST = 200.0f;

//						const float fCAMSCALE = (MAX_OFFSET-MIN_OFFSET)/(MAX_CAM_DIST-MIN_CAM_DIST);

void Tools_BatchRenderPolygonOutline::BeginBatch( void )
{

#if defined( ASURA_TARGET_SM3)
	xRenderTargetStore.Clear();
	xRenderTargetStore.StoreRenderTargets();
	Asura_Direct3D::ClearRenderTargets( 1);
#endif

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader( Vertex::FVF );
	Asura_Render::CurrentStates.ResetAll();
	Asura_Render::CurrentStates.RequestTextureIndex( 0,ASURA_TEXTURE_NONE );
	Asura_Render::CurrentStates.RequestTextureOpMode( ASURA_TEXTUREOP_SINGLE_PASS );
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);
	// Apply depth bias so these can display above coplanar geometry
	{
		Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_DEPTHBIAS, *(DWORD*)&dbg_fBatchRenderPolygonOutlineDepthBias );
	}


	Asura_Render::CurrentStates.RequestWireFrameMode( Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_WIREFRAME );
	
	uNumberOfVertices = 0;
}

void Tools_BatchRenderPolygonOutline::AddOutlineToBatch( u_int uPoly, u_int uModule, u_int uColour )
{
	if( Tools_Render_Options::IsHidden( uModule, uPoly )) return;

	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule( uModule );
	
	Asura_Vector_3 xModulePosition;		
	Asura_Module_System::GetPositionOfModule( uModule, xModulePosition );
/*
	if (Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_TRANSLUCENT)
	{
		// scale down alpha
		u_int uAlpha = (uColour/3) & 0xff000000;
		uColour = (uColour&0x00ffffff) + uAlpha;
	}
*/	
	//Asura_Vector_3 zvec = Asura_Vector_3(cameraOrientation->e13,cameraOrientation->e23,cameraOrientation->e33);	
	
	//if (Tools_Render_Options::GetPolygonHighlightMode() != TOOLS_HIGHLIGHT_WIREFRAME )
	{
		// effectively a cut'n'paste job - TODO tidy and optimise operations
		Asura_Vector_3 xZVector;
	
		Asura_Camera::GetForwardVector( xZVector );
		Asura_Vector_3 xNormal = pxMesh->NormalsDataPtr[uPoly].FaceNormal;
	
		u_int uC = Asura_Maths::FloatToInt( Asura_Maths::Max( 0.0f, -xNormal*xZVector ) * 100.0f );
		

		u_int uA = (uColour>>24)&0xff;
		u_int uR = (uColour>>16)&0xff;
		u_int uG = (uColour>>8)&0xff;
		u_int uB = (uColour)&0xff;

		uR = Asura_Maths::Max(uC,uR);
		uG = Asura_Maths::Max(uC,uG);
		uB = Asura_Maths::Max(uC,uB);

		//If translucent mode has been asked for , then scale the alpha down
		if(Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_TRANSLUCENT)
		{
			uA /= 3;
		}

		uColour = (uA<<24)|(uR<<16)|(uG<<8)|uB;
	}

	int iNumVerts = pxMesh->PolygonDataPtr[uPoly].NumberOfVertices();

	Asura_Vector_3 axPoints[4];

	for(int i=0;i<iNumVerts;i++)
	{
		axPoints[i] = pxMesh->VertexDataPtr[pxMesh->PolygonDataPtr[uPoly].VertexID[i]].Position + xModulePosition;
	}
#if 0
	for(int i=0;i<iNumVerts;i++)
	{

		//move the point towards the camera.

		
		Asura_Vector_3 xOffset = *cameraPosition - axPoints[i];
		float fDist = xOffset.Normalise();
		float fOffset;
		/*
		if(fDist<=MIN_CAM_DIST)
		{
			fOffset = MIN_OFFSET;		
		}
		else if(fDist<MAX_CAM_DIST)
		{
			fOffset = MIN_OFFSET + (fDist-MIN_CAM_DIST)*fCAMSCALE;
		}
		else
		{
			fOffset = MAX_OFFSET;		
		}
		*/

		fDist = Asura_Maths::ClampToRange( fDist, MIN_CAM_DIST, MAX_CAM_DIST );

		fOffset = MIN_OFFSET + (fDist-MIN_CAM_DIST)*fCAMSCALE;


		axPoints[i]+=xOffset*fOffset;

	}
#endif
	

	if (0 && Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_WIREFRAME)
	{
		for(int i=0;i<iNumVerts;i++)
		{
			int j = i+1;
			if (j==iNumVerts) j=0;

			//Asura_Direct3D_LineRenderer::Line3D xLine( axPoints[i], axPoints[j], 0xffff0000 );
			//Asura_Direct3D_LineRenderer::AddLine( xLine );

			axVertices[uNumberOfVertices].m_uColour = uColour;
			axVertices[uNumberOfVertices].m_fX = axPoints[i].GetX();
			axVertices[uNumberOfVertices].m_fY = axPoints[i].GetY();
			axVertices[uNumberOfVertices].m_fZ = axPoints[i].GetZ();
			++uNumberOfVertices;

			axVertices[uNumberOfVertices].m_uColour = uColour;
			axVertices[uNumberOfVertices].m_fX = axPoints[j].GetX();
			axVertices[uNumberOfVertices].m_fY = axPoints[j].GetY();
			axVertices[uNumberOfVertices].m_fZ = axPoints[j].GetZ();
			++uNumberOfVertices;
		//	Asura_Render::RenderLine( axPoints[i], axPoints[j], 0xffff0000 );

			if (uNumberOfVertices == 1024)
			{
				Asura_Direct3D::D3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, uNumberOfVertices / 2, (void*)axVertices, sizeof(Vertex) );
				uNumberOfVertices = 0;
			}
		
		}
	}
	else
	{
		{
			
			axVertices[uNumberOfVertices].m_uColour = uColour;
			axVertices[uNumberOfVertices].m_fX = axPoints[0].GetX();
			axVertices[uNumberOfVertices].m_fY = axPoints[0].GetY();
			axVertices[uNumberOfVertices].m_fZ = axPoints[0].GetZ();
			++uNumberOfVertices;

			axVertices[uNumberOfVertices].m_uColour = uColour;
			axVertices[uNumberOfVertices].m_fX = axPoints[1].GetX();
			axVertices[uNumberOfVertices].m_fY = axPoints[1].GetY();
			axVertices[uNumberOfVertices].m_fZ = axPoints[1].GetZ();
			++uNumberOfVertices;

			axVertices[uNumberOfVertices].m_uColour = uColour;
			axVertices[uNumberOfVertices].m_fX = axPoints[2].GetX();
			axVertices[uNumberOfVertices].m_fY = axPoints[2].GetY();
			axVertices[uNumberOfVertices].m_fZ = axPoints[2].GetZ();
			++uNumberOfVertices;

			if ( iNumVerts == 4 )
			{
				axVertices[uNumberOfVertices].m_uColour = uColour;
				axVertices[uNumberOfVertices].m_fX = axPoints[2].GetX();
				axVertices[uNumberOfVertices].m_fY = axPoints[2].GetY();
				axVertices[uNumberOfVertices].m_fZ = axPoints[2].GetZ();
				++uNumberOfVertices;

				axVertices[uNumberOfVertices].m_uColour = uColour;
				axVertices[uNumberOfVertices].m_fX = axPoints[3].GetX();
				axVertices[uNumberOfVertices].m_fY = axPoints[3].GetY();
				axVertices[uNumberOfVertices].m_fZ = axPoints[3].GetZ();
				++uNumberOfVertices;

				axVertices[uNumberOfVertices].m_uColour = uColour;
				axVertices[uNumberOfVertices].m_fX = axPoints[0].GetX();
				axVertices[uNumberOfVertices].m_fY = axPoints[0].GetY();
				axVertices[uNumberOfVertices].m_fZ = axPoints[0].GetZ();
				++uNumberOfVertices;
			}

			if (uNumberOfVertices >= 1000)
			{
				Asura_Direct3D::D3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, uNumberOfVertices / 3, (void*)axVertices, sizeof(Vertex) );
				uNumberOfVertices = 0;
			}
		}
	}
}
		
void Tools_BatchRenderPolygonOutline::EndBatch( void )
{
	if (uNumberOfVertices)
	{
		if (0 && Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_WIREFRAME)
		{	
			Asura_Direct3D::D3DDevice->DrawPrimitiveUP( D3DPT_LINELIST, uNumberOfVertices / 2, (void*)axVertices, sizeof(Vertex) );
		}
		else
		{
			Asura_Direct3D::D3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, uNumberOfVertices / 3, (void*)axVertices, sizeof(Vertex) );
		}
		uNumberOfVertices = 0;
	}


	
	{
		// Apply depth bias so these can display above coplanar geometry
		const float fDepthBias = 0.0f;
		Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_DEPTHBIAS, *(DWORD*)&fDepthBias );
	}

	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CCW);
	Asura_Render::CurrentStates.RequestWireFrameMode( false );
#if defined( ASURA_TARGET_SM3)
	xRenderTargetStore.RestoreRenderTargets();
	xRenderTargetStore.Clear();
#endif
}

