/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		Represents a Direct3D9 display for a window 
/// @remarks	This object relies on the window existing for it's existance. If the window is deleted then this is
///				invalid.
//=====================================================================================================================

#include "Precompiled.h"
#include "Graphics\Display.h"
#include "Graphics\DisplayEvictable.h"
#include "Graphics\RenderTarget.h"
#include "Graphics\DepthBuffer.h"
#include "Graphics\VertexBuffer.h"
#include "Windows\Window.h"

using namespace Game;

///====================================================================================================================
/// @brief		Constructor, attempts to create the DirectX9 display
/// @param		window - window to setup the display in
/// @param		fullscreen - whether or not the display is to be fullscreen
/// @param		fullscreen_size - size of the display mode in fullscreen
/// @param		aa_level - level of antialiasing to use
/// @pre		Window cannot be null
/// @pre		Given fullscreen size cannot be zero
/// @remarks	If the given antialiasing level cannot be used, the next nearest down will be applied instead
///====================================================================================================================

Display::Display( Window * window , bool fullscreen , Vec2u fullscreen_size , AntialiasingLevel aa_level )
{
	// Make sure ok:
	
	ENSURE(window,"Window cannot be null");
	
	// Make sure ok:
	
	ENSURE( fullscreen_size.x != 0 && fullscreen_size.y != 0 , "Given fullscreen size cannot be zero" );	
	
	// Initialise this:
	
	m_aa_level = ANTIALIAS_NONE;
	
	// No user render target or depth buffer in use:
	
	m_current_target = 0; m_current_depth_buffer = 0;
	
	// No vertex buffer
	
	m_current_vertex_buffer = 0;
	
	// Haven't got these initially:
	
	m_back_buffer = 0;
	m_depth_stencil	= 0;
	
	// No evictable resources yet:
	
	m_evictable_head = 0;
	m_evictable_tail = 0;
	
	// Needs to be set to false so that all resources are evicted before the next device reset
	
	m_released_evictable_resources = false;

	// Save parent window:

	m_window = window;
	
	// Save fullscreen res:
	
	m_fullscreen_size = fullscreen_size;

	// Clear presentation params:

	memset( &m_presentation_params , 0 , sizeof(D3DPRESENT_PARAMETERS) );

	// Setup the display:

	Setup(fullscreen,aa_level);
	
	// Set default render state:
	
	if ( IsValid() ) SetDefaultRenderState();
}

///====================================================================================================================
/// @brief		Destructor. Frees up all display resources.
/// @pre		The list of DisplayEvictable resources must be empty. I.E there must be none of these resources left
///				in existance.
///====================================================================================================================

Display::~Display()
{
	// Make sure no render targets exist:
	
	SetRenderTarget(0);
	
	// Shutdown the display:

	Shutdown();

	// Make sure ok:

	ENSURE
	( 
		m_evictable_tail == 0 && m_evictable_head == 0 ,
		"The list of DisplayEvictable resources must be empty. I.E there must be none of these resources left in existance."
	)
}

///====================================================================================================================
/// @brief		Creates a Direct3D9 display on top of the regular window display.
/// @param		fullscreen - whether the window is to be fullscreen or not
/// @param		aa_level - level of antialiasing to use
/// @remarks	The anti-aliasing level is a hint only, and may not be reached if the hardware does not support it
///====================================================================================================================

