//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

//
// This module implements the particle manager for the client DLL.
// In a nutshell, to create your own effect, implement the ParticleEffect 
// interface and call CParticleMgr::AddEffect to add your effect. Then you can 
// add particles and simulate and render them.

/*

Particle manager documentation
-----------------------------------------------------------------------------

All particle effects are managed by a class called CParticleMgr. It tracks 
the list of particles, manages their materials, sorts the particles, and
has callbacks to render them.

Conceptually, CParticleMgr is NOT part of VEngine's entity system. It does
not care about entities, only particle effects. Usually, the two are implemented
together, but you should be aware the CParticleMgr talks to you through its
own interfaces and does not talk to entities. Thus, it is possible to have
particle effects that are not entities.

To make a particle effect, you need two things: 

1. An implementation of the IParticleEffect interface. This is how CParticleMgr 
   talks to you for things like rendering and updating your effect.

2. A (member) variable of type CParticleEffectBinding. This allows CParticleMgr to 
   store its internal data associated with your effect.

Once you have those two things, you call CParticleMgr::AddEffect and pass them
both in. You will then get updates through IParticleEffect::Update, and you will
be asked to render your particles with IParticleEffect::SimulateAndRender.

When you want to remove the effect, call CParticleEffectBinding::SetRemoveFlag(), which
tells CParticleMgr to remove the effect next chance it gets.

Example class:

	class CMyEffect : public IParticleEffect
	{
	public:
		// Call this to start the effect by adding it to the particle manager.
		void			Start()
		{
			g_ParticleMgr.AddEffect( &m_ParticleEffect, this );
		}

		// implementation of IParticleEffect functions go here...

	public:
		CParticleEffectBinding	m_ParticleEffect;
	};



How the particle effects are integrated with the entity system
-----------------------------------------------------------------------------

There are two helper classes that you can use to create particles for your
entities. Each one is useful under different conditions.

1. CSimpleEmitter is a class that does some of the dirty work of using particles.
   If you want, you can just instantiate one of these with CSimpleEmitter::Create
   and call its AddParticle functions to add particles. When you are done and 
   want to 'free' it, call its Release function rather than deleting it, and it
   will wait until all of its particles have gone away before removing itself
   (so you don't have to write code to wait for all of the particles to go away).

   In most cases, it is the easiest and most clear to use CSimpleEmitter or
   derive a class from it, then use that class from inside an entity that wants
   to make particles.

   CSimpleEmitter and derived classes handle adding themselves to the particle
   manager, tracking how many particles in the effect are active, and 
   rendering the particles.

   CSimpleEmitter has code to simulate and render particles in a generic fashion,
   but if you derive a class from it, you can override some of its behavior
   with virtuals like UpdateAlpha, UpdateScale, UpdateColor, etc..

   Example code:
		CSimpleEmitter *pEmitter = CSimpleEmitter::Create();
		
		CEffectMaterialHandle hMaterial = pEmitter->GetCEffectMaterial( "mymaterial" );
		
		for( int i=0; i < 100; i++ )
			pEmitter->AddParticle( hMaterial, RandomVector(0,10), 4 );

		pEmitter->Release();

2. Some older effects derive from C_BaseParticleEffect and implement an entity 
   and a particle system at the same time. This gets nasty and is not encouraged anymore.

*/


#ifndef PARTICLEMGR_H
#define PARTICLEMGR_H


#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialsystem.h"
#include "vector.h"
#include "vmatrix.h"
#include "Mathlib.h"
#include "iclientrenderable.h"
#include "clientleafsystem.h"
#include "tier0/fasttimer.h"
#include "utllinkedlist.h"
#include "UtlDict.h"

//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------

class IParticleEffect;
struct Particle;
class ParticleDraw;
class CMeshBuilder;
class CMemoryPool;
class CEffectMaterial;
class CParticleSimulateIterator;
class CParticleRenderIterator;


#define INVALID_MATERIAL_HANDLE	NULL


// Various stats, disabled
// extern int			g_nParticlesDrawn;
// extern CCycleCount	g_ParticleTimer;


class CParticleSubTexture;
class CParticleSubTextureGroup;


