/*
	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		This class allows bursts of particles to be generated. Handles the creation and drawing of the 
///				particles.
//=====================================================================================================================

#include "Precompiled.h"
#include "Graphics\ParticleEmitter.h"
#include "Graphics\VertexBuffer.h"
#include "Graphics\Display.h"
#include "Graphics\ParticleEmitterBurst.h"
#include "Resources\PixelShaderManager.h"
#include "Resources\VertexShaderManager.h"
#include "GameObjects\PositionalObject.h"
#include "Game\GameCore.h"

using namespace Game;

//=====================================================================================================================
// Constants
//=====================================================================================================================

/// Maximum number of particles that can be drawn in one go

const unsigned ParticleEmitter::PARTICLE_BUFFER_SIZE = 4096;

/// Number of particle bursts to preallocate in the pool

const unsigned ParticleEmitter::BURST_POOL_SIZE = 256;

//=====================================================================================================================
/// @brief		Creates the particle emitter.
/// @param		display - the display the emitter uses
/// @param		vsh_mgr - vertex shader manager used to load vertex shaders
/// @param		psh_mgr - pixel shader manager used to load pixel shaders
/// @pre		display cannot be null
/// @pre		display must be valid
/// @pre		vertex shader manager cannot be null
/// @pre		pixel shader manager cannot be null
/// @pre		The game must exist
//=====================================================================================================================

ParticleEmitter::ParticleEmitter( Display * display , VertexShaderManager * vsh_mgr , PixelShaderManager * psh_mgr )
{
	// Debug checks:

	ENSURE(	display						, "display cannot be null"					)
	ENSURE( display->IsValid()			, "display must be valid"					)
	ENSURE( vsh_mgr						, "vertex shader manager cannot be null"	)
	ENSURE( psh_mgr						, "pixel shader manager cannot be null"		)
	ENSURE( GameCore::GetSingleton()	, "The game must exist"						)
	
	// Save the display given and add a reference to it:
	
	m_display = display; m_display->AddReference();
	
	// Good: create our vertex format first:
	
	{
		VertexFormat::ElementUsage usages[] = 
		{
			VertexFormat::VF_USAGE_POSITION	,
			VertexFormat::VF_USAGE_TEXCOORD	,
			VertexFormat::VF_USAGE_COLOR
		};
	
		VertexFormat::ElementType types[] =
		{
			VertexFormat::VF_TYPE_FLOAT3	,
			VertexFormat::VF_TYPE_FLOAT2	,
			VertexFormat::VF_TYPE_FLOAT4
		};
		
		// Now make the declaration:
		
		m_vf = VertexFormat(display,3,usages,types);
	}
	
	// Load our vertex and pixel shaders:
	
	m_vs = vsh_mgr->GetVertexShader("Shaders\\Particles.vsh.bin");
	m_ps = psh_mgr->GetPixelShader("Shaders\\Textured.psh.bin");
	
	// Get the stack from the game:
	
	Stack & stack = GameCore::GetSingleton()->GetStack();
	
	// Preallocate a certain number of particle bursts for later use and put them both onto the stack and in the list
	
	for ( unsigned i = BURST_POOL_SIZE ; i != 0 ; --i )
	{
		// Create a new particle burst:
		
		ParticleEmitterBurst * burst = new ParticleEmitterBurst();
		
		// Add onto the stack and into the list:
		
		stack.Push(burst); m_burst_allocs.push_back(burst);
	}
	
	// Now create the pool of bursts:
	
	m_burst_pool = new Pool<ParticleEmitterBurst>(stack,BURST_POOL_SIZE);
	
	// Create the vertex buffer that we can push all our vertices onto:
	
	m_vertex_buffer = new VertexBuffer(m_display,VertexBuffer::TYPE_STATIC,m_vf.GetVertexSize()*PARTICLE_BUFFER_SIZE*6);
	
	// Get all shader uniforms if the vertex shader is valid: these are used to setup each burst for GPU calculation
	
	if ( m_vs.IsValid() )
	{
		/// Vertex shader uniform holding the two particle sizes in x and y and the two particle live times in z and w
			
		m_u_sizes_and_live_times			= m_vs.GetUniform("ParticleSizesAndLiveTimes");
		m_u_angles_and_speeds				= m_vs.GetUniform("ParticleAnglesAndSpeeds");
		m_u_color1							= m_vs.GetUniform("ParticleColor1");
		m_u_color2							= m_vs.GetUniform("ParticleColor2");
		m_u_random_seed_and_time_alive		= m_vs.GetUniform("ParticleRandomSeedAndTimeAlive");
		m_u_transform						= m_vs.GetUniform("Transform");
	}
	
	// See if everything is valid:
	
	if 
	(
		m_vf.IsValid()								&&
		m_ps.IsValid()								&&
		m_vs.IsValid()								&&
		m_u_sizes_and_live_times.IsValid()			&&
		m_u_angles_and_speeds.IsValid()				&&
		m_u_color1.IsValid()						&&
		m_u_color2.IsValid()						&&
		m_u_random_seed_and_time_alive.IsValid()	&& 
		m_u_transform.IsValid()
	)
	{
		// Valid resources:
		
		m_is_valid = true;
	}
	else
	{
		// Invalid resources:
		
		m_is_valid = false;
	}
	
	// Fill the vertex buffer if it is valid:
	
	FillVertexBuffer();
}

//=====================================================================================================================
/// @brief		Destroys the particle emitter
//=====================================================================================================================

ParticleEmitter::~ParticleEmitter()
{
	// Delete our pool:
	
	m_burst_pool->RemoveReference();
	
	// Delete vertex buffer
	
	m_vertex_buffer->RemoveReference();
	
	// Now clean up all of the particle bursts:
	
	while ( m_burst_allocs.size() > 0 )
	{
		// Delete this burst
		
		delete m_burst_allocs.front(); 
		
		// Pop off the list
		
		m_burst_allocs.pop_front();
	}
	
	// Remove our reference to the display:
	
	m_display->RemoveReference();
}

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

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

//=====================================================================================================================
/// @brief		Fills if the vertex buffer with all the data needed to render particles if it is valid
/// @pre		The game must exist
//=====================================================================================================================

void ParticleEmitter::FillVertexBuffer()
{
	// Make sure ok:
	
	ENSURE( GameCore::GetSingleton() , "The game must exist" )

	// Only if we are valid:
	
	if ( m_is_valid == false || m_vertex_buffer->IsValid() == false ) return;
	
	// Get the stack from the game
	
	Stack & stack = GameCore::GetSingleton()->GetStack();
	
	// Get a random number generator from the game
	
	Random & random = GameCore::GetSingleton()->GetRandom();
	
	// Save it's current pointer for later restoration
	
	void * stack_ptr = stack.GetPointer();
	
	// Fill it with all our particle data:
	
	for ( unsigned i = PARTICLE_BUFFER_SIZE ; i != 0 ; --i )
	{
		// Pick all random numbers for this particle
		
		float zr	= random.NextFloat();	// random number to be put in z position
		float crr	= random.NextFloat();	// random number to be put in the red component of the color
		float cgr	= random.NextFloat();	// random number to be put in the green component of the color
		float cbr	= random.NextFloat();	// random number to be put in the blue component of the color
		float car	= random.NextFloat();	// random number to be put in the alpha component of the color
	
		// Triangle 1 vertex 1:
	
			stack.Push(-1.0f);	// Normalised position: x
			stack.Push(+1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(0.0f);	// Texcoord: u
			stack.Push(0.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number
			
		// Triangle 1 vertex 2:
	
			stack.Push(+1.0f);	// Normalised position: x
			stack.Push(+1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(1.0f);	// Texcoord: u
			stack.Push(0.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number
			
		// Triangle 1 vertex 3:
	
			stack.Push(+1.0f);	// Normalised position: x
			stack.Push(-1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(1.0f);	// Texcoord: u
			stack.Push(1.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number
			
		// Triangle 2 vertex 1:
	
			stack.Push(+1.0f);	// Normalised position: x
			stack.Push(-1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(1.0f);	// Texcoord: u
			stack.Push(1.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number
			
		// Triangle 2 vertex 2:
	
			stack.Push(-1.0f);	// Normalised position: x
			stack.Push(-1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(0.0f);	// Texcoord: u
			stack.Push(1.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number
			
		// Triangle 2 vertex 3:
	
			stack.Push(-1.0f);	// Normalised position: x
			stack.Push(+1.0f);	// Normalised position: y
			stack.Push(zr);		// Normalised position: z - which is a random number for the vertex shader
			stack.Push(0.0f);	// Texcoord: u
			stack.Push(0.0f);	// Texcoord: v
			stack.Push(crr);	// Color r: which is a random number
			stack.Push(cgr);	// Color r: which is a random number
			stack.Push(cbr);	// Color r: which is a random number
			stack.Push(car);	// Color r: which is a random number			
	}
	
	// Now restore the stack pointer:
	
	stack.SetPointer(stack_ptr);
	
	// Fill the vertex buffer with all the data:
	
	m_vertex_buffer->Write( stack_ptr , 0 , PARTICLE_BUFFER_SIZE * 6 * m_vf.GetVertexSize() , false );
}

//=====================================================================================================================
/// @brief		Allocates a particle burst from the pool or creates a new one if the pool is fully used and adds it to
///				the used list in the pool.
/// @returns	A particle emitter burst ready for use.
//=====================================================================================================================

ParticleEmitterBurst * ParticleEmitter::AllocBurst()
{
	// See if the pool is full:
	
	if ( m_burst_pool->GetUsedCount() < m_burst_pool->GetSize() )
	{
		// Just alloc off the pool: have enough left
		
		return m_burst_pool->Alloc();
	}
	else
	{
		// No room on the pool: need to make a new particle emitter burst
		
		ParticleEmitterBurst * burst = new ParticleEmitterBurst();
		
		// Grow the pool and add to the list of allocs:
		
		m_burst_pool->Grow( burst , 1 ); m_burst_allocs.push_back(burst);
		
		// Now alloc off the pool and return the result
		
		return m_burst_pool->Alloc();
	}
}

//=====================================================================================================================
/// @brief		Updates all the particle bursts and advances their timing.
/// @param		time_delta - time between the current frame and the last frame
//=====================================================================================================================

void ParticleEmitter::Update( float time_delta )
{
    // Get the used objects in the pool:
    
    Pool<ParticleEmitterBurst>::Enumerator i = m_burst_pool->UsedObjects();
    Pool<ParticleEmitterBurst>::Enumerator e = m_burst_pool->ListEnd();
    
    // Run through the list:
    
    while ( i != e )
    {
		// Get this burst and move on:
		
		ParticleEmitterBurst * b = (*i); ++i;
		
        // Update the time this burst has been alive for:

        b->m_time_alive += time_delta;

        // Get the max live time for this particle burst:

        float live_time_max = Float::Max( b->m_live_time1 , b->m_live_time2 );

        // If the burst should be killed then we need to stop using it:

        if ( b->m_time_alive > live_time_max )
        {
            // Free up this object:
            
            m_burst_pool->Dealloc(b);
        }
    }
}

//=====================================================================================================================
/// @brief		Creates a burst of particles from the emitter in all directions.
/// @param		num_particles - Number of particles to make.
/// @param		texture - Texture to use for the particles.
/// @param		position - Position of the burst
/// @param		live_time1 - One of two random live times for the particles.
/// @param		live_time2 - One of two random live times for the particles.
/// @param		size1 - One of two random sizes for the particles.
/// @param		size2 - One of two random sizes for the particles.
/// @param		color1 - One of two random colors for the particles.
/// @param		color2 - One of two random colors for the particles.
/// @param		speed1 - One of two random speeds for the particles.
/// @param		speed2 - One of two random speeds for the particles.
/// @param		alpha_blend - Whether to apply alpha or additve blending.
/// @pre		The given texture must be valid
/// @pre		All floats given must be valid
/// @pre		number of particles given must be > 0
/// @pre		The two live times must be > 0
/// @pre		The two sizes given must be > 0
/// @pre		The two speeds given must be > 0
/// @pre		The game must exist
/// @remarks	If more particles are created than the emitter can handle then the extra particles will be ignored
//=====================================================================================================================

void ParticleEmitter::CreateBurst
(
	int					num_particles   ,
	const Texture &		texture         ,
	const Vec2f &		position        ,
	float				live_time1      ,
	float				live_time2      ,
	float				size1           ,
	float				size2           ,
	const Color &		color1          ,
	const Color &		color2          ,
	float				speed1          ,
	float				speed2          ,
	bool				alpha_blend 
)
{
	// Safety checks:
	
	ENSURE( GameCore::GetSingleton()	, "The game must exist"						)
	ENSURE( num_particles > 0			, "Number of particles given must be > 0"	)
	ENSURE( texture.IsValid()			, "The given texture must be valid"			)
	ENSURE( live_time1 > 0				, "The two live times must be > 0"			)
	ENSURE( live_time2 > 0				, "The two live times must be > 0"			)
	ENSURE( size1 > 0					, "The two sizes given must be > 0"			)
	ENSURE( size2 > 0					, "The two sizes given must be > 0"			)
	ENSURE( speed1 > 0					, "The two speeds given must be > 0"		)
	ENSURE( speed2 > 0					, "The two speeds given must be > 0"		)
	ENSURE( Float::IsValid(position.x)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(position.y)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(live_time1)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(live_time2)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(size1)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(size2)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.r)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.g)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.b)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.a)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.a)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.r)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.g)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.b)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(speed1)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(speed2)		, "All floats given must be valid"			)

    // Grab a particle emitter burst:

    ParticleEmitterBurst * b = AllocBurst();
    
    // If we have too many particles then clamp:
    
    if ( num_particles > PARTICLE_BUFFER_SIZE ) num_particles = PARTICLE_BUFFER_SIZE;
    
    // Set particle parameters:

    b->m_num_particles   = num_particles;
    b->m_texture         = texture;
    b->m_position        = position;
    b->m_time_alive      = 0;
    b->m_live_time1      = live_time1;
    b->m_live_time2      = live_time2;
    b->m_size1           = size1;
    b->m_size2           = size2;
    b->m_direction1      = 0;
    b->m_direction2      = Float::TwoPI();
    b->m_speed1          = speed1;
    b->m_speed2          = speed2;
    b->m_alpha_blend     = alpha_blend;
    b->m_color1          = color1;
    b->m_color2          = color2; 
    b->m_random_seed     = GameCore::GetSingleton()->GetRandom().NextFloat();   
}

//=====================================================================================================================
/// @brief		Creates a directed burst of particles from the emitter.
/// @param		num_particles - Number of particles to make.
/// @param		texture - Texture to use for the particles.
/// @param		position - Position of the burst
/// @param		direction - Direction of the burst
/// @param		spread - variation from the direction of the burst
/// @param		live_time1 - One of two random live times for the particles.
/// @param		live_time2 - One of two random live times for the particles.
/// @param		size1 - One of two random sizes for the particles.
/// @param		size2 - One of two random sizes for the particles.
/// @param		color1 - One of two random colors for the particles.
/// @param		color2 - One of two random colors for the particles.
/// @param		speed1 - One of two random speeds for the particles.
/// @param		speed2 - One of two random speeds for the particles.
/// @param		alpha_blend - Whether to apply alpha or additve blending.
/// @pre		The given texture must be valid
/// @pre		All floats given must be valid
/// @pre		number of particles given must be > 0
/// @pre		The two live times must be > 0
/// @pre		The two sizes given must be > 0
/// @pre		The two speeds given must be > 0
/// @pre		Spread must be >= 0
/// @pre		The game must exist
/// @remarks	The direction need not be normalised
/// @remarks	If more particles are created than the emitter can handle then the extra particles will be ignored
//=====================================================================================================================

void ParticleEmitter::CreateDirectedBurst
(
	int					num_particles   ,
	const Texture &		texture			,
	const Vec2f &		position        ,
	const Vec2f &		direction       ,
	float				spread          ,
	float				live_time1      ,
	float				live_time2      ,
	float				size1           ,
	float				size2           ,
	const Color &		color1          ,
	const Color &		color2          ,
	float				speed1          ,
	float				speed2          ,
	bool				alpha_blend 
)
{
	// Safety checks:
	
	ENSURE( GameCore::GetSingleton()	, "The game must exist"						)
	ENSURE( num_particles > 0			, "Number of particles given must be > 0"	)
	ENSURE( texture.IsValid()			, "The given texture must be valid"			)
	ENSURE( live_time1 > 0				, "The two live times must be > 0"			)
	ENSURE( live_time2 > 0				, "The two live times must be > 0"			)
	ENSURE( size1 > 0					, "The two sizes given must be > 0"			)
	ENSURE( size2 > 0					, "The two sizes given must be > 0"			)
	ENSURE( speed1 > 0					, "The two speeds given must be > 0"		)
	ENSURE( speed2 > 0					, "The two speeds given must be > 0"		)
	ENSURE( spread >= 0					, "Spread must be >= 0"						)
	ENSURE( Float::IsValid(position.x)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(position.y)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(direction.x)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(direction.y)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(spread)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(live_time1)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(live_time2)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(size1)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(size2)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.r)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.g)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.b)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color1.a)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.a)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.r)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.g)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(color2.b)	, "All floats given must be valid"			)
	ENSURE( Float::IsValid(speed1)		, "All floats given must be valid"			)
	ENSURE( Float::IsValid(speed2)		, "All floats given must be valid"			)

    // Grab a particle emitter burst:

    ParticleEmitterBurst * b = AllocBurst();
    
    // Get angle we are shooting the burst in:

    float angle = direction.GetAngle();
    
    // If we have too many particles then clamp:
    
    if ( num_particles > PARTICLE_BUFFER_SIZE ) num_particles = PARTICLE_BUFFER_SIZE;    

    // Set particle parameters:

    b->m_num_particles   = num_particles;
    b->m_texture         = texture;
    b->m_position        = position;
    b->m_time_alive      = 0;
    b->m_live_time1      = live_time1;
    b->m_live_time2      = live_time2;
    b->m_size1           = size1;
    b->m_size2           = size2;
    b->m_direction1      = angle - spread;
    b->m_direction2      = angle + spread;
    b->m_speed1          = speed1;
    b->m_speed2          = speed2;
    b->m_alpha_blend     = alpha_blend;
    b->m_color1          = color1;
    b->m_color2          = color2; 
    b->m_random_seed     = GameCore::GetSingleton()->GetRandom().NextFloat();
}

//=====================================================================================================================
/// @brief		Draws all of the particles in the emitter
/// @param		transform - view and projection matrix for the current view. The world transforms for each particle
///				burst will be combined with this given transform.
/// @param		camera_pos - this is used for unwrapping positions in order to get particles working correctly with
///				wrapping.
/// @pre		Each particle burst should not exceed the maximum number of particles allowed
/// @pre		The display should have a valid direct3d device
/// @pre		Alpha blending should be enabled as per the default device state for this application
/// @pre		The camera position given must be a valid float
/// @pre		The given camera position should be wrapped within map boundaries
//=====================================================================================================================

void ParticleEmitter::Draw( const Matrix4f & transform , const Vec2f & camera_pos )
{
	// Make sure ok:
	
	ENSURE( Float::IsValid(camera_pos.x)			, "The camera position given must be a valid float"						)
	ENSURE( Float::IsValid(camera_pos.y)			, "The camera position given must be a valid float"						)
	ENSURE( PositionalObject::IsWrapped(camera_pos)	, "The given camera position should be wrapped within map boundaries"	)

	// If we are marked as invalid or if the vertex buffer was lost then do nothing:
	
	if ( m_is_valid == false || m_vertex_buffer->IsValid() == false ) return;
	
	// If there are no particles there to draw then skip all the other setup stages:
	
	if ( m_burst_pool->GetUsedCount() == 0 ) return;
	
	// Draw setup: 
	
	m_vf.Apply();			// bind our vertex format
	m_vs.Apply();			// bind our vertex shader
	m_ps.Apply();			// bind our pixel shader	
	
	// Make sure this is ok:
	
	ENSURE( m_display && m_display->GetDevice() , "The display should have a valid direct3d device" )
	
	// Get the direct3d device:
	
	IDirect3DDevice9 * device = m_display->GetDevice();
	
	// State setup
	
	device->SetRenderState( D3DRS_ZENABLE		, 0 );
	device->SetRenderState( D3DRS_ZWRITEENABLE	, 0 );
	
	// This is true if alpha blending is currently enabled
	
	bool alpha_enabled = true;
	
	// Make sure ok:
	
	#ifdef DEBUG
	
		HRESULT result; DWORD value;
		
		result = device->GetRenderState( D3DRS_ALPHABLENDENABLE , & value );
		ENSURE( result == D3D_OK && value == TRUE, "Alpha blending should be enabled as per the default device state for this application" )

		result = device->GetRenderState( D3DRS_SRCBLEND , & value );
		ENSURE( result == D3D_OK && value == D3DBLEND_SRCALPHA , "Alpha blending should be enabled as per the default device state for this application" )
		
		result = device->GetRenderState( D3DRS_DESTBLEND , & value ); 
		ENSURE( result == D3D_OK && value == D3DBLEND_INVSRCALPHA , "Alpha blending should be enabled as per the default device state for this application" )		
	
	#endif
	
	// Bind our vertex buffer
	
 	m_vertex_buffer->Bind(m_vf.GetVertexSize());
		
	// Get enumerators for the active emitters:
	
	Pool<ParticleEmitterBurst>::Enumerator i = m_burst_pool->UsedObjects();
	Pool<ParticleEmitterBurst>::Enumerator e = m_burst_pool->ListEnd();
	
	// This is the current texture in use:
	
	const Texture * tex = & ((*i)->m_texture);
	
	// Apply this texture:
	
	tex->Apply(0);
	
	// Go through the list:
	
	while ( i != e )
	{
		// Get this burst and move on in the list:
		
		ParticleEmitterBurst * p = *i; ++i;
		
		// Make sure this is ok:
		
		ENSURE( p->m_num_particles <= PARTICLE_BUFFER_SIZE , "Each particle burst should not exceed the maximum number of particles allowed" )
		
		// See if we need to switch texture:
		
		if ( p->m_texture != * tex )
		{
			// New texture:
			
			tex = & (p->m_texture);
			
			// Change textures:
			
			tex->Apply(0);
		}
		
		// Set all shader uniforms:
		
		{
			// Random seed, time alive and z position
			
			m_u_random_seed_and_time_alive.SetVec2f( Vec2f( p->m_random_seed , p->m_time_alive ) );
			
			// Color 1 & 1:
			
			m_u_color1.SetVec4f( p->m_color1.ToVec4() );
			m_u_color2.SetVec4f( p->m_color2.ToVec4() );
			
			// Sizes and live times:
			
			m_u_sizes_and_live_times.SetVec4f
			(
				Vec4f
				(
					p->m_size1		,
					p->m_size2		,
					p->m_live_time1	,
					p->m_live_time2	
				)
			);
			
			// Angles and movement speeds
			
			m_u_angles_and_speeds.SetVec4f
			(	
				Vec4f
				(
					p->m_direction1,
					p->m_direction2,
					p->m_speed1,
					p->m_speed2				
				)
			);
			
			// Make a matrix to translate by the burst's position:
			
			Matrix4f translation = Matrix4f::Translation
			( 
				PositionalObject::UnwrapCoordinates( camera_pos , PositionalObject::WrapCoordinates(p->m_position.ToVec2()) )		
        	);
			
			// Combine with the transform matrix:
			
     		Matrix4f combined = translation * transform;
			
			// Transform: coordinates need to be unwrapped to work properly
			
			m_u_transform.SetMatrix( combined );
		}
		
		// See if we need to switch betwen alpha blending and additive blending:
		
		if ( p->m_alpha_blend )
		{
			if ( alpha_enabled == false )
			{
				device->SetRenderState( D3DRS_SRCBLEND	, D3DBLEND_SRCALPHA		);
				device->SetRenderState( D3DRS_DESTBLEND	, D3DBLEND_INVSRCALPHA	);
				
				// Now doing alpha blending
				
				alpha_enabled = true;
			}
		}
		else
		{
			if ( alpha_enabled == true )
			{
				device->SetRenderState( D3DRS_SRCBLEND	, D3DBLEND_SRCALPHA	);
				device->SetRenderState( D3DRS_DESTBLEND	, D3DBLEND_ONE		);
				
				// No longer alpha blending
				
				alpha_enabled = false;
			}
		}
		
		// Draw this lot of particles:
		
		device->DrawPrimitive( D3DPT_TRIANGLELIST , 0 , p->m_num_particles * 2 );		
		
	}	// end for all particle bursts	
	
	// Restore back to alpha blending if we have not already done so:
	
	if ( alpha_enabled == false )
	{
		device->SetRenderState( D3DRS_SRCBLEND	, D3DBLEND_SRCALPHA		);
		device->SetRenderState( D3DRS_DESTBLEND	, D3DBLEND_INVSRCALPHA	);
	}
	
	// Put this back to what it was
	
	device->SetRenderState( D3DRS_ZENABLE		, 1 );
	device->SetRenderState( D3DRS_ZWRITEENABLE	, 1 );	
	
	// Done with our vertex buffer
	
 	m_vertex_buffer->Unbind();
}
