//
// Asura_Direct3D.cpp
//
// Created by Kevin Lea - 02/07/2001 11:11:19
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#include "Rendering/Asura_Direct3D.h"

#if defined ASURA_SUPPORT_MULTIPLE_MONITORS && WINVER < 0x0500
	#include <multimon.h>
#endif

// Asura Platform-independent
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Material_System.h"
#include "Core/Asura_Multithread.h"
#include "Core/Asura_StringBuffer.h"
#include "Core/Asura_TextureManagement.h"
#ifdef ASURA_USE_TEXTURE_STREAMING
#include "Core/Asura_Texture_Streaming.h"
#endif
#include "Core/Asura_Windows.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Debugging/Asura_Debug_Timers.h"
#include "Debugging/Asura_LogFile.h"
#include "Debugging/Asura_ScreenShot_System.h"
#include "GUIMenu/Asura_GUIMenu_System.h"
#include "Interface/Asura_CommandConsole.h"
#include "Light/Asura_Photon.h"
#include "Rendering/Asura_Fog.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Render.h"
#include "Shadows/Asura_CSM.h"
#include "SpecialFX/Asura_Glow_System.h"

// Asura PC
#include "Botanical/Asura_Direct3D_Botanicals_Game_Render.h"
#include "Carpet/Asura_Direct3D_Carpet_Render.h"
#include "Rendering/Asura_Direct3D_BackBufferCapture.h"
#include "Rendering/Asura_Direct3D_Caps.h"
#include "Rendering/Asura_Direct3D_Effects_System.h"
#if defined (ASURA_TARGET_SM3)
	#include "Rendering/Asura_Direct3D_Fog.h"
#endif
#include "Rendering/Asura_Direct3D_LineRendering.h"
#include "Rendering/Asura_Direct3D_RenderTargetManager.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Direct3D_ShadowBuffer.h"
#include "Rendering/Asura_Direct3D_UserOptions.h"
#include "Rendering/Asura_Direct3D_MaterialResponse.h"
#if defined(ASURA_USE_MATERIAL_RESPONSE_SYSTEM) && (((ASURA_LOAD_EFFECT_FILES == 1) && !defined( ASURA_NO_LOAD_EFFECTS_FILES_FROM_FILES)) || defined(ASURA_TOOLS))
#include "Rendering/Asura_Direct3D_MRPShaderCompile.h"
#endif
#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER)
	#include "Rendering/Asura_PlatformRenderCharacter.h"
#endif
#if defined( ASURA_USE_FASTPATH_PFX)
	#include "SpecialFX/Asura_Platform_PFX_System_FastPath.h"
#endif

// Tools
#if defined(ASURA_TOOLS)
	#include "Load&Save/Tools_OptionsFile.h"
	#include "Misc/Tools_AVI_Writer.h"
	#include "Misc/Tools_Render.h"
	#include "Load&Save/Tools_BatchProcessing.h"
#endif

// RJF - 16 June 2005 - NVPerfHUD support - currently on tools builds only
// If you extend this to cover retail builds too (you probably want to) then make DAMN sure you turn it off again before shipping

#ifdef ASURA_TOOLS
#define ASURA_NVPERFHUD_ENABLED (1)
#else
#define ASURA_NVPERFHUD_ENABLED (0)
#endif

Direct3D_IndexBuffer* Asura_Quad_Batch::s_pxIndexBuffer;
#if defined(ASURA_TARGET_SM3)
		Direct3D_VertexDeclaration* Asura_Direct3D::s_pxQuadBatchVertexDeclaration;
#endif	// defined(ASURA_TARGET_SM3)

Direct3D_Object* Asura_Direct3D::D3DObject=NULL;
Direct3D_Device* Asura_Direct3D::D3DDevice=NULL;

Asura_Quad_Batch* Asura_Direct3D::QuadBatch = NULL;
u_int Asura_Direct3D::m_uQuadBatchNumSwapChains = 1;
bool  Asura_Direct3D::m_bQuadBatchIsValid = false;

D3DPRESENT_PARAMETERS* Asura_Direct3D::s_pxPresentParameters = NULL; 
u_int Asura_Direct3D::s_uNumPresentParameters = 0;
Direct3D_SwapChain** Asura_Direct3D::s_ppxSwapChains = NULL;
u_int Asura_Direct3D::s_uNumSwapChains = 0;
u_int Asura_Direct3D::s_uCurrentlyBoundSwapChain = 0xFFFFFFFF;
bool Asura_Direct3D::Initialised = false;
bool Asura_Direct3D::FrameHasBegun = false;
bool Asura_Direct3D::UsingStencil = false;
Direct3D_Surface* Asura_Direct3D::s_pxAutoDepthBuffer = NULL;

// Enable this to create an alternative final rendering target
// Pix seem to have problems once we switch to the final
// swap chain rendering target, this just uses a seperate ARGB
// one until the last moment and then blits across so that
// you can still see what is being draw to it in pix
//#define PIX_DEBUG_ALTERNATE_FINAL_TARGET

#ifdef ASURA_TARGET_SM3
	Asura_Direct3D_RenderTarget* Asura_Direct3D::s_pxDeferredRenderingTarget = NULL;
#ifdef PIX_DEBUG_ALTERNATE_FINAL_TARGET
	Asura_Direct3D_RenderTarget* s_pxFinalRenderingTarget = NULL;
#endif
	bool Asura_Direct3D::s_bIsUsingDeferredRenderingTarget = false;
#endif

Direct3D_Texture* Asura_Direct3D::s_pxUntexturedTexture = NULL;

D3DXMATRIX Asura_Direct3D::s_xViewMatrix;
D3DXMATRIX Asura_Direct3D::s_xProjectionMatrix;
D3DXMATRIX Asura_Direct3D::s_xViewProjMatrix;

bool Asura_Direct3D::s_bUseClipRect = false;
float Asura_Direct3D::s_fClipRectX;
float Asura_Direct3D::s_fClipRectY;
float Asura_Direct3D::s_fClipRectWidth;
float Asura_Direct3D::s_fClipRectHeight;

Direct3D_VertexDeclaration* Asura_Direct3D::s_pxFullScreenVD = NULL;
Direct3D_VertexBuffer* Asura_Direct3D::s_pxFullScreenVB = NULL;

float Asura_Direct3D::s_fViewportMaxZ = 1.0f;
float Asura_Direct3D::s_fViewportMinZ = 0.0f;

D3DDEVTYPE Asura_Direct3D::s_eD3DDeviceType;

bool Asura_Direct3D::s_bUseGammaCorrection = true;

D3DDISPLAYMODE d3ddm;

Direct3D_Texture* NewRenderTarget=0;
Direct3D_Surface* NewZTarget=0;
Direct3D_Surface* MainRenderTarget;
Direct3D_Surface* MainZBuffer;

