|
|
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "fx.h"
#include "c_func_dust.h"
#include "func_dust_shared.h"
#include "c_te_particlesystem.h"
#include "env_wind_shared.h"
#include "engine/IEngineTrace.h"
#include "tier0/vprof.h"
#include "particles_ez.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_Func_Dust, DT_Func_Dust, CFunc_Dust ) RecvPropInt( RECVINFO(m_Color), 0, RecvProxy_Int32ToColor32 ), RecvPropInt( RECVINFO(m_SpawnRate) ), RecvPropFloat( RECVINFO(m_flSizeMin) ), RecvPropFloat( RECVINFO(m_flSizeMax) ), RecvPropInt( RECVINFO(m_LifetimeMin) ), RecvPropInt( RECVINFO(m_LifetimeMax) ), RecvPropInt( RECVINFO(m_DustFlags) ), RecvPropInt( RECVINFO(m_SpeedMax) ), RecvPropInt( RECVINFO(m_DistMax) ), RecvPropInt( RECVINFO( m_nModelIndex ) ), RecvPropFloat( RECVINFO( m_FallSpeed ) ), RecvPropBool( RECVINFO( m_bAffectedByWind ) ), RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ), END_RECV_TABLE()
// ------------------------------------------------------------------------------------ //
// CDustEffect implementation.
// ------------------------------------------------------------------------------------ //
#define DUST_ACCEL 50
void CDustEffect::RenderParticles( CParticleRenderIterator *pIterator ) { const CFuncDustParticle *pParticle = (const CFuncDustParticle*)pIterator->GetFirst(); while ( pParticle ) { // Velocity.
float flAlpha; if( m_pDust->m_DustFlags & DUSTFLAGS_FROZEN ) { flAlpha = 1; } else { // Alpha.
float flAngle = (pParticle->m_flLifetime / pParticle->m_flDieTime) * M_PI * 2; flAlpha = sin( flAngle - (M_PI * 0.5f) ) * 0.5f + 0.5f; }
Vector tPos; TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos ); float sortKey = (int) tPos.z;
if( -tPos.z <= m_pDust->m_DistMax ) { flAlpha *= 1 + (tPos.z / m_pDust->m_DistMax);
// Draw it.
float flSize = pParticle->m_flSize; if( m_pDust->m_DustFlags & DUSTFLAGS_SCALEMOTES ) flSize *= -tPos.z;
RenderParticle_Color255Size( pIterator->GetParticleDraw(), tPos, Vector( m_pDust->m_Color.r, m_pDust->m_Color.g, m_pDust->m_Color.b ), flAlpha * m_pDust->m_Color.a, flSize ); }
pParticle = (const CFuncDustParticle*)pIterator->GetNext( sortKey ); } }
void CDustEffect::SimulateParticles( CParticleSimulateIterator *pIterator ) { Vector vecWind; if ( m_pDust->m_bAffectedByWind ) GetWindspeedAtTime( gpGlobals->curtime, vecWind ); else vecWind = Vector( 0, 0, 0 );
CFuncDustParticle *pParticle = (CFuncDustParticle*)pIterator->GetFirst(); while ( pParticle ) { // Velocity.
if( !(m_pDust->m_DustFlags & DUSTFLAGS_FROZEN) ) { // Kill the particle?
pParticle->m_flLifetime += pIterator->GetTimeDelta(); if( pParticle->m_flLifetime >= pParticle->m_flDieTime ) { pIterator->RemoveParticle( pParticle ); } else { for ( int i = 0 ; i < 2 ; i++ ) { if ( pParticle->m_vVelocity[i] < vecWind[i] ) { pParticle->m_vVelocity[i] += ( gpGlobals->frametime * DUST_ACCEL );
// clamp
if ( pParticle->m_vVelocity[i] > vecWind[i] ) pParticle->m_vVelocity[i] = vecWind[i]; } else if (pParticle->m_vVelocity[i] > vecWind[i] ) { pParticle->m_vVelocity[i] -= ( gpGlobals->frametime * DUST_ACCEL );
// clamp.
if ( pParticle->m_vVelocity[i] < vecWind[i] ) pParticle->m_vVelocity[i] = vecWind[i]; } }
// Apply velocity.
pParticle->m_Pos.MulAdd( pParticle->m_Pos, pParticle->m_vVelocity, pIterator->GetTimeDelta() ); } }
pParticle = (CFuncDustParticle*)pIterator->GetNext(); } }
// ------------------------------------------------------------------------------------ //
// C_Func_Dust implementation.
// ------------------------------------------------------------------------------------ //
C_Func_Dust::C_Func_Dust() : m_Effect( "C_Func_Dust" ) { m_Effect.m_pDust = this; m_Effect.SetDynamicallyAllocated( false ); // So it doesn't try to delete itself.
}
C_Func_Dust::~C_Func_Dust() { }
void C_Func_Dust::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType );
if( updateType == DATA_UPDATE_CREATED ) { m_hMaterial = m_Effect.GetPMaterial( "particle/sparkles" );
m_Effect.SetSortOrigin( WorldSpaceCenter( ) );
// Let us think each frame.
SetNextClientThink( CLIENT_THINK_ALWAYS ); // If we're setup to be frozen, just make a bunch of particles initially.
if( m_DustFlags & DUSTFLAGS_FROZEN ) { for( int i=0; i < m_SpawnRate; i++ ) { AttemptSpawnNewParticle(); } } }
m_Spawner.Init( m_SpawnRate ); // N particles per second
}
void C_Func_Dust::ClientThink() { // If frozen, don't make new particles.
if( m_DustFlags & DUSTFLAGS_FROZEN ) return;
// Spawn particles?
if( m_DustFlags & DUSTFLAGS_ON ) { float flDelta = MIN( gpGlobals->frametime, 0.1f ); while( m_Spawner.NextEvent( flDelta ) ) { AttemptSpawnNewParticle(); } }
// Tell the particle manager our bbox.
Vector vWorldMins, vWorldMaxs; CollisionProp()->WorldSpaceAABB( &vWorldMins, &vWorldMaxs ); vWorldMins -= Vector( m_flSizeMax, m_flSizeMax, m_flSizeMax ); vWorldMaxs += Vector( m_flSizeMax, m_flSizeMax, m_flSizeMax ); m_Effect.GetBinding().SetBBox( vWorldMins, vWorldMaxs ); }
bool C_Func_Dust::ShouldDraw() { return false; }
void C_Func_Dust::AttemptSpawnNewParticle() { // Find a random spot inside our bmodel.
static int nTests=10;
for( int iTest=0; iTest < nTests; iTest++ ) { Vector vPercent = RandomVector( 0, 1 ); Vector vTest = WorldAlignMins() + (WorldAlignMaxs() - WorldAlignMins()) * vPercent;
int contents = enginetrace->GetPointContents_Collideable( GetCollideable(), vTest ); if( contents & CONTENTS_SOLID ) { CFuncDustParticle *pParticle = (CFuncDustParticle*)m_Effect.AddParticle( 10, m_hMaterial, vTest ); if( pParticle ) { pParticle->m_vVelocity = RandomVector( -m_SpeedMax, m_SpeedMax ); pParticle->m_vVelocity.z -= m_FallSpeed;
pParticle->m_flLifetime = 0; pParticle->m_flDieTime = RandomFloat( m_LifetimeMin, m_LifetimeMax );
if( m_DustFlags & DUSTFLAGS_SCALEMOTES ) pParticle->m_flSize = RandomFloat( m_flSizeMin/10000.0f, m_flSizeMax/10000.0f ); else pParticle->m_flSize = RandomFloat( m_flSizeMin, m_flSizeMax ); pParticle->m_Color = m_Color; }
break; } } }
//
// Dust
//
//-----------------------------------------------------------------------------
// Spew out dust!
//-----------------------------------------------------------------------------
void FX_Dust( const Vector &vecOrigin, const Vector &vecDirection, float flSize, float flSpeed ) { VPROF_BUDGET( "FX_Dust", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); int numPuffs = (flSize*0.5f);
if ( numPuffs < 1 ) numPuffs = 1; if ( numPuffs > 32 ) numPuffs = 32;
float speed = flSpeed * 0.1f;
if ( speed < 0 ) speed = 1.0f; if (speed > 48.0f ) speed = 48.0f;
//FIXME: Better sampling area
Vector offset = vecOrigin + ( vecDirection * flSize );
//Find area ambient light color and use it to tint smoke
Vector worldLight = WorldGetLightForPoint( offset, true );
// Throw puffs
SimpleParticle particle; for ( int i = 0; i < numPuffs; i++ ) { offset.Random( -(flSize*0.25f), flSize*0.25f ); offset += vecOrigin + ( vecDirection * flSize );
particle.m_Pos = offset; particle.m_flLifetime = 0.0f; particle.m_flDieTime = random->RandomFloat( 0.4f, 1.0f ); particle.m_vecVelocity = vecDirection * random->RandomFloat( speed*0.5f, speed ) * i; particle.m_vecVelocity[2] = 0.0f;
int color = random->RandomInt( 48, 64 );
particle.m_uchColor[0] = (color+16) + ( worldLight[0] * (float) color ); particle.m_uchColor[1] = (color+8) + ( worldLight[1] * (float) color ); particle.m_uchColor[2] = color + ( worldLight[2] * (float) color );
particle.m_uchStartAlpha= random->RandomInt( 64, 128 ); particle.m_uchEndAlpha = 0; particle.m_uchStartSize = random->RandomInt( 2, 8 ); particle.m_uchEndSize = random->RandomInt( 24, 48 ); particle.m_flRoll = random->RandomInt( 0, 360 ); particle.m_flRollDelta = random->RandomFloat( -0.5f, 0.5f );
AddSimpleParticle( &particle, g_Mat_DustPuff[random->RandomInt(0,1)] ); } }
class C_TEDust: public C_TEParticleSystem { public: DECLARE_CLASS( C_TEDust, C_TEParticleSystem ); DECLARE_CLIENTCLASS();
C_TEDust(); virtual ~C_TEDust();
public: virtual void PostDataUpdate( DataUpdateType_t updateType ); virtual bool ShouldDraw() { return true; }
public:
float m_flSize; float m_flSpeed; Vector m_vecDirection;
protected: void GetDustColor( Vector &color ); };
IMPLEMENT_CLIENTCLASS_EVENT_DT( C_TEDust, DT_TEDust, CTEDust ) RecvPropFloat(RECVINFO(m_flSize)), RecvPropFloat(RECVINFO(m_flSpeed)), RecvPropVector(RECVINFO(m_vecDirection)), END_RECV_TABLE()
//==================================================
// C_TEDust
//==================================================
C_TEDust::C_TEDust() { }
C_TEDust::~C_TEDust() { }
//-----------------------------------------------------------------------------
// Purpose:
// Input : bNewEntity - whether or not to start a new entity
//-----------------------------------------------------------------------------
void C_TEDust::PostDataUpdate( DataUpdateType_t updateType ) { FX_Dust( m_vecOrigin, m_vecDirection, m_flSize, m_flSpeed ); }
void TE_Dust( IRecipientFilter& filter, float delay, const Vector &pos, const Vector &dir, float size, float speed ) { FX_Dust( pos, dir, size, speed ); }
|