sqwarmed/sdk_src/game/client/particlemgr.h

948 lines
34 KiB
C
Raw Normal View History

2024-08-29 19:18:30 -04:00
//===== 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()
{
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
#ifdef _WIN32
#pragma once
#endif
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialsystem.h"
#include "mathlib/vector.h"
#include "mathlib/vmatrix.h"
#include "mathlib/Mathlib.h"
#include "iclientrenderable.h"
#include "clientleafsystem.h"
#include "tier0/fasttimer.h"
#include "utllinkedlist.h"
#include "UtlDict.h"
#ifdef WIN32
#include <typeinfo.h>
#else
#include <typeinfo>
#endif
#include "tier1/utlintrusivelist.h"
#include "tier1/utlobjectreference.h"
#include "tier1/utlstring.h"
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
class IParticleEffect;
class IClientParticleListener;
struct Particle;
class ParticleDraw;
class CMeshBuilder;
class CUtlMemoryPool;
class CEffectMaterial;
class CParticleSimulateIterator;
class CParticleRenderIterator;
class IThreadPool;
class CParticleSystemDefinition;
class CParticleMgr;
class CNewParticleEffect;
class CParticleCollection;
#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.
#ifdef _DEBUG
char *m_szDebugName;
#endif
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.
//-----------------------------------------------------------------------------
abstract_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 bool ShouldSimulate() const = 0;
virtual void SetShouldSimulate( bool bSim ) = 0;
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; }
virtual const char *GetEffectName() { return "???"; }
};
#define REGISTER_EFFECT( effect ) \
IParticleEffect* effect##_Factory() \
{ \
return new effect; \
} \
struct effect##_RegistrationHelper \
{ \
effect##_RegistrationHelper() \
{ \
ParticleMgr()->RegisterEffect( typeid( effect ).name(), effect##_Factory ); \
} \
}; \
static effect##_RegistrationHelper g_##effect##_RegistrationHelper
#define REGISTER_EFFECT_USING_CREATE( effect ) \
IParticleEffect* effect##_Factory() \
{ \
return effect::Create( #effect ).GetObject(); \
} \
struct effect##_RegistrationHelper \
{ \
effect##_RegistrationHelper() \
{ \
ParticleMgr()->RegisterEffect( typeid( effect ).name(), effect##_Factory ); \
} \
}; \
static effect##_RegistrationHelper g_##effect##_RegistrationHelper
// 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;
friend class CNewParticleEffect;
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 );
// gets a copy of the current bbox mins/maxs in worldspace
void GetWorldspaceBounds( Vector *pMins, Vector *pMaxs );
// 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 ); }
void SetIsNewParticleSystem( void ) { SetFlag( FLAGS_NEW_PARTICLE_SYSTEM, 1 ); }
// 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 &bboxSet, Vector &bbMin, Vector &bbMax );
void RenderStart( VMatrix &mTempModel, VMatrix &mTempView );
void RenderEnd( VMatrix &mModel, VMatrix &mView );
void BBoxCalcStart( Vector &bbMin, Vector &bbMax );
void BBoxCalcEnd( 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 the sort origin 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 const matrix3x4_t & RenderableToWorldTransform();
virtual void GetRenderBounds( Vector& mins, Vector& maxs );
virtual bool ShouldDraw( void );
virtual int DrawModel( int flags, const RenderableInstance_t &instance );
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).
FLAGS_NEW_PARTICLE_SYSTEM= (1<<12) // uses new particle system
};
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];
#ifdef INFESTED_PARTICLES
// We'll remove this when we move Infested to using the new particle system
public:
#endif
// For faster iteration.
CUtlLinkedList<CEffectMaterial*, unsigned short> m_Materials;
// auto updates the bbox after N frames
unsigned short m_UpdateBBoxCounter;
};
class CParticleLightInfo
{
public:
Vector m_vPos;
Vector m_vColor; // 0-1
float m_flIntensity;
};
typedef IParticleEffect* (*CreateParticleEffectFN)();
enum
{
TOOLPARTICLESYSTEMID_INVALID = -1,
};
class CParticleCollection;
class CNonDrawingParticleSystem
{
public:
CNonDrawingParticleSystem *m_pNext;
CNonDrawingParticleSystem *m_pPrev;
CParticleCollection *m_pSystem;
FORCEINLINE CParticleCollection *operator()( void ) const
{
return m_pSystem;
}
FORCEINLINE CParticleCollection *Get( void ) const
{
return m_pSystem;
}
~CNonDrawingParticleSystem( void );
};
class CClientTools;
class CParticleMgr
{
friend class CParticleEffectBinding;
friend class CParticleCollection;
friend class CNonDrawingParticleSystem;
friend class CClientTools;
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();
void LevelInit();
void RegisterEffect( const char *pEffectType, CreateParticleEffectFN func );
IParticleEffect *CreateEffect( const char *pEffectType );
// 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 );
void AddEffect( CNewParticleEffect *pEffect );
void RemoveEffect( CNewParticleEffect *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 );
//HACKHACK: quick fix that compensates for the fact that this system was designed to never release materials EVER.
void RepairPMaterial( PMaterialHandle hMaterial );
// 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 );
void SpewInfo( bool bDetail );
// add a class that gets notified of entity events
void AddEffectListener( IClientParticleListener *pListener );
void RemoveEffectListener( IClientParticleListener *pListener );
// Tool effect ids
int AllocateToolParticleEffectId();
// Remove all new effects
void RemoveAllNewEffects();
CNewParticleEffect *FirstNewEffect();
CNewParticleEffect *NextNewEffect( CNewParticleEffect *pEffect );
// Should particle effects be rendered?
void RenderParticleSystems( bool bEnable );
bool ShouldRenderParticleSystems() const;
void RemoveOldParticleEffects( float flTime ); // Removes all particles created more than flTime in the past immediately
int GetNumParticles() const { return m_nCurrentParticlesAllocated; }
CNonDrawingParticleSystem *CreateNonDrawingEffect( const char *pEffectName );
private:
struct RetireInfo_t
{
CParticleCollection *m_pCollection;
float m_flScreenArea;
bool m_bFirstFrame;
};
// Call Update() on all the effects.
void UpdateAllEffects( float flTimeDelta );
void UpdateNewEffects( float flTimeDelta ); // update new particle effects
void SpewActiveParticleSystems( );
CParticleSubTextureGroup* FindOrAddSubTextureGroup( IMaterial *pPageMaterial );
int ComputeParticleDefScreenArea( int nInfoCount, RetireInfo_t *pInfo, float *pTotalArea, CParticleSystemDefinition* pDef,
const CViewSetup& view, const VMatrix &worldToPixels, float flFocalDist );
bool RetireParticleCollections( CParticleSystemDefinition* pDef, int nCount, RetireInfo_t *pInfo, float flScreenArea, float flMaxTotalArea );
void BuildParticleSimList( CUtlVector< CNewParticleEffect* > &list );
bool EarlyRetireParticleSystems( int nCount, CNewParticleEffect **ppEffects );
static int RetireSort( const void *p1, const void *p2 );
private:
int m_nCurrentParticlesAllocated;
// 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;
bool m_bRenderParticleEffects;
// All the active effects.
CUtlLinkedList<CParticleEffectBinding*, unsigned short> m_Effects;
// all the active effects using the new particle interface
CUtlIntrusiveDList< CNewParticleEffect > m_NewEffects;
CUtlIntrusiveDList< CNonDrawingParticleSystem > m_NonDrawingParticleSystems;
CUtlVector< IClientParticleListener *> m_effectListeners;
IMaterialSystem *m_pMaterialSystem;
// Store the concatenated modelview matrix
VMatrix m_mModelView;
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.
CUtlMap< const char*, CreateParticleEffectFN > m_effectFactories;
int m_nToolParticleEffectId;
IThreadPool *m_pThreadPool[2];
};
inline int CParticleMgr::AllocateToolParticleEffectId()
{
return m_nToolParticleEffectId++;
}
// Implement this class and register with CParticleMgr to receive particle effect add/remove notification
class IClientParticleListener
{
public:
virtual void OnParticleEffectAdded( IParticleEffect *pEffect ) = 0;
virtual void OnParticleEffectRemoved( IParticleEffect *pEffect ) = 0;
};
// 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
// ------------------------------------------------------------------------ //
CParticleMgr *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