void Display::Setup( bool fullscreen , AntialiasingLevel aa_level )
{
	// Create the direct3D api:

	m_d3d = Direct3DCreate9(D3D_SDK_VERSION);
	
	// If no direct3d then abort:
	
	if ( m_d3d == 0 ) Errors::IssueError("Unable to initialize Direct3D9.");
	
	// Save the direct3d caps:
	
	m_d3d->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&m_d3d_caps);
	
	// Make sure we have pixel shader 2 at least:
	
	if 
	( 
		( m_d3d_caps.PixelShaderVersion & 0xffff )  < 0x0200 ||
		( m_d3d_caps.VertexShaderVersion & 0xffff ) < 0x0200
	)
	{
		// Error:
		
		Errors::IssueError
		(
			"Hardware requirements not met.								 \n"
			"This game requires at least a DirectX-9 compatible graphics \n"
			"card with support for Shader Model 2 or later.				"
		);
	}

	// Only proceed if we have that:

	if ( m_d3d != 0 )
	{
		// Presentation parameters for D3D:

		memset( &m_presentation_params , 0 , sizeof(D3DPRESENT_PARAMETERS) );
		
		if ( fullscreen )
		{
			m_presentation_params.BackBufferWidth	= m_fullscreen_size.x;
			m_presentation_params.BackBufferHeight	= m_fullscreen_size.y;			
		}
		else
		{
			Vec2u client_size = m_window->GetClientSize();
		
			m_presentation_params.BackBufferWidth	= client_size.x;
			m_presentation_params.BackBufferHeight	= client_size.y;
		}
		
		m_presentation_params.BackBufferCount				= 1;
		m_presentation_params.BackBufferFormat				= D3DFMT_X8R8G8B8;
		m_presentation_params.MultiSampleType				= D3DMULTISAMPLE_NONE;
		m_presentation_params.MultiSampleQuality			= 0;
		m_presentation_params.SwapEffect					= D3DSWAPEFFECT_DISCARD;
		m_presentation_params.hDeviceWindow					= m_window->GetWindowHandle();
		m_presentation_params.Windowed						= fullscreen == false;
		m_presentation_params.EnableAutoDepthStencil		= true;
		m_presentation_params.AutoDepthStencilFormat		= D3DFMT_D24S8;
		m_presentation_params.Flags							= 0;
		m_presentation_params.FullScreen_RefreshRateInHz	= 0;
		m_presentation_params.PresentationInterval			= D3DPRESENT_INTERVAL_ONE;

		// Behaviour flags for D3D:
		
		DWORD behaviour_flags = 0;

		behaviour_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;		// Force hardware vertex processing
		behaviour_flags	|= D3DCREATE_MULTITHREADED;					// Multithreaded D3D
		
		// Get the maximum aa level:
		
		AntialiasingLevel max_aa_level = GetMaxmimumAALevel();
		
		// See if we are above it:
		
		if ( (unsigned)(aa_level) > (unsigned)(max_aa_level) )
		{
			// Clamp to max aa level: cant support requested level
			
			aa_level = max_aa_level;
		}
		
		// Set the level of anti-aliasing:
		
		m_presentation_params.MultiSampleType = (D3DMULTISAMPLE_TYPE) aa_level;
		
		// Save level of aa:
		
		m_aa_level = aa_level;

		// Cool: try and create the Direct3D device itself... 

		m_d3d->CreateDevice
		(
			D3DADAPTER_DEFAULT			,	// use default adapter
			D3DDEVTYPE_HAL				,	// want a hardware device
			m_window->GetWindowHandle()	,	// Handle for the window
			behaviour_flags				,	// Beahavior flags for D3D	
			&m_presentation_params		,	// Presentation params
			&m_d3d_device					// Direct3D device
		);
		
		// No cursor:
		
		if ( m_d3d_device )
		{
			// Do not show the cursor
			
			m_d3d_device->ShowCursor(false);
			
			// Get the back buffer:
			
			m_d3d_device->GetRenderTarget(0,&m_back_buffer);
			
			// Get the depth stencil buffer:
			
			m_d3d_device->GetDepthStencilSurface(&m_depth_stencil);
		}
		else
		{
			Errors::IssueError("Unable to create a supported Direct3D9 display device.");
		}
	}
}

///====================================================================================================================
/// @brief		Shuts down the directx9 display
///====================================================================================================================

