//
// Tools_Render.cpp
//
// Created by Kevin Lea - 11/09/2001 10:32:47
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Misc/Tools_Render.h"

#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Windows.h"
#include "Core/Asura_MaterialResponse_System.h"
#include "Debugging/Asura_Assert.h"
#include "Interface/Asura_CommandConsole.h"
#include "Light/Asura_Light.h"
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Rendering/Asura_Direct3D.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_SortedRender.h"
#include "Rendering/Asura_Viewport.h"
#include "SpecialFX/Asura_Sphere.h"
#include "Visibility/Asura_Visibility_System.h"

#include "Tools_System.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_Render_Options.h"
#include "Rendering/Tools_RenderModules.h"
#include "Rendering/Tools_RenderCollisionMesh.h"
#include "Streaming/Tools_Streaming_System.h"
#include "Visibility/Tools_Visibility_System.h"

#if defined(ASURA_USE_INDEPENDENT_SHADOW_SYSTEM)
#include "Shadows/Asura_Shadow_System.h"
#endif

////////////////////////////////////////////////////////////////////////////////////
extern bool ForceRedrawAll;

extern void RenderOutline( const Asura_Vector_3* pxPoints, int iNumPoints, u_int uColour, bool bWireframe );

////////////////////////////////////////////////////////////////////////////////////

bool Tools_Render::HighlightPolyOrObjectUnderCursor = true;
bool Tools_Render::UseLighting = true;
#if defined( ASURA_TARGET_SM3 )
bool Tools_Render::s_bUseTexturing = true;
bool Tools_Render::s_bShowPerPixelNormals = false;
#endif // #if defined( ASURA_TARGET_SM3 )


Asura_Vector_3 Tools_Render::s_xPositionForModuleLinking ;
int Tools_Render::s_iModuleForModuleLinking;
enum Tools_Render::ModuleLinkMode Tools_Render::s_eModuleLinkMode = Tools_Render::MODULELINK_USECAMERA;

int* Tools_Render::s_piModulesToRender;
int Tools_Render::s_iModulesToRenderSize;

/////////////////////////////////////////////////////////////////////////////////////

static bool g_bFirstInitialise = true;

/////////////////////////////////////////////////////////////////////////////////////
//globals for batched icon renderer
struct Tools_RenderIcon
{
	Asura_Vector_3 m_xPosition;
	float m_fSize;
	u_int m_uColour;
	Tools_RenderIcon* m_pxNext;
};

//Head of linked lists of icons to render for each texture
static Tools_RenderIcon* g_apxHeadOfIconList[ASURA_MAX_NO_OF_TEXTURES];

//storage for all batched icons
static const int g_iIconBufferSize = 1024;
static Tools_RenderIcon g_axIconBuffer[g_iIconBufferSize];
static int g_iNumBufferIcon = 0;

//vertex and index buffer storage for renderinf the icons
static const int g_iMaxIconPerBatch = 256;
static D3DGenericVertex g_axIconVerts[g_iMaxIconPerBatch*4];
static short g_asIconInds[g_iMaxIconPerBatch*6];

//maximum number of different icon textures to keep track of.
static const int g_iMaxIconTexture = 32;
static int g_aiIconTextures[g_iMaxIconTexture];
static int g_iNumIconTexture = 0;


/////////////////////////////////////////////////////////////////////////////////////
void Tools_Render::Initialise()
{
#if defined (ASURA_TARGET_SM3)
	if(g_bFirstInitialise)
	{
		Tools_RenderModule_System::Reset();
	}
#else
	Tools_RenderModule_System::Initialise();
#endif



	if(g_bFirstInitialise)
	{
		if(Tools_SaveAsura::GetFileType()!=AsuraFile_Environment)
		{
			//default to full bright if this isn't an environment (since there will be no lights)
			ToggleLighting();
		}
	}

	#if defined ASURA_SUPPORT_COMMAND_CONSOLE && !defined ASURA_COMMAND_CONSOLE_READONLY
		if( g_bFirstInitialise )
		{
			Asura_CommandConsole::AddVar( L"Render.UseLighting", &UseLighting, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
		}
	#endif

	g_bFirstInitialise = false;
}

void Tools_Render::Shutdown()
{

#if !defined (ASURA_TARGET_SM3)
	Tools_RenderModule_System::Shutdown();
#endif
	Tools_RenderCollisionMesh_System::Shutdown();

	delete [] s_piModulesToRender;
	s_piModulesToRender = 0;
	s_iModulesToRenderSize = 0;

}


void Tools_Render::BeginRenderWorld()
{
	const int iWidth = Asura_Viewport::GetWidth();
	const int iHeight = Asura_Viewport::GetHeight();
	const float fScreenWidth = static_cast<float>(iWidth);
	const float fScreenHeight = static_cast<float>(iHeight);

	float fAspectRatio;

	Asura_Render::BeginFrame();
#ifdef ASURA_TARGET_SM3
	Asura_Direct3D::BeginDeferredRendering();
#endif
	if (Tools_Render_Options::IsInPSPMode())
	{
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		Asura_Render::Overlay.Colour = 0xff000000;
		Asura_Render::Overlay.SetRectUV(0.0f, 0.0f, 1.0f, 1.0f);
		Asura_Render::Overlay.TextureIndex = ASURA_TEXTURE_NONE;
		Asura_Render::Overlay.SetRectXY(0.0f, 0.0f, static_cast<float>(Asura_Viewport::GetWidth()), static_cast<float>(Asura_Viewport::GetHeight()));
		Asura_Render::Overlay.Render();
		Asura_Render::FlushDisplay();

		float fWidthOffset;
		float fHeightOffset;
		float fWidth;
		float fHeight;

		if (Tools_Render_Options::IsPSPModeActualSize())
		{
			fWidthOffset = static_cast<float>((iWidth - 480) >> 1) / fScreenWidth;
			fHeightOffset = static_cast<float>((iHeight - 272) >> 1) / fScreenHeight;
			fWidth = 1.0f - (fWidthOffset * 2.0f);
			fHeight = 1.0f - (fHeightOffset * 2.0f);
		}
		else
		{
			fHeight = (272.0f / fScreenHeight) * (fScreenWidth / 480.0f);

			if(fHeight > 1.0f)
			{
				fWidth	= 1.0f / fHeight;
				fHeight	= 1.0f;
			}
			else
			{
				fWidth = 1.0f;
			}
			fWidthOffset	= (1.0f - fWidth) * 0.5f;
			fHeightOffset	= (1.0f - fHeight) * 0.5f;
		}

		Asura_ViewingArea xViewArea(fWidthOffset, fHeightOffset, fWidth, fHeight);
		Asura_Camera::SetViewingArea(xViewArea);
		fAspectRatio = 480.0f / 272.0f;
	}	
	else
	{
		Asura_ViewingArea xViewArea(0.0f, 0.0f, 1.0f, 1.0f);
		Asura_Camera::SetViewingArea(xViewArea);
		fAspectRatio = fScreenWidth / fScreenHeight;
	}

	Asura_Camera::SetPhysicalScreenAspectRatio(fAspectRatio);
	Tools_Render_Options::UpdateFieldOfView();
}

void Tools_Render::EndRenderWorld()
{
	// JAT 27/11/06 - clear out any quads and lines that might have been batched
	// (Must be before the EndMRT!)
	Asura_Render::FlushDisplay();

	if (Tools_Render_Options::IsInPSPMode())
	{
		Asura_Render::AllowFullScreenRendering();
	}

	//Clear z buffer so that old and new renderers should behave in a similar way for any subsequent 3D stuff that gets drawn
	//RAR 26/09/2006
	Asura_Render::ClearZBuffer();
}

void Tools_Render::SetPositionForModuleLinking(const Asura_Vector_3& xPosition)
{
	s_xPositionForModuleLinking = xPosition;
	s_eModuleLinkMode = MODULELINK_USEPOSITION;
}

void Tools_Render::SetModuleForModuleLinking(int iIndex)
{
	s_iModuleForModuleLinking = iIndex;
	s_eModuleLinkMode = MODULELINK_USEINDEX;
}


void Tools_Render::RenderPolygonOutline(Asura_Mesh* mesh,const int polyIndex,const Asura_Vector_3* position,const Asura_Matrix_3x3* orientation,unsigned int colour, PolygonOutlineMode mode, float fScale)
{
	RenderPolygonOutline(mesh, polyIndex, position, orientation, colour, mode, Asura_Vector_3(fScale,fScale,fScale));
}

void Tools_Render::RenderPolygonOutline(Asura_Mesh* mesh,const int polyIndex,const Asura_Vector_3* position,const Asura_Matrix_3x3* orientation,unsigned int colour, PolygonOutlineMode mode, const Asura_Vector_3& xScale)
{
	SCOPEDPIXEVENT(( 0xFF00C000, L"Mesh %S polygon %i", mesh->Name.GetString(), polyIndex ));

	if(!mesh->AreNormalsValid()) mesh->CreateNormals();

	Asura_Vector_3 normal = mesh->NormalsDataPtr[polyIndex].FaceNormal;
	if(orientation)
	{
		normal = normal * (*orientation);
	}

	Asura_Vector_3 points[4];

	
	bool wireframe;

	switch(mode)
	{
		case POM_Default :
			wireframe = Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_WIREFRAME;
			break;
		case POM_ForceWireframe :
			wireframe = true;
			break;
		case POM_ForceSolid :
			wireframe = false;
			break;
	}

	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);	

	if(mode == POM_Default)
	{
		float i = -normal*zvec;
		if (i<0) i=0;
		u_int c = (u_int)(i*100.0f);
		//c = c+(c<<8)+(c<<16);
		//colour = colour|c;

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

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

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

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

	}

	int iNumVerts = mesh->PolygonDataPtr[polyIndex].NumberOfVertices();
	Asura_Vector_3 xOffset;

	for(int i=0;i<iNumVerts;i++)
	{
		points[i] = mesh->VertexDataPtr[mesh->PolygonDataPtr[polyIndex].VertexID[i]].Position;
		points[i].x *= xScale.x;
		points[i].y *= xScale.y;
		points[i].z *= xScale.z;
		
		if(orientation)
		{
			points[i] = (points[i] * *orientation) + *position;
		}
		else
		{
			points[i] += *position;
		}
		
		//move the point towards the camera.
		//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;

		
		xOffset = *cameraPosition - points[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)*(MAX_OFFSET-MIN_OFFSET)/(MAX_CAM_DIST-MIN_CAM_DIST);
		}
		else
		{
			fOffset = MAX_OFFSET;		
		}

		points[i]+=xOffset*fOffset;

	}
	RenderOutline(points,iNumVerts,colour,wireframe);
}