//-----------------------------------------------------------------------------
// The basic particle description; all particles need to inherit from this.
//-----------------------------------------------------------------------------

struct Particle
{
	Particle *m_pPrev, *m_pNext;

	// Which sub texture this particle uses (so we can get at the tcoord mins and maxs).
	CParticleSubTexture *m_pSubTexture;

	// If m_Pos isn't used to store the world position, then implement IParticleEffect::GetParticlePosition()
	Vector m_Pos;			// Position of the particle in world space
};


//-----------------------------------------------------------------------------
// This is the CParticleMgr's reference to a material in the material system.
// Particles are sorted by material.
//-----------------------------------------------------------------------------

// This indexes CParticleMgr::m_SubTextures.
typedef CParticleSubTexture* PMaterialHandle;

// Each effect stores a list of particles associated with each material. The list is 
// hashed on the IMaterial pointer.
class CEffectMaterial
{
public:
	CEffectMaterial();

public:
	// This provides the material that gets bound for this material in this effect.
	// There can be multiple subtextures all within the same CEffectMaterial.
	CParticleSubTextureGroup *m_pGroup;
	
	Particle m_Particles;
	CEffectMaterial *m_pHashedNext;
};


class CParticleSubTextureGroup
{
public:
				CParticleSubTextureGroup();
				~CParticleSubTextureGroup();

	// Even though each of the subtextures has its own material, they should all basically be 
	// the same exact material and just use different texture coordinates, so this is the 
	// material of the first subtexture that is bound.
	//
	// This is gotten from GetMaterialPage().
	IMaterial	*m_pPageMaterial;
};

// Precalculated data for each material used for particles.
// This allows us to put multiple subtextures into one VTF and sort them against each other.
class CParticleSubTexture
{
public:
	CParticleSubTexture();

	float m_tCoordMins[2];	// bbox in texel space that this particle material uses.
	float m_tCoordMaxs[2];	// Specified in the SubTextureMins/SubTextureMaxs parameter in the materials.

	// Which group does this subtexture belong to?
	CParticleSubTextureGroup *m_pGroup;
	CParticleSubTextureGroup m_DefaultGroup;	// This is used as the group if a particle's material
												// isn't using a group.
	
	IMaterial *m_pMaterial;
};


//-----------------------------------------------------------------------------
// interface IParticleEffect:
//
// This is the interface that particles effects must implement. The effect is 
// responsible for starting itself and calling CParticleMgr::AddEffect, then it 
// will get the callbacks it needs to simulate and render the particles.
//-----------------------------------------------------------------------------

class IParticleEffect
{
// Overridables.
public:
	
	virtual			~IParticleEffect() {}
	
	// Called at the beginning of a frame to precalculate data for rendering 
	// the particles. If you manage your own list of particles and want to 
	// simulate them all at once, you can do that here and just render them in 
	// the SimulateAndRender call.
	virtual void	Update( float fTimeDelta ) {}
	
	// Called once for the entire effect before the batch of SimulateAndRender() calls.
	// For particle systems using FLAGS_CAMERASPACE (the default), effectMatrix transforms the particles from
	// world space into camera space. You can change this matrix if you want your particles relative to something
	// else like an attachment's space.
	virtual void	StartRender( VMatrix &effectMatrix ) {}

	// Simulate the particles.
	virtual void	SimulateParticles( CParticleSimulateIterator *pIterator ) = 0;

	// Render the particles.
	virtual void	RenderParticles( CParticleRenderIterator *pIterator ) = 0;

	// Implementing this is optional. It is called when an effect is removed. It is useful if
	// you hold onto pointers to the particles you created (so when this is called, you should
	// clean up your data so you don't reference the particles again).
	// NOTE: after calling this, the particle manager won't touch the IParticleEffect
	// or its associated CParticleEffectBinding anymore.
	virtual void	NotifyRemove() {}

	// This method notifies the effect a particle is about to be deallocated.
	// Implementations should *not* actually deallocate it.
	// NOTE: The particle effect's GetNumActiveParticles is updated BEFORE this is called
	//       so if GetNumActiveParticles returns 0, then you know this is the last particle
	//       in the system being removed.
	virtual void	NotifyDestroyParticle( Particle* pParticle ) {}