// Vertex data for full-screen renders
const Direct3D_VertexElement axFULL_SCREEN_VERTEX_DECLARATION[] =
{
	{ 0, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	D3DDECL_END()
};

const short asFULL_SCREEN_VERTICES[] = // Need 4 verts since using D3DPT_TRIANGLEFAN
{
	-1,  1, // Top left
	 1,  1, // Top right
	 1, -1, // Bottom right
	-1, -1, // Bottom left
};

// DLH - Arrays for Post and Pre Reset callback function data
class Asura_Direct3D_Reset_PostCallbackData
{
public:
	Asura_Direct3D_Reset_PostCallbackData();
	
	bool operator==( const Asura_Direct3D_Reset_PostCallbackData& xRHS) const;
	
public:
	Asura_Direct3D::CallbackPostReset m_pfnPostCallback;
	void* m_pData;
};
static Asura_LinkList< Asura_Direct3D_Reset_PostCallbackData> s_tList_xPostResetCallbacks;

class Asura_Direct3D_Reset_PreCallbackData
{
public:
	Asura_Direct3D_Reset_PreCallbackData();
	
	bool operator==( const Asura_Direct3D_Reset_PreCallbackData& xRHS) const;
	
public:
	Asura_Direct3D::CallbackPreReset m_pfnPreCallback;
	void* m_pData;
};
static Asura_LinkList< Asura_Direct3D_Reset_PreCallbackData> s_tList_xPreResetCallbacks;

#if defined(ASURA_D3D_ENABLE_PIX) && ASURA_D3D_ENABLE_PIX
static u_int gs_PIXNestDepth = 0;

#define ASURA_D3D_PIXWRAPPER_LOGFILE "logfiles/D3DPix.txt"

// DLH - 24 August 2006 15:47:47 - I used the to track matching Begin and End events to try and find a problem with PIX crashing
void Asura_D3D_PIXWrapper::Monitor( const char* pszArgs)
{
	if( true == Asura_StringUtil::StartsWith( pszArgs, "BeginEvent", false))
	{
		Asura_SimpleLogfileOutput((ASURA_D3D_PIXWRAPPER_LOGFILE,"%d Begin: '%s'\n", gs_PIXNestDepth, pszArgs));
		Asura_Assert( gs_PIXNestDepth < 0x100, "Very Deep Nest");
		gs_PIXNestDepth += 1;
		return;
	}
	if( true == Asura_StringUtil::StartsWith( pszArgs, "EndEvent", false))
	{
		Asura_Assert( 0 < gs_PIXNestDepth, "Shallow Nest");
		gs_PIXNestDepth -= 1;
		Asura_SimpleLogfileOutput((ASURA_D3D_PIXWRAPPER_LOGFILE,"%d End: '%s'\n", gs_PIXNestDepth, pszArgs));
		return;
	}
	
	Asura_SimpleLogfileOutput((ASURA_D3D_PIXWRAPPER_LOGFILE,"Unknown: '%s'\n", pszArgs));
}
#endif

void Asura_Direct3D::CreateD3DObject()
{
	if(!D3DObject)
	{
		D3DObject = Direct3DCreate9(D3D_SDK_VERSION);
		Asura_Assert(D3DObject,"Unable to create D3D object");
	}
}

static bool g_bFpuPreserve = false;

bool Asura_Direct3D::Initialise(void)
{
	ASURA_CPU_PROFILE_SCOPED_TIMER( "Asura_Direct3D::Initialise");
	
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
    AddConsoleCommands();
#endif

#ifdef ASURA_TOOLS
	Tools_OptionsFile::LoadDisplaySettings();
#endif

#if defined(ASURA_D3D_ENABLE_PIX) && ASURA_D3D_ENABLE_PIX
	// Enable PIX attach
	D3DPERF_SetOptions(0);
#else
	// Disable PIX attach (yes, 1 means disable, check the API docs if you don't believe me)
	D3DPERF_SetOptions(1);
#endif

	// Get the D3D object 
	ASURA_CPU_PROFILE_CALL_TIMER( CreateD3DObject() );

	if (!D3DObject) return false;

	// This code is basically lifted from the NVPerfHUD manual

	//UINT AdapterToUse = D3DADAPTER_DEFAULT;
	UINT AdapterToUse = Asura_Direct3D_UserOptions::GetAdapterIndex();
#if ASURA_NVPERFHUD_ENABLED
	// Look for the PerfHUD adapter
	for (UINT Adapter = 0; Adapter < D3DObject->GetAdapterCount(); ++Adapter)
	{
		D3DADAPTER_IDENTIFIER9	Identifier;
		if(SUCCEEDED(D3DObject->GetAdapterIdentifier(Adapter, 0, &Identifier)))
		{
			if(0 == strcmp(Identifier.Description, "NVIDIA PerfHUD"))
			{
				AdapterToUse = Adapter;
				Asura_Direct3D_UserOptions::SetDevType( D3DDEVTYPE_REF);
				break;
			}
		}
	}
#endif

	D3DDEVTYPE eD3DDeviceType = Asura_Direct3D_UserOptions::GetDevType();  	

	// Get the current desktop display mode
	bool failed = FAILED( D3DObject->GetAdapterDisplayMode( AdapterToUse, &d3ddm ) );
	
	if(failed)
    {
		Asura_Assert(0,"Can't access default adapter");
		return false;
	}

	// Are we multihead?
#ifdef ASURA_SUPPORT_MULTIPLE_MONITORS
	D3DCAPS9 xDeviceCaps;
	D3DObject->GetDeviceCaps(AdapterToUse, eD3DDeviceType, &xDeviceCaps);
	s_uNumPresentParameters = xDeviceCaps.NumberOfAdaptersInGroup;
	const bool bIsMultihead = (s_uNumPresentParameters > 1) && Asura_Windows::RunningFullScreen() && Asura_Direct3D_UserOptions::GetMultiheadEnabled();
#else
	const bool bIsMultihead = false;
#endif
	if(!bIsMultihead) s_uNumPresentParameters = 1;

	// Create one PP for each head we're going to drive
	s_pxPresentParameters = new D3DPRESENT_PARAMETERS[s_uNumPresentParameters];
    ZeroMemory( s_pxPresentParameters, sizeof(D3DPRESENT_PARAMETERS) * s_uNumPresentParameters );
	RecalcPresentParameters();

	UsingStencil=true;
	
	u_int uCreateFlagsCommon = 0;
	
	uCreateFlagsCommon |= (true == Asura_Direct3D_UserOptions::GetMultiThreadingEnable()) ? D3DCREATE_MULTITHREADED : 0;

	if(g_bFpuPreserve)
	{
		uCreateFlagsCommon |= D3DCREATE_FPU_PRESERVE;
	}
	failed = true;
	
	HRESULT hr3 = S_OK;
	HRESULT hr2 = S_OK;
	HRESULT hr1 = S_OK;

	// Force software processing if ASURA_DIRECT3D_DEBUG_VS (in Asura_Direct3D_Caps.h) is defined
	
	#ifndef ASURA_DIRECT3D_DEBUG_VS
	{
		failed = FAILED(hr1 = D3DObject->CreateDevice
				(
					AdapterToUse,
					eD3DDeviceType,
					Asura_Windows::GetWindowHandle(),
					uCreateFlagsCommon | D3DCREATE_MIXED_VERTEXPROCESSING | ((bIsMultihead)?D3DCREATE_ADAPTERGROUP_DEVICE : 0),
					s_pxPresentParameters,
					&D3DDevice
				));	
	}	
	if (failed)
	#endif //ASURA_DIRECT3D_DEBUG_VS
	#if 01
	{
		failed = FAILED(hr2 = D3DObject->CreateDevice
				(
					AdapterToUse,
					eD3DDeviceType,
					Asura_Windows::GetWindowHandle(),
					uCreateFlagsCommon | D3DCREATE_SOFTWARE_VERTEXPROCESSING | ((bIsMultihead)?D3DCREATE_ADAPTERGROUP_DEVICE : 0),
					s_pxPresentParameters,
					&D3DDevice
				));	
	}
	#endif
	if(!failed) s_eD3DDeviceType = eD3DDeviceType;

	Asura_Assertf(!failed,("Failed to Create D3D Device. HR: %x, %x", hr1,hr2));

#if 01
	if (failed)
	{
#if defined(ASURA_TOOLS)
		// DLH - If in console mode with REF Rast we want the Screen as small as possible to save on CPU drawing time
		if( true == Tools_BatchProcessing::IsConsoleMode())
		{
			s_pxPresentParameters->BackBufferHeight = 32;
			s_pxPresentParameters->BackBufferWidth = 32;
		}
#endif

		failed = FAILED(hr3 = D3DObject->CreateDevice
				(
					AdapterToUse,
					D3DDEVTYPE_REF,
					Asura_Windows::GetWindowHandle(),
					uCreateFlagsCommon | D3DCREATE_SOFTWARE_VERTEXPROCESSING | ((bIsMultihead)?D3DCREATE_ADAPTERGROUP_DEVICE : 0),
					s_pxPresentParameters,
					&D3DDevice
				));

		Asura_Assertf(!failed,("Failed to Create Software Device. HR: %x", hr3));
		if(!failed)
		{
			s_eD3DDeviceType = D3DDEVTYPE_REF;
		}
#ifdef ASURA_TOOLS
		else if	( Tools_BatchProcessing::IsBatch())
		{
			// One final fallback - batch export doesn't need to render, just load resources
			D3DPRESENT_PARAMETERS presentParams;
			memset(&presentParams, 0, sizeof(presentParams));
			presentParams.BackBufferWidth            = 0;
			presentParams.BackBufferHeight           = 0;
			presentParams.BackBufferFormat           = D3DFMT_UNKNOWN;
			presentParams.BackBufferCount            = 0;
			presentParams.MultiSampleType            = D3DMULTISAMPLE_NONE;
			presentParams.MultiSampleQuality         = 0;
			presentParams.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
			presentParams.hDeviceWindow              = 0;
			presentParams.Windowed                   = TRUE;
			presentParams.EnableAutoDepthStencil     = FALSE;
			presentParams.AutoDepthStencilFormat     = (D3DFORMAT)0;
			presentParams.Flags                      = 0;
			presentParams.FullScreen_RefreshRateInHz = 0;
			presentParams.PresentationInterval       = D3DPRESENT_INTERVAL_DEFAULT;
			HRESULT hr4 = S_OK;
			failed = FAILED(hr4 = D3DObject->CreateDevice
			(
				D3DADAPTER_DEFAULT,
				D3DDEVTYPE_NULLREF,
				GetDesktopWindow(),
				D3DCREATE_SOFTWARE_VERTEXPROCESSING,
				&presentParams,
				&D3DDevice
			));

			Asura_Assertf(!failed,("Failed to Create NULLREF Device. HR: %x", hr4));
			if(!failed)
			{
				s_eD3DDeviceType = D3DDEVTYPE_NULLREF;
			}
		}
#endif
	}

	Asura_Assertf(!failed,("Failed to Create Software Device. HR: %x", hr3));
#endif

	if (failed) return false;

	// Grab swap chains
	s_uNumSwapChains = D3DDevice->GetNumberOfSwapChains();
	s_ppxSwapChains = new Direct3D_SwapChain*[s_uNumSwapChains];
	memset(s_ppxSwapChains, 0, sizeof(Direct3D_SwapChain*) * s_uNumSwapChains);
	for(u_int uChain = 0; uChain < s_uNumSwapChains; ++uChain)
	{
		D3DDevice->GetSwapChain(uChain, &s_ppxSwapChains[uChain]);
		if(!s_ppxSwapChains[uChain]) return false;
	}

	CreateAutoDepthBuffers();

	Initialised = true;

	/// Kevin Lea - 16/07/2002 15:07:55 - get hardware capabilities
	/// needs to be first thing after device creation
	Asura_Direct3D_Caps::QueryCaps();

//	void InitialiseRMVB(void);
//	InitialiseRMVB();

	InitialiseQuadBatches();
	Asura_Direct3D_LineRenderer::Initialise();

	Asura_Direct3D_Shaders::Initialise();

	Asura_Viewport::Set(s_pxPresentParameters[0].BackBufferWidth, s_pxPresentParameters[0].BackBufferHeight);

#ifdef ASURA_GUI_MENU_ALWAYS_WIDESCREEN
	Asura_GUIMenu_System::SetVirtualResolution( 1280.0f, (1280.0f / s_pxPresentParameters[0].BackBufferWidth) * s_pxPresentParameters[0].BackBufferHeight );
#endif

	ResetStates();

	D3DDevice->EvictManagedResources();

#if defined ASURA_TOOLS || defined ASURA_ASSERT
	if(s_pxPresentParameters[0].Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER) // This is only supported when we have a lockable backbuffer
	{
		// allow dialog boxes (e.g. asserts) to appear over full screen mode
		Asura_Direct3D::D3DDevice->SetDialogBoxMode(TRUE);
	}
	
#endif

	Asura_Direct3D_ShadowBufferSystem::Initialise();

	Asura_Direct3D_RenderTargetManager::Initialise();

	//Create a small white texture to use for any untextured polygons.
	CreateUntexturedTexture();

#ifdef ASURA_TARGET_SM3
	CreateDeferredRenderTargetF16( false);
#ifdef PIX_DEBUG_ALTERNATE_FINAL_TARGET	
	D3DFORMAT d3dfmt = D3DFMT_A8R8G8B8;
	s_pxFinalRenderingTarget = Asura_Direct3D_RenderTargetManager::CreateRenderTarget(1.f, 1.f,&d3dfmt, D3DFMT_D24S8, true, 1, true);
#endif
#endif

#if defined(ASURA_TOOLS)
	Tools_Render::SetVideoMode(s_pxPresentParameters[0].BackBufferWidth, s_pxPresentParameters[0].BackBufferHeight);
#else
	Asura_Windows::SetWindowSize(s_pxPresentParameters[0].BackBufferWidth, s_pxPresentParameters[0].BackBufferHeight);
#endif	//defined(ASURA_TOOLS)

	D3DDevice->CreateVertexDeclaration( axFULL_SCREEN_VERTEX_DECLARATION, &s_pxFullScreenVD );
	D3DDevice->CreateVertexBuffer( sizeof( short ) * 2 * 4, 0, 0, D3DPOOL_MANAGED, &s_pxFullScreenVB, NULL );

	Asura_Assert( s_pxFullScreenVB && s_pxFullScreenVB, "Failed to create full-screen vertex data." );

	if( s_pxFullScreenVB )
	{
		short* psVertices = NULL;
		if( SUCCEEDED(s_pxFullScreenVB->Lock( 0, 0, reinterpret_cast< void** >( &psVertices ), 0 )) && psVertices )
		{
			Asura_Mem::Copy( psVertices, asFULL_SCREEN_VERTICES, sizeof( asFULL_SCREEN_VERTICES ) );
			s_pxFullScreenVB->Unlock();
		}
		else
		{
			Asura_Assert( false, "Failed to lock full-screen vertex buffer." );
		}
	}

	// Initialise DVS variabled for UserOptions
	Asura_Direct3D_UserOptions::SetupDVSInterface();

	return true;
}

void Asura_Direct3D::RecalcPresentParameters()
{
	for(u_int uHead = 0; uHead < s_uNumPresentParameters; ++uHead)
	{
		if(false == Asura_Direct3D_UserOptions::BuildPresentParameters(s_pxPresentParameters[uHead], uHead))
			return;
		s_pxPresentParameters[uHead].hDeviceWindow = Asura_Windows::GetWindowHandle(uHead);
	}
}

void Asura_Direct3D::Shutdown(void)
{
	SAFE_RELEASE( s_pxFullScreenVD );
	SAFE_RELEASE( s_pxFullScreenVB );

#ifdef ASURA_TARGET_SM3
	if(s_pxDeferredRenderingTarget)
	{
		Asura_Direct3D_RenderTargetManager::DestroyRenderTarget(s_pxDeferredRenderingTarget);
		s_pxDeferredRenderingTarget = NULL;
	}
#ifdef PIX_DEBUG_ALTERNATE_FINAL_TARGET
	if(s_pxFinalRenderingTarget)
	{
		Asura_Direct3D_RenderTargetManager::DestroyRenderTarget(s_pxFinalRenderingTarget);
		s_pxFinalRenderingTarget = NULL;
	}
#endif
#endif

	// RJF 16-08-06: No, we don't need to do this. Destroying the device causes it to Release() all of these resources itself, internally.
	//				 However, if we (Asura) have unreleased references to ANY resource, then that resource will keep the device alive.
	//				 The result is that a single resource leak will 'keepalive cascade' the device and any resources attached to it.
	//				 I suspect that this code was originally written in an attempt to fix the Vertex Decl leaks in the Object and 
	//				 ObjHierarchy renderers.

	/*HRESULT xResult;
	if(D3DDevice)
	{
		// DLH - 19 May 2006 15:10:19 - Release all the resources that are still being held onto be the Device (Release() instance counts)
//		xResult = D3DDevice->SetVertexDeclaration( NULL);
		xResult = D3DDevice->SetFVF( D3DFVF_XYZRHW);
		ASURA_D3D_OK( xResult);
		xResult = D3DDevice->SetPixelShader( NULL);
		ASURA_D3D_OK( xResult);
		xResult = D3DDevice->SetVertexShader( NULL);
		ASURA_D3D_OK( xResult);
		xResult = D3DDevice->SetIndices( NULL);
		ASURA_D3D_OK( xResult);
		xResult = D3DDevice->SetStreamSource( 0, NULL, 0, 0);
		ASURA_D3D_OK( xResult);
		
		{
			u_int uStageCount = Asura_Direct3D_Caps::GetMaxNumberOfTextureStages();
			for( u_int uStageIndex = 0; uStageIndex < uStageCount; uStageIndex += 1)
			{
				xResult = D3DDevice->SetTexture( uStageIndex, NULL);
				ASURA_D3D_OK( xResult);
			}
		}
		
		{
			u_int uRenderTargetCount = Asura_Direct3D_Caps::GetMaxNumberOfRenderTargets();
			
			for( u_int uRenderTragetIndex = 1; uRenderTragetIndex < uRenderTargetCount; uRenderTragetIndex += 1)
			{
				xResult = D3DDevice->SetRenderTarget( uRenderTragetIndex, NULL);
				ASURA_D3D_OK( xResult);
			}
		}
		
		xResult = D3DDevice->SetDepthStencilSurface( NULL);
		ASURA_D3D_OK( xResult);
		
	}*/
	
	if(s_pxUntexturedTexture)
	{
		s_pxUntexturedTexture->Release();
		s_pxUntexturedTexture = 0;
	}

	Asura_Direct3D_RenderTargetManager::Shutdown();

	Asura_Direct3D_ShadowBufferSystem::Shutdown();

	Asura_Direct3D_Shaders::Shutdown();

	if(D3DDevice)
	{
		D3DDevice->EvictManagedResources();
	}
	
	// MGH 17/1/2008: Shutdown the quad batches and free up the memory.
	ShutdownQuadBatches();
	delete [] QuadBatch;
	QuadBatch = 0;

	Asura_Direct3D_LineRenderer::Shutdown();

	// KJL - 25-Apr-01 17:07:02 - temp for character animation
	void ShutdownRMVB(void);
	ShutdownRMVB();

	Asura_Direct3D_UserOptions::ClearResolutionCache();

	// RJF - 14 September 2004 - if anyone's been using the BackBufferCapture system, make sure it's shut down now
	Asura_Direct3D_BackBufferCapture::Shutdown();

	if(s_ppxSwapChains)
	{
		for(u_int uChain = 0; uChain < s_uNumSwapChains; ++uChain)
		{
			s_ppxSwapChains[uChain]->Release();
		}
		delete[] s_ppxSwapChains;
		s_ppxSwapChains = NULL;
	}

	DestroyAutoDepthBuffers();
	
	Asura_Assert( 0 == s_tList_xPreResetCallbacks.Size(), "Pre Reset Callback list is not empty");
	Asura_Assert( 0 == s_tList_xPostResetCallbacks.Size(), "Post Reset Callback list is not empty");
	
	if(D3DDevice)
	{
		// JAT 22/01/07 give an assert if we have any D3D resource leaks
		const u_int uNumLeaks = D3DDevice->Release();
		// ignore warning in retail builds
		(void)uNumLeaks;
		Asura_Assertf( ( uNumLeaks == 0 ), ( "D3DDevice->Release() failed with %d leaks", uNumLeaks ) );
		D3DDevice = NULL;
	}
	if(D3DObject)
	{
		D3DObject->Release();
		D3DObject = NULL;
	}

	delete[] s_pxPresentParameters;
	s_pxPresentParameters = NULL;

	Initialised = false;
	
#if defined(ASURA_D3D_ENABLE_PIX) && ASURA_D3D_ENABLE_PIX
	Asura_Assertf( 0 == gs_PIXNestDepth, ("Not all PIX Sections have been Ended"));
#endif
}

void Asura_Direct3D::CreateAutoDepthBuffers()
{
	DestroyAutoDepthBuffers();

	D3DDevice->CreateDepthStencilSurface(	/* Width = */ s_pxPresentParameters[0].BackBufferWidth, 
											/* Height = */ s_pxPresentParameters[0].BackBufferHeight, 
											/* Format = */ Asura_Direct3D_UserOptions::GetCurrentOptions().m_eDepthStencilFormat, 
											/* MultiSampleType = */ s_pxPresentParameters[0].MultiSampleType,
											/* MultiSampleQuality = */ s_pxPresentParameters[0].MultiSampleQuality,
											/* Discard = */ FALSE,
											/* ppSurface = */ &s_pxAutoDepthBuffer,
											/* pSharedHandle = */ NULL
											);
	Asura_Assert(s_pxAutoDepthBuffer, "Could not create depth stencil surface.");

	D3DDevice->SetDepthStencilSurface(s_pxAutoDepthBuffer);
}

void Asura_Direct3D::DestroyAutoDepthBuffers()
{
	if(s_pxAutoDepthBuffer)
	{
		s_pxAutoDepthBuffer->Release();
		s_pxAutoDepthBuffer = NULL;
	}
}

bool Asura_Direct3D::RestoreAll( const bool bForceReset, const bool bRetryOnFail)
{
	ASURA_CPU_PROFILE_SCOPED_TIMER( "Asura_Direct3D::RestoreAll");
	
	HRESULT h;

	Asura_Render::CurrentStates.ResetTextureIndexToLast(0);
	BindDefaultTargets();

	void ShutdownRMVB(void);
	ShutdownRMVB();
	
	ShutdownQuadBatches();

#if defined(ASURA_USE_BOTANICALS)
	// MGH 29/1/2008: The async loading thread loads botanical data and creates Direct3D resources.
	// We mustn't reset the Direct3D device during the loading of that chunk otherwise we will get
	// Direct3D resource leaks and even occasional crashes. Wait until the chunk has finished loading.
	Asura_ScopedMutex xLockMutex(Asura_Direct3D_Botanicals_Game_Render::GetLoadingMutex());
#endif

	Asura_Direct3D_LineRenderer::PreD3DReset();
	
	Asura_Glow_System::PreReset();

	// KJL - 2004/7/11 - vertex & pixel shaders survive reset on DX9,
	// so no need to call Asura_Direct3D_Shaders::ReleaseShaders(); as before

	// RJF - 14 September 2004 - D3DPOOL_DEFAULT resources, OTOH, do not. Kill the BackBufferCapture system.
	//							 If anyone's using it it'll be started up again on the first frame after the reset.
	Asura_Direct3D_BackBufferCapture::Shutdown();

	//  RJF:  (16/11/2004 12:49:30):  Likewise for the shadow buffers
	Asura_Direct3D_ShadowBufferSystem::PreReset();

#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
	// KOR - and the character renderer
	Asura_Direct3D_CharacterRenderer::PreReset();
#endif

	// and the new RenderTargetManager system
	Asura_Direct3D_RenderTargetManager::PreD3DReset();

#if defined( ASURA_USE_CARPET)
	Asura_Direct3D_Carpet_Render::PreD3DReset();
#endif
#if defined( ASURA_USE_BOTANICALS)
	Asura_Direct3D_Botanicals_Game_Render::PreD3DReset();
#endif

#if defined( ASURA_USE_FASTPATH_PFX)
	Asura_Platform_PFX_System_FastPath::PreD3DReset();
#endif

#ifdef ASURA_TOOLS
	TheAviWriter.PreD3DReset();
#endif

	{
		Asura_LinkList< Asura_Direct3D_Reset_PreCallbackData>& xList = s_tList_xPreResetCallbacks;
		for( Asura_ListElt< Asura_Direct3D_Reset_PreCallbackData>* pxIt = xList.Front(); NULL != pxIt; pxIt = pxIt->Next())
		{
			Asura_Direct3D_Reset_PreCallbackData& xCallback = pxIt->Data();
			
			if( NULL == xCallback.m_pfnPreCallback)
			{
				continue;
			}
			
			#if defined( ASURA_ASSERT)
			bool bSuccess = 
			#endif
			(xCallback.m_pfnPreCallback)( xCallback.m_pData);
			Asura_Assert( true == bSuccess, "Pre D3D Reset Callback did not work");
		}
	}

	DestroyAutoDepthBuffers();

	bool bRet = true;
	
	// This is only supported when we have a lockable backbuffer
	D3DPRESENT_PARAMETERS d3dpp;
	s_ppxSwapChains[0]->GetPresentParameters(&d3dpp);
	if(d3dpp.Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER) 
	{
		h = D3DDevice->SetDialogBoxMode( FALSE);
		ASURA_D3D_OK( h);
	} 
#ifdef ASURA_ASSERT
	else 
	{
		h = D3DDevice->SetDialogBoxMode( TRUE);
		ASURA_D3D_OK( h);
	}
#endif

	// Adam A. Brown (7.12.2004 14:52:32) : This reset is where the video mode is changed.
	// The problem previously was that if reset failed, ResetStates would crash. It is
	// entirely possible, however, for reset to fail in a supported video mode, so the
	// only way to handle this is to put everything back into a mode that is pretty well
	// guaranteed to be valid, and return false, so that the project can handle it.
	if (bForceReset) 
	{
		bool bSecondAttemptWorked = true;

		// Adam A. Brown (14.12.2004 16:30:35) : There seems to be a problem on some
		// geforce cards whereby antialiased modes can only be set on the second
		// attempt. This may actually be something to do with the DirectX 9 antialiasing
		// quality setting (but that's just a guess).
		if (D3DDevice->Reset(s_pxPresentParameters) != D3D_OK)
		{
			while( FAILED( h = D3DDevice->TestCooperativeLevel() ) )
			{
				// Surrender the rest of our thread timeslice
				Sleep(0);

				// If the device was lost, do not render until we get it back
				if( D3DERR_DEVICELOST == h )
				{	
					continue;
				}
	        
				if( D3DERR_DEVICENOTRESET == h )
				{	
					if (!SUCCEEDED(h = D3DDevice->Reset(s_pxPresentParameters)))
					{
						//Asura_Assert( 0, "Reset failed!" );
						bSecondAttemptWorked = false;
					}

					break;
				}

				if (D3DERR_DRIVERINTERNALERROR == h)
				{
					Asura_Assert( 0, "Internal Driver Error!" );
					bSecondAttemptWorked = false;
					break;
				}
			}

			if(!bSecondAttemptWorked)
			{
				if( false == bRetryOnFail)
				{
					return( false);
				}
				
				bRet = false;

				if (Asura_Windows::RunningFullScreen())
				{
					for(u_int uHead = 0; uHead < s_uNumPresentParameters; ++uHead)
					{
						s_pxPresentParameters[uHead].Windowed = FALSE;
						s_pxPresentParameters[uHead].FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
						s_pxPresentParameters[uHead].PresentationInterval = D3DPRESENT_INTERVAL_ONE;
						s_pxPresentParameters[uHead].BackBufferFormat = Asura_Direct3D_UserOptions::GetBackBufferFormat();
					}
				}
				else
				{
					Asura_Assert(s_uNumPresentParameters == 1, "Can't go into windowed mode on a multihead device");
					s_pxPresentParameters[0].Windowed = TRUE;
					s_pxPresentParameters[0].FullScreen_RefreshRateInHz = 0;
					s_pxPresentParameters[0].PresentationInterval = 0;
					s_pxPresentParameters[0].BackBufferFormat = d3ddm.Format;
				}

				// Adam A. Brown (16.12.2004 10:50:52) : The antialiasing setting can affect whether a mode
				// is achieveable or not, so we turn it off for safety.
				for(u_int uHead = 0; uHead < s_uNumPresentParameters; ++uHead)
				{
					s_pxPresentParameters[0].MultiSampleType = D3DMULTISAMPLE_NONE;
					s_pxPresentParameters[0].MultiSampleQuality = 0;
				}

				// Adam A. Brown (16.12.2004 10:50:0) : The assumption that 640 x 480 will always be
				// available is flawed. So I'm looping over all modes, finding the available mode closest to
				// that resolution.
				float fMinError = Asura_Maths::LargeFloat;
				int iModeNumber = -1;

				for(u_int u = 0; u < GetNumValidVideoModes(); u++)
				{
					D3DDISPLAYMODE xMode = GetValidVideoMode(u);

					if(xMode.Format != s_pxPresentParameters[0].BackBufferFormat) continue;

					float fError = Asura_Maths::Abs(static_cast<float>(xMode.Width - 640)) / 640.f + Asura_Maths::Abs(static_cast<float>(xMode.Height - 480)) / 480.f;

					if(fError < fMinError)
					{
						fMinError = fError;
						iModeNumber = static_cast<int>(u);
					}
				}

				if(iModeNumber >= 0)
				{
					D3DDISPLAYMODE xMode = GetValidVideoMode(static_cast<u_int>(iModeNumber));

					for(u_int uHead = 0; uHead < s_uNumPresentParameters; ++uHead)
					{
						s_pxPresentParameters[uHead].BackBufferWidth = xMode.Width;
						s_pxPresentParameters[uHead].BackBufferHeight = xMode.Height;
					}
					
					if(D3DDevice->Reset(s_pxPresentParameters) != D3D_OK)
					{
						//Now we're really messed. This is irrecoverable, so I'll just assert
						//If one were being paranoid, one might be tempted to try the reset with
						//all available enumerated modes until one worked...
						Asura_Assert(false, "Fatal error - could not find any valid mode");
					}
				}
				else
				{
					Asura_Assert(false, "Fatal error - could not find any valid mode");
				}
			}
		}
	}


	{
		// Test the cooperative level to see if it's okay to render
		while( FAILED( h = D3DDevice->TestCooperativeLevel() ) )
		{
			Sleep(0);
			// If the device was lost, do not render until we get it back
			if( D3DERR_DEVICELOST == h )
			{
				continue;
			}
        
			if( D3DERR_DEVICENOTRESET == h )
			{	
				HRESULT xResult = D3DDevice->Reset(s_pxPresentParameters);
				ASURA_D3D_OK( xResult);
				if (!SUCCEEDED( xResult))
				{
					Asura_Assert( 0, "Reset failed!" );
					// DLH - 19 January 2007 10:48:06 - I added this so the program quits at this point. The only times I have got the code to go here it just keeps looping. Which hands the system.
					// DLH - 19 January 2007 10:48:06 - If it a problem, please just comment it out (do not delete the line) as it also help me track resource leaks using PIX.
					throw "D3D Reset Failed";
				}
			}

			if (D3DERR_DRIVERINTERNALERROR == h)
			{
				Asura_Assert( 0, "Internal Driver Error!" );
			}
		
		}
	}

	CreateAutoDepthBuffers();

#if defined ASURA_TOOLS || defined ASURA_ASSERT
	// allow dialog boxes (e.g. asserts) to appear over full screen mode
	if(s_pxPresentParameters[0].Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER)
	{
		h = D3DDevice->SetDialogBoxMode( TRUE);
		ASURA_D3D_OK( h);
	}
#endif

	Asura_Viewport::Set(s_pxPresentParameters[0].BackBufferWidth, s_pxPresentParameters[0].BackBufferHeight);

#ifdef ASURA_GUI_MENU_ALWAYS_WIDESCREEN
	Asura_GUIMenu_System::SetVirtualResolution( 1280.0f, (1280.0f / s_pxPresentParameters[0].BackBufferWidth) * s_pxPresentParameters[0].BackBufferHeight );
#endif

#if defined( ASURA_USE_FASTPATH_PFX)
	Asura_Platform_PFX_System_FastPath::PostD3DReset();
#endif

#if defined( ASURA_USE_BOTANICALS)
	Asura_Direct3D_Botanicals_Game_Render::PostD3DReset();
#endif
#if defined( ASURA_USE_CARPET)
	Asura_Direct3D_Carpet_Render::PostD3DReset();
#endif

	Asura_Direct3D_RenderTargetManager::PostD3DReset();

	Asura_Direct3D_ShadowBufferSystem::PostReset();

	Asura_Glow_System::PostReset();

#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
	Asura_Direct3D_CharacterRenderer::PostReset();
#endif

#if defined (ASURA_TARGET_SM3)
	Asura_Direct3D_Fog::UpdateForNewParameters();
#endif

#if defined(ASURA_USE_MATERIAL_RESPONSE_SYSTEM)
#if ASURA_LOAD_EFFECT_FILES == 1 && !defined( ASURA_NO_LOAD_EFFECTS_FILES_FROM_FILES)
#ifdef ASURA_TOOLS
	if (Asura_Direct3D_MRPShaderCompile::GetLoadShadersFromFile())
	{
		Asura_Direct3D_MaterialResponse::Reset();
	}
#endif
#endif
#endif

	{
		Asura_LinkList< Asura_Direct3D_Reset_PostCallbackData>& xList = s_tList_xPostResetCallbacks;
		for( Asura_ListElt< Asura_Direct3D_Reset_PostCallbackData>* pxIt = xList.Front(); NULL != pxIt; pxIt = pxIt->Next())
		{
			Asura_Direct3D_Reset_PostCallbackData& xCallback = pxIt->Data();
			
			if( NULL == xCallback.m_pfnPostCallback)
			{
				continue;
			}
			
			#if defined( ASURA_ASSERT)
			bool bSuccess = 
			#endif
			(xCallback.m_pfnPostCallback)( xCallback.m_pData);
			Asura_Assert( true == bSuccess, "Post D3D Reset Callback did not work");
		}
	}

	ResetStates();

	InitialiseQuadBatches();
	Asura_Direct3D_LineRenderer::PostD3DReset();

#if ASURA_LOAD_EFFECT_FILES == 1 && !defined( ASURA_NO_LOAD_EFFECTS_FILES_FROM_FILES)
	Asura_Direct3D_Effects_System::Tools_ReloadAllEffects();
#endif

#ifdef ASURA_TOOLS
	TheAviWriter.PostD3DReset();
#endif

	Asura_Windows::SetWindowSize(s_pxPresentParameters[0].BackBufferWidth, s_pxPresentParameters[0].BackBufferHeight);

	return bRet;
}

bool Asura_Direct3D::HasBeenInitialised(void)
{
	return Initialised; 
}

void Asura_Direct3D::SetViewingArea(void)
{
	SCOPEDPIXEVENT((0xff4488aa, L"Set viewing area and camera matrices"));
#if 1
	const Asura_ViewingArea* area = Asura_Camera::GetViewingArea();

	if( NULL != D3DDevice)
	{
		Direct3D_Viewport vp;
		vp.X      = Asura_Maths::FloatToInt(Asura_Viewport::GetWidth()* area->m_fXOffset);
		vp.Y      = Asura_Maths::FloatToInt(Asura_Viewport::GetHeight()* area->m_fYOffset);
		vp.Width  = Asura_Maths::FloatToInt(Asura_Viewport::GetWidth()* area->m_fWidth);
		vp.Height = Asura_Maths::FloatToInt(Asura_Viewport::GetHeight()* area->m_fHeight);
		vp.MinZ   = s_fViewportMinZ;
		vp.MaxZ   = s_fViewportMaxZ;

		D3DDevice->SetViewport(&vp);
	}
#endif
	const Asura_Vector_2&	xSubPixelOffset		= Asura_Render::GetSubPixelOffset();
	const Asura_Vector_3*	cameraPosition		= Asura_Camera::GetPosition();
	const Asura_Matrix_3x3*	cameraOrientation	= Asura_Camera::GetOrientation();

	float					fOffsetX			= xSubPixelOffset.x / static_cast<float>(Asura_Viewport::GetWidth() / 2);
	float					fOffsetY			= xSubPixelOffset.y / static_cast<float>(Asura_Viewport::GetHeight() / 2);


	D3DXMATRIX matWorld, matView, matProj, matSubPixelOffset;


	D3DXMatrixTranslation( &matWorld, 0,0,0 );
	D3DXMatrixTranslation( &matView, 0,0,0 );
	D3DXMatrixTranslation( &matProj, -cameraPosition->x, -cameraPosition->y, -cameraPosition->z );
	D3DXMatrixTranslation( &matSubPixelOffset, fOffsetX,-fOffsetY,0 );

	matView._11 = cameraOrientation->e11;
	matView._12 = -cameraOrientation->e12;
	matView._13 = cameraOrientation->e13;
	matView._21 = cameraOrientation->e21;
	matView._22 = -cameraOrientation->e22;
	matView._23 = cameraOrientation->e23;
	matView._31 = cameraOrientation->e31;
	matView._32 = -cameraOrientation->e32;
	matView._33 = cameraOrientation->e33;

	matView = matProj*matView;

	float znear = Asura_Camera::GetNearClip();
	float zfar = Asura_Camera::GetFarClip();
	float fovy = Asura_Camera::GetVerticalFieldOfView();
	float aspect = Asura_Camera::GetXOverYAspectRatio();

	D3DXMatrixPerspectiveFovLH(&matProj, fovy, aspect, znear, zfar);

	if (Asura_Render::ShouldRenderMirrored())
	{
		matProj._11 = -matProj._11;
	}

	matProj	= matProj * matSubPixelOffset;

	if( NULL != D3DDevice)
	{
		D3DDevice->SetTransform( D3DTS_WORLD, &matWorld);
	}
	SetCameraMatrices(matView, matProj);
}

void Asura_Direct3D::SetCameraMatrices(const D3DXMATRIX& xView, const D3DXMATRIX& xProj)
{
	s_xViewMatrix = xView;
	s_xProjectionMatrix = xProj;
	s_xViewProjMatrix = s_xViewMatrix * s_xProjectionMatrix;

	if( NULL != D3DDevice)
	{
		D3DDevice->SetTransform( D3DTS_VIEW,       &s_xViewMatrix );
		D3DDevice->SetTransform( D3DTS_PROJECTION, &s_xProjectionMatrix );
	}
}

u_int Asura_Direct3D::GetNumValidVideoModes()
{
    return D3DObject->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
}

D3DDISPLAYMODE Asura_Direct3D::GetValidVideoMode(u_int uModeNo)
{       
    Asura_Assert(uModeNo < GetNumValidVideoModes(), "invalid video mode requested");
 
    D3DDISPLAYMODE xTempMode;
#ifdef ASURA_ASSERT
    HRESULT hRes = 
#endif
	D3DObject->EnumAdapterModes(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8, uModeNo, &xTempMode);

    Asura_Assert(SUCCEEDED(hRes), "Error getting adapter mode");
    return xTempMode;

}

bool Asura_Direct3D::IsDisplayModeValid(const u_int uWidth, const u_int uHeight, const D3DFORMAT xFormat, const u_int uRefreshRate)
{
	const u_int	uNumDisplayModes	= GetNumValidVideoModes();

	for(u_int u = 0; u < uNumDisplayModes; u++)
	{
		const D3DDISPLAYMODE	xDisplayMode	= GetValidVideoMode(u);

		if((uWidth == xDisplayMode.Width) && (uHeight == xDisplayMode.Height))
		{
			const bool	bHasFormat		= (xFormat != D3DFMT_UNKNOWN);
			const bool	bHasRefreshRate	= (uRefreshRate > 0);

			if(bHasFormat)
			{
				if( (xFormat == xDisplayMode.Format) &&
					(!bHasRefreshRate || (uRefreshRate == xDisplayMode.RefreshRate)) )
				{
					return(true);
				}
			}
			else
			{
				if(!bHasRefreshRate || (uRefreshRate == xDisplayMode.RefreshRate))
				{
					return(true);
				}
			}
		}
	}

	return(false);
}

bool Asura_Direct3D::SetVideoMode( const int width, const int height, const int depth, const bool fsaa, const bool bRetryOnFail)
{
	if( !D3DDevice ) return false;

	// KJFL - 2004-02-24 11:32:08 - note: the fsaa & depth parameters are not used!

	D3DDevice->EvictManagedResources();

	Asura_Direct3D_UserOptions::GetCurrentOptions().SetBackBufferDimensions(width, height);
	Asura_Direct3D::RecalcPresentParameters();

	return RestoreAll( true, bRetryOnFail);
}
	
void Asura_Direct3D::ResetStates(void)
{
	SCOPEDPIXEVENT((0xff000000, L"Reset all renderstates"));

	D3DDevice->SetRenderState(D3DRS_CLIPPING, true);
	
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);

	D3DDevice->SetRenderState(D3DRS_SPECULARENABLE, false);
	Asura_Render::CurrentStates.RequestAlphaTest(false);

	//D3DDevice->SetRenderState(D3DRS_LASTPIXEL, TRUE);
	//D3DDevice->SetRenderState(D3DRENDERSTATE_EDGEANTIALIAS, TRUE);
	//D3DDevice->SetRenderState(D3DRENDERSTATE_ANTIALIAS,D3DANTIALIAS_SORTDEPENDENT);



	Asura_Render::CurrentStates.ResetAll();


	//D3DDevice->SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACEPOSITION);
	D3DDevice->SetTextureStageState(1,D3DTSS_TEXCOORDINDEX,1);
//POINT);//_LINEAR);


	if (Asura_Direct3D_UserOptions::IsAnisotropicFilteringOn())
	{ 
		// KJL - 16/09/2003 12:23:55 - some nVidia cards lack a mag or min anisotropic setting - so we'll
		// make sure they're set to good defaults before attempting aniostropic.
		D3DDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );

		D3DCAPS9 xCaps;
		D3DDevice->GetDeviceCaps( &xCaps );

		if( xCaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC )
		{
			D3DDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC );
			D3DDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC );
		}

		if( xCaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC )
		{
			D3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC );
			D3DDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC );
		}

		D3DDevice->SetSamplerState( 0, D3DSAMP_MAXANISOTROPY, Asura_Direct3D_UserOptions::GetAnisotropySetting() );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MAXANISOTROPY, Asura_Direct3D_UserOptions::GetAnisotropySetting() );
	}
	else
	{
		D3DDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	}

	D3DDevice->SetSamplerState( 2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	D3DDevice->SetSamplerState( 2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );

	if (Asura_Direct3D_UserOptions::IsTrilinearFilteringOn())
	{
		D3DDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	}
	else
	{
		D3DDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_POINT );
		D3DDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_POINT );
	}

	D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
	D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );
	D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
	D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
	
}

