365 lines
9.9 KiB
C++
365 lines
9.9 KiB
C++
//========= Copyright © 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 ) ),
|
|
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;
|
|
GetWindspeedAtTime( gpGlobals->curtime, vecWind );
|
|
|
|
|
|
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 = RemapVal( rand(), 0, RAND_MAX, m_LifetimeMin, m_LifetimeMax );
|
|
|
|
if( m_DustFlags & DUSTFLAGS_SCALEMOTES )
|
|
pParticle->m_flSize = RemapVal( rand(), 0, RAND_MAX, m_flSizeMin/10000.0f, m_flSizeMax/10000.0f );
|
|
else
|
|
pParticle->m_flSize = RemapVal( rand(), 0, RAND_MAX, 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 );
|
|
}
|