void Tools_Render::RenderPolygonOutline(const int polyIndex, const int moduleIndex, unsigned int colour, bool forceWireframe)
{
	if (!Asura_Module_System::IsModuleVisible(moduleIndex)) return;

	//probably don't want outlines for hidden polygons
	if(Tools_Render_Options::IsHidden(moduleIndex,polyIndex)) return;
	if(!Tools_Render_Options::IsModuleWithinViewDistance(moduleIndex)) return;


	Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(moduleIndex);

	Asura_Vector_3 offset;		
	Asura_Module_System::GetPositionOfModule(moduleIndex,offset);

	RenderPolygonOutline(mesh,polyIndex,&offset,0,colour,forceWireframe ? POM_ForceWireframe : POM_Default);
}

void Tools_Render::RenderMeshOutline( int iModuleIndex, u_int uColour, bool bForceWireframe, bool bIgnoreModuleLinking )
{
	RenderMeshOutlineByZone(0,iModuleIndex,uColour,bForceWireframe,bIgnoreModuleLinking);
}

void Tools_Render::RenderMeshOutlineByZone(Asura_Hash_ID uZoneHash,int iModuleIndex,unsigned int uColour, bool bForceWireframe /*= false*/,bool bIgnoreModuleLinking /*=false*/)
{
	#ifdef ASURA_USE_STREAMING
	Asura_Module_Handler* pxHandler = uZoneHash ? Asura_Module_System::GetHandler(uZoneHash) : &Asura_Module_System::GetCurrentHandler();
	if(!pxHandler) return;
	#else
	Asura_Module_Handler* pxHandler = &Asura_Module_System::GetCurrentHandler();
	#endif

	if( !bIgnoreModuleLinking && !pxHandler->IsModuleVisible(iModuleIndex) )
    {
        return;
    }

	//Determine if the outline should be done in wireframe.
	bool bWireframe = bForceWireframe || Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_WIREFRAME;

	//If translucent mode has been asked for , then scale the alpha down
	if(Tools_Render_Options::GetPolygonHighlightMode() == TOOLS_HIGHLIGHT_TRANSLUCENT)
	{
		u_int uA = uColour >> 24;
		uA /= 3;
		uColour &= 0xffffff;
		uColour |= (uA<<24);
	}

    Asura_Vector_3 xModulePos;
    pxHandler->GetPositionOfModule( iModuleIndex, xModulePos );
	Asura_Mesh* pxMesh = pxHandler->GetMeshForModule( iModuleIndex );
    Asura_Mesh_System::PlatformRenderMeshHighlight( pxMesh, &xModulePos, NULL, uColour, bWireframe, true );
}


void Tools_Render::RenderMeshOutline(Asura_Mesh* mesh,const Asura_Vector_3* position,const Asura_Matrix_3x3* orientation,unsigned int colour, bool forceWireframe, float fScale)
{
	RenderMeshOutline( mesh, position, orientation, colour, forceWireframe, Asura_Vector_3(fScale,fScale,fScale));
}

void Tools_Render::RenderMeshOutline(Asura_Mesh* mesh,const Asura_Vector_3* position,const Asura_Matrix_3x3* orientation,unsigned int colour, bool forceWireframe, const Asura_Vector_3& xScale)
{
	SCOPEDPIXEVENT(( 0xFF00FF00, L"Mesh %S outline", mesh->Name.GetString() ));

	for(int i=0;i<mesh->NumberOfPolygons;i++)
	{
		RenderPolygonOutline(mesh,i,position,orientation,colour,forceWireframe  ? POM_ForceWireframe : POM_Default, xScale);
	}
}

void Tools_Render::RenderModuleOutline(const int moduleIndex, unsigned int colour, bool wireframe)
{
	//don't bother if module isn't visible
	if (!Asura_Module_System::IsModuleVisible(moduleIndex)) return;
	if(!Tools_Render_Options::IsModuleWithinViewDistance(moduleIndex)) return;


	Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(moduleIndex);


	Asura_Vector_3 offset;		
	Asura_Module_System::GetPositionOfModule(moduleIndex,offset);

	//get the bounding box
	if(!mesh->BoundingBoxDataValid) mesh->CalculateBoundingBox();
	Asura_Bounding_Box box = mesh->BoundingBox;

	//add on the modules location
	box.Displace(offset);
	
	RenderCube(box,colour,wireframe);
}