#include "Core/Asura_Timers.h"

static bool g_bFirstZClearThisFrame = false;

void Asura_Direct3D::ClearZBuffer(void)
{
	SCOPEDPIXEVENT((0xff000000, L"Clear buffers"));

	int clearFlags = D3DCLEAR_ZBUFFER;

	if (UsingStencil)
	{
		clearFlags|=D3DCLEAR_STENCIL;
	}

	if( g_bFirstZClearThisFrame )
    {
		if( Asura_Render::GetWireframe() )
	    {	
		    clearFlags|=D3DCLEAR_TARGET;
	    }

        g_bFirstZClearThisFrame = false;
    }

	D3DDevice->Clear(0,NULL,clearFlags,D3DCOLOR_ARGB(0,0,0,0),1.0f,0);
}

void Asura_Direct3D::BeginFrame(void)
{
	SCOPEDPIXEVENT((0xffffffff, L"Begin frame"));

	InvalidateSwapChain();
	BindSwapChain(0);

    g_bFirstZClearThisFrame = true;

	HRESULT hr;
	
	Direct3D_Viewport vp;
	vp.X      = 0;
	vp.Y      = 0;
	vp.Width  = Asura_Viewport::GetWidth();
	vp.Height = Asura_Viewport::GetHeight();
	vp.MinZ   = 0.0f;
	vp.MaxZ   = 1.0f;

	D3DDevice->SetViewport(&vp);

	hr = D3DDevice->BeginScene();

	Asura_Assert(SUCCEEDED(hr),"BeginScene returned error");

	FrameHasBegun = true;

	Asura_Direct3D_BackBufferCapture::AdviseNextFrame();
	Asura_Direct3D_ShadowBufferSystem::BeginNewFrame();

	ClearZBuffer();

	// Clear all swap chains other than chain 0 to black
	for(u_int uChain = 1; uChain < s_uNumSwapChains; ++uChain)
	{
		RequestSwapChain(uChain);
		D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0, 0, 0);
	}
	RequestSwapChain(0);

	D3DDevice->SetRenderState(D3DRS_AMBIENT,0);
