/**
*
* \file Tools_RenderModules.cpp
*
* Created by Kevin Lea - 04/04/2002 11:36:48
*
* Copyright (C) 2002 Rebellion, All Rights Reserved.
*
* \brief
*
**/

/// Kevin Lea - 04/04/2002 11:55:35 - very much a PC specific file,
/// but then the tools is PC specific really.

#include "Core/Asura.h"
#include "Animation/Asura_TextureAnimation.h"
#include "Rendering/Asura_Direct3D_EnvironmentRenderer.h"
#include "Rendering/Tools_RenderModules.h"
#include "Rendering/Asura_Direct3D.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Debugging/Asura_Assert.h"
#include "Rendering/Asura_Fog.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Mesh/Asura_Mesh.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Material_System.h"
#include "Debugging/Asura_Logfile.h"
#include "Materials/Tools_MaterialResponse_System.h"
#ifdef ASURA_USE_STREAMING
#include "Streaming/Asura_Streaming_System.h"
#endif

#include "Misc/Tools_Render_Options.h"
#include "Misc/Tools_Render.h"
#include "Misc/Tools_Adjacency.h"

#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Direct3D_Caps.h"

#include "Tools_System.h"

// Uncomment this to experiment with using D3DX mesh processing functions
// to generate tangents and binormals instead of our own. Only applies
// to "new" environment renderer
//#define TEST_D3DX_MESH_PROCESSING

#if defined TEST_D3DX_MESH_PROCESSING
	#include <d3dx9mesh.h>
	#include <dxerr9.h>
#endif

#if !defined ( ASURA_TARGET_SM3 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*--------------**
** Old Renderer **
**--------------*/

class Tools_RenderModule
{
	public:
		Tools_RenderModule()
		{
			IsValid = false;
			NeedsLightingUpdating = true; //false;	// VGP 16Feb2006
			NeedsMeshUpdating = false;
			IndexBuffer = 0;
			VertexBuffer = 0;
			PacketList = 0;
			ModuleIndex = -1;
		}
		~Tools_RenderModule()
		{
			if (IsValid)
			{
				if(VertexBuffer)
				{
					VertexBuffer->Release();
					VertexBuffer = 0;
				}
				if(IndexBuffer)
				{
					IndexBuffer->Release();
					IndexBuffer = 0;
				}
				delete [] PacketList;
				PacketList = 0;
				NumberOfPackets = 0;
			}
		}
		void Reset(void)
		{
			if (IsValid)
			{
				if(VertexBuffer)
				{
					VertexBuffer->Release();
					VertexBuffer = 0;
				}
				if(IndexBuffer)
				{
					IndexBuffer->Release();
					IndexBuffer = 0;
				}
				delete [] PacketList;
				PacketList = 0;
				NumberOfPackets = 0;
			}
			IsValid = false;
			NeedsLightingUpdating = false;
			NeedsMeshUpdating = false;
			//Asura_Assert(0,"");
		}

		// (SB - 11/12/06) Hook to override render states
		static void SetRenderStateOverride(Tools_RenderModule_Handler::RenderStateOverride* const pfnRenderStateOverride = NULL) { s_pfnRenderStateOverride = pfnRenderStateOverride; };

		Direct3D_VertexBuffer*	VertexBuffer;
		Direct3D_IndexBuffer*	IndexBuffer;

		int ModuleIndex;
		int NumberOfVertices;
		int NumberOfIndices;

		bool IsValid;
		bool NeedsLightingUpdating;
		bool NeedsMeshUpdating;

		struct Asura_Polygon_Packet* PacketList;
		int NumberOfPackets;

		void Initialise(const Asura_Mesh& mesh, const Asura_Vector_3& position,int moduleIndex);
		void Render(int iPass,bool drawBrightGreen);
		ToolsRenderModulePass GetRenderPassForPacket(int iPacket);


		void UpdateLighting(const Asura_Mesh& mesh);
		
		static float s_afShaderConstants[11][4];
		static void SetupShaderConstants();

		// (SB - 11/12/06) Setter for hook to override render states
		static Tools_RenderModule_Handler::RenderStateOverride* s_pfnRenderStateOverride;
};

// (SB - 11/12/06) Hook to override render states
Tools_RenderModule_Handler::RenderStateOverride* Tools_RenderModule::s_pfnRenderStateOverride = NULL;

static u_int GetDiscoColour(const Asura_Vector_3& xNormal)
{
	u_int uR = 127 + (u_int)(100*xNormal.x);
	u_int uG = 127 + (u_int)(100*xNormal.y);
	u_int uB = 127 + (u_int)(100*xNormal.z);


	uR = Asura_Maths::ClampToRange(uR,(u_int)0,(u_int)255);		
	uG = Asura_Maths::ClampToRange(uG,(u_int)0,(u_int)255);	
	uB = Asura_Maths::ClampToRange(uB,(u_int)0,(u_int)255);

	return (uR<<16)|(uG<<8)|uB;	
}

void Tools_RenderModule::Initialise(const Asura_Mesh& mesh, const Asura_Vector_3& position,int moduleIndex)
{
	NumberOfVertices = 0;
	NumberOfIndices = 0;
	ModuleIndex = moduleIndex;


	Asura_Polygon_Packet xConstructionPacketList[ASURA_MAX_NO_OF_MATERIALS+1];
	NumberOfPackets = 0;

	int* piPolygonToPacket = new int[mesh.NumberOfPolygons];

	//Scan through all the polygons and count the number of indices required for each material
	for(int p=0;p<mesh.NumberOfPolygons;p++)
	{
		int iNumVerts = mesh.PolygonDataPtr[p].NumberOfVertices();
		NumberOfVertices += iNumVerts;

		int iFlags = mesh.PolygonFlagDataPtr ? mesh.PolygonFlagDataPtr[p].Flags : 0;

		if((iFlags & AsuraPolygonFlag_DoubleSided) && !Tools_Render_Options::NoBackFaceCull)
		{
			//extra verts needed for double sided polygons since the normals need to be flipped
			NumberOfVertices += iNumVerts;
		}

		if(ModuleIndex==-1 || !Tools_Render_Options::IsHidden(ModuleIndex,p))
		{
			int iMaterialID = mesh.TextureDataPtr[p].m_iMaterialID;
			//get the packet for this material
			int iPacket = 0;
			for(iPacket = 0;iPacket<NumberOfPackets;iPacket++)
			{
				if(xConstructionPacketList[iPacket].m_iMaterialID == iMaterialID) break;
			}
			if(iPacket == NumberOfPackets)
			{
				//need a new packet then
				xConstructionPacketList[NumberOfPackets].m_iMaterialID = iMaterialID;
				xConstructionPacketList[NumberOfPackets].NumberOfIndices = 0;
				NumberOfPackets++;
			}
			piPolygonToPacket[p] = iPacket;

			//determine number of indices for polygon
			int iNumIndices = (iNumVerts == 3) ? 3 : 6;
			if((iFlags & AsuraPolygonFlag_DoubleSided) && !Tools_Render_Options::NoBackFaceCull)
			{
				iNumIndices *= 2;
			}
			xConstructionPacketList[iPacket].NumberOfIndices+=iNumIndices;
			NumberOfIndices += iNumIndices;
		}
		else
		{
			piPolygonToPacket[p] = -1;
		}
	}

	if(!NumberOfIndices)
	{
		//nothing visible in module
		delete [] piPolygonToPacket;
		IsValid = true;
		return;
	}

	//Initialise packets now we know how many we need
	PacketList = new Asura_Polygon_Packet[NumberOfPackets];
	int iIndexStart = 0;
	for (int i=0; i<NumberOfPackets; i++)
	{
		PacketList[i].m_iMaterialID = xConstructionPacketList[i].m_iMaterialID;
		PacketList[i].NumberOfIndices = 0;
		PacketList[i].IndexStart = iIndexStart;
		iIndexStart += xConstructionPacketList[i].NumberOfIndices;
	}

	Asura_Assertf(NumberOfVertices<=65536,("Too many triangles in %s for renderer.\nIt has %d triangles , the limit here is 21845 (65536/3)",mesh.Name.GetString(),mesh.NumberOfTriangles));


	//allocate vertex and index buffers
	Asura_Direct3D::D3DDevice->CreateVertexBuffer
	(
		NumberOfVertices*sizeof(D3DGenericVertex),
		D3DUSAGE_WRITEONLY,
		D3DFVF_GENERICVERTEX,
		D3DPOOL_MANAGED,
		&VertexBuffer,
		0
	);

	Asura_Direct3D::D3DDevice->CreateIndexBuffer
	(
		NumberOfIndices*sizeof(unsigned short),  
		D3DUSAGE_WRITEONLY,
		D3DFMT_INDEX16,
		D3DPOOL_MANAGED,
		&IndexBuffer,
		0
	);

	D3DGenericVertex* vertexdata;
// RICHARDS		VertexBuffer->Lock(0,sizeof(D3DGenericVertex)*NumberOfVertices,(BYTE**)&vertexdata,D3DLOCK_DISCARD);
	VertexBuffer->Lock(0,sizeof(D3DGenericVertex)*NumberOfVertices,(void**)&vertexdata,0);
	Asura_Assert( NULL != vertexdata, "(RMS) Failed to lock vertex buffer" );

	unsigned short* indexdata;
// RICHARDS		IndexBuffer->Lock(0,sizeof(unsigned short)*NumberOfIndices,(BYTE**)&indexdata,D3DLOCK_DISCARD);
	IndexBuffer->Lock(0,sizeof(unsigned short)*NumberOfIndices,(void**)&indexdata,0);
	Asura_Assert( NULL != indexdata, "(RMS) Failed to lock index buffer" );


	//go through polygons again and fill in the buffers
	bool bUseLighting = Tools_Render::LightingIsOn();
	bool bUnlitAsPainted = Tools_System::IsInVertexPaintingMode();
	bool bDiscoLights = Tools_Render_Options::GetUseDiscoLights();
	
	int *piPacket = piPolygonToPacket;
	unsigned short usVertIndex = 0;
	for(int p=0;p<mesh.NumberOfPolygons;p++,piPacket++)
	{
		int iNumVerts = mesh.PolygonDataPtr[p].NumberOfVertices();
		int iFlags = mesh.PolygonFlagDataPtr ? mesh.PolygonFlagDataPtr[p].Flags : 0;
		bool bDoubleSided = (iFlags & AsuraPolygonFlag_DoubleSided) && !Tools_Render_Options::NoBackFaceCull;


		//If double sided we will need to do 2 passes of adding the vertices , with
		//normals and vertex ordering being reversed for the second side of the polygons
		//RAR 03/09/2007
		for( int iMirror=0;iMirror < (bDoubleSided ? 2 : 1);iMirror++)
		{
			for (int v=0; v<iNumVerts; v++)
			{
				int vID = mesh.PolygonDataPtr[p].VertexID[v];

				Asura_Vector_3 pos = mesh.VertexDataPtr[vID].Position + position;
				Asura_Vector_3& n = mesh.NormalsDataPtr[p].VertexNormal[v];

				vertexdata->X = pos.x;
				vertexdata->Y = pos.y;
				vertexdata->Z = pos.z;

				vertexdata->Normal = n;
				if(iMirror) vertexdata->Normal = -vertexdata->Normal;

				u_int iAlpha = (int)(mesh.TextureDataPtr[p].m_afAlpha[v]*255.0f);
				u_int uColour;
				if(bDiscoLights)
				{
					uColour = GetDiscoColour(n);	
				}
				else if ( bUseLighting )
				{
					uColour = mesh.PrelightingDataPtr[p].Colour[v] & 0x00ffffff;
				}
				else if ( bUnlitAsPainted )
				{
					uColour = mesh.m_pxPaintedColourDataPtr[ p ].Colour[ v ];
				}
				else
				{
					uColour = 0x808080;
				}

				vertexdata->Diffuse = uColour | (iAlpha<<24);
				

				vertexdata->U = mesh.TextureDataPtr[p].U[0][v];
				vertexdata->V = mesh.TextureDataPtr[p].V[0][v];
					
				vertexdata->U2 = mesh.TextureDataPtr[p].U[1][v];
				vertexdata->V2 = mesh.TextureDataPtr[p].V[1][v];
				vertexdata++;
			}

			if(*piPacket == -1)
			{
				usVertIndex = (u_short)(usVertIndex + iNumVerts);
				continue;//hidden polygon
			}

			Asura_Polygon_Packet* pxPacket = &PacketList[*piPacket];

			//get the point to insert the indeces at
			u_short * pusIndexData = &indexdata[pxPacket->NumberOfIndices+pxPacket->IndexStart];

			if (iNumVerts == 3)
			{
				pxPacket->NumberOfIndices += 3;
				if(iMirror)
				{
					*pusIndexData++ = usVertIndex+2;
					*pusIndexData++ = usVertIndex+1;
					*pusIndexData++ = usVertIndex+0;
					usVertIndex += 3;
				}
				else
				{
					*pusIndexData++ = usVertIndex+0;
					*pusIndexData++ = usVertIndex+1;
					*pusIndexData++ = usVertIndex+2;
					usVertIndex += 3;
				}

			}
			else
			{
				pxPacket->NumberOfIndices += 6;
				if(iMirror)
				{
					*pusIndexData++ = usVertIndex+2;
					*pusIndexData++ = usVertIndex+1;
					*pusIndexData++ = usVertIndex+0;
					*pusIndexData++ = usVertIndex+0;
					*pusIndexData++ = usVertIndex+3;
					*pusIndexData++ = usVertIndex+2;
					usVertIndex += 4;
				}
				else
				{
					*pusIndexData++ = usVertIndex+0;
					*pusIndexData++ = usVertIndex+1;
					*pusIndexData++ = usVertIndex+2;
					*pusIndexData++ = usVertIndex+2;
					*pusIndexData++ = usVertIndex+3;
					*pusIndexData++ = usVertIndex+0;
					usVertIndex += 4;
				}
			}
		}
	}
	delete [] piPolygonToPacket;

	Asura_Assert(usVertIndex == NumberOfVertices, "Unexpected number of vertices. This will probably crash now.");

	VertexBuffer->Unlock();
	IndexBuffer->Unlock();

	IsValid = true;

}

