|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "particles_simple.h"
#include "env_wind_shared.h"
#include "keyvalues.h"
#include "toolframework_client.h"
#include "toolframework/itoolframework.h"
#include "vstdlib/ikeyvaluessystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Used for debugging to make sure all particle effects get freed when we exit.
CUtlLinkedList<CParticleEffect*,int> g_ParticleEffects; class CEffectChecker { public: ~CEffectChecker() { Assert( g_ParticleEffects.Count() == 0 ); } } g_EffectChecker;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CParticleEffect::CParticleEffect( const char *pName ) { m_pDebugName = pName; m_vSortOrigin.Init(); m_Flags = FLAG_ALLOCATED; m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID; m_RefCount = 0; m_bSimulate = true; ParticleMgr()->AddEffect( &m_ParticleEffect, this ); #if defined( _DEBUG )
g_ParticleEffects.AddToTail( this ); #endif
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CParticleEffect::~CParticleEffect( void ) { #if defined( _DEBUG )
int index = g_ParticleEffects.Find( this ); Assert( g_ParticleEffects.IsValidIndex(index) ); g_ParticleEffects.Remove( index ); #endif
// HACKHACK: Prevent re-entering the destructor, clear m_Flags.
// For some reason we'll get a callback into NotifyRemove() after being deleted!
// Investigate dangling pointer
m_Flags = 0;
if ( ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID ) && clienttools->IsInRecordingMode() ) { KeyValues *msg = new KeyValues( "OldParticleSystem_Destroy" ); msg->SetInt( "id", m_nToolParticleEffectId ); msg->SetFloat( "time", gpGlobals->curtime ); ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg ); m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID; } }
void CParticleEffect::SetDynamicallyAllocated( bool bDynamic ) { if( bDynamic ) m_Flags |= FLAG_ALLOCATED; else m_Flags &= ~FLAG_ALLOCATED; }
int CParticleEffect::IsReleased() { return m_RefCount == 0; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CParticleEffect::AddRef() { ++m_RefCount; }
void CParticleEffect::Release() { Assert( m_RefCount > 0 ); --m_RefCount;
// If all the particles are already gone, delete ourselves now.
// If there are still particles, wait for the last NotifyDestroyParticle.
if ( m_RefCount == 0 ) { if ( m_Flags & FLAG_ALLOCATED ) { if ( m_ParticleEffect.GetNumActiveParticles() == 0 ) { m_ParticleEffect.SetRemoveFlag(); } } } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vSortOrigin -
//-----------------------------------------------------------------------------
const Vector &CParticleEffect::GetSortOrigin() { Assert(m_vSortOrigin.IsValid()); return m_vSortOrigin; }
const char *CParticleEffect::GetEffectName() { return m_pDebugName; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : pParticle -
//-----------------------------------------------------------------------------
void CParticleEffect::NotifyDestroyParticle( Particle* pParticle ) { // Go away if we're released and there are no more particles.
if( m_ParticleEffect.GetNumActiveParticles() == 0 && IsReleased() && (m_Flags & FLAG_ALLOCATED) && !(m_Flags & FLAG_DONT_REMOVE) ) { m_ParticleEffect.SetRemoveFlag(); } }
void CParticleEffect::Update( float flTimeDelta ) { }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CParticleEffect::NotifyRemove() { if( m_Flags & FLAG_ALLOCATED ) { Assert( IsReleased() ); delete this; } }
void CParticleEffect::SetSortOrigin( const Vector &vSortOrigin ) { if ( GetBinding().GetAutoUpdateBBox() ) { if ( m_ParticleEffect.EnlargeBBoxToContain( vSortOrigin ) ) { m_vSortOrigin = vSortOrigin; } } else { // If not auto-updating bbox, don't change the bbox, just set the sort origin.
m_vSortOrigin = vSortOrigin; } }
void CParticleEffect::SetParticleCullRadius( float radius ) { m_ParticleEffect.SetParticleCullRadius( radius ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *name -
// Output : PMaterialHandle
//-----------------------------------------------------------------------------
PMaterialHandle CParticleEffect::GetPMaterial(const char *name) { return m_ParticleEffect.FindOrAddMaterial(name); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : particleSize -
// material -
// Output : SimpleParticle
//-----------------------------------------------------------------------------
Particle *CParticleEffect::AddParticle( unsigned int particleSize, PMaterialHandle material, const Vector &origin ) { // If you get here, then you must call SetSortOrigin before adding particles.
Assert( m_vSortOrigin.IsValid() );
Particle *pParticle = (Particle *) m_ParticleEffect.AddParticle( particleSize, material );
if( pParticle == NULL ) return NULL;
pParticle->m_Pos = origin; return pParticle; }
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
REGISTER_EFFECT_USING_CREATE( CSimpleEmitter );
CSimpleEmitter::CSimpleEmitter( const char *pDebugName ) : CParticleEffect( pDebugName ) { m_flNearClipMin = 16.0f; m_flNearClipMax = 64.0f; m_nSplitScreenPlayerSlot = -1; }
CSimpleEmitter::~CSimpleEmitter() { }
CSmartPtr<CSimpleEmitter> CSimpleEmitter::Create( const char *pDebugName ) { CSimpleEmitter *pRet = new CSimpleEmitter( pDebugName ); pRet->SetDynamicallyAllocated( true ); return pRet; }
//-----------------------------------------------------------------------------
// Purpose: Set the internal near clip range for this particle system
// Input : nearClipMin - beginning of clip range
// nearClipMax - end of clip range
//-----------------------------------------------------------------------------
void CSimpleEmitter::SetNearClip( float nearClipMin, float nearClipMax ) { m_flNearClipMin = nearClipMin; m_flNearClipMax = nearClipMax; }
SimpleParticle* CSimpleEmitter::AddSimpleParticle( PMaterialHandle hMaterial, const Vector &vOrigin, float flDieTime, unsigned char uchSize ) { SimpleParticle *pRet = (SimpleParticle*)AddParticle( sizeof( SimpleParticle ), hMaterial, vOrigin ); if ( pRet ) { pRet->m_Pos = vOrigin; pRet->m_vecVelocity.Init(); pRet->m_flRoll = 0; pRet->m_flRollDelta = 0; pRet->m_flLifetime = 0; pRet->m_flDieTime = flDieTime; pRet->m_uchColor[0] = pRet->m_uchColor[1] = pRet->m_uchColor[2] = 0; pRet->m_uchStartAlpha = pRet->m_uchEndAlpha = 255; pRet->m_uchStartSize = pRet->m_uchEndSize = uchSize; pRet->m_iFlags = 0; }
return pRet; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : fTimeDelta -
// Output : float
//-----------------------------------------------------------------------------
float CSimpleEmitter::UpdateAlpha( const SimpleParticle *pParticle ) { return (pParticle->m_uchStartAlpha/255.0f) + ( (float)(pParticle->m_uchEndAlpha/255.0f) - (float)(pParticle->m_uchStartAlpha/255.0f) ) * (pParticle->m_flLifetime / pParticle->m_flDieTime); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : fTimeDelta -
// Output : float
//-----------------------------------------------------------------------------
float CSimpleEmitter::UpdateScale( const SimpleParticle *pParticle ) { return (float)pParticle->m_uchStartSize + ( (float)pParticle->m_uchEndSize - (float)pParticle->m_uchStartSize ) * (pParticle->m_flLifetime / pParticle->m_flDieTime); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : fTimeDelta -
// Output : Vector
//-----------------------------------------------------------------------------
#define WIND_ACCEL 50
void CSimpleEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) { if (pParticle->m_iFlags & SIMPLE_PARTICLE_FLAG_WINDBLOWN) { Vector vecWind; GetWindspeedAtTime( gpGlobals->curtime, vecWind );
for ( int i = 0 ; i < 2 ; i++ ) { if ( pParticle->m_vecVelocity[i] < vecWind[i] ) { pParticle->m_vecVelocity[i] += ( timeDelta * WIND_ACCEL );
// clamp
if ( pParticle->m_vecVelocity[i] > vecWind[i] ) pParticle->m_vecVelocity[i] = vecWind[i]; } else if (pParticle->m_vecVelocity[i] > vecWind[i] ) { pParticle->m_vecVelocity[i] -= ( timeDelta * WIND_ACCEL );
// clamp.
if ( pParticle->m_vecVelocity[i] < vecWind[i] ) pParticle->m_vecVelocity[i] = vecWind[i]; } } } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : fTimeDelta -
// Output : float
//-----------------------------------------------------------------------------
float CSimpleEmitter::UpdateRoll( SimpleParticle *pParticle, float timeDelta ) { pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
return pParticle->m_flRoll; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParticle -
// timeDelta -
//-----------------------------------------------------------------------------
Vector CSimpleEmitter::UpdateColor( const SimpleParticle *pParticle ) { static Vector cColor;
cColor[0] = pParticle->m_uchColor[0] / 255.0f; cColor[1] = pParticle->m_uchColor[1] / 255.0f; cColor[2] = pParticle->m_uchColor[2] / 255.0f;
return cColor; }
void CSimpleEmitter::SimulateParticles( CParticleSimulateIterator *pIterator ) { float timeDelta = pIterator->GetTimeDelta();
SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst(); while ( pParticle ) { //Update velocity
UpdateVelocity( pParticle, timeDelta ); pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
//Should this particle die?
pParticle->m_flLifetime += timeDelta; UpdateRoll( pParticle, timeDelta );
if ( pParticle->m_flLifetime >= pParticle->m_flDieTime ) pIterator->RemoveParticle( pParticle );
pParticle = (SimpleParticle*)pIterator->GetNext(); } }
void CSimpleEmitter::RenderParticles( CParticleRenderIterator *pIterator ) { ASSERT_LOCAL_PLAYER_RESOLVABLE(); if ( m_nSplitScreenPlayerSlot != -1 && m_nSplitScreenPlayerSlot != GET_ACTIVE_SPLITSCREEN_SLOT() ) return;
const SimpleParticle *pParticle = (const SimpleParticle *)pIterator->GetFirst(); while ( pParticle ) { //Render
Vector tPos;
TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos ); float sortKey = (int) tPos.z;
//Render it
RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), tPos, UpdateColor( pParticle ), UpdateAlpha( pParticle ) * GetAlphaDistanceFade( tPos, m_flNearClipMin, m_flNearClipMax ), UpdateScale( pParticle ), pParticle->m_flRoll );
pParticle = (const SimpleParticle *)pIterator->GetNext( sortKey ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : state -
//-----------------------------------------------------------------------------
void CSimpleEmitter::SetDrawBeforeViewModel( bool state ) { m_ParticleEffect.SetDrawBeforeViewModel( state ); }
void CSimpleEmitter::SetShouldDrawForSplitScreenUser( int nSlot ) { m_nSplitScreenPlayerSlot = nSlot; }
//==================================================
// Particle Library
//==================================================
CEmberEffect::CEmberEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) { }
CSmartPtr<CEmberEffect> CEmberEffect::Create( const char *pDebugName ) { CEmberEffect *pRet = new CEmberEffect( pDebugName ); pRet->SetDynamicallyAllocated( true ); return pRet; }
void CEmberEffect::UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) { float speed = VectorNormalize( pParticle->m_vecVelocity ); Vector offset;
speed -= ( 12.0f * timeDelta );
offset.Random( -0.125f, 0.125f );
pParticle->m_vecVelocity += offset; VectorNormalize( pParticle->m_vecVelocity );
pParticle->m_vecVelocity *= speed; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParticle -
// timeDelta -
//-----------------------------------------------------------------------------
Vector CEmberEffect::UpdateColor( const SimpleParticle *pParticle ) { Vector color; float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime );
color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f; color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f; color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f;
return color; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParticle -
// timeDelta -
// Output : float
//-----------------------------------------------------------------------------
CFireSmokeEffect::CFireSmokeEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) { }
CSmartPtr<CFireSmokeEffect> CFireSmokeEffect::Create( const char *pDebugName ) { CFireSmokeEffect *pRet = new CFireSmokeEffect( pDebugName ); pRet->SetDynamicallyAllocated( true ); return pRet; }
float CFireSmokeEffect::UpdateAlpha( const SimpleParticle *pParticle ) { return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParticle -
// timeDelta -
//-----------------------------------------------------------------------------
void CFireSmokeEffect::UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) { }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParticle -
// timeDelta -
// Output : Vector
//-----------------------------------------------------------------------------
CFireParticle::CFireParticle( const char *pDebugName ) : CSimpleEmitter( pDebugName ) { }
CSmartPtr<CFireParticle> CFireParticle::Create( const char *pDebugName ) { CFireParticle *pRet = new CFireParticle( pDebugName ); pRet->SetDynamicallyAllocated( true ); return pRet; }
Vector CFireParticle::UpdateColor( const SimpleParticle *pParticle ) { for ( int i = 0; i < 3; i++ ) { //FIXME: This is frame dependant... but I don't want to store off start/end colors yet
//pParticle->m_uchColor[i] = MAX( 0, pParticle->m_uchColor[i]-2 );
}
return CSimpleEmitter::UpdateColor( pParticle ); }
|