#if 01
	Direct3D_Material mat;

	// Set the RGBA for diffuse reflection. 
	mat.Diffuse.r = 0.5f; 
	mat.Diffuse.g = 0.5f; 
	mat.Diffuse.b = 0.5f; 
	mat.Diffuse.a = 0.5f; 

	// Set the RGBA for ambient reflection. 
	mat.Ambient.r = 1.0f; 
	mat.Ambient.g = 1.0f; 
	mat.Ambient.b = 1.0f; 
	mat.Ambient.a = 1.0f; 

	// Set the color and sharpness of specular highlights. 
	mat.Specular.r = 0.0f; 
	mat.Specular.g = 0.0f; 
	mat.Specular.b = 0.0f; 
	mat.Specular.a = 0.0f; 
	mat.Power = 0.0f; 

	mat.Emissive.r = 0.5f;
	mat.Emissive.g = 0.5f;
	mat.Emissive.b = 0.5f;
	mat.Emissive.a = 0.5f;

	D3DDevice->SetMaterial(&mat);

	D3DDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
	D3DDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
	D3DDevice->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
#else
	D3DDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);

#endif


	// Do something about fog 
	if (Asura_Fog::IsFogRenderable())
	{
		float fStart = Asura_Fog::GetFogNearPlane();
		float fEnd = Asura_Fog::GetFogFarPlane();

		// at least on ATI cards, trying to set fog parameters with fog turned off doesn't do anything...
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);

		// Set the fog color.
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGCOLOR, Asura_Fog::GetFogColourAsUint());
	  
		// set the linear range info
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGSTART, *(DWORD *)(&fStart));
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGEND,   *(DWORD *)(&fEnd));

		// we turn off fog so individual renderers can turn it on if they want it
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
	}
	else
	{
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_NONE);
	}
}