void Tools_Render::RenderModuleOutline(const int iModuleIndex,u_int uFaceColour,u_int uEdgeColour)
{
	//don't bother if module isn't visible
	if (!Asura_Module_System::IsModuleVisible(iModuleIndex)) return;
	if(!Tools_Render_Options::IsModuleWithinViewDistance(iModuleIndex)) return;


	Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(iModuleIndex);


	Asura_Vector_3 offset;		
	Asura_Module_System::GetPositionOfModule(iModuleIndex,offset);

	//get the bounding box
	if(!mesh->BoundingBoxDataValid) mesh->CalculateBoundingBox();
	Asura_Bounding_Box box = mesh->BoundingBox;

	//add on the modules location
	box.Displace(offset);
	
	RenderCube(box,uFaceColour,false);
	RenderCube(box,uEdgeColour,true);
}

void Tools_Render::RenderVertexOutline(int iModuleIndex,int iVertexIndex,unsigned int uColour)
{
	//don't bother if module isn't visible
	if (!Asura_Module_System::IsModuleVisible(iModuleIndex)) return;
	if(!Tools_Render_Options::IsModuleWithinViewDistance(iModuleIndex)) return;


	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);


	Asura_Vector_3 xPosition;		
	Asura_Module_System::GetPositionOfModule(iModuleIndex,xPosition);

	xPosition += pxMesh->VertexDataPtr[iVertexIndex].Position;

	float fDist = (xPosition - *Asura_Camera::GetPosition()).Magnitude();

	//scale outline down if vertex is close
	float fSize = 0.05f;
	if(fDist < 10.0f)
	{
		if(fDist < 0.1f) fDist = 0.1f;
		fSize *= fDist/10.0f;	
	}

	Asura_Bounding_Box xBox;
	xBox.MinX = xPosition.x - fSize;
	xBox.MinY = xPosition.y - fSize;
	xBox.MinZ = xPosition.z - fSize;
	xBox.MaxX = xPosition.x + fSize;
	xBox.MaxY = xPosition.y + fSize;
	xBox.MaxZ = xPosition.z + fSize;

	
	RenderCube(xBox,uColour,false);
}

void Tools_Render::RenderCube(const Asura_Bounding_Box& box,unsigned int colour, bool wireframe,int iHighlightSide,int iHighlightColour)
{
	Asura_Vector_3 xPos(0,0,0);
	Asura_Matrix_3x3 xOrient;
	xOrient.Identity();	


	RenderCube(box,xPos,xOrient,colour,wireframe,iHighlightSide,iHighlightColour);

}

void Tools_Render::RenderCube(const Asura_Bounding_Box& box,const Asura_Vector_3& xPosition,const Asura_Matrix_3x3& xOrient,unsigned int colour, bool wireframe,int iHighlightSide,int iHighlightColour)
{
	if(Asura_SortedRender::IsActive())
	{
		//send through sorted render ,since thes don't write to the Z buffer.
		Asura_SortedRender::AddToolsCuboid(box, xPosition, xOrient, colour, wireframe, iHighlightSide, iHighlightColour);
		return;
	}
	
	
	
	extern void Render_Cuboid(const Asura_Bounding_Box& box,const Asura_Vector_3& xPosition,const Asura_Matrix_3x3& xOrient, int colour,bool forceWireframe = false,int iHighlightSide = -1,int iHighlightColour = 0xffff0000);

    Asura_Render::CurrentStates.RequestWireFrameMode(false);
	Render_Cuboid(box,xPosition,xOrient,colour,wireframe,iHighlightSide,iHighlightColour);
	Asura_Render::CurrentStates.RequestWireFrameMode(Asura_Render::GetWireframe());

}


// RenderCube: Render a rotated cuboid.
// MGH: TODO: currently only does wireframe, add a solid rendering mode
void Tools_Render::RenderCube(const Asura_Bounding_Box& xBox,
							  const Asura_Vector_3& xPosition,
							  const Asura_Matrix_3x3& xOrient,
							  unsigned int uColour /*=0xffffffff*/)
{
	if (xBox.MaxX <= xBox.MinX || xBox.MaxY <= xBox.MinY || xBox.MaxZ <= xBox.MinZ) return;

	Asura_Vector_3 xVertices[8];
	
	for (int i=0; i<8; i++)
	{
		xVertices[i].x = (i&1) ? xBox.MaxX : xBox.MinX;
		xVertices[i].y = (i&2) ? xBox.MaxY : xBox.MinY;
		xVertices[i].z = (i&4) ? xBox.MaxZ : xBox.MinZ;

		// Rotate into world space
		xVertices[i] = xVertices[i] * xOrient;

		// Translate
		xVertices[i] += xPosition;
	}

	short sIndices[24] = 
		{
			0,2,2,3,3,1,1,0,
			0,4,2,6,3,7,1,5,
			4,6,6,7,7,5,5,4,
		};
	
	for (int j=0; j<12; j++)
	{
		Asura_Render::RenderLine(xVertices[sIndices[2*j]], xVertices[sIndices[2*j+1]], uColour);
	}
}

void Tools_Render::RenderEdge(int iModuleIndex,int iVert1,int iVert2, u_int uColour)
{
	Asura_Assert(iModuleIndex>=0 && iModuleIndex<Asura_Module_System::GetNumberOfModules(),"");
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Vector_3 xPosition;
	Asura_Module_System::GetPositionOfModule(iModuleIndex,xPosition);
	
	if(!pxMesh->AreNormalsValid()) pxMesh->CreateNormals();

	Asura_Vector_3 axPoints[2];
	float fScale =  0.06f;
	
	const Asura_Vector_3* pxCameraPosition = Asura_Camera::GetPosition();

	Asura_Assert(iVert1>=0 && iVert1 < pxMesh->NumberOfVertices,"");
	Asura_Assert(iVert2>=0 && iVert2 < pxMesh->NumberOfVertices,"");

	axPoints[0] = pxMesh->VertexDataPtr[iVert1].Position + xPosition;
	axPoints[1] = pxMesh->VertexDataPtr[iVert2].Position + xPosition;
	

	Asura_Vector_3 xVec;

	for(int j=0; j<2; j++)
	{
		xVec = *pxCameraPosition - axPoints[j];
		xVec.Normalise();
		axPoints[j]+=xVec*fScale;
	}

	Asura_Render::RenderLine(axPoints[0],axPoints[1],uColour);

}


void Tools_Render::RenderVertexPerPoly( int iModuleIndex, int iPoly, int iVert, u_int uColour)
{
	//don't bother if module isn't visible
	if (!Asura_Module_System::IsModuleVisible(iModuleIndex)) return;
	if(!Tools_Render_Options::IsModuleWithinViewDistance(iModuleIndex)) return;

	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

	Asura_Vector_3 xPosition;		
	Asura_Module_System::GetPositionOfModule(iModuleIndex,xPosition);

	// get average vert position of poly

	Asura_Vector_3 xAveragePolyPosition = Asura_Maths::ZeroVector3;
	for ( int i = 0; i < pxMesh->PolygonDataPtr[ iPoly ].NumberOfVertices(); i++ )
	{
		xAveragePolyPosition += pxMesh->VertexDataPtr[ pxMesh->PolygonDataPtr[ iPoly ].VertexID[ i ] ].Position;
	}
	xAveragePolyPosition /= (float)( pxMesh->PolygonDataPtr[ iPoly ].NumberOfVertices() );

	// get position of this vert

	Asura_Vector_3 xVertPos = pxMesh->VertexDataPtr[ pxMesh->PolygonDataPtr[ iPoly ].VertexID[ iVert ] ].Position;

	// get proper position

	xPosition += xVertPos * 0.9f;
	xPosition += xAveragePolyPosition * 0.1f;

	// now render

	float fDist = (xPosition - *Asura_Camera::GetPosition()).Magnitude();

	//scale outline down if vertex is close
	float fSize = 0.05f;
	if(fDist < 10.0f)
	{
		if(fDist < 0.1f) fDist = 0.1f;
		fSize *= fDist/10.0f;	
	}

	Asura_Bounding_Box xBox;
	xBox.MinX = xPosition.x - fSize;
	xBox.MinY = xPosition.y - fSize;
	xBox.MinZ = xPosition.z - fSize;
	xBox.MaxX = xPosition.x + fSize;
	xBox.MaxY = xPosition.y + fSize;
	xBox.MaxZ = xPosition.z + fSize;

	RenderCube(xBox,uColour,false);
}