	// Fill in the origin used to sort this entity.
	// This is a world space position.
	virtual const Vector &GetSortOrigin() = 0;

	// Fill in the origin used to sort this entity.
// TODO: REMOVE THIS. ALL PARTICLE SYSTEMS SHOULD EITHER SET m_Pos IN CONJUNCTION WITH THE
// PARTICLE_LOCALSPACE FLAG, OR DO SETBBOX THEMSELVES.
	virtual const Vector *GetParticlePosition( Particle *pParticle ) { return &pParticle->m_Pos; }
};


// In order to create a particle effect, you must have one of these around and
// implement IParticleEffect. Pass them both into CParticleMgr::AddEffect and you
// are good to go.
class CParticleEffectBinding : public CDefaultClientRenderable
{
friend class CParticleMgr;
friend class CParticleSimulateIterator;

public:
					CParticleEffectBinding();
					~CParticleEffectBinding();


// Helper functions to setup, add particles, etc..
public:

	// Simulate all the particles.
	void			SimulateParticles( float flTimeDelta );

	// Use this to specify materials when adding particles. 
	// Returns the index of the material it found or added.
	// Returns INVALID_MATERIAL_HANDLE if it couldn't find or add a material.
	PMaterialHandle	FindOrAddMaterial( const char *pMaterialName );

	// Allocate particles. The Particle manager will automagically
	// deallocate them when the IParticleEffect SimulateAndRender() method 
	// returns false. The first argument is the size of the particle
	// structure in bytes
	Particle*		AddParticle( int sizeInBytes, PMaterialHandle pMaterial );

	// This is an optional call you can make if you want to manually manage the effect's
	// bounding box. Normally, the bounding box is managed automatically, but in certain
	// cases it is more efficient to set it manually.
	//
	// Note: this is a WORLD SPACE bounding box, even if you've used SetLocalSpaceTransform.
	//
	// After you make this call, the particle manager will no longer update the bounding
	// box automatically if bDisableAutoUpdate is true.
	void			SetBBox( const Vector &bbMin, const Vector &bbMax, bool bDisableAutoUpdate = true );

	// This tells the particle manager that your particles are transformed by the specified matrix.
	// That way, it can transform the bbox defined by Particle::m_Pos into world space correctly.
	//
	// It also sets up the matrix returned by CParticleMgr::GetModelView() to include this matrix, so you
	// can do TransformParticle with it like any other particle system.
	const matrix3x4_t&	GetLocalSpaceTransform() const;
	void			SetLocalSpaceTransform( const matrix3x4_t &transform );

	// This expands the bbox to contain the specified point. Returns true if bbox changed
	bool			EnlargeBBoxToContain( const Vector &pt );

	// The EZ particle singletons use this - they don't want to be added to all the leaves and drawn through the
	// leaf system - they are specifically told to draw each frame at a certain point.
	void			SetDrawThruLeafSystem( int bDraw );

	// Some view model particle effects want to be drawn right before the view model (after everything else is 
	// drawn).
	void			SetDrawBeforeViewModel( int bDraw );

	// Call this to have the effect removed whenever it safe to do so.
	// This is a lot safer than calling CParticleMgr::RemoveEffect.
	int				GetRemoveFlag()									{ return GetFlag( FLAGS_REMOVE ); }
	void			SetRemoveFlag()									{ SetFlag( FLAGS_REMOVE, 1 ); }

	// Set this flag to tell the particle manager to simulate your particles even
	// if the particle system isn't visible. Tempents and fast effects can always use
	// this if they want since they want to simulate their particles until they go away.
	// This flag is ON by default.
	int				GetAlwaysSimulate()								{ return GetFlag( FLAGS_ALWAYSSIMULATE ); }
	void			SetAlwaysSimulate( int bAlwaysSimulate )		{ SetFlag( FLAGS_ALWAYSSIMULATE, bAlwaysSimulate ); }

