aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/particlemgr.h
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/client/particlemgr.h
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/client/particlemgr.h')
-rw-r--r--mp/src/game/client/particlemgr.h1822
1 files changed, 911 insertions, 911 deletions
diff --git a/mp/src/game/client/particlemgr.h b/mp/src/game/client/particlemgr.h
index f0f09029..3214a55f 100644
--- a/mp/src/game/client/particlemgr.h
+++ b/mp/src/game/client/particlemgr.h
@@ -1,911 +1,911 @@
-//========= Copyright 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/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 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).
- 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];
-
- // 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 CParticleMgr
-{
- friend class CParticleEffectBinding;
- friend class CParticleCollection;
-
-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 );
-
- // 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();
-
- // Should particle effects be rendered?
- void RenderParticleSystems( bool bEnable );
- bool ShouldRenderParticleSystems() const;
-
- // Quick profiling (counts only, not clock cycles).
- bool m_bStatsRunning;
- int m_nStatsFramesSinceLastAlert;
-
- void StatsAccumulateActiveParticleSystems();
- void StatsReset();
- void StatsSpewResults();
- void StatsNewParticleEffectDrawn ( CNewParticleEffect *pParticles );
- void StatsOldParticleEffectDrawn ( CParticleEffectBinding *pParticles );
-
-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
-
- 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;
-
-
- 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
-
-
+//========= Copyright 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/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 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).
+ 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];
+
+ // 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 CParticleMgr
+{
+ friend class CParticleEffectBinding;
+ friend class CParticleCollection;
+
+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 );
+
+ // 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();
+
+ // Should particle effects be rendered?
+ void RenderParticleSystems( bool bEnable );
+ bool ShouldRenderParticleSystems() const;
+
+ // Quick profiling (counts only, not clock cycles).
+ bool m_bStatsRunning;
+ int m_nStatsFramesSinceLastAlert;
+
+ void StatsAccumulateActiveParticleSystems();
+ void StatsReset();
+ void StatsSpewResults();
+ void StatsNewParticleEffectDrawn ( CNewParticleEffect *pParticles );
+ void StatsOldParticleEffectDrawn ( CParticleEffectBinding *pParticles );
+
+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
+
+ 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;
+
+
+ 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
+
+