#define D3DFVF_CONEVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
struct D3DConeVertex
{
	Asura_Vector_3 xPos;
	unsigned int uDiffuse;
};

void Tools_Render::RenderCone(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,float fSize,Asura_Angle fAngle,u_int uColour,bool bWireframe)
{
	SCOPEDPIXEVENT((0xffffffff, L"Tools: Render Cone"));

	const u_int uConeDetail = 20;

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_CONEVERTEX);

	D3DConeVertex axVerts[uConeDetail+1];

	//get height and radius of cone
	float fHeight = Asura_Maths::Cosine(fAngle/2.0f)*fSize;
	float fRadius = Asura_Maths::Sine(fAngle/2.0f)*fSize;

	for(int i=0;i<uConeDetail;i++)
	{
		float fTheta = (Asura_Maths::Pi*2.0f*i)/(float)uConeDetail;

		
		axVerts[i].xPos.x = Asura_Maths::Sine(fTheta)*fRadius;
		axVerts[i].xPos.y = Asura_Maths::Cosine(fTheta)*fRadius;
		axVerts[i].xPos.z = fHeight;
		axVerts[i].uDiffuse = uColour;
	}

	//final vertex is the tip of the cone
	axVerts[uConeDetail].xPos.Zero();
	axVerts[uConeDetail].uDiffuse = uColour;

	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);

	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	Asura_Render::CurrentStates.RequestTextureIndex(0,ASURA_TEXTURE_NONE);


	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

	D3DXMATRIX matWorld;
	Asura_Direct3D::SetupD3DMatrix(matWorld, xOrient, xPos, true);
	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD,      &matWorld );

	if(bWireframe)
	{
		short ausIndeces[uConeDetail*4];

		int iIndex = 0;
		for(int i=0;i<uConeDetail;i++)
		{
			ausIndeces[iIndex++] = uConeDetail;
			ausIndeces[iIndex++] = (short)i;
		}

		for(int i=0;i<uConeDetail;i++)
		{
			ausIndeces[iIndex++] = (short)i;
			ausIndeces[iIndex++] = (short)((i+1)%uConeDetail);
		}
		


		Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
			(
			D3DPT_LINELIST,
			0,
			uConeDetail+1,
			uConeDetail*2,
			(void*)ausIndeces,
			D3DFMT_INDEX16,
			(void*)axVerts,
			sizeof(D3DConeVertex)
			);

	}
	else
	{
		short ausIndeces[uConeDetail*3];

		int iIndex = 0;
		for(int i=0;i<uConeDetail;i++)
		{
			ausIndeces[iIndex++] = uConeDetail;
			ausIndeces[iIndex++] = (short)i;
			ausIndeces[iIndex++] = (short)((i+1)%uConeDetail);
		}


		Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
			(
			D3DPT_TRIANGLELIST,
			0,
			uConeDetail+1,
			uConeDetail,
			(void*)ausIndeces,
			D3DFMT_INDEX16,
			(void*)axVerts,
			sizeof(D3DConeVertex)
			);
	}

	D3DXMatrixTranslation( &matWorld, 0,0,0 );
	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD,      &matWorld );
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
	
		

}

void Tools_Render::RenderSpheroid(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,const Asura_Vector_3& xExtents,u_int uColour,bool bWireframe, bool bPlain /* = false */)
{
	SCOPEDPIXEVENT((0xffffffff, L"Tools: Render Spheroid"));

	#define SPHERE_VERTEX_DESC (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) 
	static int s_iFogTexture = ASURA_TEXTURE_NONE;

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(SPHERE_VERTEX_DESC);

	if (s_iFogTexture == ASURA_TEXTURE_NONE)
	{
		s_iFogTexture = Asura_Texture_Management::AddTextureWithResetGroup("\\tools\\fog.bmp", ASURA_TEXTURE_LOADED_PERMANENT );
		Asura_Texture_Management::LoadTextures();
	}

	Asura_Render::CurrentStates.RequestWireFrameMode(bWireframe);

	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);

	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
	if(bPlain)
	{
		Asura_Render::CurrentStates.RequestTextureIndex(0,ASURA_TEXTURE_NONE);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	}
	else
	{
		Asura_Render::CurrentStates.RequestTextureIndex(0,s_iFogTexture);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
	}

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

	D3DXMATRIX matWorld;

	matWorld._11 = xOrient.e11*xExtents.x;
	matWorld._12 = xOrient.e21*xExtents.x;
	matWorld._13 = xOrient.e31*xExtents.x;
	matWorld._14 = 0;
	matWorld._21 = xOrient.e12*xExtents.y;
	matWorld._22 = xOrient.e22*xExtents.y;
	matWorld._23 = xOrient.e32*xExtents.y;
	matWorld._24 = 0;
	matWorld._31 = xOrient.e13*xExtents.z;
	matWorld._32 = xOrient.e23*xExtents.z;
	matWorld._33 = xOrient.e33*xExtents.z;
	matWorld._34 = 0;
	matWorld._41 = xPos.x;
	matWorld._42 = xPos.y;
	matWorld._43 = xPos.z;
	matWorld._44 = 1.0f;

	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD,      &matWorld );

	for (int i=0; i<SPHERE_VERTICES; i++)
	{
		SphereVertex[i].Colour = uColour;
	}
	


	//HRESULT h = 
	Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
		(
		D3DPT_TRIANGLELIST,
		0,
		SPHERE_VERTICES,
		SPHERE_INDICES/3,
		(void*)SphereIndices,
		D3DFMT_INDEX16,
		(void*)SphereVertex,
		sizeof(Asura_Sphere_Vertex)
		);


	D3DXMatrixTranslation( &matWorld, 0,0,0 );
	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD,      &matWorld );
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);

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

}

void Tools_Render::RenderCollisionMesh(const class Asura_CollisionMesh* pxCollMesh,const Asura_Vector_3* pxPosition,const Asura_Matrix_3x3* pxOrient,u_int uColour)
{
	Asura_Assert(pxCollMesh,"");

	if(!pxPosition) pxPosition = &Asura_Maths::ZeroVector3;
	if(!pxOrient) pxOrient = &Asura_Maths::IdentityMatrix3x3;

	Asura_Vector_3 axVerts[4];

	float fScale =  0.01f;
	const Asura_Vector_3* pxCameraPosition = Asura_Camera::GetPosition();

	for(u_int i=0;i<pxCollMesh->GetNumberOfPolys();i++)
	{
		int iNumVerts = pxCollMesh->GetNumVerticesInPoly(i);
		pxCollMesh->GetVertices(axVerts,i);

		//move the verts slightly towards the camera
		for(int j=0; j<iNumVerts; j++)
		{
			axVerts[j] = axVerts[j] * *pxOrient;
			axVerts[j] += *pxPosition;
			
			Asura_Vector_3 xVec = *pxCameraPosition - axVerts[j];
			xVec.Normalise();
			axVerts[j]+=xVec*fScale;
		}

		//draw the outline for this polygon
		RenderOutline(axVerts,iNumVerts,uColour,true);
 	}
}