	// Set if the effect was drawn the previous frame.
	// This can be used by particle effect classes
	// to decide whether or not they want to spawn
	// new particles - if they weren't drawn, then
	// they can 'freeze' the particle system to avoid
	// overhead.
	int				WasDrawnPrevFrame()								{ return GetFlag( FLAGS_DRAWN_PREVFRAME ); }
	void			SetWasDrawnPrevFrame( int bWasDrawnPrevFrame )	{ SetFlag( FLAGS_DRAWN_PREVFRAME, bWasDrawnPrevFrame ); }

	// When the effect is in camera space mode, then the transforms are setup such that
	// the particle vertices are specified in camera space (in CParticleDraw) rather than world space. 
	//
	// This makes it faster to specify the particles - you only have to transform the center 
	// by CParticleMgr::GetModelView then add to X and Y to build the quad.
	//
	// Effects that want to specify verts (in CParticleDraw) in world space should set this to false and
	// ignore CParticleMgr::GetModelView.
	//
	// Camera space mode is ON by default.
	int				IsEffectCameraSpace()							{ return GetFlag( FLAGS_CAMERASPACE ); }
	void			SetEffectCameraSpace( int bCameraSpace )		{ SetFlag( FLAGS_CAMERASPACE, bCameraSpace ); }

	// This tells it whether or not to apply the local transform to the matrix returned by CParticleMgr::GetModelView().
	// Usually, you'll want this, so you can just say TransformParticle( pMgr->GetModelView(), vPos ), but you may want
	// to manually apply your local transform before saying TransformParticle.
	//
	// This is ON by default.
	int				GetAutoApplyLocalTransform() const				{ return GetFlag( FLAGS_AUTOAPPLYLOCALTRANSFORM ); }
	void			SetAutoApplyLocalTransform( int b )				{ SetFlag( FLAGS_AUTOAPPLYLOCALTRANSFORM, b ); }

	// If this is true, then the bbox is calculated from particle positions. This works
	// fine if you always simulate (SetAlwaysSimulateFlag) so the system can become visible
	// if it moves into the PVS. If you don't use this, then you should call SetBBox at 
	// least once to tell the particle manager where your entity is.
	int				GetAutoUpdateBBox()							{ return GetFlag( FLAGS_AUTOUPDATEBBOX ); }
	void			SetAutoUpdateBBox( int bAutoUpdate )		{ SetFlag( FLAGS_AUTOUPDATEBBOX, bAutoUpdate ); }

	// Get the current number of particles in the effect.
	int				GetNumActiveParticles();

	// The is the max size of the particles for use in bounding	computation
	void			SetParticleCullRadius( float flMaxParticleRadius );

	// Build a list of all active particles, returns actual count filled in
	int				GetActiveParticleList( int nCount, Particle **ppParticleList );

	// detect origin/bbox changes and update leaf system if necessary
	void			DetectChanges();

private:

	// Change flags..
	void			SetFlag( int flag, int bOn )	{ if( bOn ) m_Flags |= flag; else m_Flags &= ~flag; }
	int				GetFlag( int flag ) const		{ return m_Flags & flag; }

	void			Init( CParticleMgr *pMgr, IParticleEffect *pSim );
	void			Term();

	// Get rid of the specified particle.
	void			RemoveParticle( Particle *pParticle );

	void			StartDrawMaterialParticles(
						CEffectMaterial *pMaterial,
						float flTimeDelta,
						IMesh* &pMesh,
						CMeshBuilder &builder,
						ParticleDraw &particleDraw,
						bool bWireframe );

	int				DrawMaterialParticles( 
						bool bBucketSort,
						CEffectMaterial *pMaterial, 
						float flTimeDelta,
						bool bWireframe
						 );

	void			GrowBBoxFromParticlePositions( CEffectMaterial *pMaterial, bool bBucketSort, bool &bboxSet, Vector &bbMin, Vector &bbMax );

	void			RenderStart( VMatrix &mTempModel, VMatrix &mTempView );
	void			RenderEnd( VMatrix &mModel, VMatrix &mView );

	void			BBoxCalcStart( bool bFullBBoxUpdate, Vector &bbMin, Vector &bbMax );
	void			BBoxCalcEnd( bool bFullBBoxUpdate, bool bboxSet, Vector &bbMin, Vector &bbMax );
	