#include "Rendering/Asura_Text_Output.h"
void Asura_Direct3D::EndFrame(void)
{
	if( NULL == Asura_Direct3D::D3DDevice)
	{
		return;
	}

	SCOPEDPIXEVENT((0xffffffff, L"End frame"));

	BindDefaultTargets();
	HRESULT hr;
#ifdef PIX_DEBUG_ALTERNATE_FINAL_TARGET
	Direct3D_SwapChain* pxChain = GetSwapChain(0);
	if(pxChain)
	{
		SCOPEDPIXEVENT((0xffffffff, L"Bind swap chain %u", 0));

		Direct3D_Surface* pxBackbuffer;
		pxChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pxBackbuffer);
		D3DDevice->SetRenderTarget(0, pxBackbuffer);
		pxBackbuffer->Release();

		for(u_int uRT = 1; uRT < Asura_Direct3D_Caps::GetMaxNumberOfRenderTargets(); ++uRT)
		{
			D3DDevice->SetRenderTarget(uRT, NULL);
		}

		D3DDevice->SetDepthStencilSurface(s_pxAutoDepthBuffer);

		s_uCurrentlyBoundSwapChain = 0xFFFFFFFF;

		//Set the viewport of a clipping rectangle has been set. RAR 06/09/2006
		if(s_bUseClipRect)
		{
			Asura_Bounding_Rectangle xDisplayRect = GetSwapChainDisplayLimits(s_uCurrentlyBoundSwapChain);

			Direct3D_Viewport vp;
			vp.X      = Asura_Maths::FloatToInt(s_fClipRectX-xDisplayRect.MinX);
			vp.Y      = Asura_Maths::FloatToInt(s_fClipRectY-xDisplayRect.MinY);
			vp.Width  = Asura_Maths::FloatToInt(s_fClipRectWidth);
			vp.Height = Asura_Maths::FloatToInt(s_fClipRectHeight);
			vp.MinZ   = 0.0f;
			vp.MaxZ   = 1.0f;

			D3DDevice->SetViewport(&vp);
		}
	}

	Asura_Render::CurrentStates.RequestTextureIndex( 0, ASURA_TEXTURE_INVALID);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_QUADVERTEX);
	Asura_Render::CurrentStates.RequestZBufferEnabled( false);
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
	Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_OFF);
	Asura_Render::CurrentStates.RequestAlphaTest(false);
	Asura_Render::CurrentStates.RequestWireFrameMode(false);
	if( Asura_Direct3D::UseGammaCorrection() )
	{
		// Now we're using an 8888 RT, enable sRGB writes
		Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, TRUE );
	}

	Asura_Direct3D_Shaders::RequestVertexShader( ASURA_D3D_VS_DEFERRED_DEBUGSHOWBUFFERS);


	Direct3D_Texture *pxFrameTexture = s_pxFinalRenderingTarget->GetTexture(0);
	Asura_Direct3D_Shaders::RequestPixelShader( ASURA_D3D_PS_DEFERRED_DEBUGSHOWBUFFERS);
	float afConstants[] = {
		1.0f, 0.0f, 0.0f, 0.0f, 
		0.0f, 1.0f, 0.0f, 0.0f, 
		0.0f, 0.0f, 1.0f, 0.0f, 
		0.0f, 0.0f, 0.0f, 1.0f, 
		0.0f, 0.0f, 0.0f, 0.0f, 
	};
	Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 0, afConstants, 5);
	Asura_Direct3D::D3DDevice->SetTexture( 0, pxFrameTexture);
	const bool bSRGB = Asura_Direct3D::UseGammaCorrection();
	Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_SRGBTEXTURE, bSRGB );

	struct _D3DQuadVertex
	{
	public:
		float X, Y, Z;
		float U, V;
	};
	{
		_D3DQuadVertex axVert[4] =
		{
			{ -1.0f, 1.0f, 1.0f, 0.0f, 0.0f},
			{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f},
			{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f},
			{ 1.0f, -1.0f, 1.0f, 1.0f, 1.0f}
		};

		Asura_Direct3D::D3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, axVert, sizeof( axVert[0]));
	}
	Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_SRGBTEXTURE, FALSE );
#endif
	
//void RenderExpTexture(void);
//RenderExpTexture();

//	void EndEnv(void);
//	EndEnv();

	hr = D3DDevice->EndScene();
	Asura_Assert(SUCCEEDED(hr),"EndScene returned error");

	// Are we taking any screenshots?
	if (Asura_ScreenShot_System::IsScreenShotPending())
	{
		// Yes - handle them...
		Asura_ScreenShot_System::DrawBufferIsReady	();
	}

	FrameHasBegun = false;

	ASURA_CPU_PROFILE__START_LABEL( ASURA_CPU_PROFILE__PRESENT_FRAME, "Present" );

	bool bDeviceLost = false;

	for(u_int uChain = 0; uChain < s_uNumSwapChains; ++uChain)
	{
		if(s_ppxSwapChains[uChain]->Present(NULL, NULL, NULL, NULL, 0) == D3DERR_DEVICELOST)
		{
			bDeviceLost = true;
		}
	}

	if(bDeviceLost)
	{
		do
		{
			MSG msg;
			if(GetMessage(&msg, NULL, 0, 0))
			{
				DispatchMessage(&msg);
			}
			else
			{
				break;
			}
		}
		while(D3DDevice->TestCooperativeLevel() != D3DERR_DEVICENOTRESET);

		bDeviceLost = false;
		Asura_Direct3D::RestoreAll();
	}
	
	ASURA_CPU_PROFILE__STOP( ASURA_CPU_PROFILE__PRESENT_FRAME);

}

void Asura_Direct3D::FlushFrame(void)
{
	FlushQuadBatches();

    Asura_Direct3D_LineRenderer::Flush<Asura_Direct3D_LineRenderer::Line2D>();
	Asura_Direct3D_LineRenderer::Flush<Asura_Direct3D_LineRenderer::Line3D>();

	// Go back to default display device
	Asura_Direct3D::RequestSwapChain(0);
}

void Asura_Direct3D::BindDefaultTargets()
{
	RequestSwapChain(0);
}

#ifdef ASURA_TARGET_SM3

DEBUGVAR int dbg_iMRTBufferToCopy = 0;

void Asura_Direct3D::BeginDeferredRendering()
{
	HRESULT xResult;
	SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Begin deferred rendering" ));

	Asura_Assert(s_pxDeferredRenderingTarget, "No deferred rendering target exists");
	if( NULL == s_pxDeferredRenderingTarget) 
	{
		return;
	}
	
	s_pxDeferredRenderingTarget->Bind();

	// No need to use sRGB writes to the float buffers, we'll convert when we do tone mapping
	Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, FALSE );

	xResult = Asura_Direct3D::D3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0);
	ASURA_D3D_OK( xResult);
	
	s_bIsUsingDeferredRenderingTarget = true;
}

#endif


void Asura_Direct3D::ResetClippingRectangle()
{
	if( NULL == Asura_Direct3D::D3DDevice)
	{
		return;
	}

	SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Reset clipping rectangle" ));

	s_bUseClipRect = false;
	Direct3D_Viewport vp;
	vp.X      = 0;
	vp.Y      = 0;
	vp.Width  = Asura_Viewport::GetWidth();
	vp.Height = Asura_Viewport::GetHeight();
	vp.MinZ   = 0.0f;
	vp.MaxZ   = 1.0f;

	D3DDevice->SetViewport(&vp);
}

void Asura_Direct3D::SetClippingRectangle(float fX,float fY,float fWidth,float fHeight)
{
	//Store the values so they can be set again when BindSwapChain is used. RAR 06/09/2006
	s_bUseClipRect = true;
	s_fClipRectX = fX;
	s_fClipRectY = fY;
	s_fClipRectWidth = fWidth;
	s_fClipRectHeight = fHeight;

	//set the current viewport using these values
	Asura_Bounding_Rectangle xDisplayRect = GetSwapChainDisplayLimits(s_uCurrentlyBoundSwapChain);

	Direct3D_Viewport vp;
	vp.X      = Asura_Maths::FloatToInt(s_fClipRectX-xDisplayRect.MinX);
	vp.Y      = Asura_Maths::FloatToInt(s_fClipRectY-xDisplayRect.MinY);
	vp.Width  = Asura_Maths::FloatToInt(s_fClipRectWidth);
	vp.Height = Asura_Maths::FloatToInt(s_fClipRectHeight);
	vp.MinZ   = 0.0f;
	vp.MaxZ   = 1.0f;

	D3DDevice->SetViewport(&vp);
}

bool Asura_Direct3D::GetClippingRectangle(float& fX,float& fY,float& fWidth,float& fHeight)
{
	fX = s_fClipRectX;
	fY = s_fClipRectY;
	fWidth = s_fClipRectWidth;
	fHeight = s_fClipRectHeight;

	return s_bUseClipRect;
}

void Asura_Direct3D::RequestSwapChain(u_int uSwapChainIndex)
{
#ifdef ASURA_TARGET_SM3
	// JAT 27/11/06 ignore this request if inside the deferred rendering loop
	if ( s_bIsUsingDeferredRenderingTarget == true ) 
	{
		return;
	}
#endif // #ifdef ASURA_TARGET_SM3

	if( s_uCurrentlyBoundSwapChain != uSwapChainIndex) 
	{
		BindSwapChain(uSwapChainIndex);
	}
}

void Asura_Direct3D::InvalidateSwapChain()
{
	s_uCurrentlyBoundSwapChain = 0xFFFFFFFF;
}

void Asura_Direct3D::BindSwapChain(u_int uSwapChainIndex)
{
#ifdef PIX_DEBUG_ALTERNATE_FINAL_TARGET
	if (uSwapChainIndex == 0)
	{
		s_pxFinalRenderingTarget->Bind();

		//Set the viewport of a clipping rectangle has been set. RAR 06/09/2006
		if(s_bUseClipRect)
		{
			Asura_Bounding_Rectangle xDisplayRect = GetSwapChainDisplayLimits(s_uCurrentlyBoundSwapChain);

			Direct3D_Viewport vp;
			vp.X      = Asura_Maths::FloatToInt(s_fClipRectX-xDisplayRect.MinX);
			vp.Y      = Asura_Maths::FloatToInt(s_fClipRectY-xDisplayRect.MinY);
			vp.Width  = Asura_Maths::FloatToInt(s_fClipRectWidth);
			vp.Height = Asura_Maths::FloatToInt(s_fClipRectHeight);
			vp.MinZ   = 0.0f;
			vp.MaxZ   = 1.0f;

			D3DDevice->SetViewport(&vp);
		}

		return;
	}
#endif
	Direct3D_SwapChain* pxChain = GetSwapChain(uSwapChainIndex);
	if(!pxChain) return;

	SCOPEDPIXEVENT((0xffffffff, L"Bind swap chain %u", uSwapChainIndex));

	Direct3D_Surface* pxBackbuffer;
	pxChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pxBackbuffer);
	D3DDevice->SetRenderTarget(0, pxBackbuffer);
	pxBackbuffer->Release();

	for(u_int uRT = 1; uRT < Asura_Direct3D_Caps::GetMaxNumberOfRenderTargets(); ++uRT)
	{
		D3DDevice->SetRenderTarget(uRT, NULL);
	}

	if(uSwapChainIndex == 0)
	{
		D3DDevice->SetDepthStencilSurface(s_pxAutoDepthBuffer);
	}
	else
	{
		D3DDevice->SetDepthStencilSurface(NULL);
	}

	s_uCurrentlyBoundSwapChain = uSwapChainIndex;

	//Set the viewport of a clipping rectangle has been set. RAR 06/09/2006
	if(s_bUseClipRect)
	{
		Asura_Bounding_Rectangle xDisplayRect = GetSwapChainDisplayLimits(s_uCurrentlyBoundSwapChain);

		Direct3D_Viewport vp;
		vp.X      = Asura_Maths::FloatToInt(s_fClipRectX-xDisplayRect.MinX);
		vp.Y      = Asura_Maths::FloatToInt(s_fClipRectY-xDisplayRect.MinY);
		vp.Width  = Asura_Maths::FloatToInt(s_fClipRectWidth);
		vp.Height = Asura_Maths::FloatToInt(s_fClipRectHeight);
		vp.MinZ   = 0.0f;
		vp.MaxZ   = 1.0f;

		D3DDevice->SetViewport(&vp);
	}
}