void Tools_Render::ToggleLighting()
{
	UseLighting = !UseLighting;
		

	//enable/disable lighting for Tools_RenderModule by updating the diffuse colour , since
	//using material ambient doesn't appear to be working. RAR 04/10/2004 
	Tools_RenderModule_System::UpdateAllModuleLighting();
}


void Tools_Render::UpdateAmbient()
{
	if(UseLighting)
	{
		//force lighting update
		{
			for (int m=0; m<Asura_Module_System::GetNumberOfModules(); ++m)
			{
				Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(m);
				mesh->PrelightingDataValid = false;
				Asura_Mesh_System::PlatformResetRenderMesh(mesh);
			}
		}
	}
}

#include "Rendering/Asura_Render.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Rendering/Asura_Surface.h"
#include "Rendering/Asura_Bicubic_Patch.h"

bool DrawUnlinkedModules;

static bool DrawModuleBrightGreen;

void Tools_Render::RenderEnvironment(void)
{	
#ifdef ASURA_USE_HDR
	if (Asura_Render::IsCurrentlyHDR())
	{
#endif
		switch(s_eModuleLinkMode)
		{
			case MODULELINK_USECAMERA :
				Asura_Module_System::Update();
				break;
			case MODULELINK_USEPOSITION:
				Asura_Module_System::Update(&s_xPositionForModuleLinking);
				break;
			case MODULELINK_USEINDEX :
				Asura_Module_System::Update(&s_iModuleForModuleLinking);
				break;
		
		}
#ifdef ASURA_USE_HDR
	}
#endif


	if( Tools_Render_Options::HideEnvironment
		|| !Asura_Render::IsSystemEnabled( ASURA_RENDER_SYSTEM_ENV ) )
	{
		return;
	}

	D3DPERFCALL( BeginEvent(D3DCOLOR_ARGB(0xff, 0xff, 0x00, 0x00), L"Environment (Tools)"));

	for(Tools_Streaming_Iterator xIt;!xIt.Done();xIt.Next())
	{
		Tools_Render::RenderEnvironmentForCurrentZone();
	}
#ifdef ASURA_USE_MATERIAL_RESPONSE_SYSTEM
	if (Asura_MaterialResponse_System::GetMaterialProjectContextCallback())
	{
		Asura_MaterialResponse_System::GetMaterialProjectContextCallback()(ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID,ASURA_MATERIAL_RESPONSE_PROJECT_CONTEXT_INVALID);
	}
#endif

	D3DPERFCALL( EndEvent());
}

void Tools_Render::RenderEnvironmentForCurrentZone(void)
{
	#ifdef ASURA_USE_STREAMING
	//Check for option for only rendering the main zone.
	if(!Tools_Streaming_System::IsSetToMainZone() && Tools_Render_Options::IsHidingOtherZones())
	{
		return;
	}
	#endif


	DrawModuleBrightGreen = false;
	
	//extern void BeginPatchOutput(void);
	//extern void EndPatchOutput(void);
	
//	Asura_Render::PatchesOn=true;
//	Asura_Render::StaticSubdivision=false;
	
//	BeginPatchOutput();

//	void RenderMesh(Asura_Mesh* mesh, Asura_Vector_3& position,int iModuleIndex);
	Asura_Matrix_3x3 orient;
	orient.Identity();

	#if defined (ASURA_TARGET_SM3)

#ifdef ASURA_USE_HDR
	if (Asura_Render::IsCurrentlyHDR())
	{
#endif
		// render the actual environment using the visibility system, sorted by material, and so forth	
		if ( !Tools_RenderCollisionMesh_System::IsRenderingCollMeshes() )
		{
			Tools_RenderModule_System::RenderEnvironment();
		}
#ifdef ASURA_USE_HDR
	} 
	else 
	{
#endif
		//determine number of passes through all the modules required
		int iNumPasses;
		if(Tools_RenderCollisionMesh_System::IsRenderingCollMeshes())
		{
			iNumPasses = Tools_RenderCollisionMesh_System::GetNumPasses();
		}
		else
		{
			iNumPasses = 1;
		}
		for(int iPass=0;iPass<iNumPasses;iPass++)
		{

			// then render any outlines, collision mesh etc
			for (int m=0; m<Asura_Module_System::GetNumberOfModules(); m++)
			{
				bool renderThisModule = ForceRedrawAll;
				bool bRenderGreenForModuleLinking = false;

#if defined(ASURA_USE_NEW_VISIBILITY_SYSTEM)
				if( Tools_RenderCollisionMesh_System::IsRenderingCollMeshes() )
                {
				    // KJFL - 22 April 2008
				    // It's pointless checking module visibility status for collision meshes - the visibility system
				    // will be automatically rejecting them if they don't have renderable polys.
				    // 
				    // Presumably the old module linking would link in collision-only meshes as "visible". We don't
				    // want to even try to do this in the new system, as collision mesh behind some other collision 
				    // or renderable mesh would get hidden etc.
                    if( Asura_Module_System::IsModuleInFrustum(m) )
				    {
					    renderThisModule = true;
				    }
                }
                else
                {
                    if( Asura_Module_System::IsModuleInFrustum(m) )
                    {
                        if( Asura_Visibility_System::IsModuleLinkedInFromCurrentCamera(m) )
                        {
                            renderThisModule = true;
                        }
                        else if( Tools_Visibility_System::ShouldHighlightNonVisibleGeometry() )
                        {
                            renderThisModule = true;
                            bRenderGreenForModuleLinking = true;
                        }
                    }
                }
#else
				if(Asura_Module_System::IsModuleVisible(m))
				{
					renderThisModule = true;
				}
				else if (DrawUnlinkedModules && Asura_Module_System::IsModuleInFrustum(m))
				{
					renderThisModule = true;
					bRenderGreenForModuleLinking = true;
				}
#endif
				
				if(!ForceRedrawAll && !Tools_Render_Options::IsModuleWithinViewDistance(m))
				{
					renderThisModule = false;
				}

				if (renderThisModule)
				{
					Asura_Vector_3 position;
					Asura_Module_System::GetPositionOfModule(m,position);

					if(Tools_RenderCollisionMesh_System::IsRenderingCollMeshes()  && !Tools_Render_Options::IsHidden(m))
					{
						int iModule = m;

						Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(iModule);
						
						Asura_Vector_3 position;
						Asura_Module_System::GetPositionOfModule(iModule,position);

						RenderModule(iPass,iModule,*mesh,position);
					}

					// (MP - 11.04.2006) Crappy fix for this not being properly
					// supported in the new renderer.
					if( bRenderGreenForModuleLinking )
					{
						Tools_HighlightMode eHighlightBackup = Tools_Render_Options::GetPolygonHighlightMode();
						Tools_Render_Options::SetPolygonHighlightMode(TOOLS_HIGHLIGHT_SOLID);
						RenderMeshOutline( m, 0xFF50FF80, false, true );
						Tools_Render_Options::SetPolygonHighlightMode(eHighlightBackup);
					}

					if(Tools_Render_Options::GetWireframeMode() != TOOLS_WIREFRAME_OFF)
					{
						RenderMeshOutline( m, Tools_Render_Options::GetWireframeColour(true,m), true, true );
					}
				}
			}
		}
#ifdef ASURA_USE_HDR
	} 
#endif
	
	#else

	//make sure module render 'queue' is large enough
	if(s_iModulesToRenderSize<Asura_Module_System::GetNumberOfModules())
	{
		//increase size of 'queue'
		delete [] s_piModulesToRender;
		s_iModulesToRenderSize = Asura_Module_System::GetNumberOfModules();
		s_piModulesToRender = new int[s_iModulesToRenderSize];
	}

	//determine which modules to render
	int iNumModulesToRender = 0;

	for (int m=0; m<Asura_Module_System::GetNumberOfModules(); m++)
	{
		bool renderThisModule = ForceRedrawAll;

		if(Asura_Module_System::IsModuleVisible(m))
		{
			renderThisModule = true;
		}
		else if (DrawUnlinkedModules && Asura_Module_System::IsModuleInFrustum(m))
		{
			renderThisModule = true;
		}
		if(!ForceRedrawAll && !Tools_Render_Options::IsModuleWithinViewDistance(m))
		{
			renderThisModule = false;
		}

		if(renderThisModule)
		{
			s_piModulesToRender[iNumModulesToRender++] = m;
		}
	}

	//determine number of passes through all the modules required
	int iNumPasses;
	if(Tools_RenderCollisionMesh_System::IsRenderingCollMeshes())
	{
		iNumPasses = Tools_RenderCollisionMesh_System::GetNumPasses();
	}
	else
	{
		iNumPasses = Tools_RenderModule_System::GetNumPasses();
	}

	for(int iPass=0;iPass<iNumPasses;iPass++)
	{
		for (int i=0; i<iNumModulesToRender; i++)
		{
			int m = s_piModulesToRender[i];


			DrawModuleBrightGreen = false;
			if(!Asura_Module_System::IsModuleVisible(m) && DrawUnlinkedModules)
			{
				DrawModuleBrightGreen = true;
			}

			Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(m);
			
			Asura_Vector_3 position;
			Asura_Module_System::GetPositionOfModule(m,position);

			RenderModule(iPass,m,*mesh,position);
		}
	}
	//render wireframe outlines if needed
	if(Tools_Render_Options::GetWireframeMode() == TOOLS_WIREFRAME_MIXED)
	{
		for (int i=0; i<iNumModulesToRender; i++)
		{
			int m = s_piModulesToRender[i];
			RenderMeshOutline(m,Tools_Render_Options::GetWireframeColour(true,m),true);
		}
	}


	#endif
	
	/// Kevin Lea - 05/02/2003 15:50:21 - set DirectX lighting to normal
	Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_AMBIENT,0);
	Asura_Render::CurrentStates.RequestDynamicLights(false);

	//EndPatchOutput();


	if(UseLighting)
	{
#ifdef ASURA_USE_HDR
		if (Asura_Render::IsCurrentlyHDR())
		{
#endif
			RenderCoronas();
#ifdef ASURA_USE_HDR
		}
#endif
	}

	ForceRedrawAll = false;
}

