Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

919 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. //
  9. // This module implements the particle manager for the client DLL.
  10. // In a nutshell, to create your own effect, implement the ParticleEffect
  11. // interface and call CParticleMgr::AddEffect to add your effect. Then you can
  12. // add particles and simulate and render them.
  13. /*
  14. Particle manager documentation
  15. -----------------------------------------------------------------------------
  16. All particle effects are managed by a class called CParticleMgr. It tracks
  17. the list of particles, manages their materials, sorts the particles, and
  18. has callbacks to render them.
  19. Conceptually, CParticleMgr is NOT part of VEngine's entity system. It does
  20. not care about entities, only particle effects. Usually, the two are implemented
  21. together, but you should be aware the CParticleMgr talks to you through its
  22. own interfaces and does not talk to entities. Thus, it is possible to have
  23. particle effects that are not entities.
  24. To make a particle effect, you need two things:
  25. 1. An implementation of the IParticleEffect interface. This is how CParticleMgr
  26. talks to you for things like rendering and updating your effect.
  27. 2. A (member) variable of type CParticleEffectBinding. This allows CParticleMgr to
  28. store its internal data associated with your effect.
  29. Once you have those two things, you call CParticleMgr::AddEffect and pass them
  30. both in. You will then get updates through IParticleEffect::Update, and you will
  31. be asked to render your particles with IParticleEffect::SimulateAndRender.
  32. When you want to remove the effect, call CParticleEffectBinding::SetRemoveFlag(), which
  33. tells CParticleMgr to remove the effect next chance it gets.
  34. Example class:
  35. class CMyEffect : public IParticleEffect
  36. {
  37. public:
  38. // Call this to start the effect by adding it to the particle manager.
  39. void Start()
  40. {
  41. ParticleMgr()->AddEffect( &m_ParticleEffect, this );
  42. }
  43. // implementation of IParticleEffect functions go here...
  44. public:
  45. CParticleEffectBinding m_ParticleEffect;
  46. };
  47. How the particle effects are integrated with the entity system
  48. -----------------------------------------------------------------------------
  49. There are two helper classes that you can use to create particles for your
  50. entities. Each one is useful under different conditions.
  51. 1. CSimpleEmitter is a class that does some of the dirty work of using particles.
  52. If you want, you can just instantiate one of these with CSimpleEmitter::Create
  53. and call its AddParticle functions to add particles. When you are done and
  54. want to 'free' it, call its Release function rather than deleting it, and it
  55. will wait until all of its particles have gone away before removing itself
  56. (so you don't have to write code to wait for all of the particles to go away).
  57. In most cases, it is the easiest and most clear to use CSimpleEmitter or
  58. derive a class from it, then use that class from inside an entity that wants
  59. to make particles.
  60. CSimpleEmitter and derived classes handle adding themselves to the particle
  61. manager, tracking how many particles in the effect are active, and
  62. rendering the particles.
  63. CSimpleEmitter has code to simulate and render particles in a generic fashion,
  64. but if you derive a class from it, you can override some of its behavior
  65. with virtuals like UpdateAlpha, UpdateScale, UpdateColor, etc..
  66. Example code:
  67. CSimpleEmitter *pEmitter = CSimpleEmitter::Create();
  68. CEffectMaterialHandle hMaterial = pEmitter->GetCEffectMaterial( "mymaterial" );
  69. for( int i=0; i < 100; i++ )
  70. pEmitter->AddParticle( hMaterial, RandomVector(0,10), 4 );
  71. pEmitter->Release();
  72. 2. Some older effects derive from C_BaseParticleEffect and implement an entity
  73. and a particle system at the same time. This gets nasty and is not encouraged anymore.
  74. */
  75. #ifndef PARTICLEMGR_H
  76. #define PARTICLEMGR_H
  77. #ifdef _WIN32
  78. #pragma once
  79. #endif
  80. #include "materialsystem/imaterial.h"
  81. #include "materialsystem/imaterialsystem.h"
  82. #include "mathlib/vector.h"
  83. #include "mathlib/vmatrix.h"
  84. #include "mathlib/mathlib.h"
  85. #include "iclientrenderable.h"
  86. #include "clientleafsystem.h"
  87. #include "tier0/fasttimer.h"
  88. #include "utllinkedlist.h"
  89. #include "utldict.h"
  90. #ifdef WIN32
  91. #include <typeinfo.h>
  92. #else
  93. #include <typeinfo>
  94. #endif
  95. #include "tier1/utlintrusivelist.h"
  96. #include "tier1/utlstring.h"
  97. //-----------------------------------------------------------------------------
  98. // forward declarations
  99. //-----------------------------------------------------------------------------
  100. class IParticleEffect;
  101. class IClientParticleListener;
  102. struct Particle;
  103. class ParticleDraw;
  104. class CMeshBuilder;
  105. class CUtlMemoryPool;
  106. class CEffectMaterial;
  107. class CParticleSimulateIterator;
  108. class CParticleRenderIterator;
  109. class IThreadPool;
  110. class CParticleSystemDefinition;
  111. class CParticleMgr;
  112. class CNewParticleEffect;
  113. class CParticleCollection;
  114. #define INVALID_MATERIAL_HANDLE NULL
  115. // Various stats, disabled
  116. // extern int g_nParticlesDrawn;
  117. // extern CCycleCount g_ParticleTimer;
  118. class CParticleSubTexture;
  119. class CParticleSubTextureGroup;
  120. //-----------------------------------------------------------------------------
  121. // The basic particle description; all particles need to inherit from this.
  122. //-----------------------------------------------------------------------------
  123. struct Particle
  124. {
  125. Particle *m_pPrev, *m_pNext;
  126. // Which sub texture this particle uses (so we can get at the tcoord mins and maxs).
  127. CParticleSubTexture *m_pSubTexture;
  128. // If m_Pos isn't used to store the world position, then implement IParticleEffect::GetParticlePosition()
  129. Vector m_Pos; // Position of the particle in world space
  130. };
  131. //-----------------------------------------------------------------------------
  132. // This is the CParticleMgr's reference to a material in the material system.
  133. // Particles are sorted by material.
  134. //-----------------------------------------------------------------------------
  135. // This indexes CParticleMgr::m_SubTextures.
  136. typedef CParticleSubTexture* PMaterialHandle;
  137. // Each effect stores a list of particles associated with each material. The list is
  138. // hashed on the IMaterial pointer.
  139. class CEffectMaterial
  140. {
  141. public:
  142. CEffectMaterial();
  143. public:
  144. // This provides the material that gets bound for this material in this effect.
  145. // There can be multiple subtextures all within the same CEffectMaterial.
  146. CParticleSubTextureGroup *m_pGroup;
  147. Particle m_Particles;
  148. CEffectMaterial *m_pHashedNext;
  149. };
  150. class CParticleSubTextureGroup
  151. {
  152. public:
  153. CParticleSubTextureGroup();
  154. ~CParticleSubTextureGroup();
  155. // Even though each of the subtextures has its own material, they should all basically be
  156. // the same exact material and just use different texture coordinates, so this is the
  157. // material of the first subtexture that is bound.
  158. //
  159. // This is gotten from GetMaterialPage().
  160. IMaterial *m_pPageMaterial;
  161. };
  162. // Precalculated data for each material used for particles.
  163. // This allows us to put multiple subtextures into one VTF and sort them against each other.
  164. class CParticleSubTexture
  165. {
  166. public:
  167. CParticleSubTexture();
  168. float m_tCoordMins[2]; // bbox in texel space that this particle material uses.
  169. float m_tCoordMaxs[2]; // Specified in the SubTextureMins/SubTextureMaxs parameter in the materials.
  170. // Which group does this subtexture belong to?
  171. CParticleSubTextureGroup *m_pGroup;
  172. CParticleSubTextureGroup m_DefaultGroup; // This is used as the group if a particle's material
  173. // isn't using a group.
  174. #ifdef _DEBUG
  175. char *m_szDebugName;
  176. #endif
  177. IMaterial *m_pMaterial;
  178. };
  179. // Particle simulation list, used to determine what particles to simulate and how.
  180. struct ParticleSimListEntry_t
  181. {
  182. CNewParticleEffect* m_pNewParticleEffect;
  183. bool m_bBoundingBoxOnly;
  184. };
  185. //-----------------------------------------------------------------------------
  186. // interface IParticleEffect:
  187. //
  188. // This is the interface that particles effects must implement. The effect is
  189. // responsible for starting itself and calling CParticleMgr::AddEffect, then it
  190. // will get the callbacks it needs to simulate and render the particles.
  191. //-----------------------------------------------------------------------------
  192. abstract_class IParticleEffect
  193. {
  194. // Overridables.
  195. public:
  196. virtual ~IParticleEffect() {}
  197. // Called at the beginning of a frame to precalculate data for rendering
  198. // the particles. If you manage your own list of particles and want to
  199. // simulate them all at once, you can do that here and just render them in
  200. // the SimulateAndRender call.
  201. virtual void Update( float fTimeDelta ) {}
  202. // Called once for the entire effect before the batch of SimulateAndRender() calls.
  203. // For particle systems using FLAGS_CAMERASPACE (the default), effectMatrix transforms the particles from
  204. // world space into camera space. You can change this matrix if you want your particles relative to something
  205. // else like an attachment's space.
  206. virtual void StartRender( VMatrix &effectMatrix ) {}
  207. // Simulate the particles.
  208. virtual bool ShouldSimulate() const = 0;
  209. virtual void SetShouldSimulate( bool bSim ) = 0;
  210. virtual void SimulateParticles( CParticleSimulateIterator *pIterator ) = 0;
  211. // Render the particles.
  212. virtual void RenderParticles( CParticleRenderIterator *pIterator ) = 0;
  213. // Implementing this is optional. It is called when an effect is removed. It is useful if
  214. // you hold onto pointers to the particles you created (so when this is called, you should
  215. // clean up your data so you don't reference the particles again).
  216. // NOTE: after calling this, the particle manager won't touch the IParticleEffect
  217. // or its associated CParticleEffectBinding anymore.
  218. virtual void NotifyRemove() {}
  219. // This method notifies the effect a particle is about to be deallocated.
  220. // Implementations should *not* actually deallocate it.
  221. // NOTE: The particle effect's GetNumActiveParticles is updated BEFORE this is called
  222. // so if GetNumActiveParticles returns 0, then you know this is the last particle
  223. // in the system being removed.
  224. virtual void NotifyDestroyParticle( Particle* pParticle ) {}
  225. // Fill in the origin used to sort this entity.
  226. // This is a world space position.
  227. virtual const Vector &GetSortOrigin() = 0;
  228. // Fill in the origin used to sort this entity.
  229. // TODO: REMOVE THIS. ALL PARTICLE SYSTEMS SHOULD EITHER SET m_Pos IN CONJUNCTION WITH THE
  230. // PARTICLE_LOCALSPACE FLAG, OR DO SETBBOX THEMSELVES.
  231. virtual const Vector *GetParticlePosition( Particle *pParticle ) { return &pParticle->m_Pos; }
  232. virtual const char *GetEffectName() { return "???"; }
  233. };
  234. #define REGISTER_EFFECT( effect ) \
  235. IParticleEffect* effect##_Factory() \
  236. { \
  237. return new effect; \
  238. } \
  239. struct effect##_RegistrationHelper \
  240. { \
  241. effect##_RegistrationHelper() \
  242. { \
  243. ParticleMgr()->RegisterEffect( typeid( effect ).name(), effect##_Factory ); \
  244. } \
  245. }; \
  246. static effect##_RegistrationHelper g_##effect##_RegistrationHelper
  247. #define REGISTER_EFFECT_USING_CREATE( effect ) \
  248. IParticleEffect* effect##_Factory() \
  249. { \
  250. return effect::Create( #effect ).GetObject(); \
  251. } \
  252. struct effect##_RegistrationHelper \
  253. { \
  254. effect##_RegistrationHelper() \
  255. { \
  256. ParticleMgr()->RegisterEffect( typeid( effect ).name(), effect##_Factory ); \
  257. } \
  258. }; \
  259. static effect##_RegistrationHelper g_##effect##_RegistrationHelper
  260. // In order to create a particle effect, you must have one of these around and
  261. // implement IParticleEffect. Pass them both into CParticleMgr::AddEffect and you
  262. // are good to go.
  263. class CParticleEffectBinding : public CDefaultClientRenderable
  264. {
  265. friend class CParticleMgr;
  266. friend class CParticleSimulateIterator;
  267. friend class CNewParticleEffect;
  268. public:
  269. CParticleEffectBinding();
  270. ~CParticleEffectBinding();
  271. // Helper functions to setup, add particles, etc..
  272. public:
  273. // Simulate all the particles.
  274. void SimulateParticles( float flTimeDelta );
  275. // Use this to specify materials when adding particles.
  276. // Returns the index of the material it found or added.
  277. // Returns INVALID_MATERIAL_HANDLE if it couldn't find or add a material.
  278. PMaterialHandle FindOrAddMaterial( const char *pMaterialName );
  279. // Allocate particles. The Particle manager will automagically
  280. // deallocate them when the IParticleEffect SimulateAndRender() method
  281. // returns false. The first argument is the size of the particle
  282. // structure in bytes
  283. Particle* AddParticle( int sizeInBytes, PMaterialHandle pMaterial );
  284. // This is an optional call you can make if you want to manually manage the effect's
  285. // bounding box. Normally, the bounding box is managed automatically, but in certain
  286. // cases it is more efficient to set it manually.
  287. //
  288. // Note: this is a WORLD SPACE bounding box, even if you've used SetLocalSpaceTransform.
  289. //
  290. // After you make this call, the particle manager will no longer update the bounding
  291. // box automatically if bDisableAutoUpdate is true.
  292. void SetBBox( const Vector &bbMin, const Vector &bbMax, bool bDisableAutoUpdate = true );
  293. // gets a copy of the current bbox mins/maxs in worldspace
  294. void GetWorldspaceBounds( Vector *pMins, Vector *pMaxs );
  295. // This tells the particle manager that your particles are transformed by the specified matrix.
  296. // That way, it can transform the bbox defined by Particle::m_Pos into world space correctly.
  297. //
  298. // It also sets up the matrix returned by CParticleMgr::GetModelView() to include this matrix, so you
  299. // can do TransformParticle with it like any other particle system.
  300. const matrix3x4_t& GetLocalSpaceTransform() const;
  301. void SetLocalSpaceTransform( const matrix3x4_t &transform );
  302. // This expands the bbox to contain the specified point. Returns true if bbox changed
  303. bool EnlargeBBoxToContain( const Vector &pt );
  304. // The EZ particle singletons use this - they don't want to be added to all the leaves and drawn through the
  305. // leaf system - they are specifically told to draw each frame at a certain point.
  306. void SetDrawThruLeafSystem( int bDraw );
  307. // Some view model particle effects want to be drawn right before the view model (after everything else is
  308. // drawn).
  309. void SetDrawBeforeViewModel( int bDraw );
  310. // Call this to have the effect removed whenever it safe to do so.
  311. // This is a lot safer than calling CParticleMgr::RemoveEffect.
  312. int GetRemoveFlag() { return GetFlag( FLAGS_REMOVE ); }
  313. void SetRemoveFlag() { SetFlag( FLAGS_REMOVE, 1 ); }
  314. // Set this flag to tell the particle manager to simulate your particles even
  315. // if the particle system isn't visible. Tempents and fast effects can always use
  316. // this if they want since they want to simulate their particles until they go away.
  317. // This flag is ON by default.
  318. int GetAlwaysSimulate() { return GetFlag( FLAGS_ALWAYSSIMULATE ); }
  319. void SetAlwaysSimulate( int bAlwaysSimulate ) { SetFlag( FLAGS_ALWAYSSIMULATE, bAlwaysSimulate ); }
  320. void SetIsNewParticleSystem( void ) { SetFlag( FLAGS_NEW_PARTICLE_SYSTEM, 1 ); }
  321. // Set if the effect was drawn the previous frame.
  322. // This can be used by particle effect classes
  323. // to decide whether or not they want to spawn
  324. // new particles - if they weren't drawn, then
  325. // they can 'freeze' the particle system to avoid
  326. // overhead.
  327. int WasDrawnPrevFrame() { return GetFlag( FLAGS_DRAWN_PREVFRAME ); }
  328. void SetWasDrawnPrevFrame( int bWasDrawnPrevFrame ) { SetFlag( FLAGS_DRAWN_PREVFRAME, bWasDrawnPrevFrame ); }
  329. // When the effect is in camera space mode, then the transforms are setup such that
  330. // the particle vertices are specified in camera space (in CParticleDraw) rather than world space.
  331. //
  332. // This makes it faster to specify the particles - you only have to transform the center
  333. // by CParticleMgr::GetModelView then add to X and Y to build the quad.
  334. //
  335. // Effects that want to specify verts (in CParticleDraw) in world space should set this to false and
  336. // ignore CParticleMgr::GetModelView.
  337. //
  338. // Camera space mode is ON by default.
  339. int IsEffectCameraSpace() { return GetFlag( FLAGS_CAMERASPACE ); }
  340. void SetEffectCameraSpace( int bCameraSpace ) { SetFlag( FLAGS_CAMERASPACE, bCameraSpace ); }
  341. // This tells it whether or not to apply the local transform to the matrix returned by CParticleMgr::GetModelView().
  342. // Usually, you'll want this, so you can just say TransformParticle( pMgr->GetModelView(), vPos ), but you may want
  343. // to manually apply your local transform before saying TransformParticle.
  344. //
  345. // This is ON by default.
  346. int GetAutoApplyLocalTransform() const { return GetFlag( FLAGS_AUTOAPPLYLOCALTRANSFORM ); }
  347. void SetAutoApplyLocalTransform( int b ) { SetFlag( FLAGS_AUTOAPPLYLOCALTRANSFORM, b ); }
  348. // If this is true, then the bbox is calculated from particle positions. This works
  349. // fine if you always simulate (SetAlwaysSimulateFlag) so the system can become visible
  350. // if it moves into the PVS. If you don't use this, then you should call SetBBox at
  351. // least once to tell the particle manager where your entity is.
  352. int GetAutoUpdateBBox() { return GetFlag( FLAGS_AUTOUPDATEBBOX ); }
  353. void SetAutoUpdateBBox( int bAutoUpdate ) { SetFlag( FLAGS_AUTOUPDATEBBOX, bAutoUpdate ); }
  354. // Get the current number of particles in the effect.
  355. int GetNumActiveParticles();
  356. // The is the max size of the particles for use in bounding computation
  357. void SetParticleCullRadius( float flMaxParticleRadius );
  358. // Build a list of all active particles, returns actual count filled in
  359. int GetActiveParticleList( int nCount, Particle **ppParticleList );
  360. // detect origin/bbox changes and update leaf system if necessary
  361. void DetectChanges();
  362. private:
  363. // Change flags..
  364. void SetFlag( int flag, int bOn ) { if( bOn ) m_Flags |= flag; else m_Flags &= ~flag; }
  365. int GetFlag( int flag ) const { return m_Flags & flag; }
  366. void Init( CParticleMgr *pMgr, IParticleEffect *pSim );
  367. void Term();
  368. // Get rid of the specified particle.
  369. void RemoveParticle( Particle *pParticle );
  370. void StartDrawMaterialParticles(
  371. CEffectMaterial *pMaterial,
  372. float flTimeDelta,
  373. IMesh* &pMesh,
  374. CMeshBuilder &builder,
  375. ParticleDraw &particleDraw,
  376. bool bWireframe );
  377. int DrawMaterialParticles(
  378. bool bBucketSort,
  379. CEffectMaterial *pMaterial,
  380. float flTimeDelta,
  381. bool bWireframe
  382. );
  383. void GrowBBoxFromParticlePositions( CEffectMaterial *pMaterial, bool &bboxSet, Vector &bbMin, Vector &bbMax );
  384. void RenderStart( VMatrix &mTempModel, VMatrix &mTempView );
  385. void RenderEnd( VMatrix &mModel, VMatrix &mView );
  386. void BBoxCalcStart( Vector &bbMin, Vector &bbMax );
  387. void BBoxCalcEnd( bool bboxSet, Vector &bbMin, Vector &bbMax );
  388. void DoBucketSort(
  389. CEffectMaterial *pMaterial,
  390. float *zCoords,
  391. int nZCoords,
  392. float minZ,
  393. float maxZ );
  394. int GetRemovalInProgressFlag() { return GetFlag( FLAGS_REMOVALINPROGRESS ); }
  395. void SetRemovalInProgressFlag() { SetFlag( FLAGS_REMOVALINPROGRESS, 1 ); }
  396. // BBox is recalculated before it's put into the tree for the first time.
  397. int GetNeedsBBoxUpdate() { return GetFlag( FLAGS_NEEDS_BBOX_UPDATE ); }
  398. void SetNeedsBBoxUpdate( int bFirstUpdate ) { SetFlag( FLAGS_NEEDS_BBOX_UPDATE, bFirstUpdate ); }
  399. // Set on creation and cleared after the first PostRender (whether or not the system was rendered).
  400. int GetFirstFrameFlag() { return GetFlag( FLAGS_FIRST_FRAME ); }
  401. void SetFirstFrameFlag( int bFirstUpdate ) { SetFlag( FLAGS_FIRST_FRAME, bFirstUpdate ); }
  402. int WasDrawn() { return GetFlag( FLAGS_DRAWN ); }
  403. void SetDrawn( int bDrawn ) { SetFlag( FLAGS_DRAWN, bDrawn ); }
  404. // Update m_Min/m_Max. Returns false and sets the bbox to the sort origin if there are no particles.
  405. bool RecalculateBoundingBox();
  406. CEffectMaterial* GetEffectMaterial( CParticleSubTexture *pSubTexture );
  407. // IClientRenderable overrides.
  408. public:
  409. virtual const Vector& GetRenderOrigin( void );
  410. virtual const QAngle& GetRenderAngles( void );
  411. virtual const matrix3x4_t & RenderableToWorldTransform();
  412. virtual void GetRenderBounds( Vector& mins, Vector& maxs );
  413. virtual bool ShouldDraw( void );
  414. virtual bool IsTransparent( void );
  415. virtual int DrawModel( int flags );
  416. private:
  417. enum
  418. {
  419. FLAGS_REMOVE = (1<<0), // Set in SetRemoveFlag
  420. FLAGS_REMOVALINPROGRESS = (1<<1), // Set while the effect is being removed to prevent
  421. // infinite recursion.
  422. FLAGS_NEEDS_BBOX_UPDATE = (1<<2), // This is set until the effect's bbox has been updated once.
  423. FLAGS_AUTOUPDATEBBOX = (1<<3), // Update bbox automatically? Cleared in SetBBox.
  424. FLAGS_ALWAYSSIMULATE = (1<<4), // See SetAlwaysSimulate.
  425. FLAGS_DRAWN = (1<<5), // Set if the effect is drawn through the leaf system.
  426. FLAGS_DRAWN_PREVFRAME = (1<<6), // Set if the effect was drawn the previous frame.
  427. // This can be used by particle effect classes
  428. // to decide whether or not they want to spawn
  429. // new particles - if they weren't drawn, then
  430. // they can 'freeze' the particle system to avoid
  431. // overhead.
  432. FLAGS_CAMERASPACE = (1<<7), // See SetEffectCameraSpace.
  433. FLAGS_DRAW_THRU_LEAF_SYSTEM=(1<<8), // This is the default - do the effect's visibility through the leaf system.
  434. 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.
  435. FLAGS_AUTOAPPLYLOCALTRANSFORM=(1<<10), // Automatically apply the local transform to CParticleMgr::GetModelView()'s matrix.
  436. FLAGS_FIRST_FRAME = (1<<11), // Cleared after the first frame that this system exists (so it can simulate after rendering once).
  437. FLAGS_NEW_PARTICLE_SYSTEM= (1<<12) // uses new particle system
  438. };
  439. VMatrix m_LocalSpaceTransform;
  440. bool m_bLocalSpaceTransformIdentity; // If this is true, then m_LocalSpaceTransform is assumed to be identity.
  441. // Bounding box. Stored in WORLD space.
  442. Vector m_Min;
  443. Vector m_Max;
  444. // paramter copies to detect changes
  445. Vector m_LastMin;
  446. Vector m_LastMax;
  447. // The particle cull size
  448. float m_flParticleCullRadius;
  449. // Number of active particles.
  450. unsigned short m_nActiveParticles;
  451. // See CParticleMgr::m_FrameCode.
  452. unsigned short m_FrameCode;
  453. // For CParticleMgr's list index.
  454. unsigned short m_ListIndex;
  455. IParticleEffect *m_pSim;
  456. CParticleMgr *m_pParticleMgr;
  457. // Combination of the CParticleEffectBinding::FLAGS_ flags.
  458. int m_Flags;
  459. // Materials this effect is using.
  460. enum { EFFECT_MATERIAL_HASH_SIZE = 8 };
  461. CEffectMaterial *m_EffectMaterialHash[EFFECT_MATERIAL_HASH_SIZE];
  462. // For faster iteration.
  463. CUtlLinkedList<CEffectMaterial*, unsigned short> m_Materials;
  464. // auto updates the bbox after N frames
  465. unsigned short m_UpdateBBoxCounter;
  466. };
  467. class CParticleLightInfo
  468. {
  469. public:
  470. Vector m_vPos;
  471. Vector m_vColor; // 0-1
  472. float m_flIntensity;
  473. };
  474. typedef IParticleEffect* (*CreateParticleEffectFN)();
  475. enum
  476. {
  477. TOOLPARTICLESYSTEMID_INVALID = -1,
  478. };
  479. class CParticleMgr
  480. {
  481. friend class CParticleEffectBinding;
  482. friend class CParticleCollection;
  483. public:
  484. CParticleMgr();
  485. virtual ~CParticleMgr();
  486. // Call at init time to preallocate the bucket of particles.
  487. bool Init(unsigned long nPreallocatedParticles, IMaterialSystem *pMaterial);
  488. // Shutdown - free everything.
  489. void Term();
  490. void LevelInit();
  491. void RegisterEffect( const char *pEffectType, CreateParticleEffectFN func );
  492. IParticleEffect *CreateEffect( const char *pEffectType );
  493. // Add and remove effects from the active list.
  494. // Note: once you call AddEffect, CParticleEffectBinding will automatically call
  495. // RemoveEffect in its destructor.
  496. // Note: it's much safer to call CParticleEffectBinding::SetRemoveFlag instead of
  497. // CParticleMgr::RemoveEffect.
  498. bool AddEffect( CParticleEffectBinding *pEffect, IParticleEffect *pSim );
  499. void RemoveEffect( CParticleEffectBinding *pEffect );
  500. void AddEffect( CNewParticleEffect *pEffect );
  501. void RemoveEffect( CNewParticleEffect *pEffect );
  502. // Called at level shutdown to free all the lingering particle effects (usually
  503. // CParticleEffect-derived effects that can linger with noone holding onto them).
  504. void RemoveAllEffects();
  505. // This should be called at the start of the frame.
  506. void IncrementFrameCode();
  507. // This updates all the particle effects and inserts them into the leaves.
  508. void Simulate( float fTimeDelta );
  509. // This just marks effects that were drawn so during their next simulation they can know
  510. // if they were drawn in the previous frame.
  511. void PostRender();
  512. // Draw the effects marked with SetDrawBeforeViewModel.
  513. void DrawBeforeViewModelEffects();
  514. // Returns the modelview matrix
  515. VMatrix& GetModelView();
  516. Particle *AllocParticle( int size );
  517. void FreeParticle( Particle * );
  518. PMaterialHandle GetPMaterial( const char *pMaterialName );
  519. IMaterial* PMaterialToIMaterial( PMaterialHandle hMaterial );
  520. //HACKHACK: quick fix that compensates for the fact that this system was designed to never release materials EVER.
  521. void RepairPMaterial( PMaterialHandle hMaterial );
  522. // Particles drawn with the ParticleSphere material will use this info.
  523. // This should be set in IParticleEffect.
  524. void GetDirectionalLightInfo( CParticleLightInfo &info ) const;
  525. void SetDirectionalLightInfo( const CParticleLightInfo &info );
  526. // add a class that gets notified of entity events
  527. void AddEffectListener( IClientParticleListener *pListener );
  528. void RemoveEffectListener( IClientParticleListener *pListener );
  529. // Tool effect ids
  530. int AllocateToolParticleEffectId();
  531. // Remove all new effects
  532. void RemoveAllNewEffects();
  533. // Should particle effects be rendered?
  534. void RenderParticleSystems( bool bEnable );
  535. bool ShouldRenderParticleSystems() const;
  536. // Quick profiling (counts only, not clock cycles).
  537. bool m_bStatsRunning;
  538. int m_nStatsFramesSinceLastAlert;
  539. void StatsAccumulateActiveParticleSystems();
  540. void StatsReset();
  541. void StatsSpewResults();
  542. void StatsNewParticleEffectDrawn ( CNewParticleEffect *pParticles );
  543. void StatsOldParticleEffectDrawn ( CParticleEffectBinding *pParticles );
  544. private:
  545. struct RetireInfo_t
  546. {
  547. CParticleCollection *m_pCollection;
  548. float m_flScreenArea;
  549. bool m_bFirstFrame;
  550. };
  551. // Call Update() on all the effects.
  552. void UpdateAllEffects( float flTimeDelta );
  553. void UpdateNewEffects( float flTimeDelta ); // update new particle effects
  554. CParticleSubTextureGroup* FindOrAddSubTextureGroup( IMaterial *pPageMaterial );
  555. int ComputeParticleDefScreenArea( int nInfoCount, RetireInfo_t *pInfo, float *pTotalArea, CParticleSystemDefinition* pDef,
  556. const CViewSetup& view, const VMatrix &worldToPixels, float flFocalDist );
  557. bool RetireParticleCollections( CParticleSystemDefinition* pDef, int nCount, RetireInfo_t *pInfo, float flScreenArea, float flMaxTotalArea );
  558. void BuildParticleSimList( CUtlVector< ParticleSimListEntry_t > &list );
  559. bool EarlyRetireParticleSystems( int nCount, ParticleSimListEntry_t *ppEffects );
  560. static int RetireSort( const void *p1, const void *p2 );
  561. private:
  562. int m_nCurrentParticlesAllocated;
  563. // Directional lighting info.
  564. CParticleLightInfo m_DirectionalLight;
  565. // Frame code, used to prevent CParticleEffects from simulating multiple times per frame.
  566. // Their DrawModel can be called multiple times per frame because of water reflections,
  567. // but we only want to simulate the particles once.
  568. unsigned short m_FrameCode;
  569. bool m_bUpdatingEffects;
  570. bool m_bRenderParticleEffects;
  571. // All the active effects.
  572. CUtlLinkedList<CParticleEffectBinding*, unsigned short> m_Effects;
  573. // all the active effects using the new particle interface
  574. CUtlIntrusiveDList< CNewParticleEffect > m_NewEffects;
  575. CUtlVector< IClientParticleListener *> m_effectListeners;
  576. IMaterialSystem *m_pMaterialSystem;
  577. // Store the concatenated modelview matrix
  578. VMatrix m_mModelView;
  579. CUtlVector<CParticleSubTextureGroup*> m_SubTextureGroups; // lookup by group name
  580. CUtlDict<CParticleSubTexture*,unsigned short> m_SubTextures; // lookup by material name
  581. CParticleSubTexture m_DefaultInvalidSubTexture; // Used when they specify an invalid material name.
  582. CUtlMap< const char*, CreateParticleEffectFN > m_effectFactories;
  583. int m_nToolParticleEffectId;
  584. IThreadPool *m_pThreadPool[2];
  585. };
  586. inline int CParticleMgr::AllocateToolParticleEffectId()
  587. {
  588. return m_nToolParticleEffectId++;
  589. }
  590. // Implement this class and register with CParticleMgr to receive particle effect add/remove notification
  591. class IClientParticleListener
  592. {
  593. public:
  594. virtual void OnParticleEffectAdded( IParticleEffect *pEffect ) = 0;
  595. virtual void OnParticleEffectRemoved( IParticleEffect *pEffect ) = 0;
  596. };
  597. // Helper functions to abstract out the particle testbed app.
  598. float Helper_GetTime();
  599. float Helper_GetFrameTime();
  600. float Helper_RandomFloat( float minVal, float maxVal );
  601. int Helper_RandomInt( int minVal, int maxVal );
  602. // ------------------------------------------------------------------------ //
  603. // CParticleMgr inlines
  604. // ------------------------------------------------------------------------ //
  605. inline VMatrix& CParticleMgr::GetModelView()
  606. {
  607. return m_mModelView;
  608. }
  609. // ------------------------------------------------------------------------ //
  610. // CParticleEffectBinding inlines.
  611. // ------------------------------------------------------------------------ //
  612. inline const matrix3x4_t& CParticleEffectBinding::GetLocalSpaceTransform() const
  613. {
  614. return m_LocalSpaceTransform.As3x4();
  615. }
  616. // ------------------------------------------------------------------------ //
  617. // GLOBALS
  618. // ------------------------------------------------------------------------ //
  619. CParticleMgr *ParticleMgr();
  620. //-----------------------------------------------------------------------------
  621. // StandardParticle_t; this is just one type of particle
  622. // effects may implement their own particle data structures
  623. //-----------------------------------------------------------------------------
  624. struct StandardParticle_t : public Particle
  625. {
  626. // Color and alpha values are 0 - 1
  627. void SetColor(float r, float g, float b);
  628. void SetAlpha(float a);
  629. Vector m_Velocity;
  630. // How this is used is up to the effect's discretion. Some use it for how long it has been alive
  631. // and others use it to count down until the particle disappears.
  632. float m_Lifetime;
  633. unsigned char m_EffectData; // Data specific to the IParticleEffect. This can be used to distinguish between
  634. // different types of particles the effect is simulating.
  635. unsigned short m_EffectDataWord;
  636. unsigned char m_Color[4]; // RGBA - not all effects need to use this.
  637. };
  638. // ------------------------------------------------------------------------ //
  639. // Transform a particle.
  640. // ------------------------------------------------------------------------ //
  641. inline void TransformParticle(const VMatrix &vMat, const Vector &vIn, Vector &vOut)
  642. {
  643. //vOut = vMat.VMul4x3(vIn);
  644. vOut.x = vMat.m[0][0]*vIn.x + vMat.m[0][1]*vIn.y + vMat.m[0][2]*vIn.z + vMat.m[0][3];
  645. vOut.y = vMat.m[1][0]*vIn.x + vMat.m[1][1]*vIn.y + vMat.m[1][2]*vIn.z + vMat.m[1][3];
  646. vOut.z = vMat.m[2][0]*vIn.x + vMat.m[2][1]*vIn.y + vMat.m[2][2]*vIn.z + vMat.m[2][3];
  647. }
  648. // ------------------------------------------------------------------------ //
  649. // CEffectMaterial inlines
  650. // ------------------------------------------------------------------------ //
  651. inline void StandardParticle_t::SetColor(float r, float g, float b)
  652. {
  653. m_Color[0] = (unsigned char)(r * 255.9f);
  654. m_Color[1] = (unsigned char)(g * 255.9f);
  655. m_Color[2] = (unsigned char)(b * 255.9f);
  656. }
  657. inline void StandardParticle_t::SetAlpha(float a)
  658. {
  659. m_Color[3] = (unsigned char)(a * 255.9f);
  660. }
  661. //-----------------------------------------------------------------------------
  662. // List functions.
  663. //-----------------------------------------------------------------------------
  664. inline void UnlinkParticle( Particle *pParticle )
  665. {
  666. pParticle->m_pPrev->m_pNext = pParticle->m_pNext;
  667. pParticle->m_pNext->m_pPrev = pParticle->m_pPrev;
  668. }
  669. inline void InsertParticleBefore( Particle *pInsert, Particle *pNext )
  670. {
  671. // link pCur before pPrev
  672. pInsert->m_pNext = pNext;
  673. pInsert->m_pPrev = pNext->m_pPrev;
  674. pInsert->m_pNext->m_pPrev = pInsert->m_pPrev->m_pNext = pInsert;
  675. }
  676. inline void InsertParticleAfter( Particle *pInsert, Particle *pPrev )
  677. {
  678. pInsert->m_pPrev = pPrev;
  679. pInsert->m_pNext = pPrev->m_pNext;
  680. pInsert->m_pNext->m_pPrev = pInsert->m_pPrev->m_pNext = pInsert;
  681. }
  682. inline void SwapParticles( Particle *pPrev, Particle *pCur )
  683. {
  684. // unlink pCur
  685. UnlinkParticle( pCur );
  686. InsertParticleBefore( pCur, pPrev );
  687. }
  688. #include "particle_iterators.h"
  689. #endif