Asura_Bounding_Rectangle Asura_Direct3D::GetSwapChainDisplayLimits(u_int uSwapChainIndex)
{
	if(uSwapChainIndex >= s_uNumSwapChains || uSwapChainIndex >= s_uNumPresentParameters) return Asura_Bounding_Rectangle(0, 0, 0, 0);

	// Get this chain's present-params
	D3DPRESENT_PARAMETERS& xPP(s_pxPresentParameters[uSwapChainIndex]);

#ifdef ASURA_SUPPORT_MULTIPLE_MONITORS
	if(Asura_Windows::RunningFullScreen())
	{

		HWND hWnd = xPP.hDeviceWindow;

		// Find out which monitor this window is on
		HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);

		// Get info on this monitor
		MONITORINFO xMI; 
		ZeroMemory(&xMI, sizeof(xMI)); xMI.cbSize = sizeof(xMI);
		GetMonitorInfo(hMonitor, &xMI);

		Asura_Bounding_Rectangle xRect;
		xRect.MinX = static_cast<float>(xMI.rcMonitor.left);
		xRect.MinY = static_cast<float>(xMI.rcMonitor.top);
		xRect.MaxX = static_cast<float>(xMI.rcMonitor.right);
		xRect.MaxY = static_cast<float>(xMI.rcMonitor.bottom);

		// Ensure that secondary monitors are 'nestled against' the primary
		if(uSwapChainIndex > 0)
		{
			const Asura_Bounding_Rectangle xPrimaryRect(GetSwapChainDisplayLimits(0));

			const Asura_Vector_2 xPrimaryCenter(xPrimaryRect.CalculateCentre());
			const Asura_Vector_2 xMonitorCenter(xRect.CalculateCentre());
			Asura_Vector_2 xOffset(xMonitorCenter - xPrimaryCenter);
			if(xOffset.x != 0.f)
			{
				xOffset /= (xOffset.x > 0) ? xOffset.x : -xOffset.x;
				xOffset *= (xPrimaryRect.GetWidth() + static_cast<float>(xPP.BackBufferWidth)) * 0.5f;
			}
			else
			{
				Asura_Vector_2 xOffset(xMonitorCenter - xPrimaryCenter);
				xOffset /= (xOffset.y > 0) ? xOffset.y : -xOffset.y;
				xOffset *= (xPrimaryRect.GetHeight() + static_cast<float>(xPP.BackBufferHeight)) * 0.5f;
			}
			xRect.MinX = xPrimaryCenter.x + xOffset.x - static_cast<float>(xPP.BackBufferWidth) * 0.5f;
			xRect.MinY = xPrimaryCenter.y + xOffset.y - static_cast<float>(xPP.BackBufferHeight) * 0.5f;
			xRect.MaxX = xPrimaryCenter.x + xOffset.x + static_cast<float>(xPP.BackBufferWidth) * 0.5f;
			xRect.MaxY = xPrimaryCenter.y + xOffset.y + static_cast<float>(xPP.BackBufferHeight) * 0.5f;
		}

		return xRect;
	}
	else
#endif
	{
		Asura_Bounding_Rectangle xRect;
		xRect.MinX = 0;
		xRect.MinY = 0;
		xRect.MaxX = static_cast<float>(xPP.BackBufferWidth);
		xRect.MaxY = static_cast<float>(xPP.BackBufferHeight);
		return xRect;
	}
}

void Asura_Direct3D::InitialiseQuadBatches(void)
{
	// MGH 17/1/2008: In the unlikely event that the number of swap chains has changed we need
	// to resize the quad batch array.
	Asura_Assert(m_bQuadBatchIsValid == false, "Called InitialiseQuadBatches without shutting down first.");
	if (m_uQuadBatchNumSwapChains != Asura_Direct3D::GetNumSwapChains())
	{
		delete [] QuadBatch;
		QuadBatch = 0;
	}

	if (!QuadBatch)
	{
		QuadBatch = new Asura_Quad_Batch[NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains()];
		m_uQuadBatchNumSwapChains = Asura_Direct3D::GetNumSwapChains();
	}

	m_bQuadBatchIsValid = true;

	for(u_int i=0; i<NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains(); i++)
	{
		Asura_Assert( NULL == QuadBatch[i].VertexBuffer, "Vertex Buffer not cleared");
		
		QuadBatch[i].Clear();		

		D3DDevice->CreateVertexBuffer
		(
			NUMBER_OF_VERTICES_PER_QUAD_BATCH*sizeof(D3DQuadVertex),
			D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
			D3DFVF_QUADVERTEX,
			D3DPOOL_DEFAULT,
			&QuadBatch[i].VertexBuffer,
			NULL
		);

		QuadBatch[i].VertexBufferLocked = false;

		QuadBatch[i].uSwapChainIndex = i % Asura_Direct3D::GetNumSwapChains();
	}
	
	u_int uNumberOfIndices = (NUMBER_OF_VERTICES_PER_QUAD_BATCH*3)/2;

	Asura_Assert( NULL == Asura_Quad_Batch::s_pxIndexBuffer, "Index Buffer not cleared");
	
	Asura_Direct3D::D3DDevice->CreateIndexBuffer
	(
		sizeof(u_short)*uNumberOfIndices,
		D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
		D3DFMT_INDEX16,
		D3DPOOL_DEFAULT,
		&Asura_Quad_Batch::s_pxIndexBuffer,
		NULL
	);

	unsigned short* pusIndexData;
	Asura_Quad_Batch::s_pxIndexBuffer->Lock
	(
		0,
		sizeof(u_short)*uNumberOfIndices, 
		(void**)&pusIndexData,
		D3DLOCK_DISCARD
	);

	u_int v=0;
	for (u_int u=0; u<(NUMBER_OF_VERTICES_PER_QUAD_BATCH/4) ; u++)
	{
		*pusIndexData++ = (u_short)(v+0);
		*pusIndexData++ = (u_short)(v+1);
		*pusIndexData++ = (u_short)(v+2);
		*pusIndexData++ = (u_short)(v+2);
		*pusIndexData++ = (u_short)(v+3);
		*pusIndexData++ = (u_short)(v+0);
		v+=4;
	}
		
	Asura_Quad_Batch::s_pxIndexBuffer->Unlock();

#if defined(ASURA_TARGET_SM3)
	// DJG 13/03/2007 - now that we are able to render quads using the fx system, we also need
	// a proper vertex declaration to use in those cases
	const D3DVERTEXELEMENT9 xDecl[] =
	{
		{ 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
		{ 0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		D3DDECL_END()
	};
#if defined(ASURA_ASSERT)
	HRESULT hr =
#endif // defined(ASURA_ASSERT)
		Asura_Direct3D::D3DDevice->CreateVertexDeclaration( xDecl, &s_pxQuadBatchVertexDeclaration );

#if defined(ASURA_ASSERT)
	Asura_Assert( SUCCEEDED(hr), "Failed to create vertex declaration for quad batches" );
#endif // defined(ASURA_ASSERT)

#endif // defined(ASURA_TARGET_SM3)
}
void Asura_Direct3D::ShutdownQuadBatches(void)
{
	if(QuadBatch && m_bQuadBatchIsValid)
	{
		for(u_int i=0; i<NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains(); i++)
		{
			Direct3D_VertexBuffer*& pxVertexBuffer = QuadBatch[i].VertexBuffer;
			if( NULL != pxVertexBuffer)
			{
				pxVertexBuffer->Release();
				pxVertexBuffer = NULL;
			}
		}
	}

	// MGH 17/1/2008: No longer deletes the QuadBatch here as we don't want to reallocate
	// the memory every time we Alt-Tab. This is to fix memory fragmentation issues.
	m_bQuadBatchIsValid = false;

	if( NULL != Asura_Quad_Batch::s_pxIndexBuffer)
	{
		Asura_Quad_Batch::s_pxIndexBuffer->Release();
		Asura_Quad_Batch::s_pxIndexBuffer = NULL;
	}

#if defined(ASURA_TARGET_SM3)
	// DJG 13/03/2007 - if we created the vertex declaration for our next-gen quads then we
	// need to get rid of it when we're done
	if ( NULL != s_pxQuadBatchVertexDeclaration )
	{
		s_pxQuadBatchVertexDeclaration->Release();
		s_pxQuadBatchVertexDeclaration = NULL;
	}
#endif // defined(ASURA_TARGET_SM3)
}

void Asura_Direct3D::FlushQuadBatches(void)
{
	bool bMadePixEvent = false;

	for( u_int u = 0; u < NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains(); ++u)
	{
		if( QuadBatch[u].NumberOfVertices > 0 && !bMadePixEvent )
		{
			// Only insert the event if we're going to render anything, otherwise we end up with
			// loads of empty entries in the event view
			D3DPERFCALL( BeginEvent( 0xff94728b, L"Flush quad batches" ) );
			bMadePixEvent = true;
		}

		QuadBatch[u].FlushPolygons();
	}

	if( bMadePixEvent )
	{
		D3DPERFCALL( EndEvent() ); // Flush quad batches
	}
}


void Asura_Direct3D::AddQuadToBatch(class Asura_Overlay_Quad* quad, class Asura_Direct3D_Effect* pxOverrideEffect)
{
	for(u_int uSwapChainTarget = 0; uSwapChainTarget < Asura_Direct3D::GetNumSwapChains(); ++uSwapChainTarget)
	{
		u_int uBatchTarget;
		for(uBatchTarget=0; uBatchTarget<NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains(); uBatchTarget++)
		{
			if ((QuadBatch[uBatchTarget].uSwapChainIndex == uSwapChainTarget) && (QuadBatch[uBatchTarget].CanAddPolygonToBatch(quad, pxOverrideEffect)))
			{
				break;
			}
		}
		if(uBatchTarget == NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains())
		{
			uBatchTarget = 0;
			for (u_int i=1; i<NUMBER_OF_QUAD_BATCHES * Asura_Direct3D::GetNumSwapChains(); i++)
			{
				if ((QuadBatch[i].uSwapChainIndex == uSwapChainTarget) && (QuadBatch[i].NumberOfVertices>QuadBatch[uBatchTarget].NumberOfVertices)) uBatchTarget = i;
			}

			QuadBatch[uBatchTarget].FlushPolygons();
		}
		
		if(QuadBatch[uBatchTarget].CanAddPolygonToBatch(quad, pxOverrideEffect))
		{
			QuadBatch[uBatchTarget].AddPolygonToBatch(quad);
		}
	}
}


bool Asura_Quad_Batch::CanAddPolygonToBatch(Asura_Overlay_Quad* quad, class Asura_Direct3D_Effect* pxOverrideEffect)
{
	if (NumberOfVertices == 0)
	{
		TextureIndex = quad->TextureIndex;
		TranslucencyMode = quad->TranslucencyMode;
		m_eTextureOp = quad->m_eTextureOp;
		m_pxOverrideEffect = pxOverrideEffect;

		return true;
	}

	if (TextureIndex == quad->TextureIndex &&
		TranslucencyMode == quad->TranslucencyMode &&
		m_eTextureOp == quad->m_eTextureOp &&
		m_pxOverrideEffect == pxOverrideEffect)
	{
		return true;
	}

	return false;
}

void Asura_Quad_Batch::AddPolygonToBatch(Asura_Overlay_Quad* quad)
{
	Asura_Bounding_Rectangle xChainBounds = Asura_Direct3D::GetSwapChainDisplayLimits(uSwapChainIndex);

	if ( !VertexBufferLocked ) LockVertexBuffer();

	// JAT 07/12/07 - if the lock succeeded then put some data into it
	// If the lock failed then we are writing into bad memory and can do very nasty things esp. to video drivers
	if ( VertexBufferLocked == false )
	{
		Asura_Assert( NumberOfVertices == 0, "Failed to lock vertex buffer in AddPolygonToBatch" );
		NumberOfVertices = 0;
		return;
	}

	for(int i=0; i<4; i++, NumberOfVertices++)
	{
		Vertex[NumberOfVertices].X = quad->X[i] - xChainBounds.MinX;
		Vertex[NumberOfVertices].Y = quad->Y[i] - xChainBounds.MinY;
		Vertex[NumberOfVertices].Z = 0;
		Vertex[NumberOfVertices].RHW = 1000.0f;
		if(quad->m_bGouraudShaded)
		{
			Vertex[NumberOfVertices].Diffuse= quad->GouraudColour[i];
		}
		else
		{
			Vertex[NumberOfVertices].Diffuse= quad->Colour;
		}

		Vertex[NumberOfVertices].U = quad->U[i];
		Vertex[NumberOfVertices].V = quad->V[i];
	}

//	if (VertexBufferLocked) UnlockVertexBuffer();
	
	if (NumberOfVertices>=NUMBER_OF_VERTICES_PER_QUAD_BATCH-1)
	{
		FlushPolygons();
	}
}

void Asura_Quad_Batch::LockVertexBuffer(void)
{
	SCOPEDPIXEVENT(( 0xFF94728B, L"Lock quad vertex buffer" ));
	if (SUCCEEDED(VertexBuffer->Lock(0,sizeof(D3DQuadVertex)*NUMBER_OF_VERTICES_PER_QUAD_BATCH,(void**)&Vertex,D3DLOCK_DISCARD)))
	{
		VertexBufferLocked = true;
	}
}

void Asura_Quad_Batch::UnlockVertexBuffer(void)
{
	SCOPEDPIXEVENT(( 0xFF94728B, L"Unlock quad vertex buffer" ));
	VertexBuffer->Unlock();
	VertexBufferLocked = false;
}

void Asura_Quad_Batch::FlushPolygons()
{
	if (NumberOfVertices)
	{
		SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Quad batch" ));

		Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
		Asura_Render::CurrentStates.RequestWireFrameMode(false);
	
		//Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		if (VertexBufferLocked) UnlockVertexBuffer();

		Asura_Render::CurrentStates.RequestTranslucencyMode(TranslucencyMode);
		Asura_Render::CurrentStates.RequestTextureOpMode(m_eTextureOp);

		// (AAM - 29.05.2008) Slightly ucky - decided it would be best to disable sRGB writes for
		// overlay quads to preserve old interface behaviour
		DWORD uOldSRGBWrite;
		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Disable sRGB writes" ));
			Asura_Direct3D::D3DDevice->GetRenderState( D3DRS_SRGBWRITEENABLE, &uOldSRGBWrite );
			Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, FALSE );
		}

		// (AAM - 08.06.2004) Disable depth buffer writes - saves some bandwidth and not necessary anyway.
		// Really ought to disable reads too, but the render state request for that is not called properly by most
		// other renderers, so we'd have to restore the state afterwards which is probably too big a hit for
		// quad drawing

		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

		// (AAM - 17.01.2006) Disable antialiasing to remove fringes around quads
		DWORD ulOldMultisample;
		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Disable antialiasing" ));
			Asura_Direct3D::D3DDevice->GetRenderState( D3DRS_MULTISAMPLEANTIALIAS, &ulOldMultisample );
			Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, FALSE );
		}

		// (AAM - 21.02.2006) Need to disable anisotropic filtering too
		DWORD ulOldMaxAnisotropy;
		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Disable anisotropic filtering" ));
			Asura_Direct3D::D3DDevice->GetSamplerState( 0, D3DSAMP_MAXANISOTROPY, &ulOldMaxAnisotropy );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_MAXANISOTROPY, 1 );
		}

		// (AAM - 10.05.2007) Must ensure some effect hasn't left clamped addressing on
		DWORD ulOldAddressMode[2];
		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Set texture addressing to wrap" ));
			Asura_Direct3D::D3DDevice->GetSamplerState( 0, D3DSAMP_ADDRESSU, &ulOldAddressMode[0] );
			Asura_Direct3D::D3DDevice->GetSamplerState( 0, D3DSAMP_ADDRESSV, &ulOldAddressMode[1] );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );
		}

		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Bind quad vertex and index buffers" ));
			Asura_Direct3D::D3DDevice->SetStreamSource( 0, VertexBuffer, 0, sizeof(D3DQuadVertex) );
			Asura_Direct3D::D3DDevice->SetIndices( s_pxIndexBuffer );
		}

		Asura_Direct3D::RequestSwapChain(uSwapChainIndex);