extern Asura_Bicubic_Patch patch;
void Tools_Render::RenderModule(int iPass,int moduleIndex, Asura_Mesh& mesh, Asura_Vector_3& position)
{
	Asura_Surface_Mesh* surfaceMesh = Asura_Surface_Mesh_System::GetSurfaceMesh(mesh.Index);
	
	Asura_Assert(surfaceMesh!=NULL,"SurfaceMesh not created");
	if (!mesh.AreNormalsValid())
	{
		surfaceMesh->Invalidate();
		mesh.CreateNormals();
		Tools_RenderModule_System::UpdateModuleMesh(mesh.Index);
	}

	if(!mesh.PrelightingDataValid)
	{
		surfaceMesh->Invalidate();
		Tools_RenderModule_System::UpdateModuleLighting(mesh.Index);
		//Asura_Lighting_System::CalculatePrelighting(mesh,position);
		//CalculatePrelighting moved from here to avoid some problems with prelighting being recalculated
		//more than once per frame when then curvy renderer was being used.
		//RAR 25/06/02
	}

	bool bUseCurvyRenderer = Asura_Render::GetPatchesSubdivLevel() != 0 && !Tools_RenderCollisionMesh_System::IsRenderingCollMeshes();
	#ifdef ASURA_USE_STREAMING
	//Subdivided meshes only exist for the main level at the moment RAR 06/04/2006
	if(!Tools_Streaming_System::IsSetToMainZone()) bUseCurvyRenderer = false;
	#endif
	
	if(bUseCurvyRenderer)
	{
		if(iPass==0)//only doing 1 pass on subdivided stuff atm. RAR 06/10/2005
		{
			if (!surfaceMesh->SurfaceDataValid)
			{
				if(!mesh.PrelightingDataValid  && Tools_Render::LightingIsOn())
				{
					Asura_Lighting_System::CalculatePrelighting(mesh,position,moduleIndex);
				}

				Asura_Surface_Mesh_System::ProcessModuleMeshIntoSurfaces(moduleIndex);
			}

			if (Tools_Render_Options::NoBackFaceCull)
			{
				Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
			}
			else
			{
				Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);
			}
			if(!Tools_Render_Options::IsHidden(moduleIndex))
			{
				extern void RenderMesh(Asura_Mesh* mesh, const Asura_Vector_3& xPosition,int iModuleIndex);
				RenderMesh(&mesh,position,moduleIndex);
			}
		}
	}
	else
	{
		if(!mesh.PrelightingDataValid && Tools_Render::LightingIsOn())
		{
			Asura_Lighting_System::CalculatePrelighting(mesh,position,moduleIndex);
			
		}
		if(!Tools_Render_Options::IsHidden(moduleIndex))
		{

			if(Tools_RenderCollisionMesh_System::IsRenderingCollMeshes())
			{
				Tools_RenderCollisionMesh_System::RenderModule(iPass,moduleIndex);
			}
			else
			{
				Tools_RenderModule_System::RenderModule(iPass,mesh,position,DrawModuleBrightGreen,moduleIndex);
			}
		}
	}
}



void Tools_Render::RenderCoronas(void)
{
	static int CoronaTexture = 0;
	if (!CoronaTexture)
	{
		CoronaTexture = Asura_Texture_Management::AddTexture("\\specialfx\\corona.bmp");
		Asura_Texture_Management::LoadTextures();
	}
	

	unsigned int colour = 0xffffffff;//0000;
	Asura_Vector_3 vertex[4];
	float size = 0.5f;
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CCW);
	Asura_Render::CurrentStates.RequestTextureIndex(0,CoronaTexture);

	{
	
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
	}


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




	for (int i=0; i<Asura_Lighting_System::NumberOfActiveLights; i++)
	{
		Asura_Light* light = &(Asura_Lighting_System::LightStorage[i]);

		if (!light->HasCorona) continue;
		
		colour = 0xff000000	+ (Asura_Maths::FloatToInt(light->R)<<16)
							+ (Asura_Maths::FloatToInt(light->G)<<8)
							+ (Asura_Maths::FloatToInt(light->B));
		
		{
			Asura_Vector_3 disp = light->Position - *cameraPosition;
			float dist = disp.Magnitude();

			if (dist>10.0f) size = 1.0f+(dist-10.0f)/20.0f;
			else size = 1.0f;
			//scale the corona according the the range setting
			size *= light->Range/5;

		}
		Asura_Vector_3 deltaX = Asura_Vector_3(cameraOrientation->e11,cameraOrientation->e21,cameraOrientation->e31)*size;
		Asura_Vector_3 deltaY = Asura_Vector_3(cameraOrientation->e12,cameraOrientation->e22,cameraOrientation->e32)*size;

		Asura_Vector_3 delta = *cameraPosition-light->Position;
		delta.Normalise();
		delta=light->Position+delta;

		vertex[0] = delta - deltaX - deltaY; 
		vertex[1] = delta + deltaX - deltaY;
		vertex[2] = delta + deltaX + deltaY;
		vertex[3] = delta - deltaX + deltaY;

		{
			D3DGenericVertex v[4];
			
			v[0].U = 0.0f;
			v[0].V = 0.0f;

			v[1].U = 1.0f;
			v[1].V = 0.0f;

			v[2].U = 1.0f;
			v[2].V = 1.0f;

			v[3].U = 0.0f;
			v[3].V = 1.0f;

			for (int i=0; i<4; i++)
			{
				v[i].X = vertex[i].x;
				v[i].Y = vertex[i].y;
				v[i].Z = vertex[i].z;

				v[i].Diffuse = colour;
			}
			
			Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_GENERICVERTEX);
		
			Asura_Direct3D::D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, (LPVOID)v, sizeof(D3DGenericVertex) );
		
			
		
		}	
	}
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
}