void Display::Shutdown()
{
	// Release reference to these:
	
	if ( m_back_buffer ) m_back_buffer->Release();
	if ( m_depth_stencil ) m_depth_stencil->Release();
	
	// Free up the vertex buffer
	
	SetVertexBuffer(0,0);
	
	// Free the direct3d device:

	if ( m_d3d_device ) m_d3d_device->Release();

	// Free the direct3d object:

	if ( m_d3d ) m_d3d->Release();

	// Clear all pointers:

	m_d3d_device = 0; m_d3d = 0;
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * Display::GetTypeName() const
{
	return "Display";
}

///====================================================================================================================
/// @brief		Resets the Direct3D9 display
/// @remarks	If the reset fails, this function will wait for one millisecond to avoid the application eating up
///				all the CPU time while it is waiting for the graphcis adapter to reset successfully.
///====================================================================================================================

void Display::Reset()
{
	// Do the reset:

	if ( m_d3d_device )
	{
		// Make sure we can reset:
		
		HRESULT result = m_d3d_device->TestCooperativeLevel();
		
		// See if possible:
		
		if ( result == D3D_OK || result == D3DERR_DEVICENOTRESET )
		{
			// If not done so then release all evictable resources:
			
			if ( m_released_evictable_resources == false )
			{
				// Release all these resources:
				
				ReleaseEvictableResources();
				
				// Need to release these:
				
				m_back_buffer->Release(); m_back_buffer = 0;
				m_depth_stencil->Release(); m_depth_stencil = 0;
			}
					
			// Resize the front and back buffers: if we are fullscreen use the fullscreen dimensions, otherwise use client width and height
			
			if ( m_presentation_params.Windowed )
			{		
				Vec2u client_size = m_window->GetClientSize();
			
				m_presentation_params.BackBufferWidth	= client_size.x;
				m_presentation_params.BackBufferHeight	= client_size.y;
			}
			else
			{
				m_presentation_params.BackBufferWidth	= m_fullscreen_size.x;
				m_presentation_params.BackBufferHeight	= m_fullscreen_size.y;
			}

			// Reset the device
		
			result = m_d3d_device->Reset(&m_presentation_params);
			
			// See if the reset went ok:
			
			if ( result == D3D_OK )
			{		
				// Back to default render state
				
				SetDefaultRenderState();
				
				// Do not show the cursor
				
				m_d3d_device->ShowCursor(false);				
				
				// Excellent: recreate all evictable resoruces if not done so already:
				
				if ( m_released_evictable_resources == true )
				{
					// Recreate all these:
				
					RecreateEvictableResources();
					
					// Get the back buffer:
					
					m_d3d_device->GetRenderTarget(0,&m_back_buffer);
					
					// Get the depth stencil buffer:
					
					m_d3d_device->GetDepthStencilSurface(&m_depth_stencil);
				}		
			}
			else
			{
				// Couldn't reset the device: pause for a little while until the graphics adapter gets its stuff in order
				
				Sleep(1);
			}
		}
	}
}


///====================================================================================================================
/// @brief		Clears the display
/// @param		color - color to clear the screen to, in three component rgb
/// @param		stencil - value to clear stencil buffer to
/// @param		depth - depth to clear depth buffer to
///====================================================================================================================

void Display::Clear( const Color & color , unsigned char stencil , float depth )
{
	// Only if there is a device:

	if ( m_d3d_device != 0 )
	{
		// Do the clear 

		m_d3d_device->Clear
		( 
			0 , 
			0 , 
			D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER , 
			color.ToDxUnsigned() , 
			depth , 
			stencil 
		);
	}
}

///====================================================================================================================
/// @brief		Presents the display to the screen by swapping the front and back buffers 
///====================================================================================================================

void Display::Present()
{
	// Only if there is a device:

	if ( m_d3d_device != 0 )
	{
		// Present:

		m_d3d_device->Present(0,0,0,0);
	}	
}

///====================================================================================================================
/// @brief		Tells if the display needs a reset
/// @returns	True if the display needs a reset after being lost
/// @remarks	This function will never return true if the display is invalid
///====================================================================================================================

bool Display::RequiresReset()
{
	// Make sure valid:
	
	if ( m_d3d_device != 0 )
	{
		// See what way the device is:

		HRESULT device_state = m_d3d_device->TestCooperativeLevel();

		// Now see what the story is:

		if ( device_state == D3D_OK ) return false; else return true;
	}
	else
	{
		// No device: so no need to reset
		
		return false;
	}
}

///====================================================================================================================
/// @brief		Begins drawing: must be called before rendering
///====================================================================================================================

void Display::BeginDraw()
{
	// Only if there is a device:

	if ( m_d3d_device != 0 ) m_d3d_device->BeginScene();
}

///====================================================================================================================
/// @brief		Ends drawing: must be called after rendering
///====================================================================================================================

void Display::EndDraw()
{
	// Only if there is a device:

	if ( m_d3d_device != 0 ) m_d3d_device->EndScene();
}

//=====================================================================================================================
/// @brief		Sets the default render state, which is optimised for the application in question
/// @pre		The display must be valid
/// @remarks	This is a pretty slow operation, so only call at startup
//=====================================================================================================================

void Display::SetDefaultRenderState()
{
	// Make sure ok:

	ENSURE( IsValid() , "The display must be valid" );
	
	// Get the device caps:
	
	D3DCAPS9 caps; m_d3d_device->GetDeviceCaps(&caps);
	
	// Set default state:
	
		// Samplers:
		
		for ( unsigned i = 0 ; i < caps.MaxSimultaneousTextures ; ++i )
		{
			m_d3d_device->SetSamplerState(i,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
			m_d3d_device->SetSamplerState(i,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
			m_d3d_device->SetSamplerState(i,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);
		}
		
		// Render state:
		
		m_d3d_device->SetRenderState( D3DRS_CULLMODE			, D3DCULL_NONE			);		
		m_d3d_device->SetRenderState( D3DRS_ALPHABLENDENABLE	, TRUE					);
		m_d3d_device->SetRenderState( D3DRS_ALPHAFUNC			, D3DCMP_GREATER		);
		m_d3d_device->SetRenderState( D3DRS_ALPHAREF			, 0						);		
		m_d3d_device->SetRenderState( D3DRS_SRCBLEND			, D3DBLEND_SRCALPHA		);
		m_d3d_device->SetRenderState( D3DRS_DESTBLEND			, D3DBLEND_INVSRCALPHA	);
}

//=====================================================================================================================
/// @brief		Releases all evictable resources before a device reset if not done already
/// @pre		This should not be called if evictable resources have already been released without being recreated
/// @remarks	The released evictable resources flag is set to TRUE after this function ends
//=====================================================================================================================

void Display::ReleaseEvictableResources()
{
	// Make sure ok:
	
	ENSURE( m_released_evictable_resources == false , "This should not be called if evictable resources have already been released without being recreated" )
	
	// Clear current depth buffer and render target:
	
	SetRenderTarget(0);
	SetDepthBuffer(0);

	// Run through the list of evictable resources
	
	DisplayEvictable * current = m_evictable_head;
	
	// Run through the list:
	
	while ( current )
	{
		// Release this resource: but only if valid
		
		if ( current->IsValid() )
		{		
			current->ReleaseEvictableResources();
		}
	
		// Next resource
		
		current = current->m_evictable_next;
	}
	
	// Now released all evictable resources:
	
	m_released_evictable_resources = true;
}

//=====================================================================================================================
/// @brief		Recreates all evictable resources after a device reset has occured
/// @pre		This should only be called after evictable resources have been released
/// @remarks	The released evictable resources flag is set to FALSE after this function ends
//=====================================================================================================================

void Display::RecreateEvictableResources()
{
	// Make sure ok:
	
	ENSURE( m_released_evictable_resources == true , "This should only be called after evictable resources have been released" )
	
	// Run through the list of evictable resources
	
	DisplayEvictable * current = m_evictable_head;
	
	// Run through the list:
	
	while ( current )
	{
		// Recreate this resource
		
		current->RecreateEvictableResources();
	
		// Next resource
		
		current = current->m_evictable_next;
	}
	
	// Now recreated all evictable resources:
	
	m_released_evictable_resources = false;
}

//=====================================================================================================================
/// @brief		Gets the maximum level of antialiasing that is supported by the graphics device
/// @returns	Maximum level of anti-aliasing that can be used on the display
//=====================================================================================================================

Display::AntialiasingLevel Display::GetMaxmimumAALevel()
{	
	// Store the result of level checking here:
	
	HRESULT result = D3D_OK;
	
	// Macro to check the levels:
			
	#define CHECK_AA_LEVEL(AA_LEVEL)			\
												\
	result = m_d3d->CheckDeviceMultiSampleType	\
	(											\
		D3DADAPTER_DEFAULT	,					\
		D3DDEVTYPE_HAL		,					\
		D3DFMT_X8R8G8B8		,					\
		m_presentation_params.Windowed ,		\
		AA_LEVEL ,								\
		0										\
	);

	// Check 2x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_2_SAMPLES); if ( result != D3D_OK ) return ANTIALIAS_NONE;
	
	// Check 4x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_4_SAMPLES); if ( result != D3D_OK ) return ANTIALIAS_2_SAMPLES;
	
	// Check 8x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_8_SAMPLES); if ( result != D3D_OK ) return ANTIALIAS_4_SAMPLES;	

	// Check 16x:
	
	CHECK_AA_LEVEL(D3DMULTISAMPLE_16_SAMPLES); if ( result != D3D_OK ) return ANTIALIAS_8_SAMPLES;	
	
	// Return 16x:
			
	return ANTIALIAS_16_SAMPLES;
}

//=====================================================================================================================
/// @brief		Sets the render target to use for rendering. Set to null to use back buffer.
/// @param		target - render target to use. If null then the back buffer is used
/// @pre		The device must be valid
/// @pre		The given render target must be valid, if a target is given
/// @pre		If there is a user depth buffer bound then the format of the render target must match
//=====================================================================================================================

void Display::SetRenderTarget( RenderTarget * target )
{
	// Make sure ok:
	
	ENSURE( IsValid() , "The device must be valid" )
	
	#ifdef DEBUG
	
		if ( target )
		{
			ENSURE( target->IsValid() , "The given render target must be valid, if a target is given" )
		}
		
	#endif
	
	// Debug only check:
	
	#ifdef DEBUG
	
		if ( m_current_depth_buffer && target )
		{
			ENSURE( m_current_depth_buffer->GetAALevel() == target->GetAALevel() , "If there is a user depth buffer bound then the format of the render target must match" )
			ENSURE( m_current_depth_buffer->GetSize() == target->GetSize() , "If there is a user depth buffer bound then the format of the render target must match" )
		}
	
	#endif
	
	// See if the render target is null:
	
	if ( target )
	{
		// Add a reference to the new target
		
		target->AddReference();
		
		// Remove reference from old target if there:
		
		if ( m_current_target ) m_current_target->RemoveReference();
		
		// Save:
		
		m_current_target = target;
		
		// Bind on the display:
		
		m_d3d_device->SetRenderTarget(0,m_current_target->GetSurface());
	}
	else
	{
		// Remove reference from old target if there:
		
		if ( m_current_target ) m_current_target->RemoveReference();
		
		// Clear render target
		
		m_current_target = 0;
		
		// Set the target to the back buffer
		
		m_d3d_device->SetRenderTarget(0,m_back_buffer);
	}
}

//=====================================================================================================================
/// @brief		Sets the depth buffer to use for rendering with a render target. Set to null to use back buffer 
///				depth buffer.
/// @param		depth_buffer - depth buffer to use. If null then the depth buffer associated with the back buffer is used.
/// @pre		The device must be valid
/// @pre		The given depth buffer must be valid, if a depth buffer is given
/// @pre		If there is a user render target bound then the format of the stencil buffer must match
//=====================================================================================================================
				
void Display::SetDepthBuffer( DepthBuffer * depth_buffer )
{
	// Make sure ok:
	
	ENSURE( IsValid() , "The device must be valid" )
	
	#ifdef DEBUG
	
		if ( depth_buffer )
		{
			ENSURE( depth_buffer->IsValid() , "The given depth buffer must be valid, if a depth buffer is given" )
		}
		
	#endif
	
	// Debug only check:
	
	#ifdef DEBUG
	
		if ( m_current_target && depth_buffer )
		{
			ENSURE( m_current_target->GetAALevel() == depth_buffer->GetAALevel() , "If there is a user render target bound then the format of the stencil buffer must match" )
			ENSURE( m_current_target->GetSize() == depth_buffer->GetSize() , "If there is a user render target bound then the format of the stencil buffer must match" )
		}
	
	#endif	
	
	// See if the depth buffer is null:
	
	if ( depth_buffer )
	{
		// Add a reference to the new target
		
		depth_buffer->AddReference();
		
		// Remove reference from old depth buffer if there:
		
		if ( m_current_depth_buffer ) m_current_depth_buffer->RemoveReference();
		
		// Save:
		
		m_current_depth_buffer = depth_buffer;
		
		// Bind on the display:
		
		m_d3d_device->SetDepthStencilSurface(m_current_depth_buffer->GetSurface());
	}
	else
	{
		// Remove reference from old depth buffer if there:
		
		if ( m_current_depth_buffer ) m_current_depth_buffer->RemoveReference();
		
		// Clear depth buffer
		
		m_current_depth_buffer = 0;
		
		// Set the target to the back buffer's depth buffer
		
		m_d3d_device->SetDepthStencilSurface(m_depth_stencil);
	}	
}	

//=====================================================================================================================
/// @brief		Sets the current vertex buffer to use. Set to null to unbind.
/// @param		buffer - vertex buffer to use
/// @param		stride - size of each vertex currently in the buffer
/// @pre		The display must be valid
/// @pre		If a vertex buffer is given it must be valid
/// @pre		stride - size of each vertex to be drawn
/// @post		The result of the bind must be successful
//=====================================================================================================================

void Display::SetVertexBuffer( VertexBuffer * buffer , unsigned stride )
{
	// Make sure ok:

	ENSURE( IsValid() , "The display must be valid" )
	
	// Another debug check
	
	#ifdef DEBUG
	
		if ( buffer )
		{
			ENSURE( buffer->IsValid() , "If a vertex buffer is given it must be valid" )
		}
	
	#endif

	// See if different to current buffer:
	
	if ( buffer != m_current_vertex_buffer )
	{
		// Add a reference to the new buffer if given
		
		if ( buffer ) 
		{
			// Add a ref to it:
		
			buffer->AddReference();
			
			// Set the new vertex buffer in use:
			
			#ifdef DEBUG
			
				// Save the result in debug
			
				HRESULT result = m_d3d_device->SetStreamSource( 0 , buffer->GetDXBuffer() , 0 , stride );
				
				// Validate that it went ok:
				
				ENSURE( result == D3D_OK , "The result of the bind must be successful" )
			
			#else
			
				m_d3d_device->SetStreamSource( 0 , buffer->GetDXBuffer() , 0 , stride );
			
			#endif					
		}
		else				
		{
			#ifdef DEBUG
			
				// Save the result in debug
			
				HRESULT result = m_d3d_device->SetStreamSource( 0 , 0 , 0 , 0 );
				
				// Validate that it went ok:
				
				ENSURE( result == D3D_OK , "The result of the bind must be successful" )				
			
			#else
			
				// Set the stream to null
			
				m_d3d_device->SetStreamSource( 0 , 0 , 0 , 0 );
			
			#endif
		}
		
		// Remove reference from the old buffer if there:
		
		if ( m_current_vertex_buffer ) m_current_vertex_buffer->RemoveReference();
		
		// Save the new vertex buffer
		
		m_current_vertex_buffer = buffer;
	}
}