#if defined(ASURA_TARGET_SM3)
		IDirect3DVertexDeclaration9* pxOldVertexDeclaration=NULL;
		if ( this->m_pxOverrideEffect )
		{
			// This does an AddRef - be sure to Release() it later
			Asura_Direct3D::D3DDevice->GetVertexDeclaration(&pxOldVertexDeclaration);

			Asura_Direct3D::D3DDevice->SetVertexDeclaration(Asura_Direct3D::s_pxQuadBatchVertexDeclaration);

			if ( TextureIndex != ASURA_TEXTURE_NONE && TextureIndex != ASURA_TEXTURE_INVALID )
			{
				m_pxOverrideEffect->SetParameterByName( "g_xTexture", Asura_Direct3D::GetDirect3DTextureByIndex( TextureIndex ) );
			}
			m_pxOverrideEffect->Begin();
			m_pxOverrideEffect->GetD3DEffect()->CommitChanges();

			m_pxOverrideEffect->RenderAllIndexedPrimitivePasses( D3DPT_TRIANGLELIST, 0, NumberOfVertices, 0, NumberOfVertices/2, NULL, 0 );

			m_pxOverrideEffect->End();

			if ( TextureIndex != ASURA_TEXTURE_NONE && TextureIndex != ASURA_TEXTURE_INVALID)
			{
				m_pxOverrideEffect->SetParameterTextureByName( "g_xTexture", NULL );
				m_pxOverrideEffect->GetD3DEffect()->CommitChanges();
			}

			Asura_Direct3D::D3DDevice->SetVertexDeclaration(pxOldVertexDeclaration);
			// Release the old vertex decl to ensure the reference count balances at the end of the application
			pxOldVertexDeclaration->Release();

			// Invalidate the fixed function texture
			for (int i = 0; i < MAX_TEXTURES_PER_POLYGON; ++i)
			{
				Asura_Render::CurrentStates.RequestTextureIndex(i,ASURA_TEXTURE_INVALID);
			}

		}
		else
		{
			Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_QUADVERTEX);
			Asura_Render::CurrentStates.RequestTextureIndex(0,TextureIndex);
			Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumberOfVertices,0,NumberOfVertices/2);
		}

#else
		Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_QUADVERTEX);
		Asura_Render::CurrentStates.RequestTextureIndex(0,TextureIndex);

		Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumberOfVertices,0,NumberOfVertices/2);
#endif // defined(ASURA_TARGET_SM3)

		NumberOfVertices = 0;
		//Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
	
		{
			SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Restore state" ));
			Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, ulOldMultisample );
			Asura_Direct3D::D3DDevice->SetRenderState( D3DRS_SRGBWRITEENABLE, uOldSRGBWrite );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_MAXANISOTROPY, ulOldMaxAnisotropy );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, ulOldAddressMode[0] );
			Asura_Direct3D::D3DDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, ulOldAddressMode[1] );
		}

		Asura_Render::CurrentStates.RequestWireFrameMode(Asura_Render::GetWireframe());
		Asura_Direct3D::RequestSwapChain(0);
	}
}




// KJL - 11/06/2003 19:54:38 - these things below are only now used by the clouds in the skybox
// 

Direct3D_VertexBuffer* RenderMeshVertexBuffer=0;
Direct3D_VertexBuffer* RenderMeshVertexBuffer2=0;
Direct3D_IndexBuffer* RenderMeshIndexBuffer=0;

void InitialiseRMVB(void)
{
	if(!RenderMeshVertexBuffer) 
	{
		Asura_Direct3D::D3DDevice->CreateVertexBuffer
		(
			12800*sizeof(D3DGenericVertex),
			D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
			D3DFVF_GENERICVERTEX,
			D3DPOOL_DEFAULT,
			//D3DPOOL_MANAGED,
			&RenderMeshVertexBuffer,
			0
		);
		Asura_Direct3D::D3DDevice->CreateVertexBuffer
		(
			12800*sizeof(D3DGenericVertex),
			D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
			D3DFVF_GENERICVERTEX,
			D3DPOOL_DEFAULT,
			//D3DPOOL_MANAGED,
			&RenderMeshVertexBuffer2,
			0
		);
		Asura_Direct3D::D3DDevice->CreateIndexBuffer
		(
			12800*3*sizeof(unsigned short),
			D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
			D3DFMT_INDEX16,
			D3DPOOL_DEFAULT,
			//D3DPOOL_MANAGED,
			&RenderMeshIndexBuffer,
			0
		);
	}
	#if 0
/*	if (NewRenderTarget)
	{
		NewRenderTarget->Release();
		NewZTarget->Release();
		NewRenderTarget = 0;
	}
*/
	if (!NewRenderTarget)
	{
		int w = 512;
		int h = 512;
		HRESULT hres1 = Asura_Direct3D::D3DDevice->CreateTexture(w,h, 0, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &NewRenderTarget ) ;
	
		HRESULT hres2 = Asura_Direct3D::D3DDevice->CreateDepthStencilSurface(w,h,D3DFMT_D24S8,D3DMULTISAMPLE_NONE,&NewZTarget);
		//HRESULT hres2 = Asura_Direct3D::D3DDevice->CreateTexture(size,size, 0, D3DUSAGE_DEPTHSTENCIL, D3DFMT_D24S8, D3DPOOL_DEFAULT, &NewZTarget ) ;

	}
	#endif
}
void ShutdownRMVB(void)
{
	if (RenderMeshVertexBuffer) RenderMeshVertexBuffer->Release();	
	if (RenderMeshVertexBuffer2) RenderMeshVertexBuffer2->Release();	
	if (RenderMeshIndexBuffer) RenderMeshIndexBuffer->Release();
	RenderMeshVertexBuffer=0;
	RenderMeshVertexBuffer2=0;
	RenderMeshIndexBuffer=0;
	
	if (NewRenderTarget)
	{
		NewRenderTarget->Release();
		NewRenderTarget = 0;
		NewZTarget->Release();
	}
}
int ShadowTexture=0;


void RenderShadow(Asura_Bounding_Box& bb, Asura_Vector_3& position, Asura_Vector_3& lightSource)
{
	if (!ShadowTexture)
	{
		ShadowTexture = Asura_Texture_Management::AddTextureWithResetGroup("\\graphicnovel\\shadow2.bmp", ASURA_TEXTURE_LOADED_PERMANENT );
		Asura_Texture_Management::LoadTextures();
	}

#if 01
	D3DGenericVertex v[4];
	float size = 0.4f;
	float scale = 1.4f;
	unsigned int colour = 0xff606060;

	v[0].U = 1.0f;
	v[0].V = 1.0f;
	v[0].Diffuse = colour;

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

	v[2].U = 0.0f;
	v[2].V = 0.0f;
	v[2].Diffuse = colour;

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

	Asura_Vector_3 shadowDirection = position-lightSource;

	shadowDirection.y = 0;
	shadowDirection.Normalise();
	shadowDirection*=0.3f;

	shadowDirection.Zero();

	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_INVCOLOUR);
	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_GENERICVERTEX);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_NONE);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
 	
	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);

	v[0].X = (bb.MaxX-position.x)*scale+size+position.x+shadowDirection.x;
	v[0].Y = -0.01f+position.y;
	v[0].Z = (bb.MaxZ-position.z)*scale+size+position.z+shadowDirection.z;

	v[1].X = (bb.MaxX-position.x)*scale+size+position.x+shadowDirection.x;
	v[1].Y = -0.01f+position.y;
	v[1].Z = (bb.MinZ-position.z)*scale-size+position.z+shadowDirection.z;

	v[2].X = (bb.MinX-position.x)*scale-size+position.x+shadowDirection.x;
	v[2].Y = -0.01f+position.y;
	v[2].Z = (bb.MinZ-position.z)*scale-size+position.z+shadowDirection.z;

	v[3].X = (bb.MinX-position.x)*scale-size+position.x+shadowDirection.x;
	v[3].Y = -0.01f+position.y;
	v[3].Z = (bb.MaxZ-position.z)*scale+size+position.z+shadowDirection.z;

	Asura_Render::CurrentStates.RequestTextureIndex(0,ShadowTexture);
	Asura_Direct3D::D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, (LPVOID)v, sizeof(D3DGenericVertex) );


	Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);
#endif	
}


void Asura_Direct3D::CreateUntexturedTexture()
{
	/*
	This creates a small white texture to use on untextured polygons (or for when the texture is missing).
	In theory just using a NULL texture pointer ought to have just the same effect , and it does ,
	for the most part...
		
	However on the Geforce 4 (and possibly other cards for all I know) there is an oddity with using
	D3DTA_CURRENT to get the previous texture stage when using a null texture pointer.

	So 
	Stage 0 : Select Arg 1 - Texture
	Stage 1 : Modulate 2X - Current and Diffuse

	gives completely different results to
	Stage 0 : Modulate 2X - Texture and Diffuse
	
	RAR 10/03/2005
	*/


	bool bOkay = false;
	Direct3D_Surface* pxSurface = 0;


	HRESULT hres = D3DDevice->CreateTexture
	(
		4,//width
		4,//height
		1,//mip levels
		0,//usage
		D3DFMT_A8R8G8B8,//format
		D3DPOOL_MANAGED,//pool
		&s_pxUntexturedTexture,//output texture
		0//pSharedHandle
	);

	if(FAILED(hres))
	{
		//Failed to create texture for this...not entirely critical since using a NULL texture works
		//for the most part.
		Asura_Assert(0,"Failed to create default texture for untextured polygons");
		goto Clearup;
	}

	//get the surface
	hres = s_pxUntexturedTexture->GetSurfaceLevel(0,&pxSurface);
	if(FAILED(hres))
	{
		Asura_Assert(0,"Failed to get texture surface");
		goto Clearup;
	}

	//get the surface description
	D3DSURFACE_DESC xSurfaceDesc;
	hres = pxSurface->GetDesc(&xSurfaceDesc);
	if(FAILED(hres))
	{
		Asura_Assert(0,"Failed to get texture surface description");
		goto Clearup;
	}

	//lock the surface
	D3DLOCKED_RECT xRect;
	hres = pxSurface->LockRect(&xRect,0,0);
	if(FAILED(hres))
	{
		Asura_Assert(0,"Failed to lock the texture surface");
		goto Clearup;
	}

	//fill in the texutre
	for(u_int i=0;i<xSurfaceDesc.Height;i++)
	{
		
		memset((char*)xRect.pBits + xRect.Pitch*i,0xffffffff,xSurfaceDesc.Width*4);
	}

	hres = pxSurface->UnlockRect();
	if(FAILED(hres))
	{
		Asura_Assert(0,"Failed to unlock the texture surface");
		goto Clearup;
	}
	//all is well
	bOkay = true;

Clearup :

	if(pxSurface) pxSurface->Release();

	if(!bOkay && s_pxUntexturedTexture)
	{
		s_pxUntexturedTexture->Release();
		s_pxUntexturedTexture = 0;
	}
}