	void			DoBucketSort( 
		CEffectMaterial *pMaterial, 
		float *zCoords, 
		int nZCoords,
		float minZ,
		float maxZ );

	int				GetRemovalInProgressFlag()					{ return GetFlag( FLAGS_REMOVALINPROGRESS ); }
	void			SetRemovalInProgressFlag()					{ SetFlag( FLAGS_REMOVALINPROGRESS, 1 ); }

	// BBox is recalculated before it's put into the tree for the first time.
	int				GetNeedsBBoxUpdate()						{ return GetFlag( FLAGS_NEEDS_BBOX_UPDATE ); }
	void			SetNeedsBBoxUpdate( int bFirstUpdate )		{ SetFlag( FLAGS_NEEDS_BBOX_UPDATE, bFirstUpdate ); }

	// Set on creation and cleared after the first PostRender (whether or not the system was rendered).
	int				GetFirstFrameFlag()							{ return GetFlag( FLAGS_FIRST_FRAME ); }
	void			SetFirstFrameFlag( int bFirstUpdate )		{ SetFlag( FLAGS_FIRST_FRAME, bFirstUpdate ); }

	int				WasDrawn()									{ return GetFlag( FLAGS_DRAWN ); }
	void			SetDrawn( int bDrawn )						{ SetFlag( FLAGS_DRAWN, bDrawn ); }

	// Update m_Min/m_Max. Returns false and sets the bbox to (0,0,0) if there are no particles.
	bool			RecalculateBoundingBox();

	CEffectMaterial* GetEffectMaterial( CParticleSubTexture *pSubTexture );


// IClientRenderable overrides.
public:		

	virtual const Vector&			GetRenderOrigin( void );
	virtual const QAngle&			GetRenderAngles( void );
	virtual void					GetRenderBounds( Vector& mins, Vector& maxs );
	virtual bool					ShouldDraw( void );
	virtual bool					IsTransparent( void );
	virtual int						DrawModel( int flags );


private:

	enum
	{
		FLAGS_REMOVE =				(1<<0),	// Set in SetRemoveFlag
		FLAGS_REMOVALINPROGRESS =	(1<<1), // Set while the effect is being removed to prevent
											// infinite recursion.
		FLAGS_NEEDS_BBOX_UPDATE =	(1<<2),	// This is set until the effect's bbox has been updated once.
		FLAGS_AUTOUPDATEBBOX =		(1<<3),	// Update bbox automatically? Cleared in SetBBox.
		FLAGS_ALWAYSSIMULATE =		(1<<4), // See SetAlwaysSimulate.
		FLAGS_DRAWN =				(1<<5),	// Set if the effect is drawn through the leaf system.
		FLAGS_DRAWN_PREVFRAME =		(1<<6),	// Set if the effect was drawn the previous frame.
											// This can be used by particle effect classes
											// to decide whether or not they want to spawn
											// new particles - if they weren't drawn, then
											// they can 'freeze' the particle system to avoid
											// overhead.
		FLAGS_CAMERASPACE =			(1<<7),	// See SetEffectCameraSpace.
		FLAGS_DRAW_THRU_LEAF_SYSTEM=(1<<8),	// This is the default - do the effect's visibility through the leaf system.
		FLAGS_DRAW_BEFORE_VIEW_MODEL=(1<<9),// Draw before the view model? If this is set, it assumes FLAGS_DRAW_THRU_LEAF_SYSTEM goes off.
		FLAGS_AUTOAPPLYLOCALTRANSFORM=(1<<10), // Automatically apply the local transform to CParticleMgr::GetModelView()'s matrix.
		FLAGS_FIRST_FRAME =         (1<<11)	// Cleared after the first frame that this system exists (so it can simulate after rendering once).
	};


	VMatrix m_LocalSpaceTransform;
	bool m_bLocalSpaceTransformIdentity;	// If this is true, then m_LocalSpaceTransform is assumed to be identity.
	
	// Bounding box. Stored in WORLD space.
	Vector							m_Min;
	Vector							m_Max;

	// paramter copies to detect changes
	Vector							m_LastMin;
	Vector							m_LastMax;
	
	// The particle cull size
	float							m_flParticleCullRadius;

	// Number of active particles.
	unsigned short					m_nActiveParticles;