void Tools_RenderModule::UpdateLighting(const Asura_Mesh& mesh)
{
	if(!VertexBuffer)
	{
		NeedsLightingUpdating = false;
		return;
	}

	D3DGenericVertex* vertexdata;
	VertexBuffer->Lock(0,sizeof(D3DGenericVertex)*NumberOfVertices,(void**)&vertexdata,0);

	bool bUseLighting = Tools_Render::LightingIsOn();
	bool bDiscoLights = Tools_Render_Options::GetUseDiscoLights();
	bool bUnlitAsPainted = Tools_System::IsInVertexPaintingMode();
	
	for (int p=0; p<mesh.NumberOfPolygons; p++)
	{
		int noOfVerts = mesh.PolygonDataPtr[p].NumberOfVertices();

		int iFlags = mesh.PolygonFlagDataPtr ? mesh.PolygonFlagDataPtr[p].Flags : 0;
		bool bDoubleSided = (iFlags & AsuraPolygonFlag_DoubleSided) && !Tools_Render_Options::NoBackFaceCull;

		for(int iMirror = 0; iMirror < (bDoubleSided ? 2 : 1); iMirror++)
		{
		 
			for (int v=0; v<noOfVerts; v++)
			{
				u_int iAlpha = (int)(mesh.TextureDataPtr[p].m_afAlpha[v]*255.0f);
				u_int uColour;
				if(bDiscoLights)
				{
					uColour = GetDiscoColour(mesh.NormalsDataPtr[p].VertexNormal[v]);
				}
				else if ( bUseLighting )
				{
					uColour = mesh.PrelightingDataPtr[p].Colour[v] & 0x00ffffff;
				}
				else if ( bUnlitAsPainted )
				{
					uColour = mesh.m_pxPaintedColourDataPtr[ p ].Colour[ v ];
				}
				else
				{
					uColour = 0x808080;
				}

				vertexdata->Diffuse = uColour | (iAlpha<<24);
				vertexdata++;
			}
		}
	}
	VertexBuffer->Unlock();
	NeedsLightingUpdating = false;
}

ToolsRenderModulePass Tools_RenderModule::GetRenderPassForPacket(int iPacket)
{
	int iMaterialID = PacketList[iPacket].m_iMaterialID;

	if(Asura_Material_System::IsMaterialDontRender(iMaterialID))
	{
		return TOOLS_RENDERMODULEPASS_NONE;
	}

	if(Asura_Material_System::IsMaterialPS2AdditiveAlpha(iMaterialID))
	{
		return TOOLS_RENDERMODULEPASS_ADDALPHA;
	}
	if ( Asura_Material_System::IsMaterialAlphaDecal(iMaterialID) )
	{
		return TOOLS_RENDERMODULEPASS_ALPHADECAL;
	}
	if(Asura_Material_System::IsMaterialTransparent(iMaterialID))
	{
		return TOOLS_RENDERMODULEPASS_TRANSPARENT;
	}
	return TOOLS_RENDERMODULEPASS_OPAQUE;
}

