//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "fx_fleck.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // enable this to have the fleck_merge cvar as well as the current system count displayed as it changes (for profiling) #define REPORT_MERGED_FLECKS 0 // // class PARTICLE_MERGE //{ //public: // bool MergeParticleSystems( CFleckParticles *pSystem, const char *pEffectName, const Vector ¢er, const Vector &extents ) // { merge; return true; } //}; // a singly linked list through all particle effects of a specific type // with a specific rule for sharing them. // Needs a hook to the particle effect's constructor/destructor and factory method // The factory needs to support optionally merging the new particles into a previously built particle effect // this cuts down on lots of scene management overhead as well as rendering/batch overhead template< class PARTICLE_EFFECT, class PARTICLE_MERGE > class CParticleMergeList { public: CParticleMergeList() : m_pHead(NULL) {} void AddParticleSystem( PARTICLE_EFFECT *pSystem ); void RemoveParticleSystem( PARTICLE_EFFECT *pRemove ); PARTICLE_EFFECT *FindAndMergeParticleSystem( const char *pEffectName, const Vector ¢er, const Vector &extents ); bool MergeParticleSystems( PARTICLE_EFFECT *pSystem, const char *pEffectName, const Vector ¢er, const Vector &extents ); private: PARTICLE_EFFECT *m_pHead; PARTICLE_MERGE m_merge; }; #if REPORT_MERGED_FLECKS ConVar fleck_merge("fleck_merge","1"); int g_PCount = 0; #endif template< class PARTICLE_EFFECT, class PARTICLE_MERGE > void CParticleMergeList::AddParticleSystem( PARTICLE_EFFECT *pSystem ) { #if REPORT_MERGED_FLECKS g_PCount++; Msg("PS: %d\n", g_PCount); #endif pSystem->m_pNextParticleSystem = m_pHead; m_pHead = pSystem; } template< class PARTICLE_EFFECT, class PARTICLE_MERGE > void CParticleMergeList::RemoveParticleSystem( PARTICLE_EFFECT *pRemove ) { #if REPORT_MERGED_FLECKS g_PCount--; Msg("PS: %d\n", g_PCount); #endif PARTICLE_EFFECT **pPrev = &m_pHead; PARTICLE_EFFECT *pCur = *pPrev; while ( pCur ) { if ( pCur == pRemove ) { *pPrev = pCur->m_pNextParticleSystem; return; } pPrev = &pCur->m_pNextParticleSystem; pCur = *pPrev; } } template< class PARTICLE_EFFECT, class PARTICLE_MERGE > PARTICLE_EFFECT *CParticleMergeList::FindAndMergeParticleSystem( const char *pEffectName, const Vector ¢er, const Vector &extents ) { #if REPORT_MERGED_FLECKS if ( !fleck_merge.GetBool() ) return NULL; #endif for ( PARTICLE_EFFECT *pMerge = m_pHead; pMerge != NULL; pMerge = pMerge->m_pNextParticleSystem ) { if ( m_merge.MergeParticleSystems( pMerge, pEffectName, center, extents ) ) return pMerge; } return NULL; } // merge anything within 10 feet const float MAX_RADIUS_BBOX_MERGE = 120.0f; template< class PARTICLE_EFFECT > class CMergeSameNameBbox { public: bool MergeParticleSystems( PARTICLE_EFFECT *pSystem, const char *pEffectName, const Vector ¢er, const Vector &extents ) { // by default, match names if ( !Q_stricmp(pSystem->GetEffectName(), pEffectName) ) { Vector mins, maxs; pSystem->GetBinding().GetWorldspaceBounds( &mins, &maxs ); AddPointToBounds( center - extents, mins, maxs ); AddPointToBounds( center + extents, mins, maxs ); Vector size = maxs - mins; float radius = size.Length(); if ( radius < MAX_RADIUS_BBOX_MERGE ) { pSystem->GetBinding().SetBBox( mins, maxs ); // put sort origin at center of the new box Vector sortOrigin = 0.5f * (mins+maxs); pSystem->SetSortOrigin(sortOrigin); return true; } } return false; } }; CParticleMergeList< CFleckParticles, CMergeSameNameBbox > g_FleckMergeList; // // CFleckParticles // CSmartPtr CFleckParticles::Create( const char *pDebugName, const Vector &vCenter, const Vector &extents ) { CFleckParticles *pMerge = g_FleckMergeList.FindAndMergeParticleSystem( pDebugName, vCenter, extents ); if ( pMerge ) return pMerge; CFleckParticles *pRet = new CFleckParticles( pDebugName ); if ( pRet ) { pRet->GetBinding().SetBBox( vCenter - extents, vCenter + extents ); pRet->SetSortOrigin(vCenter); } return pRet; } CFleckParticles::CFleckParticles( const char *pDebugName ) : CSimpleEmitter( pDebugName ), m_pNextParticleSystem(NULL) { g_FleckMergeList.AddParticleSystem(this); } CFleckParticles::~CFleckParticles() { g_FleckMergeList.RemoveParticleSystem(this); } //----------------------------------------------------------------------------- // Purpose: Test for surrounding collision surfaces for quick collision testing for the particle system // Input : &origin - starting position // *dir - direction of movement (if NULL, will do a point emission test in four directions) // angularSpread - looseness of the spread // minSpeed - minimum speed // maxSpeed - maximum speed // gravity - particle gravity for the sytem // dampen - dampening amount on collisions // flags - extra information //----------------------------------------------------------------------------- void CFleckParticles::Setup( const Vector &origin, const Vector *direction, float angularSpread, float minSpeed, float maxSpeed, float gravity, float dampen, int flags ) { //See if we've specified a direction m_ParticleCollision.Setup( origin, direction, angularSpread, minSpeed, maxSpeed, gravity, dampen ); } void CFleckParticles::RenderParticles( CParticleRenderIterator *pIterator ) { const FleckParticle *pParticle = (const FleckParticle*)pIterator->GetFirst(); while ( pParticle ) { Vector tPos; TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos ); float sortKey = (int) tPos.z; Vector color; color[0] = pParticle->m_uchColor[0] / 255.0f; color[1] = pParticle->m_uchColor[1] / 255.0f; color[2] = pParticle->m_uchColor[2] / 255.0f; //Render it RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), tPos, color, 1.0f - (pParticle->m_flLifetime / pParticle->m_flDieTime), pParticle->m_uchSize, pParticle->m_flRoll ); pParticle = (const FleckParticle*)pIterator->GetNext( sortKey ); } } void CFleckParticles::SimulateParticles( CParticleSimulateIterator *pIterator ) { FleckParticle *pParticle = (FleckParticle*)pIterator->GetFirst(); while ( pParticle ) { const float timeDelta = pIterator->GetTimeDelta(); //Should this particle die? pParticle->m_flLifetime += timeDelta; if ( pParticle->m_flLifetime >= pParticle->m_flDieTime ) { pIterator->RemoveParticle( pParticle ); } else { pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta; //Simulate the movement with collision trace_t trace; m_ParticleCollision.MoveParticle( pParticle->m_Pos, pParticle->m_vecVelocity, &pParticle->m_flRollDelta, timeDelta, &trace ); // If we're in solid, then stop moving if ( trace.allsolid ) { pParticle->m_vecVelocity = vec3_origin; pParticle->m_flRollDelta = 0.0f; } } pParticle = (FleckParticle*)pIterator->GetNext(); } }