	// See CParticleMgr::m_FrameCode.
	unsigned short					m_FrameCode;

	// For CParticleMgr's list index.
	unsigned short					m_ListIndex;

	IParticleEffect					*m_pSim;
	CParticleMgr					*m_pParticleMgr;
	
	// Combination of the CParticleEffectBinding::FLAGS_ flags.
	int								m_Flags;

	// Materials this effect is using.
	enum { EFFECT_MATERIAL_HASH_SIZE = 8 };
	CEffectMaterial *m_EffectMaterialHash[EFFECT_MATERIAL_HASH_SIZE];
	
	// For faster iteration.
	CUtlLinkedList<CEffectMaterial*, unsigned short> m_Materials;
};


class CParticleLightInfo
{
public:
	Vector	m_vPos;
	Vector	m_vColor;	// 0-1
	float	m_flIntensity;
};


class CParticleMgr
{
friend class CParticleEffectBinding;

public:

					CParticleMgr();
	virtual			~CParticleMgr();

	// Call at init time to preallocate the bucket of particles.
	bool			Init(unsigned long nPreallocatedParticles, IMaterialSystem *pMaterial);

	// Shutdown - free everything.
	void			Term();

	// Add and remove effects from the active list.
	// Note: once you call AddEffect, CParticleEffectBinding will automatically call
	//       RemoveEffect in its destructor.
	// Note: it's much safer to call CParticleEffectBinding::SetRemoveFlag instead of
	//       CParticleMgr::RemoveEffect.
	bool			AddEffect( CParticleEffectBinding *pEffect, IParticleEffect *pSim );
	void			RemoveEffect( CParticleEffectBinding *pEffect );

	// Called at level shutdown to free all the lingering particle effects (usually
	// CParticleEffect-derived effects that can linger with noone holding onto them).
	void			RemoveAllEffects();

	// This should be called at the start of the frame.
	void			IncrementFrameCode();
	
	// This updates all the particle effects and inserts them into the leaves.
	void			Simulate( float fTimeDelta );

	// This just marks effects that were drawn so during their next simulation they can know
	// if they were drawn in the previous frame.
	void			PostRender();

	// Draw the effects marked with SetDrawBeforeViewModel.
	void			DrawBeforeViewModelEffects();

	// Returns the modelview matrix
	VMatrix&		GetModelView();

	Particle		*AllocParticle( int size );
	void			FreeParticle( Particle * );

	PMaterialHandle	GetPMaterial( const char *pMaterialName );
	IMaterial*		PMaterialToIMaterial( PMaterialHandle hMaterial ) const;

	// Particles drawn with the ParticleSphere material will use this info.
	// This should be set in IParticleEffect.
	void GetDirectionalLightInfo( CParticleLightInfo &info ) const;
	void SetDirectionalLightInfo( const CParticleLightInfo &info );


private:
	// Call Update() on all the effects.
	void			UpdateAllEffects( float flTimeDelta );

	CParticleSubTextureGroup* FindOrAddSubTextureGroup( IMaterial *pPageMaterial );

private:

	// Directional lighting info.
	CParticleLightInfo m_DirectionalLight;

	// Frame code, used to prevent CParticleEffects from simulating multiple times per frame.
	// Their DrawModel can be called multiple times per frame because of water reflections,
	// but we only want to simulate the particles once.
	unsigned short					m_FrameCode;

	bool							m_bUpdatingEffects;

	// All the active effects.
	CUtlLinkedList<CParticleEffectBinding*, unsigned short>		m_Effects;

	IMaterialSystem					*m_pMaterialSystem;

	// Store the concatenated modelview matrix
	VMatrix							m_mModelView;
	
	// This is a big array that is used to allocate and index the particles.
	CMemoryPool						*m_pParticleBucket;

	
	CUtlVector<CParticleSubTextureGroup*>				m_SubTextureGroups;	// lookup by group name
	CUtlDict<CParticleSubTexture*,unsigned short>		m_SubTextures;		// lookup by material name
	CParticleSubTexture m_DefaultInvalidSubTexture; // Used when they specify an invalid material name.
};