bool Tools_Render::SetVideoMode(int iWidth,int iHeight,bool bFSAA)
{
	//can't change render mode mid-avi
	Tools_System::EndAVI();

	bool bSuccess = Asura_Render::SetVideoMode(iWidth,iHeight,bFSAA);

	//Update the aspect ratio based on the video mode that was obtained
	int iNewWidth = Asura_Viewport::GetWidth();
	int iNewHeight = Asura_Viewport::GetHeight();

	//If aspect ratio is less than 4/3 in full screen mode then treat it as 4/3
	//(Since 1280x1024 can be used on 4/3 monitors)
	//RAR 16/06/2006
	if(Asura_Windows::RunningFullScreen() && (iNewHeight*4)>(iNewWidth*3))
	{
		Asura_Camera::SetPhysicalScreenAspectRatio( 4.0f/3.0f );
	}
	else
	{
		Asura_Camera::SetPhysicalScreenAspectRatio( (float)iNewWidth / (float) iNewHeight );
	}

	return bSuccess;
}

#define D3DFVF_CYLINDERVERTEX ( D3DFVF_XYZ|D3DFVF_DIFFUSE )
struct D3DCylinderVertex
{
	Asura_Vector_3	m_xPos;
	unsigned int	m_uDiffuse;
};

void Tools_Render::RenderCylinder(const Asura_Vector_3& xPos1, const Asura_Vector_3& xPos2, float fRadius, u_int uColour, bool bWireframe )
{
	RenderCylinder(xPos1, xPos2, fRadius, uColour, uColour, uColour, bWireframe);
}

void Tools_Render::RenderCylinder(const Asura_Vector_3& xPos1, const Asura_Vector_3& xPos2, float fRadius, u_int uEndColour1, u_int uEndColour2, u_int uSideColour, bool bWireframe )
{
	SCOPEDPIXEVENT(( 0xffffffff, L"Tools: Render Cylinder"));

	const u_int uCylinderDetail = 20;

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader( D3DFVF_CYLINDERVERTEX );

	D3DCylinderVertex axVertsEnd0[ uCylinderDetail + 1 ];
	D3DCylinderVertex axVertsEnd1[ uCylinderDetail + 1 ];
	D3DCylinderVertex axVertsWall[ 2 * uCylinderDetail ];

	const float fThetaDelta = ( Asura_Maths::Pi * 2.0f ) / static_cast<float>(uCylinderDetail);

	Asura_Vector_3 xFrontVector = xPos2 - xPos1;
	Asura_Vector_3 xUpVector = Asura_Vector_3(0.0f, 0.0f, 1.0f);
	Asura_Vector_3 xSideVector;
	xFrontVector.Normalise();
	if (Asura_Maths::Abs(xFrontVector * xUpVector) > 0.9f)
	{
		xSideVector = Asura_Vector_3(1.0f, 0.0f, 0.0f);
		xUpVector = xSideVector ^ xFrontVector;
		xUpVector.Normalise();
	} 
	xSideVector = xUpVector ^ xFrontVector;
	xSideVector.Normalise();
	xUpVector = xSideVector ^ xFrontVector;
	xUpVector.Normalise();


	int iIndexWall = 0;
	for ( int i = 0; i < uCylinderDetail; i++ )
	{
		const float fTheta = fThetaDelta * i;
		const float fSineRadius = Asura_Maths::Sine(fTheta) * fRadius;
		const float fCosineRadius = Asura_Maths::Cosine(fTheta) * fRadius;

		axVertsEnd0[i].m_xPos = xPos1 + xSideVector * fSineRadius + xUpVector * fCosineRadius; 
		axVertsEnd0[i].m_uDiffuse = uEndColour1;

		axVertsEnd1[i].m_xPos = xPos2 + xSideVector * fSineRadius + xUpVector * fCosineRadius; 
		axVertsEnd1[i].m_uDiffuse = uEndColour2;

		axVertsWall[iIndexWall].m_xPos = axVertsEnd0[i].m_xPos;
		axVertsWall[iIndexWall++].m_uDiffuse = uSideColour;

		axVertsWall[iIndexWall].m_xPos = axVertsEnd1[i].m_xPos;
		axVertsWall[iIndexWall++].m_uDiffuse = uSideColour;
	}		

	axVertsEnd0[uCylinderDetail].m_xPos = xPos1;
	axVertsEnd1[uCylinderDetail].m_xPos = xPos2;
	axVertsEnd0[uCylinderDetail].m_uDiffuse = uEndColour1;
	axVertsEnd1[uCylinderDetail].m_uDiffuse = uEndColour2;

	Asura_Render::CurrentStates.RequestCullMode( ASURA_CULLMODE_NONE );
	Asura_Render::CurrentStates.RequestTextureOpMode( ASURA_TEXTUREOP_SINGLE_PASS );
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_NORMAL );
	Asura_Render::CurrentStates.RequestTextureIndex( 0, ASURA_TEXTURE_NONE );

	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( FALSE );

	D3DXMATRIX matWorld;
	D3DXMatrixTranslation( &matWorld, 0,0,0 );
	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD, &matWorld );

	short ausLidIndices[ uCylinderDetail * 3 ];
	short ausWallIndices[ uCylinderDetail * 6 ];

	int iIndexLid = 0;
	iIndexWall = 0; 
	for( short s = 0; s < uCylinderDetail; ++s)
	{
		ausLidIndices[ iIndexLid++ ] = s;
		ausLidIndices[ iIndexLid++ ] = uCylinderDetail;
		ausLidIndices[ iIndexLid++ ] = ( ( ( uCylinderDetail + 1 ) + s )  % uCylinderDetail ); 

		ausWallIndices[ iIndexWall++ ] = 0 + (2 * s);
		ausWallIndices[ iIndexWall++ ] = 1 + ( 2 * s );
		ausWallIndices[ iIndexWall++ ] = ( ( s + 1 ) != uCylinderDetail ) ? ( 3 + ( 2 * s ) ) : 1;
		ausWallIndices[ iIndexWall++ ] = ( ( s + 1 ) != uCylinderDetail ) ? ( 2 + ( 2 * s ) ) : 0;
		ausWallIndices[ iIndexWall++ ] = ( 2 * s );
		ausWallIndices[ iIndexWall++ ] = ( ( s + 1 ) != uCylinderDetail ) ? ( 3 + ( 2 * s ) ) : 1;
	}


	if ( bWireframe )
	{
		for ( int i = 0; i < uCylinderDetail; i++ )
		{
			int iNext = ( uCylinderDetail + 1 + i ) % uCylinderDetail;
			Asura_Render::RenderLine( axVertsEnd0[i].m_xPos, axVertsEnd0[iNext].m_xPos, uEndColour1 );
			Asura_Render::RenderLine( axVertsEnd1[i].m_xPos, axVertsEnd1[iNext].m_xPos, uEndColour2 );
			Asura_Render::RenderLine( axVertsEnd0[i].m_xPos, axVertsEnd1[i].m_xPos, uSideColour );
		}
	}
	else
	{
		// Cylinder's bottom lid
		Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
			(
			D3DPT_TRIANGLELIST,
			0,
			uCylinderDetail + 1,
			uCylinderDetail,
			(void*)ausLidIndices,
			D3DFMT_INDEX16,
			(void*)axVertsEnd0,
			sizeof(D3DCylinderVertex)
			);

		// Cylinder's top lid
		Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
			(
			D3DPT_TRIANGLELIST,
			0,
			uCylinderDetail + 1,
			uCylinderDetail,
			(void*)ausLidIndices,
			D3DFMT_INDEX16,
			(void*)axVertsEnd1,
			sizeof(D3DCylinderVertex)
			);

		// Cylinder's wall
		u_int uCylinderWallDetail = 2 * uCylinderDetail;
		Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
			(
			D3DPT_TRIANGLELIST,
			0,
			uCylinderWallDetail,
			uCylinderWallDetail,
			(void*)ausWallIndices,
			D3DFMT_INDEX16,
			(void*)axVertsWall,
			sizeof(D3DCylinderVertex)
			);
	}

	D3DXMatrixTranslation( &matWorld, 0,0,0 );
	Asura_Direct3D::D3DDevice->SetTransform( D3DTS_WORLD, &matWorld );
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF );
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );
}


