#ifndef PARTICLEMGR_H #define PARTICLEMGR_H #ifdef _WIN32 #pragma once #endif #include "imaterial.h" #include "imaterialsystem.h" #include "vector.h" #include "vmatrix.h" #include "Mathlib.h" #include "iclientrenderable.h" #include "clientleafsystem.h" #include "fasttimer.h" #include "utllinkedlist.h" #include "UtlDict.h" #ifdef WIN32 #include #else #include #endif #include "utlintrusivelist.h" #include "utlobjectreference.h" #include "utlstring.h" 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 class CParticleSubTexture; class CParticleSubTextureGroup; struct Particle { Particle* m_pPrev, * m_pNext; CParticleSubTexture* m_pSubTexture; Vector m_Pos; }; typedef CParticleSubTexture* PMaterialHandle; class CEffectMaterial { public: CEffectMaterial(); public: CParticleSubTextureGroup* m_pGroup; Particle m_Particles; CEffectMaterial* m_pHashedNext; }; class CParticleSubTextureGroup { public: CParticleSubTextureGroup(); ~CParticleSubTextureGroup(); IMaterial* m_pPageMaterial; }; class CParticleSubTexture { public: CParticleSubTexture(); float m_tCoordMins[2]; float m_tCoordMaxs[2]; CParticleSubTextureGroup* m_pGroup; CParticleSubTextureGroup m_DefaultGroup; #ifdef _DEBUG char* m_szDebugName; #endif IMaterial* m_pMaterial; }; abstract_class IParticleEffect { public: virtual ~IParticleEffect() {} virtual void Update(float fTimeDelta) {} virtual void StartRender(VMatrix & effectMatrix) {} virtual bool ShouldSimulate() const = 0; virtual void SetShouldSimulate(bool bSim) = 0; virtual void SimulateParticles(CParticleSimulateIterator* pIterator) = 0; virtual void RenderParticles(CParticleRenderIterator* pIterator) = 0; virtual void NotifyRemove() {} virtual void NotifyDestroyParticle(Particle* pParticle) {} virtual const Vector& GetSortOrigin() = 0; 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 class CParticleEffectBinding : public CDefaultClientRenderable { friend class CParticleMgr; friend class CParticleSimulateIterator; friend class CNewParticleEffect; public: CParticleEffectBinding(); ~CParticleEffectBinding(); public: void SimulateParticles(float flTimeDelta); PMaterialHandle FindOrAddMaterial(const char* pMaterialName); Particle* AddParticle(int sizeInBytes, PMaterialHandle pMaterial); void SetBBox(const Vector& bbMin, const Vector& bbMax, bool bDisableAutoUpdate = true); void GetWorldspaceBounds(Vector* pMins, Vector* pMaxs); const matrix3x4_t& GetLocalSpaceTransform() const; void SetLocalSpaceTransform(const matrix3x4_t& transform); bool EnlargeBBoxToContain(const Vector& pt); void SetDrawThruLeafSystem(int bDraw); void SetDrawBeforeViewModel(int bDraw); int GetRemoveFlag() { return GetFlag(FLAGS_REMOVE); } void SetRemoveFlag() { SetFlag(FLAGS_REMOVE, 1); } int GetAlwaysSimulate() { return GetFlag(FLAGS_ALWAYSSIMULATE); } void SetAlwaysSimulate(int bAlwaysSimulate) { SetFlag(FLAGS_ALWAYSSIMULATE, bAlwaysSimulate); } void SetIsNewParticleSystem(void) { SetFlag(FLAGS_NEW_PARTICLE_SYSTEM, 1); } int WasDrawnPrevFrame() { return GetFlag(FLAGS_DRAWN_PREVFRAME); } void SetWasDrawnPrevFrame(int bWasDrawnPrevFrame) { SetFlag(FLAGS_DRAWN_PREVFRAME, bWasDrawnPrevFrame); } int IsEffectCameraSpace() { return GetFlag(FLAGS_CAMERASPACE); } void SetEffectCameraSpace(int bCameraSpace) { SetFlag(FLAGS_CAMERASPACE, bCameraSpace); } int GetAutoApplyLocalTransform() const { return GetFlag(FLAGS_AUTOAPPLYLOCALTRANSFORM); } void SetAutoApplyLocalTransform(int b) { SetFlag(FLAGS_AUTOAPPLYLOCALTRANSFORM, b); } int GetAutoUpdateBBox() { return GetFlag(FLAGS_AUTOUPDATEBBOX); } void SetAutoUpdateBBox(int bAutoUpdate) { SetFlag(FLAGS_AUTOUPDATEBBOX, bAutoUpdate); } int GetNumActiveParticles(); void SetParticleCullRadius(float flMaxParticleRadius); int GetActiveParticleList(int nCount, Particle** ppParticleList); void DetectChanges(); private: 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(); 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); } int GetNeedsBBoxUpdate() { return GetFlag(FLAGS_NEEDS_BBOX_UPDATE); } void SetNeedsBBoxUpdate(int bFirstUpdate) { SetFlag(FLAGS_NEEDS_BBOX_UPDATE, bFirstUpdate); } 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); } bool RecalculateBoundingBox(); CEffectMaterial* GetEffectMaterial(CParticleSubTexture* pSubTexture); public: virtual const Vector& GetRenderOrigin(void); virtual const QAngle& GetRenderAngles(void); virtual const matrix3x4_t& RenderableToWorldTransform(); virtual void GetRenderBounds(Vector& mins, Vector& maxs); virtual bool ShouldDraw(void); virtual int DrawModel(int flags, const RenderableInstance_t& instance); private: enum { FLAGS_REMOVE = (1 << 0), FLAGS_REMOVALINPROGRESS = (1 << 1), FLAGS_NEEDS_BBOX_UPDATE = (1 << 2), FLAGS_AUTOUPDATEBBOX = (1 << 3), FLAGS_ALWAYSSIMULATE = (1 << 4), FLAGS_DRAWN = (1 << 5), FLAGS_DRAWN_PREVFRAME = (1 << 6), FLAGS_CAMERASPACE = (1 << 7), FLAGS_DRAW_THRU_LEAF_SYSTEM = (1 << 8), FLAGS_DRAW_BEFORE_VIEW_MODEL = (1 << 9), FLAGS_AUTOAPPLYLOCALTRANSFORM = (1 << 10), FLAGS_FIRST_FRAME = (1 << 11), FLAGS_NEW_PARTICLE_SYSTEM = (1 << 12) }; VMatrix m_LocalSpaceTransform; bool m_bLocalSpaceTransformIdentity; Vector m_Min; Vector m_Max; Vector m_LastMin; Vector m_LastMax; float m_flParticleCullRadius; unsigned short m_nActiveParticles; unsigned short m_FrameCode; unsigned short m_ListIndex; IParticleEffect* m_pSim; CParticleMgr* m_pParticleMgr; int m_Flags; enum { EFFECT_MATERIAL_HASH_SIZE = 8 }; CEffectMaterial* m_EffectMaterialHash[EFFECT_MATERIAL_HASH_SIZE]; #ifdef INFESTED_PARTICLES public: #endif CUtlLinkedList m_Materials; unsigned short m_UpdateBBoxCounter; }; class CParticleLightInfo { public: Vector m_vPos; Vector m_vColor; float m_flIntensity; }; typedef IParticleEffect* (*CreateParticleEffectFN)(); enum { TOOLPARTICLESYSTEMID_INVALID = -1, }; class CParticleCollection; class CNonDrawingParticleSystem { public: CNonDrawingParticleSystem* m_pNext; CNonDrawingParticleSystem* m_pPrev; CParticleCollection* m_pSystem; FORCEINLINE CParticleCollection* operator()(void) const { return m_pSystem; } FORCEINLINE CParticleCollection* Get(void) const { return m_pSystem; } ~CNonDrawingParticleSystem(void); }; class CClientTools; class CParticleMgr { friend class CParticleEffectBinding; friend class CParticleCollection; friend class CNonDrawingParticleSystem; friend class CClientTools; public: CParticleMgr(); virtual ~CParticleMgr(); bool Init(unsigned long nPreallocatedParticles, IMaterialSystem* pMaterial); void Term(); void LevelInit(); void RegisterEffect(const char* pEffectType, CreateParticleEffectFN func); IParticleEffect* CreateEffect(const char* pEffectType); bool AddEffect(CParticleEffectBinding* pEffect, IParticleEffect* pSim); void RemoveEffect(CParticleEffectBinding* pEffect); void AddEffect(CNewParticleEffect* pEffect); void RemoveEffect(CNewParticleEffect* pEffect); void RemoveAllEffects(); void IncrementFrameCode(); void Simulate(float fTimeDelta); void PostRender(); void DrawBeforeViewModelEffects(); VMatrix& GetModelView(); Particle* AllocParticle(int size); void FreeParticle(Particle*); PMaterialHandle GetPMaterial(const char* pMaterialName); IMaterial* PMaterialToIMaterial(PMaterialHandle hMaterial); void RepairPMaterial(PMaterialHandle hMaterial); void GetDirectionalLightInfo(CParticleLightInfo& info) const; void SetDirectionalLightInfo(const CParticleLightInfo& info); void SpewInfo(bool bDetail); void AddEffectListener(IClientParticleListener* pListener); void RemoveEffectListener(IClientParticleListener* pListener); int AllocateToolParticleEffectId(); void RemoveAllNewEffects(); CNewParticleEffect* FirstNewEffect(); CNewParticleEffect* NextNewEffect(CNewParticleEffect* pEffect); void RenderParticleSystems(bool bEnable); bool ShouldRenderParticleSystems() const; void RemoveOldParticleEffects(float flTime); int GetNumParticles() const { return m_nCurrentParticlesAllocated; } CNonDrawingParticleSystem* CreateNonDrawingEffect(const char* pEffectName); private: struct RetireInfo_t { CParticleCollection* m_pCollection; float m_flScreenArea; bool m_bFirstFrame; }; void UpdateAllEffects(float flTimeDelta); void UpdateNewEffects(float flTimeDelta); void SpewActiveParticleSystems(); CParticleSubTextureGroup* FindOrAddSubTextureGroup(IMaterial* pPageMaterial); int ComputeParticleDefScreenArea(int nInfoCount, RetireInfo_t* pInfo, float* pTotalArea, CParticleSystemDefinition* pDef, const CViewSetup& view, const VMatrix& worldToPixels, float flFocalDist); bool RetireParticleCollections(CParticleSystemDefinition* pDef, int nCount, RetireInfo_t* pInfo, float flScreenArea, float flMaxTotalArea); void BuildParticleSimList(CUtlVector< CNewParticleEffect* >& list); bool EarlyRetireParticleSystems(int nCount, CNewParticleEffect** ppEffects); static int RetireSort(const void* p1, const void* p2); private: int m_nCurrentParticlesAllocated; CParticleLightInfo m_DirectionalLight; unsigned short m_FrameCode; bool m_bUpdatingEffects; bool m_bRenderParticleEffects; CUtlLinkedList m_Effects; CUtlIntrusiveDList< CNewParticleEffect > m_NewEffects; CUtlIntrusiveDList< CNonDrawingParticleSystem > m_NonDrawingParticleSystems; CUtlVector< IClientParticleListener*> m_effectListeners; IMaterialSystem* m_pMaterialSystem; VMatrix m_mModelView; CUtlVector m_SubTextureGroups; CUtlDict m_SubTextures; CParticleSubTexture m_DefaultInvalidSubTexture; CUtlMap< const char*, CreateParticleEffectFN > m_effectFactories; int m_nToolParticleEffectId; IThreadPool* m_pThreadPool[2]; }; inline int CParticleMgr::AllocateToolParticleEffectId() { return m_nToolParticleEffectId++; } class IClientParticleListener { public: virtual void OnParticleEffectAdded(IParticleEffect* pEffect) = 0; virtual void OnParticleEffectRemoved(IParticleEffect* pEffect) = 0; }; float Helper_GetTime(); float Helper_GetFrameTime(); float Helper_RandomFloat(float minVal, float maxVal); int Helper_RandomInt(int minVal, int maxVal); inline VMatrix& CParticleMgr::GetModelView() { return m_mModelView; } inline const matrix3x4_t& CParticleEffectBinding::GetLocalSpaceTransform() const { return m_LocalSpaceTransform.As3x4(); } CParticleMgr* ParticleMgr(); struct StandardParticle_t : public Particle { void SetColor(float r, float g, float b); void SetAlpha(float a); Vector m_Velocity; float m_Lifetime; unsigned char m_EffectData; unsigned short m_EffectDataWord; unsigned char m_Color[4]; }; inline void TransformParticle(const VMatrix& vMat, const Vector& vIn, Vector& vOut) { 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]; } 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); } 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) { 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) { UnlinkParticle(pCur); InsertParticleBefore(pCur, pPrev); } #include "particle_iterators.h" #endif