// Helper functions to abstract out the particle testbed app.
float	Helper_GetTime();
float	Helper_GetFrameTime();
float	Helper_RandomFloat( float minVal, float maxVal );
int		Helper_RandomInt( int minVal, int maxVal );



// ------------------------------------------------------------------------ //
// CParticleMgr inlines
// ------------------------------------------------------------------------ //

inline VMatrix& CParticleMgr::GetModelView()
{
	return m_mModelView;
}



// ------------------------------------------------------------------------ //
// CParticleEffectBinding inlines.
// ------------------------------------------------------------------------ //

inline const matrix3x4_t& CParticleEffectBinding::GetLocalSpaceTransform() const
{
	return m_LocalSpaceTransform.As3x4();
}



// ------------------------------------------------------------------------ //
// GLOBALS
// ------------------------------------------------------------------------ //

extern CParticleMgr g_ParticleMgr;




//-----------------------------------------------------------------------------
// StandardParticle_t; this is just one type of particle
// effects may implement their own particle data structures
//-----------------------------------------------------------------------------

struct StandardParticle_t : public Particle
{
	// Color and alpha values are 0 - 1
	void			SetColor(float r, float g, float b);
	void			SetAlpha(float a);

	Vector			m_Velocity;
	
	// How this is used is up to the effect's discretion. Some use it for how long it has been alive
	// and others use it to count down until the particle disappears.
	float			m_Lifetime;

	unsigned char	m_EffectData;	// Data specific to the IParticleEffect. This can be used to distinguish between
									// different types of particles the effect is simulating.
	unsigned short	m_EffectDataWord;

	unsigned char	m_Color[4];		// RGBA - not all effects need to use this.
};


// ------------------------------------------------------------------------ //
// Transform a particle.
// ------------------------------------------------------------------------ //

inline void TransformParticle(const VMatrix &vMat, const Vector &vIn, Vector &vOut)
{
	//vOut = vMat.VMul4x3(vIn);
	vOut.x = vMat.m[0][0]*vIn.x + vMat.m[0][1]*vIn.y + vMat.m[0][2]*vIn.z + vMat.m[0][3];
	vOut.y = vMat.m[1][0]*vIn.x + vMat.m[1][1]*vIn.y + vMat.m[1][2]*vIn.z + vMat.m[1][3];
	vOut.z = vMat.m[2][0]*vIn.x + vMat.m[2][1]*vIn.y + vMat.m[2][2]*vIn.z + vMat.m[2][3];
}


// ------------------------------------------------------------------------ //
// CEffectMaterial inlines
// ------------------------------------------------------------------------ //

inline void StandardParticle_t::SetColor(float r, float g, float b)
{
	m_Color[0] = (unsigned char)(r * 255.9f);
	m_Color[1] = (unsigned char)(g * 255.9f);
	m_Color[2] = (unsigned char)(b * 255.9f);
}

inline void StandardParticle_t::SetAlpha(float a)
{
	m_Color[3] = (unsigned char)(a * 255.9f);
}



//-----------------------------------------------------------------------------
// List functions.
//-----------------------------------------------------------------------------

inline void UnlinkParticle( Particle *pParticle )
{
	pParticle->m_pPrev->m_pNext = pParticle->m_pNext;
	pParticle->m_pNext->m_pPrev = pParticle->m_pPrev;
}

inline void InsertParticleBefore( Particle *pInsert, Particle *pNext )
{
	// link pCur before pPrev
	pInsert->m_pNext = pNext;
	pInsert->m_pPrev = pNext->m_pPrev;
	pInsert->m_pNext->m_pPrev = pInsert->m_pPrev->m_pNext = pInsert;
}

inline void InsertParticleAfter( Particle *pInsert, Particle *pPrev )
{
	pInsert->m_pPrev = pPrev;
	pInsert->m_pNext = pPrev->m_pNext;

	pInsert->m_pNext->m_pPrev = pInsert->m_pPrev->m_pNext = pInsert;
}

inline void SwapParticles( Particle *pPrev, Particle *pCur )
{
	// unlink pCur
	UnlinkParticle( pCur );
	InsertParticleBefore( pCur, pPrev );
}


#include "particle_iterators.h"


#endif