void Asura_Direct3D::ClearRenderTargets( const u_int uStartRenderTargetIndex)
{
	SCOPEDPIXEVENT(( 0xFFFFFFFF, L"Clear render targets from %u up", uStartRenderTargetIndex ));

	const u_int uRenderTargetCount = Asura_Direct3D_Caps::GetMaxNumberOfRenderTargets();
	
	for( u_int uRenderTargetIndex = 1; uRenderTargetIndex < uRenderTargetCount; uRenderTargetIndex += 1)
	{
		Asura_Direct3D::D3DDevice->SetRenderTarget( uRenderTargetIndex, NULL);
	}
}


#if defined( ASURA_D3D_ENABLE_D3D_OK) && ASURA_D3D_ENABLE_D3D_OK == 2

static const char* g_szD3DErrorLogFile = "logfiles\\D3DError.txt";

#define ASURA_PRINT_HRESULT_MAKE_STRUCT2( _HRESULT) _HRESULT
#define ASURA_PRINT_HRESULT_MAKE_STRUCT( _HRESULT) ResultTexture( (_HRESULT), ASURA_PRINT_HRESULT_MAKE_STRUCT2( #_HRESULT))

void Asura_Direct3D::PrintHRESULT( const HRESULT xResult, const char* const pszFilename, const uint32 uiLine)
{
	const char* pszString = "Unknown";
	
	{
		struct ResultTexture
		{
			inline ResultTexture( const HRESULT xResult, const char* const pszString) :
				m_xResult( xResult),
				m_pszString( pszString)
			{ 
			}
			HRESULT m_xResult;
			const char* const m_pszString;
		};
		static const ResultTexture axResults[] =
		{
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3D_OK),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DOK_NOAUTOGEN),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_CONFLICTINGRENDERSTATE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_CONFLICTINGTEXTUREFILTER),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_CONFLICTINGTEXTUREPALETTE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_DEVICELOST),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_DEVICENOTRESET),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_DRIVERINTERNALERROR),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_DRIVERINVALIDCALL),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_INVALIDCALL),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_INVALIDDEVICE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_MOREDATA),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_NOTAVAILABLE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_NOTFOUND),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_OUTOFVIDEOMEMORY),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_TOOMANYOPERATIONS),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDALPHAARG),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDALPHAOPERATION),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDCOLORARG),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDCOLOROPERATION),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDFACTORVALUE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_UNSUPPORTEDTEXTUREFILTER),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_WASSTILLDRAWING),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( D3DERR_WRONGTEXTUREFORMAT),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( E_FAIL),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( E_INVALIDARG),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( E_NOINTERFACE),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( E_NOTIMPL),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( E_OUTOFMEMORY),
			ASURA_PRINT_HRESULT_MAKE_STRUCT( S_OK),
			ResultTexture( 0, NULL)
		};
		
		const ResultTexture* pxResult = axResults;
		
		for( ; NULL != pxResult->m_pszString; pxResult += 1)
		{
			if( xResult == pxResult->m_xResult)
			{
				pszString = pxResult->m_pszString;
				break;
			}
		}
	}

	{
		Asura_StringBuffer<512> xBuffer( "0x%X %s(%d) 0x%08X '%s'\n", 
			                             Asura_Timers::GetFrameNumber(), pszFilename, uiLine, (uint32)xResult, pszString );
		OutputDebugString( xBuffer.GetString() );
		Asura_SimpleLogfileOutput( ( g_szD3DErrorLogFile, xBuffer.GetString() ) );
	}
}

#undef ASURA_PRINT_HRESULT_MAKE_STRUCT2
#undef ASURA_PRINT_HRESULT_MAKE_STRUCT

#endif

Asura_Direct3D_Reset_PreCallbackData::Asura_Direct3D_Reset_PreCallbackData()
{
	this->m_pfnPreCallback = NULL;
	this->m_pData = NULL;
}
	
bool Asura_Direct3D_Reset_PreCallbackData::operator==( const Asura_Direct3D_Reset_PreCallbackData& xRHS) const
{
	if( this->m_pfnPreCallback != xRHS.m_pfnPreCallback)
	{
		return( false);
	}
	if( this->m_pData != xRHS.m_pData)
	{
		return( false);
	}
	return( true);
}

Asura_Direct3D_Reset_PostCallbackData::Asura_Direct3D_Reset_PostCallbackData()
{
	this->m_pfnPostCallback = NULL;
	this->m_pData = NULL;
}

bool Asura_Direct3D_Reset_PostCallbackData::operator==( const Asura_Direct3D_Reset_PostCallbackData& xRHS) const
{
	if( this->m_pfnPostCallback != xRHS.m_pfnPostCallback)
	{
		return( false);
	}
	if( this->m_pData != xRHS.m_pData)
	{
		return( false);
	}
	return( true);
}

void Asura_Direct3D::AddPostResetCallback( CallbackPostReset pfnPostCallback, void* pData)
{
	Asura_LinkList< Asura_Direct3D_Reset_PostCallbackData>& xList = s_tList_xPostResetCallbacks;
	Asura_ListElt< Asura_Direct3D_Reset_PostCallbackData>* ptIt = xList.Push_Back( Asura_Direct3D_Reset_PostCallbackData());
	Asura_Direct3D_Reset_PostCallbackData& xCallbackData = ptIt->Data();
	
	xCallbackData.m_pfnPostCallback = pfnPostCallback;
	xCallbackData.m_pData = pData;
}

void Asura_Direct3D::RemovePostResetCallback( CallbackPostReset pfnPostCallback, void* pData)
{
	Asura_LinkList< Asura_Direct3D_Reset_PostCallbackData>& xList = s_tList_xPostResetCallbacks;
	
	Asura_Direct3D_Reset_PostCallbackData xCallbackData;
	
	xCallbackData.m_pfnPostCallback = pfnPostCallback;
	xCallbackData.m_pData = pData;
	
	Asura_ListElt< Asura_Direct3D_Reset_PostCallbackData>* pxIt = xList.Find( xCallbackData);
	if( NULL == pxIt)
	{
		Asura_Assert( false, "Callback data not found in Post Reset list");
		return;
	}
	
	xList.Remove( pxIt);
}

void Asura_Direct3D::AddPreResetCallback( CallbackPreReset pfnPreCallback, void* pData)
{
	Asura_LinkList< Asura_Direct3D_Reset_PreCallbackData>& xList = s_tList_xPreResetCallbacks;
	Asura_ListElt< Asura_Direct3D_Reset_PreCallbackData>* ptIt = xList.Push_Back( Asura_Direct3D_Reset_PreCallbackData());
	Asura_Direct3D_Reset_PreCallbackData& xCallbackData = ptIt->Data();
	
	xCallbackData.m_pfnPreCallback = pfnPreCallback;
	xCallbackData.m_pData = pData;
}

void Asura_Direct3D::RemovePreResetCallback( CallbackPreReset pfnPreCallback, void* pData)
{
	Asura_LinkList< Asura_Direct3D_Reset_PreCallbackData>& xList = s_tList_xPreResetCallbacks;
	
	Asura_Direct3D_Reset_PreCallbackData xCallbackData;
	
	xCallbackData.m_pfnPreCallback = pfnPreCallback;
	xCallbackData.m_pData = pData;
	
	Asura_ListElt< Asura_Direct3D_Reset_PreCallbackData>* pxIt = xList.Find( xCallbackData);
	if( NULL == pxIt)
	{
		Asura_Assert( false, "Callback data not found in Pre Reset list");
		return;
	}
	
	xList.Remove( pxIt);
}


#ifdef ASURA_TARGET_SM3
bool Asura_Direct3D::CreateDeferredRenderTargetF32( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf(L"Create deferred render targets using F32 format");
		return true;
	}
	if( NULL != s_pxDeferredRenderingTarget)
	{
		Asura_Direct3D_RenderTargetManager::DestroyRenderTarget(s_pxDeferredRenderingTarget);
		s_pxDeferredRenderingTarget = NULL;
	}
	
	D3DFORMAT aeRenderTargetFormats[3] = 
	{
		D3DFMT_A32B32G32R32F,
		D3DFMT_A32B32G32R32F,
		D3DFMT_A32B32G32R32F
	};
	s_pxDeferredRenderingTarget = Asura_Direct3D_RenderTargetManager::CreateRenderTarget(1.f, 1.f, aeRenderTargetFormats, D3DFMT_D24S8, true, 3, true);
	return( true);
}
bool Asura_Direct3D::CreateDeferredRenderTargetF16( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf(L"Create deferred render targets using F16 format");
		return true;
	}
	if( NULL != s_pxDeferredRenderingTarget)
	{
		Asura_Direct3D_RenderTargetManager::DestroyRenderTarget(s_pxDeferredRenderingTarget);
		s_pxDeferredRenderingTarget = NULL;
	}
	
	D3DFORMAT aeRenderTargetFormats[3] = 
	{
		D3DFMT_A16B16G16R16F,
		D3DFMT_A16B16G16R16F,
		D3DFMT_A16B16G16R16F
	};
	s_pxDeferredRenderingTarget = Asura_Direct3D_RenderTargetManager::CreateRenderTarget(1.f, 1.f, aeRenderTargetFormats, D3DFMT_D24S8, true, 3, true);
	return( true);
}
bool Asura_Direct3D::CreateDeferredRenderTargetI8( bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf(L"Create deferred render targets using I8 format");
		return true;
	}
	if( NULL != s_pxDeferredRenderingTarget)
	{
		Asura_Direct3D_RenderTargetManager::DestroyRenderTarget(s_pxDeferredRenderingTarget);
		s_pxDeferredRenderingTarget = NULL;
	}

	D3DFORMAT aeRenderTargetFormats[3] = 
	{
		D3DFMT_A8R8G8B8,
		D3DFMT_A8R8G8B8,
		D3DFMT_A8R8G8B8
	};
	s_pxDeferredRenderingTarget = Asura_Direct3D_RenderTargetManager::CreateRenderTarget(1.f, 1.f, aeRenderTargetFormats, D3DFMT_D24S8, true, 3, true);
	return( true);
}
#endif


#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
void Asura_Direct3D::AddConsoleCommands()
{
#ifdef ASURA_TARGET_SM3
	Asura_CommandConsole::AddCmd( L"Render.Deferred.SetBufferF32", Asura_Direct3D::CreateDeferredRenderTargetF32, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );	
	Asura_CommandConsole::AddCmd( L"Render.Deferred.SetBufferF16", Asura_Direct3D::CreateDeferredRenderTargetF16, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );	
	Asura_CommandConsole::AddCmd( L"Render.Deferred.SetBufferI8", Asura_Direct3D::CreateDeferredRenderTargetI8, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );	
#endif

	Asura_CommandConsole::AddVar( L"Render.UseGammaCorrection", &s_bUseGammaCorrection,
		Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
}
#endif


Direct3D_BaseTexture* Asura_Direct3D::GetDirect3DTextureByIndex(int iIndex)
{
	extern Direct3D_BaseTexture* evilg_apxTextureStorage[];
	
	if (iIndex>=0)
	{
		#if defined( ASURA_USE_TEXTURE_STREAMING )
		extern Direct3D_BaseTexture* evilg_apxStreamingTextureStorage[];
		
		// if we have a texture that's been streamed in, use that in preference
		if ( evilg_apxStreamingTextureStorage[ iIndex ] && Asura_Texture_Streaming_System::IsTextureLoaded( iIndex ))
		{
			return evilg_apxStreamingTextureStorage[ iIndex ];
		}
		#endif

		// if we don't have any texture for this index, return a blank one instead
		return evilg_apxTextureStorage[ iIndex ] ? evilg_apxTextureStorage[ iIndex ] : GetUntexturedTexture();
	}
	else
	{
		return NULL;
	}
}

void Asura_Direct3D::SetupD3DMatrix(D3DXMATRIX& xD3DMatrix, const Asura_Matrix_3x3& xOrient, const Asura_Vector_3& xPos, bool bTranspose)
{
	if(bTranspose)
	{
		xD3DMatrix._11 = xOrient.e11;	xD3DMatrix._12 = xOrient.e21;	xD3DMatrix._13 = xOrient.e31;	xD3DMatrix._14 = 0.f;
		xD3DMatrix._21 = xOrient.e12;	xD3DMatrix._22 = xOrient.e22;	xD3DMatrix._23 = xOrient.e32;	xD3DMatrix._24 = 0.f;
		xD3DMatrix._31 = xOrient.e13;	xD3DMatrix._32 = xOrient.e23;	xD3DMatrix._33 = xOrient.e33;	xD3DMatrix._34 = 0.f;
		xD3DMatrix._41 = xPos.x;		xD3DMatrix._42 = xPos.y;		xD3DMatrix._43 = xPos.z;		xD3DMatrix._44 = 1.f;
	}
	else
	{
		xD3DMatrix._11 = xOrient.e11;	xD3DMatrix._12 = xOrient.e12;	xD3DMatrix._13 = xOrient.e13;	xD3DMatrix._14 = xPos.x;
		xD3DMatrix._21 = xOrient.e21;	xD3DMatrix._22 = xOrient.e22;	xD3DMatrix._23 = xOrient.e23;	xD3DMatrix._24 = xPos.y;
		xD3DMatrix._31 = xOrient.e31;	xD3DMatrix._32 = xOrient.e32;	xD3DMatrix._33 = xOrient.e33;	xD3DMatrix._34 = xPos.z;
		xD3DMatrix._41 = 0.f;			xD3DMatrix._42 = 0.f;			xD3DMatrix._43 = 0.f;			xD3DMatrix._44 = 1.f;
	}
}