void Tools_RenderModule::Render( int iPass,bool bDrawBrightGreen )
{
	if(!IndexBuffer || !VertexBuffer) return;

	if(Asura_Render::GetWireframe() || bDrawBrightGreen)
	{
		//only do stuff on the first pass for wireframe/bright green
		if(iPass>0) return;
	}
	else
	{
		//check if there are any packets for this pass
		bool bNeedPass = false;
		
		for(int i=0; i<NumberOfPackets; i++)
		{
			if(GetRenderPassForPacket(i) == iPass)
			{
				bNeedPass = true;
				break;
			}
		}
		if(!bNeedPass) return;
	}

	Asura_Render::CurrentStates.RequestAlphaTest(false);
	// (MH - 30.07.2002) no BFC option
    if (Tools_Render_Options::NoBackFaceCull)
    {
		Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
    }
    else
    {
		Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);
    }
	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);

	Asura_Direct3D::D3DDevice->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(1,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(1,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(2,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(2,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(3,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(3,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	
	HRESULT h;

	h=Asura_Direct3D::D3DDevice->SetStreamSource(0,VertexBuffer,0,sizeof(D3DGenericVertex));
	//Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_GENERICVERTEX);
	h=Asura_Direct3D::D3DDevice->SetIndices(IndexBuffer);

	if(Asura_Render::GetWireframe() || bDrawBrightGreen)
	{
		if(iPass == 0)
		{
			Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader( D3DFVF_GENERICVERTEX );
        	Asura_Render::CurrentStates.RequestTextureOpMode( bDrawBrightGreen ? ASURA_TEXTUREOP_BRIGHTGREEN : ASURA_TEXTUREOP_WHITE );
			if(!bDrawBrightGreen)
			{
				Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, Tools_Render_Options::GetWireframeColour(false,ModuleIndex));
			}
			h=Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumberOfVertices,0,NumberOfIndices/3);
			Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
			Asura_Render::AddToTrianglesRendered(NumberOfIndices/3);
		}
		return;
	}

	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);

	Asura_Direct3D::D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
	Asura_Direct3D::D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );

	D3DXMATRIX xMat;
	D3DXMatrixIdentity(&xMat);
	Asura_Direct3D::D3DDevice->SetTransform(D3DTS_TEXTURE0, &xMat);
	Asura_Direct3D::D3DDevice->SetTransform(D3DTS_TEXTURE1, &xMat);
	Asura_Direct3D::D3DDevice->SetTransform(D3DTS_TEXTURE2, &xMat);
	Asura_Direct3D::D3DDevice->SetTransform(D3DTS_TEXTURE3, &xMat);
	

	if(iPass == TOOLS_RENDERMODULEPASS_OPAQUE || iPass == TOOLS_RENDERMODULEPASS_TRANSPARENT)
	{
		for(int i=0; i<NumberOfPackets; i++)
		{
			if(GetRenderPassForPacket(i) != iPass) continue;
			
			
			// Select a vertex shader
			if(Asura_Fog::IsFogRenderable())
			{
				Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
				if(Asura_Fog::IsGraphFogEnabled())
				{
					Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE_GRAPHFOG);
				}
				else
				{
					Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE_LINEARFOG);
				}
			}
			else
			{
				Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE);
			}


			// Pass on vertex shader constants
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(0, &s_afShaderConstants[0][0], 11);

			//texture animation
			Asura_TextureAnimationOutput xTextureAnim;
			Asura_Material_System::GetTextureAnimationState(PacketList[i].m_iMaterialID,0,&xTextureAnim);

			float afAnimConstants[4] = {xTextureAnim.m_fUOffset,xTextureAnim.m_fVOffset,0,0};
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(11, afAnimConstants, 1);

			// pull in textures
			Asura_Render::CurrentStates.RequestTextureIndex(0,Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID));
			Asura_Render::CurrentStates.RequestTextureIndex(1, Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 1));
			Asura_Render::CurrentStates.RequestTextureIndex(2, ASURA_TEXTURE_NONE);
			if( ( Tools_Render::LightingIsOn() || Tools_System::IsInVertexPaintingMode() ) && Asura_Material_System::IsMaterialSelfIlluminating(PacketList[i].m_iMaterialID))
			{
				Asura_Render::CurrentStates.RequestTextureIndex(3, Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 0));
			}
			else
			{
				Asura_Render::CurrentStates.RequestTextureIndex(3, ASURA_TEXTURE_NONE);
			}

			// DLH - 28 June 2006 15:08:24 - Invalidate the cached mode
			Asura_Render::CurrentStates.RequestTextureOpMode( ASURA_TEXTUREOP_CUSTOM);

			// set up render states
			Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
			Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
			
			if(ASURA_TEXTURE_NONE != Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 1))
			{

				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

				// blenddiffusealpha this texture in
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_BLENDDIFFUSEALPHA);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_BLENDDIFFUSEALPHA);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
			}
			else
			{
				//No second texture , so blend in the vertex alpha for transparency or whatever
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

				// just pass through to the next stage
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);

				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}

			if ( Tools_Render::LightingIsOn() || Tools_System::IsInVertexPaintingMode() )
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLOROP,   D3DTOP_MODULATE2X);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}
			else
			{
				// just pass through to the next stage
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);

				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}

			if(Asura_Material_System::IsMaterialSelfIlluminating(PacketList[i].m_iMaterialID))
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLOROP,   D3DTOP_MODULATEALPHA_ADDCOLOR);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_TEXTURE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
			}
			else
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLOROP,	 D3DTOP_DISABLE);
			}

			// (AAM - 18.10.2004) If specular, ignore all that and use a PS instead
			if( Asura_Material_System::IsMaterialSpecularSharp( PacketList[i].m_iMaterialID ) )
			{
				Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_TOOLS_SPECULARSHARP );
			}
			else if( Asura_Material_System::IsMaterialSpecularSoft( PacketList[i].m_iMaterialID ) )
			{
				Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_TOOLS_SPECULARSOFT );
			}
			else
			{
				Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_NONE );
			}

			if(Asura_Material_System::IsMaterialTransparent(PacketList[i].m_iMaterialID))
			{
				Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
				Asura_Render::CurrentStates.RequestAlphaTest(true);
				Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
				Asura_Render::CurrentStates.RequestZBufferEnabled(true);
			}else{
				Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
				Asura_Render::CurrentStates.RequestAlphaTest(false);
				Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
				Asura_Render::CurrentStates.RequestZBufferEnabled(true);
			}

			// (SB - 06/12/06) Call render state override hook
			if (s_pfnRenderStateOverride)
			{
				s_pfnRenderStateOverride();
			}

			if (PacketList[i].NumberOfIndices)
			{
				h=Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumberOfVertices,PacketList[i].IndexStart,PacketList[i].NumberOfIndices/3);

			}			
		}
	}
	else if(iPass==TOOLS_RENDERMODULEPASS_ALPHADECAL)
	{
		for(int i=0; i<NumberOfPackets; i++)
		{
			if ( TOOLS_RENDERMODULEPASS_ALPHADECAL != GetRenderPassForPacket(i) ) continue;
			
			// Select a vertex shader

			{
				if(Asura_Fog::IsFogRenderable())
				{
					Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
					if(Asura_Fog::IsGraphFogEnabled())
					{
						Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE_GRAPHFOG);
					}
					else
					{
						Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE_LINEARFOG);
					}
				}
				else
				{
					Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE);
				}

				//texture animation
				Asura_TextureAnimationOutput xTextureAnim;
				Asura_Material_System::GetTextureAnimationState(PacketList[i].m_iMaterialID,0,&xTextureAnim);

				float afAnimConstants[4] = {xTextureAnim.m_fUOffset,xTextureAnim.m_fVOffset,0,0};
				Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(11, afAnimConstants, 1);

				// Pass on vertex shader constants
				Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(0, &s_afShaderConstants[0][0], 11);
			}

			// pull in textures
			Asura_Render::CurrentStates.RequestTextureIndex(0,Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID));
			Asura_Render::CurrentStates.RequestTextureIndex(1, Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 1));
			Asura_Render::CurrentStates.RequestTextureIndex(2, ASURA_TEXTURE_NONE);
			if( ( Tools_Render::LightingIsOn() || Tools_System::IsInVertexPaintingMode() ) && Asura_Material_System::IsMaterialSelfIlluminating(PacketList[i].m_iMaterialID))
			{
				Asura_Render::CurrentStates.RequestTextureIndex(3, Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 0));
			}
			else
			{
				Asura_Render::CurrentStates.RequestTextureIndex(3, ASURA_TEXTURE_NONE);
			}

			// set up render states
			Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
			Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
			
			if(ASURA_TEXTURE_NONE != Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID, 1))
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

				// blenddiffusealpha this texture in
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_BLENDDIFFUSEALPHA);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_BLENDDIFFUSEALPHA);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
			}
			else
			{
				//No second texture , so blend in the vertex alpha for transparency or whatever
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
				Asura_Direct3D::D3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

				// just pass through to the next stage
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);

				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}

			if ( Tools_Render::LightingIsOn() || Tools_System::IsInVertexPaintingMode() )
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLOROP,   D3DTOP_MODULATE2X);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}
			else
			{
				// just pass through to the next stage
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);

				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
			}

			if(Asura_Material_System::IsMaterialSelfIlluminating(PacketList[i].m_iMaterialID))
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLOROP,   D3DTOP_MODULATEALPHA_ADDCOLOR);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_TEXTURE);

				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
			}
			else
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(3, D3DTSS_COLOROP,	 D3DTOP_DISABLE);
			}

			{
				Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_NONE );
			}

			
			{
				Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
				Asura_Render::CurrentStates.RequestAlphaTest(true);
				Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
			}

			if (PacketList[i].NumberOfIndices)
			{
				h=Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumberOfVertices,PacketList[i].IndexStart,PacketList[i].NumberOfIndices/3);

			}
		}
	}
	else if(iPass == TOOLS_RENDERMODULEPASS_ADDALPHA)
	{

		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);

		Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_NONE);
		Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE);


		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS_2X);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		Asura_Render::CurrentStates.RequestZBufferEnabled(true);
		Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);

		
		for(int i=0; i<NumberOfPackets; i++)
		{
			if(GetRenderPassForPacket(i) != TOOLS_RENDERMODULEPASS_ADDALPHA) continue;

			Asura_Render::CurrentStates.RequestTextureIndex(0,Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID));

			//texture animation
			Asura_TextureAnimationOutput xTextureAnim;
			Asura_Material_System::GetTextureAnimationState(PacketList[i].m_iMaterialID,0,&xTextureAnim);

			float afAnimConstants[4] = {xTextureAnim.m_fUOffset,xTextureAnim.m_fVOffset,0,0};
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(11, afAnimConstants, 1);

			// Pass on vertex shader constants
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(0, &s_afShaderConstants[0][0], 11);

			// (SB - 06/12/06) Call render state override hook
			if (s_pfnRenderStateOverride)
			{
				s_pfnRenderStateOverride();
			}

			if(PacketList[i].NumberOfIndices)
			{
				Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, NumberOfVertices, PacketList[i].IndexStart, PacketList[i].NumberOfIndices/3);
			}
		}
	}

	//deal with reflective versions of each pass. RAR 31/10/2005
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
		Asura_Render::CurrentStates.RequestTextureIndex(1, Asura_Render::GetReflectanceMap());
		Asura_Render::CurrentStates.RequestZBufferEnabled(true);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);

		Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_TOOLS_MODULE_REFLECTIVE);
		Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_ENVIRONMENT_REFLECTIVE);
		//Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_BRIGHTGREEN);

		D3DXMATRIX xMatView, xMatProj, xMatRefl, xMatInvView;
		Asura_Direct3D::D3DDevice->GetTransform(D3DTS_VIEW, &xMatView);
		Asura_Direct3D::D3DDevice->GetTransform(D3DTS_VIEW, &xMatInvView);
		xMatInvView._14 = xMatInvView._24 = xMatInvView._34 = 0.0f;
		xMatInvView._41 = xMatInvView._42 = xMatInvView._43 = 0.0f;
		D3DXMatrixInverse(&xMatInvView, NULL, &xMatInvView);
		//Asura_Direct3D::D3DDevice->GetTransform(D3DTS_PROJECTION, &xMatProj);
		//D3DXMatrixMultiply(&xMatProj, &xMatView, &xMatProj);
		xMatRefl._11 = 0.5f; xMatRefl._12 = 0.0f; xMatRefl._13 = 0.0f; xMatRefl._14 = 0.0f; 
		xMatRefl._21 = 0.0f; xMatRefl._22 = -0.5f; xMatRefl._23 = 0.0f; xMatRefl._24 = 0.0f; 
		xMatRefl._31 = 0.0f; xMatRefl._32 = 0.0f; xMatRefl._33 = 1.0f; xMatRefl._34 = 0.0f; 
		xMatRefl._41 = 0.5f; xMatRefl._42 = 0.5f; xMatRefl._43 = 0.0f; xMatRefl._44 = 1.0f; 
		D3DXMatrixMultiply(&xMatRefl, &xMatInvView, &xMatRefl);

		D3DXMatrixTranspose(&xMatView, &xMatView);
		//D3DXMatrixTranspose(&xMatProj, &xMatProj);
		D3DXMatrixTranspose(&xMatRefl, &xMatRefl);
		//Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(0, (float*)&xMatProj, 4);
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(4, (float*)&xMatView, 4);
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(8, (float*)&xMatRefl, 2);
		float fConstants[4] = { 1.f, 0.f, 0.f, 0.f };
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(10, fConstants, 1);

		// (SB - 06/12/06) Call render state override hook
		if (s_pfnRenderStateOverride)
		{
			s_pfnRenderStateOverride();
		}
			
		for(int i=0; i<NumberOfPackets; i++)
		{
			if(!Asura_Material_System::IsMaterialReflective(PacketList[i].m_iMaterialID)) continue;

			if(iPass != GetRenderPassForPacket(i)) continue;

			Asura_Render::CurrentStates.RequestTextureIndex(0,Asura_Material_System::GetTextureID(PacketList[i].m_iMaterialID));

			if(PacketList[i].NumberOfIndices)
			{
				Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, NumberOfVertices, PacketList[i].IndexStart, PacketList[i].NumberOfIndices/3);
			}
		}
	}


	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);

	// turn off fog if we have to...
	if (Asura_Fog::IsFogRenderable())
	{
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
	}


	Asura_Render::CurrentStates.RequestTextureIndex(0, ASURA_TEXTURE_NONE);
	Asura_Render::CurrentStates.RequestTextureIndex(1, ASURA_TEXTURE_NONE);
	Asura_Render::CurrentStates.RequestTextureIndex(2, ASURA_TEXTURE_NONE);
	Asura_Render::CurrentStates.RequestTextureIndex(3, ASURA_TEXTURE_NONE);

	Asura_Direct3D::D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
	Asura_Direct3D::D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

	Asura_Direct3D::D3DDevice->SetTextureStageState(0,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(1,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(1,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(2,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(2,D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(3,D3DTSS_COLOROP, D3DTOP_DISABLE);
	Asura_Direct3D::D3DDevice->SetTextureStageState(3,D3DTSS_ALPHAOP, D3DTOP_DISABLE);

	//change render states a couple of times to get around the fact we have changed a lot of things
	//behind Asura's back. RAR 24/05/04
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS_2X);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader( D3DFVF_GENERICVERTEX );
	Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_NONE );

	if(iPass==0)
	{
		//just count all the triangles on the first pass ,since each triangle should
		//go to exactly one pass
		Asura_Render::AddToTrianglesRendered(NumberOfIndices/3);
	}
}

// Static data

// (SB - 28/11/06) Setup hooks for additional render passes
Tools_RenderModule_Handler::InitialiseHook* Tools_RenderModule_Handler::m_pfnInitialiseHook = NULL;
Tools_RenderModule_Handler::ShutdownHook* Tools_RenderModule_Handler::m_pfnShutdownHook = NULL;
Tools_RenderModule_Handler::RenderHook* Tools_RenderModule_Handler::m_pfnRenderHook = NULL;

Tools_RenderModule_Handler::Tools_RenderModule_Handler()
{
	m_iNumberOfRenderModules = 0;
	m_pxRenderModules = 0;
}

Tools_RenderModule_Handler::~Tools_RenderModule_Handler()
{
	Shutdown();
}

// (SB - 28/11/06) Setup hooks for additional render passes
void Tools_RenderModule_Handler::SetHooks(InitialiseHook* const pfnInitialiseHook, ShutdownHook* const pfnShutdownHook, RenderHook* const pfnRenderHook)
{
	m_pfnInitialiseHook = pfnInitialiseHook;
	m_pfnShutdownHook = pfnShutdownHook;
	m_pfnRenderHook = pfnRenderHook;
}

void Tools_RenderModule_Handler::Initialise()
{
	m_iNumberOfRenderModules = Asura_Mesh_System::GetNumberOfMeshes();
	Asura_Assert(m_iNumberOfRenderModules!=0,"No environment?");

	m_pxRenderModules = new Tools_RenderModule[m_iNumberOfRenderModules];

	// (SB - 28/11/06) Call hook to initialise any extra render passes (needs to be here so the meshes exist)
	if (m_pfnInitialiseHook)
	{
		m_pfnInitialiseHook();
	}
}

void Tools_RenderModule_Handler::Shutdown()
{
	// (SB - 28/11/06) Call hook to shutdown any extra render passes - initialise after this class, shutdown before
	if (m_pfnShutdownHook)
	{
		m_pfnShutdownHook();
	}

	delete [] m_pxRenderModules;
	m_pxRenderModules = 0;
	m_iNumberOfRenderModules = 0;
}

// (SB - 11/12/06) No external access to Tools_RenderModule so we need to forward the call to it from here
#if !defined(ASURA_TARGET_SM3)
void Tools_RenderModule_Handler::SetRenderStateOverride(Tools_RenderModule_Handler::RenderStateOverride* const pfnRenderStateOverride /*= NULL*/)
{
	Tools_RenderModule::SetRenderStateOverride(pfnRenderStateOverride);
}
#endif

void Tools_RenderModule_Handler::RenderModule(int iPass,const Asura_Mesh& mesh, const Asura_Vector_3& position, bool drawBrightGreen,int moduleIndex)
{
	int meshIndex = mesh.Index;

	Asura_Assert(meshIndex<m_iNumberOfRenderModules,"");

	Tools_RenderModule& renderModule = m_pxRenderModules[meshIndex];


	bool bNeedsMeshUpdating = renderModule.NeedsMeshUpdating;
	if (renderModule.NeedsMeshUpdating)
	{
		renderModule.Reset();
	}

	/// Kevin Lea - 04/04/2002 14:49:15 - if not valid, initialise
	if (!renderModule.IsValid)
	{
		renderModule.Initialise(mesh,position,moduleIndex);
	}

	if (renderModule.NeedsLightingUpdating)
	{
		renderModule.UpdateLighting(mesh);
	}

	Tools_RenderModule::SetupShaderConstants();

	/// Kevin Lea - 04/04/2002 14:49:24 - let's draw it
	renderModule.Render(iPass,drawBrightGreen);

	// (SB - 28/11/06) Hook to add another rendering pass to the tools renderer
	if (m_pfnRenderHook)
	{
		m_pfnRenderHook(iPass, mesh, position, drawBrightGreen, moduleIndex, bNeedsMeshUpdating);
	}
}

void Tools_RenderModule_Handler::UpdateModuleMesh(int meshIndex)
{
	Asura_Assert(meshIndex<m_iNumberOfRenderModules,"");
	if(meshIndex >= m_iNumberOfRenderModules) return;

	Tools_RenderModule& renderModule = m_pxRenderModules[meshIndex];
	renderModule.NeedsMeshUpdating = true;
}

void Tools_RenderModule_Handler::UpdateModuleLighting(int meshIndex)
{
	Asura_Assert(meshIndex<m_iNumberOfRenderModules,"");
	if(meshIndex >= m_iNumberOfRenderModules) return;

	Tools_RenderModule& renderModule = m_pxRenderModules[meshIndex];
	renderModule.NeedsLightingUpdating = true;
}


void Tools_RenderModule_Handler::UpdateAllModuleLighting()
{
	for(int i=0;i<m_iNumberOfRenderModules;i++)
	{
		m_pxRenderModules[i].NeedsLightingUpdating = true;
	}
}

#if defined(ASURA_USE_STREAMING)
Asura_TemplateHashTree<Tools_RenderModule_Handler>	Tools_RenderModule_System::s_xHandlerTree;
Tools_RenderModule_Handler*						Tools_RenderModule_System::s_pxCurrentHandler	= NULL;
#else
Tools_RenderModule_Handler		Tools_RenderModule_System::s_xHandler;
#endif	// defined(ASURA_USE_STREAMING)


void Tools_RenderModule_System::Initialise(void)
{
	#ifdef ASURA_USE_STREAMING
	s_xHandlerTree.Traverse(InitialiseCallback);
	#else
	s_xHandler.Initialise();
	#endif
}

#ifdef ASURA_USE_STREAMING
void Tools_RenderModule_System::InitialiseCallback(Asura_Hash_ID uHash,Tools_RenderModule_Handler* pxHandler)
{
	Asura_Streaming_Zone*	pxPreviousZone	= Asura_Streaming_System::GetCurrentZone();

	//make sure this zone is the current one before intialising it
	Asura_Streaming_System::SetCurrentZone(uHash);

	pxHandler->Initialise();

	//restore current zone
	Asura_Streaming_System::SetCurrentZone(pxPreviousZone->GetHashID());

}
#endif //ASURA_USE_STREAMING

void Tools_RenderModule_System::Shutdown(void)
{
	#ifdef ASURA_USE_STREAMING
	s_xHandlerTree.Traverse(ShutdownCallback);
	#else
	s_xHandler.Shutdown();
	#endif
}

#ifdef ASURA_USE_STREAMING
void Tools_RenderModule_System::ShutdownCallback(Asura_Hash_ID uHash,Tools_RenderModule_Handler* pxHandler)
{
	pxHandler->Shutdown();
}
#endif //ASURA_USE_STREAMING

#if defined(ASURA_USE_STREAMING)
Tools_RenderModule_Handler* Tools_RenderModule_System::CreateHandler (const Asura_Hash_ID uHash)
{
	Tools_RenderModule_Handler*	pxHandler	= s_xHandlerTree.GetDataByHash(uHash);

	// Do we already have this handler?
	if(pxHandler == NULL)
	{
		// No - create one...
		pxHandler	= s_xHandlerTree.AddNode(uHash);
	}
	else
	{
		// Yes - this shouldn't happen...
		Asura_Assert(false, "Already have a texture animation handler with this hash-ID.");
	}

	// Return the handler...
	return(pxHandler);
}

bool Tools_RenderModule_System::DestroyHandler (const Asura_Hash_ID uHash)
{
	// Is this the current handler?
	if(s_pxCurrentHandler == s_xHandlerTree.GetDataByHash(uHash))
	{
		s_pxCurrentHandler	= NULL;
	}

	// Call the delete function...
	return(s_xHandlerTree.DeleteNodeByHash(uHash));
}

void Tools_RenderModule_System::SetCurrentHandler(Tools_RenderModule_Handler* pxHandler)
{
	if(pxHandler != s_pxCurrentHandler)
	{
		s_pxCurrentHandler = pxHandler;
	}
}
#endif	// defined(ASURA_USE_STREAMING)





#else // defined ( ASURA_TARGET_SM3 )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*--------------**
** New Renderer **
**--------------*/



#include "Light/Asura_Light.h"
#include "Light/Asura_Photon.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Direct3D_EnvironmentRenderer.h"
#include "Lighting/Tools_EnvironmentalLightTransport.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "MeshProcessing/Tools_TangentSpace.h"
//#include "Visibility/Asura_Visibility_System.h"

#include "Rendering/Asura_Direct3D_Effects_System.h"



static Tools_RenderModule* RenderModules = 0;
static int NumberOfRenderModules = 0;

void Tools_RenderModule_System::Initialise(void)
{
	NumberOfRenderModules = Asura_Mesh_System::GetNumberOfMeshes();
	RenderModules = new Tools_RenderModule[NumberOfRenderModules];
}

void Tools_RenderModule_System::Reset(void)
{
	Shutdown();
	Initialise();
}

void Tools_RenderModule_System::Shutdown( void )
{
	delete [] RenderModules;
	RenderModules = 0;
	NumberOfRenderModules = 0;
}

void Tools_RenderModule_System::UpdateRenderData()
{
	bool bMeshesChanged = PrepareForRendering();

	if (bMeshesChanged)
	{
		Asura_Direct3D_EnvironmentRenderer::Tools_CreateData( Asura_Module_System::GetNumberOfModules() );
	}
}

void Tools_RenderModule_System::RenderEnvironment( void )
{
	UpdateRenderData();
	Asura_Direct3D_EnvironmentRenderer::Render( false );
}

bool Tools_RenderModule_System::PrepareForRendering( void )
{
	// Scan through modules and make sure we have up-to-date rendering data for them.
	// If meshes have changed we may need to recalc the visibility system too (but we don't
	// have to do that if only lighting has changed).

	bool bMeshesChanged = false;

	u_int uNumberOfStrips = 0;

	Tools_PhotonMapping_System::CalculateEnvironmentIrradiance();
	Tools_PhotonMapping_System::CheckValidityOfGlobalIllumination();


	for (int iModuleIndex=0; iModuleIndex<Asura_Module_System::GetNumberOfModules(); iModuleIndex++)
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

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

		if (!pxMesh->AreNormalsValid())
		{
			pxMesh->CreateNormals();
			UpdateModuleMesh(pxMesh->Index);
		}

		if(!pxMesh->PrelightingDataValid && Tools_Render::LightingIsOn())
		{
			Asura_Lighting_System::CalculatePrelighting( *pxMesh, xPosition, iModuleIndex );
			UpdateModuleLighting( pxMesh->Index );
			//	pxMesh->PrelightingDataValid = true;
		}

		int iMeshIndex = pxMesh->Index;

		Asura_Assert(iMeshIndex<NumberOfRenderModules,"");

		Tools_RenderModule& xRenderModule = RenderModules[iMeshIndex];

		if (Tools_System::IsActive() && !Tools_System::IsShuttingDown())
		{
			if(xRenderModule.IsHidden() != Tools_Render_Options::IsHidden(iModuleIndex))
			{
				xRenderModule.m_bNeedsMeshUpdating = true;
			}
		}

		if (xRenderModule.m_bNeedsMeshUpdating)
		{
			xRenderModule.Reset();
		}

		if (!xRenderModule.m_bIsValid)
		{
			xRenderModule.Initialise( *pxMesh, xPosition, iModuleIndex );
			bMeshesChanged = true;
		}

		if (xRenderModule.m_bNeedsLightingUpdating)
		{
			xRenderModule.UpdateLighting( *pxMesh );
		}

		uNumberOfStrips += xRenderModule.GetNumberOfPackets();
	}

	return bMeshesChanged;
}

const Tools_RenderModule& Tools_RenderModule_System::GetRenderModule( u_int uModuleIndex )
{
	const Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(uModuleIndex);
	int iMeshIndex = pxMesh->Index;
	
	Asura_Assert(iMeshIndex<NumberOfRenderModules,"");

	return RenderModules[iMeshIndex];
}

void Tools_RenderModule_System::RenderModule(int iPass,const Asura_Mesh& mesh, const Asura_Vector_3& position, bool drawBrightGreen,int moduleIndex)
{

}

void Tools_RenderModule_System::UpdateModuleMesh(int meshIndex)
{
	Asura_Assert(meshIndex<NumberOfRenderModules,"");
	if(meshIndex >= NumberOfRenderModules) return;

	Tools_RenderModule& renderModule = RenderModules[meshIndex];
	renderModule.m_bNeedsMeshUpdating = true;
}

void Tools_RenderModule_System::UpdateModuleLighting(int meshIndex)
{
	Asura_Assert(meshIndex<NumberOfRenderModules,"");
	if(meshIndex >= NumberOfRenderModules) return;

	Tools_RenderModule& renderModule = RenderModules[meshIndex];
	renderModule.m_bNeedsLightingUpdating = true;
}


void Tools_RenderModule_System::UpdateAllModuleLighting()
{
	for(int i=0;i<NumberOfRenderModules;i++)
	{
		RenderModules[i].m_bNeedsLightingUpdating = true;
	}
}




Tools_RenderModule::Tools_RenderModule()
{
	m_bIsValid = false;
	m_bNeedsLightingUpdating = false;
	m_bNeedsMeshUpdating = false;
	m_pxIndexBuffer = NULL;
	m_pusIndexBufferData = NULL;
	m_pxVertexBuffer = 0;
	m_uNumberOfPackets = 0;
	m_pxPacketList = 0;
	m_uNumberOfVertices = 0;
	m_bHidden = false;
}

Tools_RenderModule::~Tools_RenderModule()
{
	Reset();
}

void Tools_RenderModule::Reset(void)
{
	if (m_bIsValid)
	{
		if(m_pxVertexBuffer)
		{
			m_pxVertexBuffer->Release();
			m_pxVertexBuffer = 0;
		}

		if(m_pxIndexBuffer)
		{
			m_pxIndexBuffer->Release();
			m_pxIndexBuffer = 0;
		}

		if(m_pusIndexBufferData)
		{
			delete[] m_pusIndexBufferData;
			m_pusIndexBufferData = 0;
		}
		
		if (m_pxPacketList)
		{
			delete [] m_pxPacketList;
			m_pxPacketList = 0;
		}
	}
	m_bIsValid = false;
	m_bNeedsLightingUpdating = false;
	m_bNeedsMeshUpdating = false;
	m_uNumberOfPackets = 0;
	
}

void Tools_RenderModule::Initialise( const Asura_Mesh& xMesh, const Asura_Vector_3& xPosition, int iModuleIndex )
{
	const bool bAllowDoubleSided = Tools_System::IsActive() ? !Tools_Render_Options::NoBackFaceCull : true;


	m_iModuleIndex = iModuleIndex;
	m_uNumberOfPackets = 0;

	u_int uNumberOfVertices = 0;
	u_int uNumberOfIndices = 0;

	if (Tools_System::IsActive() && !Tools_System::IsShuttingDown())
	{
		m_bHidden = Tools_Render_Options::IsHidden(iModuleIndex);
	}
	else
	{
		m_bHidden = false;
	}
	if(m_bHidden)
	{
		m_bIsValid = true;
		return;
	}
	

	#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
		// Have to dynamically allocate this because we don't have a constant max no. of materials
		const int iNumMaterials = Tools_MaterialResponse_System::GetNumMaterials();
		u_int uConstructionPacketListSize = iNumMaterials+1;
		Tools_RenderModule_Polygon_Packet* xConstructionPacketList = new Tools_RenderModule_Polygon_Packet[ uConstructionPacketListSize];
	#else
		Tools_RenderModule_Polygon_Packet xConstructionPacketList[ASURA_MAX_NO_OF_MATERIALS+1];
	#endif

	int* piPolygonToPacket = new int[xMesh.NumberOfPolygons];

	// Scan through all the polygons and count the number of indices & vertices required for each material
	// For speed no attempt is made to merge vertices which are the same. This facilitates relighting etc

	for( int iPoly=0; iPoly<xMesh.NumberOfPolygons; ++iPoly ) 
	{
		int iNumVerts = xMesh.PolygonDataPtr[iPoly].NumberOfVertices();

		uNumberOfVertices += iNumVerts;

		int iFlags = xMesh.PolygonFlagDataPtr ? xMesh.PolygonFlagDataPtr[iPoly].Flags : 0;

		// check to see if the module is hidden, or the polygon individually
		// TODO - should be able to do 'module hidden' outside of loop, surely?

		{
			// if poly is double sided double the number of indices required
			bool bDoubleSided = bAllowDoubleSided && (iFlags & AsuraPolygonFlag_DoubleSided);
			
			if( bDoubleSided )
			{
				//double sided polygons will also need another copy of the vertices
				uNumberOfVertices += iNumVerts;
			}
		}
		
		bool bIncludePoly = true;

		if (Tools_System::IsActive() && !Tools_System::IsShuttingDown())
		{
			bIncludePoly = iModuleIndex==-1 || !Tools_Render_Options::IsHidden( iModuleIndex, iPoly);
		}
		else
		{
			//preparing environment for in game , so base visbility on the dont render flag
			//RAR 29/10/2007
			bIncludePoly = !(iFlags & AsuraPolygonFlag_DontRender);
		}

		if ( bIncludePoly )
		{
			// find the poly's material, and see if we have a packet of this type already
			#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
				const Asura_Hash_ID* puMaterialResponse = xMesh.TextureDataPtr[iPoly].m_auMaterialResponseHashID;

			#else
				int iMaterialID = xMesh.TextureDataPtr[iPoly].m_iMaterialID;
			#endif

			u_int iPacket = 0;

			for( ; iPacket<m_uNumberOfPackets; iPacket++)
			{
				#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
					bool bMaterialMatch = true;
					for(int iMatSlot=0; iMatSlot<TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMatSlot++)
					{
						if( xConstructionPacketList[ iPacket ].m_auMaterialResponseHashID[iMatSlot] != puMaterialResponse[iMatSlot] )
						{
							bMaterialMatch = false;
							break;
						}
					}
					if(bMaterialMatch)
				#else
					if( xConstructionPacketList[iPacket].m_iMaterialID == iMaterialID )
				#endif
				{
					// found a matching packet
					break;
				}
			}

			if(iPacket == m_uNumberOfPackets) // did we scan through all packets?
			{
				//need a new packet then
				#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
					if(m_uNumberOfPackets == uConstructionPacketListSize)
					{
						//enlarge packet array
						Tools_RenderModule_Polygon_Packet* pxNewConstructionPacketList = new Tools_RenderModule_Polygon_Packet[uConstructionPacketListSize*2];
						Asura_Mem::Copy(pxNewConstructionPacketList, xConstructionPacketList, sizeof(Tools_RenderModule_Polygon_Packet) * uConstructionPacketListSize*2);
						delete [] xConstructionPacketList;
						xConstructionPacketList = pxNewConstructionPacketList;
						uConstructionPacketListSize*=2;
					}
					for(int iMatSlot=0 ;iMatSlot<TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMatSlot++)
					{
						xConstructionPacketList[ m_uNumberOfPackets ].m_auMaterialResponseHashID[iMatSlot] = puMaterialResponse[iMatSlot];
					}
				#else
					xConstructionPacketList[m_uNumberOfPackets].m_iMaterialID = iMaterialID;
				#endif

				xConstructionPacketList[m_uNumberOfPackets].m_uNumberOfIndices = 0;
				m_uNumberOfPackets++;
			}

			// set the lookup table for this poly, so we know later which packet it is in
			piPolygonToPacket[iPoly] = iPacket;

			// determine number of indices for polygon
			// three for a tri, 6 for a quad (because we're going to trilist them)
			int iNumIndices = (iNumVerts == 3) ? 3 : 6;

			// if poly is double sided double the number of indices required
			bool bDoubleSided = bAllowDoubleSided && (iFlags & AsuraPolygonFlag_DoubleSided);
			
			if( bDoubleSided )
			{
				iNumIndices *= 2;
			}

			// increment packet data
			xConstructionPacketList[iPacket].m_uNumberOfIndices += iNumIndices;

			// increment running total
			uNumberOfIndices += iNumIndices;
		}
		else
		{
			// note in lookup table that poly is to be ignored.
			piPolygonToPacket[iPoly] = -1;
		}
	}

	// if we counted no vertices, we can exit here (invisible module)
	if(!uNumberOfIndices)
	{
		//nothing visible in module
		delete [] piPolygonToPacket;
		m_bIsValid = true;

		#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
			delete[] xConstructionPacketList;
		#endif

		return;
	}

	// Initialise packets now we know how many we need
	m_pxPacketList = new Tools_RenderModule_Polygon_Packet[ m_uNumberOfPackets ];

	int iIndexStart = 0;
	for ( u_int i=0; i<m_uNumberOfPackets; ++i )
	{
		#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
			for(int iMatSlot=0 ; iMatSlot<TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMatSlot++)
			{
				m_pxPacketList[i].m_auMaterialResponseHashID[iMatSlot] = xConstructionPacketList[i].m_auMaterialResponseHashID[iMatSlot];
			}
		#else
			m_pxPacketList[i].m_iMaterialID = xConstructionPacketList[i].m_iMaterialID;
		#endif

		m_pxPacketList[i].m_uNumberOfIndices = 0;
		m_pxPacketList[i].m_uStartIndex = iIndexStart;
		m_pxPacketList[i].m_uStartVertex = 0;
		m_pxPacketList[i].m_uEndVertex = uNumberOfVertices;
		m_pxPacketList[i].m_xBB.Reset();

		iIndexStart += xConstructionPacketList[i].m_uNumberOfIndices;
	}

	const u_int									uVertexBufferSize	= uNumberOfVertices * sizeof(Asura_Direct3D_EnvironmentRenderer_Vertex);
	const u_int									uIndexBufferSize	= uNumberOfIndices * sizeof(u_short);

	Asura_Direct3D_EnvironmentRenderer_Vertex*	pxVertexData = NULL;
	u_short*									pusIndexBuffer = NULL;

	HRESULT xResult;
	
	#if defined TEST_D3DX_MESH_PROCESSING

		// Create a D3DX mesh so we can use its mesh processing functions
	
		ID3DXMesh* pxD3DXMesh = NULL;

		Asura_Assertf( uNumberOfIndices % 3 == 0, ("Generated invalid number of indices (%u) while processing mesh '%s'.", uNumberOfIndices, xMesh.Name.GetString()) );

		const HRESULT xResult = D3DXCreateMesh( uNumberOfIndices / 3, uNumberOfVertices, D3DXMESH_SYSTEMMEM, axASURA_D3D_ENV_VERTEX_DECLARATION, Asura_Direct3D::D3DDevice, &pxD3DXMesh );
		if( FAILED( xResult ) || !pxD3DXMesh )
		{
			Asura_Assert( false, "Failed to create D3DX mesh for tangent space computation." );
			return;
		}

		pxD3DXMesh->LockVertexBuffer( 0, reinterpret_cast< void** >( &pxVertexData ) );
		pxD3DXMesh->LockIndexBuffer( 0, reinterpret_cast< void** >( &pusIndexBuffer ) );

		Asura_Assertf( pxVertexData && pusIndexBuffer, ("Failed to lock buffer for mesh '%s'.", xMesh.Name.GetString()) );

	#else // !defined TEST_D3DX_MESH_PROCESSING

		// Lock the output buffers directly

		xResult = Asura_Direct3D::D3DDevice->CreateVertexBuffer	(uVertexBufferSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_pxVertexBuffer, 0);
		ASURA_D3D_OK( xResult);
		xResult = m_pxVertexBuffer->Lock	(0, uVertexBufferSize, reinterpret_cast<void**>(&pxVertexData), 0);
		ASURA_D3D_OK( xResult);

		m_pusIndexBufferData = new uint16[ uNumberOfIndices];
		
		xResult = Asura_Direct3D::D3DDevice->CreateIndexBuffer	(uIndexBufferSize,
														D3DUSAGE_WRITEONLY,
														D3DFMT_INDEX16,
														D3DPOOL_MANAGED,
														&m_pxIndexBuffer,
														0);
		ASURA_D3D_OK( xResult);

		m_pxIndexBuffer->Lock( 0, sizeof(unsigned short)*uNumberOfIndices, (void**)&pusIndexBuffer, 0 );

	#endif // !defined TEST_D3DX_MESH_PROCESSING

#if defined( ASURA_ASSERT)
	Asura_Direct3D_EnvironmentRenderer_Vertex const * const pxVertexData0 = pxVertexData;
	Asura_Direct3D_EnvironmentRenderer_Vertex const * const pxVertexDataN = pxVertexData + uNumberOfVertices;
	u_short const * const pusIndexBuffer0 = pusIndexBuffer;
	u_short const * const pusIndexBufferN = pusIndexBuffer + uNumberOfIndices;
#endif
	
	// go through polygons again and fill in the buffers
//			bool bUseLighting = Tools_Render::LightingIsOn();
//			bool bDiscoLights = Tools_Render_Options::GetUseDiscoLights();
	
	Asura_SphericalHarmonics_Order2_RGB* pxHarmonics = new Asura_SphericalHarmonics_Order2_RGB[xMesh.NumberOfVertices];
    
	for (u_int v=0; v<(u_int)xMesh.NumberOfVertices; ++v )
	{
		Tools_EnvironmentalLightTransport::GetDiffuseForVertex
		(
			pxHarmonics[v],
			v,
			m_iModuleIndex
		);
	}

	#if !defined TEST_D3DX_MESH_PROCESSING
	//Initialise tangent and binormal calculation
	//RAR 09/07/2007
	Tools_Mesh_TangentSpace xMeshTangentSpace;
	xMeshTangentSpace.Initialise(iModuleIndex);
	#endif //!defined TEST_D3DX_MESH_PROCESSING

	if( NULL != pxVertexData && NULL != pusIndexBuffer)
	{
		int *piPacket = piPolygonToPacket;

		unsigned short usVertIndex = 0;

		for( int p=0; p<xMesh.NumberOfPolygons; p++, piPacket++ )
		{
			const Asura_Mesh_Normals&		xMeshNormal		= xMesh.NormalsDataPtr [p];
			const Asura_Mesh_Polygon&		xMeshPolygon	= xMesh.PolygonDataPtr [p];
			const Asura_Mesh_TextureInfo&	xMeshTexture	= xMesh.TextureDataPtr [p];

			int								iNumVerts		= xMeshPolygon.NumberOfVertices ();
			int								iFlags			= (NULL == xMesh.PolygonFlagDataPtr)
															? 0
															: xMesh.PolygonFlagDataPtr [p].Flags;

			bool bDoubleSided = bAllowDoubleSided && (iFlags & AsuraPolygonFlag_DoubleSided);

			//If double sided we will need to do 2 passes of adding the vertices , with
			//normals and vertex ordering being reversed for the second side of the polygons
			//RAR 03/09/2007
			for( int iMirror=0;iMirror < (bDoubleSided ? 2 : 1);iMirror++)
			{
				for (int v = 0; v < iNumVerts; v++)
				{
					const int									vID				= xMeshPolygon.VertexID [v];
					const Asura_Mesh_Vertex&					xMeshVertex		= xMesh.VertexDataPtr [vID];
					Asura_SphericalHarmonics_Order2_RGB			xHarmonic		= pxHarmonics [vID];

					const u_int									uAlpha			= (int)(xMeshTexture.m_afAlpha [v] * 255.0f);
					const u_int									uDiffuse		= (uAlpha << 24);

					const Asura_Vector_2						xTextureCoord0 (xMeshTexture.U [0][v], xMeshTexture.V [0][v]);
					const Asura_Vector_2						xTextureCoord1 (xMeshTexture.U [1][v], xMeshTexture.V [1][v]);
					const Asura_Vector_3						xVertexPosition (xMeshVertex.Position + xPosition);

					Asura_Vector_3								xVertexNormal	= xMeshNormal.VertexNormal [v];
					if(iMirror) xVertexNormal = -xVertexNormal;


					#if !defined TEST_D3DX_MESH_PROCESSING

						Asura_Vector_3								xTangent;
						Asura_Vector_3								xBinormal;
						Asura_Vector_3								xTangent2;
						Asura_Vector_3								xBinormal2;

						xMeshTangentSpace.GetTangentAndBinormal(p, vID, 0, xVertexNormal, xTangent, xBinormal);
						xMeshTangentSpace.GetTangentAndBinormal(p, vID, 1, xVertexNormal, xTangent2, xBinormal2);

					#endif // !defined TEST_D3DX_MESH_PROCESSING
					
					Asura_Assertf( pxVertexData0 <= pxVertexData, ("'%s' Vertex Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pxVertexData, pxVertexData0, pxVertexDataN));
					Asura_Assertf( pxVertexData < pxVertexDataN, ("'%s' Vertex Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pxVertexData, pxVertexData0, pxVertexDataN));

					// Set the position data for this vertex...
					pxVertexData->SetPosition	(xVertexPosition);

					// Set the diffuse colour and texture co-ordinate data for this vertex...
					pxVertexData->SetDiffuseRGBA	(uDiffuse);

					pxVertexData->SetTextureCoord	(0, xTextureCoord0);
					pxVertexData->SetTextureCoord	(1, xTextureCoord1);


					// Copy the tangent-space data for this vertex...

					pxVertexData->SetNormal		(xVertexNormal);

					#if !defined TEST_D3DX_MESH_PROCESSING
						pxVertexData->SetTangentBinormal	(xTangent, xBinormal);
						pxVertexData->SetTangentBinormal2	(xTangent2, xBinormal2);
					#endif

					// JAT 20/07/07 include the vertex painting colour 
					Tools_EnvironmentalLightTransport::UpdateLightingFromVertexPainting( xHarmonic, &xMesh, p, v );

					// Copy the spherical harmonic lighting data...

					pxVertexData->SetDiffuseSH_Red		(xHarmonic.m_afRedCoefficients);
					pxVertexData->SetDiffuseSH_Green	(xHarmonic.m_afGreenCoefficients);
					pxVertexData->SetDiffuseSH_Blue		(xHarmonic.m_afBlueCoefficients);
					


					// Step onto the next vertex...

					pxVertexData++;

				}

				if(*piPacket == -1)
				{
					usVertIndex = (u_short)(usVertIndex + iNumVerts);
					continue;//hidden polygon
				}

				Tools_RenderModule_Polygon_Packet* pxPacket = &m_pxPacketList[*piPacket];

				for ( int v=0; v<iNumVerts; v++ )
				{
					int vID = xMesh.PolygonDataPtr[p].VertexID[v];

					pxPacket->m_xBB.Set( xMesh.VertexDataPtr[vID].Position + xPosition );
				}


				//get the point to insert the indeces at
				u_int uIndexBufferOffset = pxPacket->m_uNumberOfIndices+pxPacket->m_uStartIndex;
				u_short * pusIndexData = &pusIndexBuffer[ uIndexBufferOffset];
				u_short * pusIndexBufferData = &m_pusIndexBufferData[ uIndexBufferOffset];

				if (iNumVerts == 3)
				{
					Asura_Assertf( pusIndexBuffer0 <= pusIndexData, ("'%s' Index Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pusIndexData, pusIndexBuffer0, pusIndexBufferN));
					Asura_Assertf( (pusIndexData+2) < (pusIndexBufferN), ("'%s'Index Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pusIndexData, pusIndexBuffer0, pusIndexBufferN));

					pxPacket->m_uNumberOfIndices += 3;
					
					if(iMirror)
					{
						pusIndexData[2] = pusIndexBufferData[2] = usVertIndex++;
						pusIndexData[1] = pusIndexBufferData[1] = usVertIndex++;
						pusIndexData[0] = pusIndexBufferData[0] = usVertIndex++;
					
					}
					else
					{
						pusIndexData[0] = pusIndexBufferData[0] = usVertIndex++;
						pusIndexData[1] = pusIndexBufferData[1] = usVertIndex++;
						pusIndexData[2] = pusIndexBufferData[2] = usVertIndex++;
					}
					
					pusIndexData += 3;
					pusIndexBufferData += 3;

				}
				else
				{
					Asura_Assertf( pusIndexBuffer0 <= pusIndexData, ("'%s' Index Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pusIndexData, pusIndexBuffer0, pusIndexBufferN));
					Asura_Assertf( (pusIndexData+5) < (pusIndexBufferN), ("'%s' Index Data Pointer %p is out of range (%p-%p)", xMesh.Name.GetString(), pusIndexData, pusIndexBuffer0, pusIndexBufferN));

					pxPacket->m_uNumberOfIndices += 6;
					
					if(iMirror)
					{
						pusIndexData[2] = pusIndexBufferData[2] = usVertIndex++;
						pusIndexData[1] = pusIndexBufferData[1] = usVertIndex++;
						pusIndexData[0] = pusIndexBufferData[0] = usVertIndex;
						pusIndexData[5] = pusIndexBufferData[5] = usVertIndex++;
						pusIndexData[4] = pusIndexBufferData[4] = usVertIndex++;
						pusIndexData[3] = pusIndexBufferData[3] = (unsigned short)(usVertIndex-4);
					}
					else
					{
						pusIndexData[0] = pusIndexBufferData[0] = usVertIndex++;
						pusIndexData[1] = pusIndexBufferData[1] = usVertIndex++;
						pusIndexData[2] = pusIndexBufferData[2] = usVertIndex;
						pusIndexData[3] = pusIndexBufferData[3] = usVertIndex++;
						pusIndexData[4] = pusIndexBufferData[4] = usVertIndex++;
						pusIndexData[5] = pusIndexBufferData[5] = (unsigned short)(usVertIndex-4);
					}
					 
					pusIndexData += 6;
					pusIndexBufferData += 6;

				}
			}
		}
	}

	Asura_Assertf( pxVertexData == pxVertexDataN, ("'%s' Vertex Data Pointer %p is not at the end of the range (%p-%p)", xMesh.Name.GetString(), pxVertexData, pxVertexData0, pxVertexDataN));

	delete [] pxHarmonics;

	delete [] piPolygonToPacket;

	#if defined TEST_D3DX_MESH_PROCESSING

		// Compute per-vertex tangents and binormals
		{
			pxD3DXMesh->UnlockVertexBuffer();
			pxD3DXMesh->UnlockIndexBuffer();

			// NB D3DX uses its tangents and binormals the other way round from us, so explicitly
			// switch them over
			const HRESULT xResult = D3DXComputeTangentFrameEx( pxD3DXMesh,
															D3DDECLUSAGE_TEXCOORD, 0,
															D3DDECLUSAGE_TANGENT, 0,
															D3DDECLUSAGE_BINORMAL, 0,
															D3DDECLUSAGE_NORMAL, 0,
															D3DXTANGENT_GENERATE_IN_PLACE,
															NULL,
															0.01f, 0.025f, 0.01f,
															NULL, NULL );

			const bool bOK = SUCCEEDED( xResult );
			const char* const szErrorString = DXGetErrorString9( xResult );
			const char* const szErrorDescription = DXGetErrorDescription9( xResult );

			Asura_Assertf( bOK, ("Failed to compute tangent frame for mesh '%s'.\nReason: (%s) %s", xMesh.Name.GetString(), szErrorString, szErrorDescription) );
		}

		// Copy processed vertices to output VB
		{
			Asura_Direct3D::D3DDevice->CreateVertexBuffer( uVertexBufferSize, D3DUSAGE_WRITEONLY, 0,
														D3DPOOL_MANAGED, &m_pxVertexBuffer, NULL );

			void* pProcessedData = NULL;
			pxD3DXMesh->LockVertexBuffer( D3DLOCK_READONLY, &pProcessedData );

			void* pFinalData = NULL;
			m_pxVertexBuffer->Lock( 0, uVertexBufferSize, &pFinalData, 0 );

			Asura_Mem::Copy( pFinalData, pProcessedData, uVertexBufferSize );

			m_pxVertexBuffer->Unlock();
			pxD3DXMesh->UnlockVertexBuffer();
		}

		// Copy processed indices to output IB
		{
			Asura_Direct3D::D3DDevice->CreateIndexBuffer( uIndexBufferSize,
														D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
														D3DPOOL_MANAGED, &m_pxIndexBuffer, NULL );

			void* pProcessedData = NULL;
			pxD3DXMesh->LockIndexBuffer( D3DLOCK_READONLY, &pProcessedData );

			void* pFinalData = NULL;
			m_pxIndexBuffer->Lock( 0, uIndexBufferSize, &pFinalData, 0 );

			Asura_Mem::Copy( pFinalData, pProcessedData, uIndexBufferSize );

			m_pxIndexBuffer->Unlock();
			pxD3DXMesh->UnlockIndexBuffer();
		}

		pxD3DXMesh->Release();

	#else // !defined TEST_D3DX_MESH_PROCESSING

		m_pxVertexBuffer->Unlock();
		m_pxIndexBuffer->Unlock();

	#endif // !defined TEST_D3DX_MESH_PROCESSING

	m_uNumberOfVertices = uNumberOfVertices;

	m_bIsValid = true;

	#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM && !defined ASURA_MATERIAL_RESPONSE_CHARACTERS_ONLY
		delete[] xConstructionPacketList;
	#endif
}


void Tools_RenderModule::CalculateTangentSpaceVectors(const class Asura_Mesh* pxMesh, u_int uPoly, Asura_Vector_3& xTangent, Asura_Vector_3& xBinormal)
{
	Asura_Vector_3	axPositions [3];
	Asura_Vector_2	axTextureCoords [3];


	// Initialise the vertex-data...

	for (u_int u = 0; u < 3; u++)
	{
		u_int	uVertexID	= pxMesh->PolygonDataPtr [uPoly].VertexID [u];


		// Fill in the data for this vertex...

		axPositions [u]			= pxMesh->VertexDataPtr [uVertexID].Position;
		axTextureCoords [u].x	= pxMesh->TextureDataPtr [uPoly].U [0][u];
		axTextureCoords [u].y	= 1.0f - pxMesh->TextureDataPtr [uPoly].V [0][u];
	}


	// Calculate the tangent AND binormal...

	Tools_TangentSpace::CalculateTangentAndBinormal	(axPositions, axTextureCoords, xTangent, xBinormal);
}


void Tools_RenderModule::UpdateLighting( const Asura_Mesh& xMesh )
{
	if (!m_uNumberOfPackets) return;

	if (!m_bIsValid) return;

	if (!m_pxVertexBuffer || !m_pxIndexBuffer) return;

	const u_int									uVertexBufferSize	= m_uNumberOfVertices * sizeof(Asura_Direct3D_EnvironmentRenderer_Vertex);

	Asura_Direct3D_EnvironmentRenderer_Vertex*	pxVertexData;

	// lock buffers ready to write to
	m_pxVertexBuffer->Lock	(0,	uVertexBufferSize,	reinterpret_cast<void**>(&pxVertexData),	0);


	Asura_SphericalHarmonics_Order2_RGB* pxHarmonics = new Asura_SphericalHarmonics_Order2_RGB[xMesh.NumberOfVertices];
                
	for (u_int v=0; v<(u_int)xMesh.NumberOfVertices; ++v )
	{
		Tools_EnvironmentalLightTransport::GetDiffuseForVertex
		(
			pxHarmonics[v],
			v,
			m_iModuleIndex
		);
	}
	

	for( int p=0; p<xMesh.NumberOfPolygons; p++ )
	{
		const Asura_Mesh_Polygon&		xMeshPolygon	= xMesh.PolygonDataPtr [p];
		const Asura_Mesh_TextureInfo&	xMeshTexture	= xMesh.TextureDataPtr [p];
		int								iNumVerts		= xMeshPolygon.NumberOfVertices ();

		int iFlags = xMesh.PolygonFlagDataPtr ? xMesh.PolygonFlagDataPtr[p].Flags : 0;
		bool bDoubleSided = (iFlags & AsuraPolygonFlag_DoubleSided) && !Tools_Render_Options::NoBackFaceCull;


		for(int iMirror=0 ; iMirror < (bDoubleSided ? 2 : 1) ; iMirror++)
		{
			for ( int v=0; v<iNumVerts; v++ )
			{
				const int									vID			= xMeshPolygon.VertexID[v];
				Asura_SphericalHarmonics_Order2_RGB			xHarmonic	= pxHarmonics [vID];

				const u_int									uAlpha		= (int)(xMeshTexture.m_afAlpha [v] * 255.0f);
				const u_int									uDiffuse	= (uAlpha << 24);


				// Update the diffuse colour...
				pxVertexData->SetDiffuseRGBA	(uDiffuse);

				// JAT 20/07/07 include the vertex painting colour 
				Tools_EnvironmentalLightTransport::UpdateLightingFromVertexPainting( xHarmonic, &xMesh, p, v );

				// Update the spherical harmonic lighting values...

				pxVertexData->SetDiffuseSH_Red		(xHarmonic.m_afRedCoefficients);
				pxVertexData->SetDiffuseSH_Green	(xHarmonic.m_afGreenCoefficients);
				pxVertexData->SetDiffuseSH_Blue		(xHarmonic.m_afBlueCoefficients);


				// Step onto the next vertex...

				pxVertexData++;

			}
		}
	
	}

	delete [] pxHarmonics;

	m_pxVertexBuffer->Unlock();



	m_bNeedsLightingUpdating = false;
}


#endif // ( ASURA_TARGET_SM3 )


// DaveR 25/06/06 - Exposing this to the outside world, so other 
// tools rendering code (e.g. Botanicals) doesn't need a big fugly 
// copy+paste of the setup code for the fog constants.

// (AAM - 18.08.2006) I've made this available in both the "old" and "new" PC
// renderers, so the botanicals renderer can access it in next-gen builds too

float Tools_RenderModule::s_afShaderConstants[11][4];

void Tools_RenderModule_System::SetupShaderConstantsForFog()
{
	Tools_RenderModule::SetupShaderConstants();	
	Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(4,&Tools_RenderModule::s_afShaderConstants[4][0],7);
}

void Tools_RenderModule::SetupShaderConstants()
{
	ZeroMemory(&s_afShaderConstants[0][0], sizeof(s_afShaderConstants));

	// Viewproj matrix

	D3DXMATRIX xView, xProj, xViewProj;
	Asura_Direct3D::D3DDevice->GetTransform(D3DTS_VIEW, &xView);
	Asura_Direct3D::D3DDevice->GetTransform(D3DTS_PROJECTION, &xProj);

	D3DXMatrixMultiply(&xViewProj, &xView, &xProj);
	D3DXMatrixTranspose(&xViewProj, &xViewProj);

	memcpy(&s_afShaderConstants[0][0], &xViewProj, sizeof(float)*16);

	// Linear fog constant

	s_afShaderConstants[4][0] = -Asura_Fog::GetOneOverFogPlaneSeparation() * Asura_Fog::GetFogValueAtFarPlane();
	s_afShaderConstants[4][1] = 1.0f + (Asura_Fog::GetFogNearPlane() * Asura_Fog::GetOneOverFogPlaneSeparation() * Asura_Fog::GetFogValueAtFarPlane()) ;
	s_afShaderConstants[4][2] = 1.0f - Asura_Fog::GetFogValueAtFarPlane();
	s_afShaderConstants[4][3] = 1.0f;

	// Graph fog constants

	const Asura_Vector_2 &xCenterpoint = Asura_Fog::GetGraphFogPoint();

	s_afShaderConstants[5][0] = Asura_Fog::GetOneOverFogPlaneSeparation();
	s_afShaderConstants[5][1] = -Asura_Fog::GetFogNearPlane() * Asura_Fog::GetOneOverFogPlaneSeparation();
	s_afShaderConstants[5][2] = xCenterpoint.x;
	s_afShaderConstants[5][3] = 1.0f;

	s_afShaderConstants[6][0] = 1.0f / xCenterpoint.x;
	s_afShaderConstants[6][1] = 0.0f;
	s_afShaderConstants[6][2] = 0.0f;
	s_afShaderConstants[6][3] = 0.0f;

	s_afShaderConstants[7][0] = 1.0f / (1.0f-xCenterpoint.x);
	s_afShaderConstants[7][1] = -xCenterpoint.x / (1.0f - xCenterpoint.x);
	s_afShaderConstants[7][2] = 0.0f;
	s_afShaderConstants[7][3] = 0.0f;

	float xValue0, xValue1, xValue2, xValue3;
	xValue0 = 1.f - (-xCenterpoint.y);
	xValue1 = 1.f - (0.f);
	xValue2 = 1.f - (xCenterpoint.y);
	xValue3 = 1.f - (Asura_Fog::GetFogValueAtFarPlane());

	s_afShaderConstants[8][0] =  0.5f * ( xValue0 * -1.0f + xValue1 *  3.0f + xValue2 * -3.0f + xValue3 *  1.0f );
	s_afShaderConstants[8][1] =  0.5f * ( xValue0 *  2.0f + xValue1 * -5.0f + xValue2 *  4.0f + xValue3 * -1.0f );
	s_afShaderConstants[8][2] =  0.5f * ( xValue0 * -1.0f +                   xValue2 *  1.0f                   );
	s_afShaderConstants[8][3] =  xValue1;

	xValue0 = 1.f - (0.f);
	xValue1 = 1.f - (xCenterpoint.y);
	xValue2 = 1.f - (Asura_Fog::GetFogValueAtFarPlane());
	xValue3 = 1.f - (2.f * Asura_Fog::GetFogValueAtFarPlane() - xCenterpoint.y);

	s_afShaderConstants[9][0] =  0.5f * ( xValue0 * -1.0f + xValue1 *  3.0f + xValue2 * -3.0f + xValue3 *  1.0f );
	s_afShaderConstants[9][1] =  0.5f * ( xValue0 *  2.0f + xValue1 * -5.0f + xValue2 *  4.0f + xValue3 * -1.0f );
	s_afShaderConstants[9][2] =  0.5f * ( xValue0 * -1.0f +                   xValue2 *  1.0f                   );
	s_afShaderConstants[9][3] =  xValue1;

	s_afShaderConstants[10][0] = 0.0f;
	s_afShaderConstants[10][1] = 1.0f;
	s_afShaderConstants[10][2] = 0.0f;
	s_afShaderConstants[10][3] = 1.0f - Asura_Fog::GetFogValueAtFarPlane();
}