void Tools_Render::AddIconToBatch(const Asura_Vector_3& xPosition, int iTextureID, float fSize, u_int uColour )
{
	if(iTextureID == ASURA_TEXTURE_NONE)
	{
		Asura_Assert(0, "Not expecting to render an icon with no texture");
		return;
	}


	if(g_iNumBufferIcon == g_iIconBufferSize)
	{
		//Buffer full , so render what we have so far
		RenderBatchedIcons();
	}
	//Is this the first icon for this texture?
	if(!g_apxHeadOfIconList[iTextureID])
	{
		//Add to the list of icon textures used.
		if(g_iNumIconTexture == g_iMaxIconTexture)
		{
			//too many different textures  , so render what we have so far
			RenderBatchedIcons();
		}
		g_aiIconTextures[g_iNumIconTexture++] = iTextureID;
	}

	//get the next entry in the icon buffer
	Tools_RenderIcon* pxIcon = &g_axIconBuffer[g_iNumBufferIcon++];
	pxIcon->m_xPosition = xPosition;
	pxIcon->m_fSize = fSize;
	pxIcon->m_uColour = uColour;

	//add to the start of the linked list for this texture
	pxIcon->m_pxNext = g_apxHeadOfIconList[iTextureID];
	g_apxHeadOfIconList[iTextureID] = pxIcon;
}

void Tools_Render::RenderBatchedIcons()
{
	if(!g_iNumIconTexture) return;

	static bool s_bFirstTime = true;
	if(s_bFirstTime)
	{
		s_bFirstTime = false;
		
		//fill in some bits of the vertex and index buffers that are going to be fixed across all icons
		D3DGenericVertex* pxVert = g_axIconVerts;
		short* psInd = g_asIconInds;
		short sVertInd = 0;
		for(int i=0; i <g_iMaxIconPerBatch; i++, sVertInd+=4)
		{
			pxVert->U = 0.0f;
			pxVert->V = 0.0f;
			pxVert++;
			pxVert->U = 1.0f;
			pxVert->V = 0.0f;
			pxVert++;
			pxVert->U = 1.0f;
			pxVert->V = 1.0f;
			pxVert++;
			pxVert->U = 0.0f;
			pxVert->V = 1.0f;
			pxVert++;

			*psInd++ = sVertInd;
			*psInd++ = sVertInd+1;
			*psInd++ = sVertInd+2;
			*psInd++ = sVertInd+2;
			*psInd++ = sVertInd+3;
			*psInd++ = sVertInd;
		}
	}

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_GENERICVERTEX);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CCW);
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);

	const Asura_Vector_3* pxCameraPosition = Asura_Camera::GetPosition();
	const Asura_Matrix_3x3* pxCameraOrientation = Asura_Camera::GetOrientation();
	
	//deal with each of the icon textures
	for(int i=0; i<g_iNumIconTexture; i++)
	{
		int iTextureID = g_aiIconTextures[i];
		Asura_Render::CurrentStates.RequestTextureIndex(0,iTextureID);

		int iNumIcon = 0;
		D3DGenericVertex* pxVert = g_axIconVerts;
		
		for(Tools_RenderIcon* pxIcon = g_apxHeadOfIconList[iTextureID]; pxIcon; pxIcon = pxIcon->m_pxNext)
		{
			if(iNumIcon == g_iMaxIconPerBatch)
			{
				//Vertex and index buffers full , so render this lot
				Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
					(
					D3DPT_TRIANGLELIST,
					0,
					iNumIcon*4,
					iNumIcon*2,
					(void*)g_asIconInds,
					D3DFMT_INDEX16,
					(void*)g_axIconVerts,
					sizeof(D3DGenericVertex)
					);

				iNumIcon = 0;
				pxVert = &g_axIconVerts[0];

			}

			Asura_Vector_3 xVertPos[4];


			{
				Asura_Vector_3 xDisp = pxIcon->m_xPosition - *pxCameraPosition;
				float fDist = xDisp.Magnitude();

				if (fDist>20.0f) pxIcon->m_fSize *= fDist/20.0f;

			}

			Asura_Vector_3 deltaX = Asura_Vector_3(pxCameraOrientation->e11,pxCameraOrientation->e21,pxCameraOrientation->e31)*pxIcon->m_fSize;
			Asura_Vector_3 deltaY = Asura_Vector_3(pxCameraOrientation->e12,pxCameraOrientation->e22,pxCameraOrientation->e32)*pxIcon->m_fSize;

			xVertPos[0] = pxIcon->m_xPosition - deltaX - deltaY; 
			xVertPos[1] = pxIcon->m_xPosition + deltaX - deltaY;
			xVertPos[2] = pxIcon->m_xPosition + deltaX + deltaY;
			xVertPos[3] = pxIcon->m_xPosition - deltaX + deltaY;

			

			for (int i=0; i<4; i++)
			{
				pxVert[i].X = xVertPos[i].x;
				pxVert[i].Y = xVertPos[i].y;
				pxVert[i].Z = xVertPos[i].z;

				pxVert[i].Diffuse = pxIcon->m_uColour;
			}
			pxVert+=4;

			iNumIcon++;		
		}
		if(iNumIcon)
		{
			//render the last icons for this texture
			Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
				(
				D3DPT_TRIANGLELIST,
				0,
				iNumIcon*4,
				iNumIcon*2,
				(void*)g_asIconInds,
				D3DFMT_INDEX16,
				(void*)g_axIconVerts,
				sizeof(D3DGenericVertex)
				);
		}
		g_apxHeadOfIconList[iTextureID] = 0;

	}
	//reset the buffer positions
	g_iNumBufferIcon = 0;
	g_iNumIconTexture = 0;
}
