diff options
Diffstat (limited to 'particles/builtin_particle_emitters.cpp')
| -rw-r--r-- | particles/builtin_particle_emitters.cpp | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/particles/builtin_particle_emitters.cpp b/particles/builtin_particle_emitters.cpp new file mode 100644 index 0000000..8e1c728 --- /dev/null +++ b/particles/builtin_particle_emitters.cpp @@ -0,0 +1,876 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: particle system code +// +//===========================================================================// + +#include "tier0/platform.h" +#include "particles/particles.h" +#include "filesystem.h" +#include "tier2/tier2.h" +#include "tier2/fileutils.h" +#include "tier1/UtlStringMap.h" +#include "tier1/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern int g_nParticle_Multiplier; + +//----------------------------------------------------------------------------- +// Emits particles immediately +//----------------------------------------------------------------------------- +struct InstantaneousEmitterContext_t +{ + int m_nRemainingParticles; + int m_ActualParticlesToEmit; + float m_flTimeOffset; + bool m_bReadScaleFactor; +}; + +class C_OP_InstantaneousEmitter : public CParticleOperatorInstance +{ + DECLARE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter ); + + uint32 GetWrittenAttributes( void ) const + { + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; + } + + uint32 GetReadAttributes( void ) const + { + return 0; + } + + virtual uint64 GetReadControlPointMask() const + { + if ( m_nScaleControlPoint >= 0 ) + return ( 1ULL << m_nScaleControlPoint ); + return 0; + } + + virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const; + + // unpack structure will be applied by creator. add extra initialization needed here + virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement ) + { + if ( m_nMinParticlesToEmit >= 0 ) + { + if ( m_nMinParticlesToEmit > m_nParticlesToEmit ) + { + V_swap( m_nParticlesToEmit, m_nMinParticlesToEmit ); + } + } + + if ( m_nPerFrameNum < 0 ) + { + m_nPerFrameNum = INT_MAX; + } + m_nScaleControlPointField = clamp( m_nScaleControlPointField, 0, 2 ); + } + + virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly ) + { + pCtx->m_nRemainingParticles = 0; + } + } + virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly ) + { + pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit; + SkipToTime( pParticles->m_flCurTime, pParticles, pCtx ); + } + } + + // Called when the SFM wants to skip forward in time + virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const + { + // NOTE: This is a bit of a hack. We're saying that if we're skipping more than two seconds, that we're + // probably not going to bother emitting at all. Really, this would have to know the maximum + // lifetime of the child particles and only skip if past that. + + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( flTime > ( flStartTime + 2.0f ) ) + { + pCtx->m_nRemainingParticles = 0; + } + } + + virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const + { + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + if ( m_nMinParticlesToEmit >= 0 ) + { + pCtx->m_ActualParticlesToEmit = pParticles->RandomInt( m_nMinParticlesToEmit, m_nParticlesToEmit ); + } + else + { + pCtx->m_ActualParticlesToEmit = m_nParticlesToEmit; + } + pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit; + pCtx->m_flTimeOffset = 0.0f; + pCtx->m_bReadScaleFactor = false; + } + + virtual void Restart( CParticleCollection *pParticles, void *pContext ) + { + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit; + pCtx->m_flTimeOffset = pParticles->m_flCurTime; + pCtx->m_bReadScaleFactor = false; + } + + size_t GetRequiredContextBytes( void ) const + { + return sizeof( InstantaneousEmitterContext_t ); + } + + virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const + { + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + return !(pCtx->m_nRemainingParticles <= 0); + } + + int m_nParticlesToEmit; + int m_nMinParticlesToEmit; + float m_flStartTime; + int m_nPerFrameNum; + int m_nScaleControlPoint; + int m_nScaleControlPointField; +}; + +DEFINE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter, "emit_instantaneously", OPERATOR_GENERIC ); + +BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter ) + DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime ) + DMXELEMENT_UNPACK_FIELD( "num_to_emit_minimum", "-1", int, m_nMinParticlesToEmit ) + DMXELEMENT_UNPACK_FIELD( "num_to_emit", "100", int, m_nParticlesToEmit ) + DMXELEMENT_UNPACK_FIELD( "maximum emission per frame", "-1", int, m_nPerFrameNum ) + DMXELEMENT_UNPACK_FIELD( "emission count scale control point", "-1", int, m_nScaleControlPoint ) + DMXELEMENT_UNPACK_FIELD( "emission count scale control point field", "0", int, m_nScaleControlPointField ) +END_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter ) + + +uint32 C_OP_InstantaneousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const +{ + // Don't emit any more if the particle system has emitted all it's supposed to. + InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext ); + if ( pCtx->m_nRemainingParticles <= 0 ) + return 0; + + // Wait until we're told to start emitting + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( pParticles->m_flCurTime < flStartTime ) + return 0; + + if ( pCtx->m_ActualParticlesToEmit == 0 ) + return 0; + + if ( ( m_nScaleControlPoint >= 0 ) && !pCtx->m_bReadScaleFactor ) + { + Vector vecScale; + if ( flStartTime <= pParticles->m_flCurTime && flStartTime >= pParticles->m_flCurTime - pParticles->m_flPreviousDt ) + { + pParticles->GetControlPointAtTime( m_nScaleControlPoint, flStartTime, &vecScale ); + } + else + { + pParticles->GetControlPointAtPrevTime( m_nScaleControlPoint, &vecScale ); + } + + pCtx->m_ActualParticlesToEmit *= vecScale[m_nScaleControlPointField]; + pCtx->m_nRemainingParticles *= vecScale[m_nScaleControlPointField]; + pCtx->m_bReadScaleFactor = true; + } + + pCtx->m_nRemainingParticles = max( pCtx->m_nRemainingParticles, 0 ); + + // NOTE: Applying the scale here because I don't believe we can sample the control point + // values inside + // We're only allowed to emit so many particles, though.. + // If we run out of room, only emit the last N particles + int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles; + // Cap to the maximum emission per frame + int nParticlesThisFrame = min( m_nPerFrameNum, pCtx->m_nRemainingParticles ); + nAllowedParticlesToEmit = min( nAllowedParticlesToEmit, nParticlesThisFrame ); + int nActualParticlesToEmit = min( nAllowedParticlesToEmit, pCtx->m_ActualParticlesToEmit * g_nParticle_Multiplier ); + pCtx->m_nRemainingParticles -= nParticlesThisFrame; + Assert( pCtx->m_nRemainingParticles >= 0 ); + + if ( nActualParticlesToEmit == 0 ) + return 0; + + int nStartParticle = pParticles->m_nActiveParticles; + pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles ); + + // !! speed!! do sse init here + for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ ) + { + float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i ); + *pTimeStamp = flStartTime; + } + + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; +} + + +//----------------------------------------------------------------------------- +// Emits particles over time +//----------------------------------------------------------------------------- +struct ContinuousEmitterContext_t +{ + float m_flTotalActualParticlesSoFar; + int m_nTotalEmittedSoFar; + float m_flNextEmitTime; + float m_flTimeOffset; + bool m_bStoppedEmission; +}; + +bool g_bDontMakeSkipToTimeTakeForever = false; + + +class C_OP_ContinuousEmitter : public CParticleOperatorInstance +{ + DECLARE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter ); + + uint32 GetWrittenAttributes( void ) const + { + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; + } + + uint32 GetReadAttributes( void ) const + { + return 0; + } + + virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement ) + { + if ( m_flEmitRate < 0.0f ) + { + m_flEmitRate = 0.0f; + } + if ( m_flEmissionDuration < 0.0f ) + { + m_flEmissionDuration = 0.0f; + } + m_flEmitRate *= g_nParticle_Multiplier; + } + + virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const ; + + inline bool IsInfinitelyEmitting() const + { + return ( m_flEmissionDuration == 0.0f ); + } + + virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly || IsInfinitelyEmitting() ) + { + pCtx->m_bStoppedEmission = true; + } + } + virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly || IsInfinitelyEmitting() ) + { + pCtx->m_bStoppedEmission = false; + SkipToTime( pParticles->m_flCurTime, pParticles, pCtx ); + } + } + + virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + pCtx->m_flNextEmitTime = m_flStartTime; + pCtx->m_flTotalActualParticlesSoFar = 0.0f; + pCtx->m_nTotalEmittedSoFar = 0; + pCtx->m_flTimeOffset = 0.0f; + pCtx->m_bStoppedEmission = false; + } + + virtual void Restart( CParticleCollection *pParticles, void *pContext ) + { + if ( !IsInfinitelyEmitting() ) + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + pCtx->m_flNextEmitTime = pParticles->m_flCurTime + m_flStartTime; + pCtx->m_flTotalActualParticlesSoFar = 0.0f; + pCtx->m_nTotalEmittedSoFar = 0; + pCtx->m_flTimeOffset = pParticles->m_flCurTime; + } + } + + // Called when the SFM wants to skip forward in time + // Currently hacked for save/load pre-sim - correct solution is to serialize rather + // than skip-to-time and simulate + virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( flTime <= flStartTime ) + return; + + float flControlPointScale = pParticles->GetHighestControlPoint(); + flControlPointScale *= m_flEmissionScale; + float flEmissionRate = m_flEmitRate; + + float flEmitStrength; + if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) ) + { + flEmissionRate *= flEmitStrength; + } + + if ( flControlPointScale != 0.0f ) + { + flEmissionRate *= flControlPointScale; + } + + float flPrevDrawTime = pParticles->m_flCurTime - flTime; + float flCurrDrawTime = pParticles->m_flCurTime; + + if ( !IsInfinitelyEmitting() ) + { + if ( flPrevDrawTime < flStartTime ) + { + flPrevDrawTime = flStartTime; + } + //if ( flCurrDrawTime > flStartTime + m_flEmissionDuration ) + //{ + // flCurrDrawTime = flStartTime + m_flEmissionDuration; + //} + } + float flDeltaTime = flCurrDrawTime - flPrevDrawTime; + flDeltaTime = min( flDeltaTime, 4.f ); + flPrevDrawTime = flCurrDrawTime - flDeltaTime; + //disabled for now + pCtx->m_flTotalActualParticlesSoFar = flDeltaTime * flEmissionRate; + + + //if ( !IsInfinitelyEmitting() ) + // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar ); + pCtx->m_nTotalEmittedSoFar = 0; + //simulate a bunch + int nActualParticlesToEmit = floor (pCtx->m_flTotalActualParticlesSoFar); + int nStartParticle = pParticles->m_nActiveParticles; + + if ( pParticles->m_nMaxAllowedParticles < nStartParticle + nActualParticlesToEmit ) + { + nActualParticlesToEmit = pParticles->m_nMaxAllowedParticles - nStartParticle; + } + + pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles ); + + float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit ); + float flTimeStep = flPrevDrawTime + flTimeStampStep; + + // Set the particle creation time to the exact sub-frame particle emission time + // !! speed!! do sse init here + for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ ) + { + float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i ); + flTimeStep = min( flTimeStep, flCurrDrawTime ); + *pTimeStamp = flTimeStep; + flTimeStep += flTimeStampStep; + } + + if ( !g_bDontMakeSkipToTimeTakeForever ) + { + flPrevDrawTime = max( flPrevDrawTime, flCurrDrawTime - pParticles->m_pDef->m_flNoDrawTimeToGoToSleep ); + pParticles->m_flCurTime = flPrevDrawTime; + pParticles->m_fl4CurTime = ReplicateX4( flPrevDrawTime ); + for( float i = flPrevDrawTime; i < flCurrDrawTime; i += 0.1 ) + { + pParticles->Simulate( .1, false ); + } + } + } + + size_t GetRequiredContextBytes( void ) const + { + return sizeof( ContinuousEmitterContext_t ); + } + + virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const + { + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + if ( pCtx->m_bStoppedEmission ) + return false; + + if ( m_flEmitRate <= 0.0f ) + return false; + + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) ) + return false; + + return true; + } + + float m_flEmissionDuration; + float m_flStartTime; + float m_flEmitRate; + float m_flTimePerEmission; + float m_flEmissionScale; + bool m_bScalePerParticle; +}; + +DEFINE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter, "emit_continuously", OPERATOR_GENERIC ); + +BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter ) + DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime ) + DMXELEMENT_UNPACK_FIELD( "emission_rate", "100", float, m_flEmitRate ) + DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration ) + DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale ) + DMXELEMENT_UNPACK_FIELD( "use parent particles for emission scaling", "0", bool, m_bScalePerParticle ) +END_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter ) + +uint32 C_OP_ContinuousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const +{ + // Have we emitted all the particles we're going to emit? + // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call + ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext ); + + //Allows for dynamic scaling via changes in number of control points. + float flControlPointScale = pParticles->GetHighestControlPoint(); + //The emission scale here allows for a scalar value per controlpoint, like 2 or .25... + flControlPointScale *= m_flEmissionScale; + //Global strength scale brought in by operator fade in/fade out/oscillate + float flEmissionRate = m_flEmitRate * flCurStrength; + if ( flControlPointScale != 0.0f || m_bScalePerParticle ) + { + if ( m_bScalePerParticle ) + { + if ( pParticles->m_pParent ) + { + flControlPointScale = pParticles->m_pParent->m_nActiveParticles * m_flEmissionScale; + } + else + { + flControlPointScale = m_flEmissionScale; + } + + } + flEmissionRate *= flControlPointScale; + } + + if ( flEmissionRate == 0.0f ) + return 0; + + if ( !C_OP_ContinuousEmitter::MayCreateMoreParticles( pParticles, pContext ) ) + return 0; + + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( pParticles->m_flCurTime < flStartTime ) + return 0; + + Assert( flEmissionRate != 0.0f ); + + // determine our previous and current draw times and clamp them to start time and emission duration + float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt; + float flCurrDrawTime = pParticles->m_flCurTime; + + if ( !IsInfinitelyEmitting() ) + { + if ( flPrevDrawTime < flStartTime ) + { + flPrevDrawTime = flStartTime; + } + if ( flCurrDrawTime > flStartTime + m_flEmissionDuration ) + { + flCurrDrawTime = flStartTime + m_flEmissionDuration; + } + } + + float flDeltaTime = flCurrDrawTime - flPrevDrawTime; + + //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float + float flActualParticlesToEmit = flEmissionRate * flDeltaTime; + + //Add emitted particle to float counter to allow for fractional emission + pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit; + + //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame + int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar; + + //Add emitted particles to running int total. + pCtx->m_nTotalEmittedSoFar += nParticlesToEmit; + + + if ( nParticlesToEmit == 0 ) + return 0; + + // We're only allowed to emit so many particles, though.. + // If we run out of room, only emit the last N particles + int nActualParticlesToEmit = nParticlesToEmit; + int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles; + if ( nAllowedParticlesToEmit < nParticlesToEmit ) + { + nActualParticlesToEmit = nAllowedParticlesToEmit; + //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit; + } + if ( nActualParticlesToEmit == 0 ) + return 0; + + int nStartParticle = pParticles->m_nActiveParticles; + pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles ); + + + float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit ); + float flTimeStep = flPrevDrawTime + flTimeStampStep; + + // Set the particle creation time to the exact sub-frame particle emission time + // !! speed!! do sse init here + for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ ) + { + float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i ); + flTimeStep = min( flTimeStep, flCurrDrawTime ); + *pTimeStamp = flTimeStep; + flTimeStep += flTimeStampStep; + } + + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; +} + + +//----------------------------------------------------------------------------- +// Noise Emitter +//----------------------------------------------------------------------------- +struct NoiseEmitterContext_t +{ + float m_flTotalActualParticlesSoFar; + int m_nTotalEmittedSoFar; + float m_flNextEmitTime; + float m_flTimeOffset; + bool m_bStoppedEmission; +}; + +class C_OP_NoiseEmitter : public CParticleOperatorInstance +{ + DECLARE_PARTICLE_OPERATOR( C_OP_NoiseEmitter ); + + uint32 GetWrittenAttributes( void ) const + { + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; + } + + uint32 GetReadAttributes( void ) const + { + return 0; + } + + virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement ) + { + if ( m_flEmitRate < 0.0f ) + { + m_flEmitRate = 0.0f; + } + if ( m_flEmissionDuration < 0.0f ) + { + m_flEmissionDuration = 0.0f; + } + m_flEmitRate *= g_nParticle_Multiplier; + } + + virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const ; + + inline bool IsInfinitelyEmitting() const + { + return ( m_flEmissionDuration == 0.0f ); + } + + virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly || IsInfinitelyEmitting() ) + { + pCtx->m_bStoppedEmission = true; + } + } + virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + if ( !bInfiniteOnly || IsInfinitelyEmitting() ) + { + pCtx->m_bStoppedEmission = false; + SkipToTime( pParticles->m_flCurTime, pParticles, pCtx ); + } + } + + virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + pCtx->m_flNextEmitTime = m_flStartTime; + pCtx->m_flTotalActualParticlesSoFar = 1.0f; + pCtx->m_nTotalEmittedSoFar = 0; + pCtx->m_flTimeOffset = 0.0f; + pCtx->m_bStoppedEmission = false; + } + + virtual void Restart( CParticleCollection *pParticles, void *pContext ) + { + if ( !IsInfinitelyEmitting() ) + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + pCtx->m_flNextEmitTime = m_flStartTime + pParticles->m_flCurTime; + pCtx->m_flTotalActualParticlesSoFar = 1.0f; + pCtx->m_nTotalEmittedSoFar = 0; + pCtx->m_flTimeOffset = pParticles->m_flCurTime; + } + } + + // Called when the SFM wants to skip forward in time + virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( flTime <= flStartTime ) + return; + + float flControlPointScale = pParticles->GetHighestControlPoint(); + flControlPointScale *= m_flEmissionScale; + float flEmissionRate = m_flEmitRate; + + float flEmitStrength; + if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) ) + { + flEmissionRate *= flEmitStrength; + } + + if ( flControlPointScale != 0.0f ) + { + flEmissionRate *= flControlPointScale; + } + pCtx->m_flTotalActualParticlesSoFar = ( pParticles->m_flCurTime - flStartTime ) * flEmissionRate + 1; + + //if ( !IsInfinitelyEmitting() ) + // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar ); + pCtx->m_nTotalEmittedSoFar = 0; + + } + + size_t GetRequiredContextBytes( void ) const + { + return sizeof( NoiseEmitterContext_t ); + } + + virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const + { + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + if ( pCtx->m_bStoppedEmission ) + return false; + + if ( m_flEmitRate <= 0.0f ) + return false; + + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) ) + return false; + + return true; + } + + float m_flEmissionDuration; + float m_flStartTime; + float m_flEmitRate; + float m_flTimePerEmission; + float m_flEmissionScale; + bool m_bAbsVal, m_bAbsValInv; + float m_flOffset; + float m_flOutputMin; + float m_flOutputMax; + float m_flNoiseScale, m_flNoiseScaleLoc; + Vector m_vecOffsetLoc; + float m_flWorldTimeScale; +}; + + +DEFINE_PARTICLE_OPERATOR( C_OP_NoiseEmitter, "emit noise", OPERATOR_GENERIC ); + +BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter ) + DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime ) + DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration ) + DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale ) + DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","0.1",float,m_flNoiseScale) + //DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","0.001",float,m_flNoiseScaleLoc) + DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset ) + //DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc ) + DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal ) + DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv ) + DMXELEMENT_UNPACK_FIELD( "emission minimum","0", float, m_flOutputMin ) + DMXELEMENT_UNPACK_FIELD( "emission maximum","100", float, m_flOutputMax ) + DMXELEMENT_UNPACK_FIELD( "world time noise coordinate scale","0", float, m_flWorldTimeScale ) +END_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter ) + +uint32 C_OP_NoiseEmitter::Emit( CParticleCollection *pParticles, float flCurStrength, + void *pContext ) const +{ + // Have we emitted all the particles we're going to emit? + // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call + NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext ); + + //Allows for dynamic scaling via changes in number of control points. + float flControlPointScale = pParticles->GetHighestControlPoint(); + //The emission scale here allows for a scalar value per controlpoint, like 2 or .25... + flControlPointScale *= m_flEmissionScale; + + float flAbsScale; + int nAbsVal; + nAbsVal = 0xffffffff; + flAbsScale = 0.5; + if ( m_bAbsVal ) + { + nAbsVal = 0x7fffffff; + flAbsScale = 1.0; + } + + float fMin = m_flOutputMin; + float fMax = m_flOutputMax; + + + float CoordScale = m_flNoiseScale; + //float CoordScaleLoc = m_flNoiseScaleLoc; + + + float ValueScale, ValueBase; + + Vector Coord, CoordLoc, CoordWorldTime; + //CoordLoc.x = pxyz[0]; + //CoordLoc.y = pxyz[4]; + //CoordLoc.z = pxyz[8]; + //CoordLoc += m_vecOffsetLoc; + + float Offset = m_flOffset; + Coord = Vector ( (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset) ); + Coord *= CoordScale; + //CoordLoc *= CoordScaleLoc; + //Coord += CoordLoc; + + CoordWorldTime = Vector( (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale) ); + Coord += CoordWorldTime; + + fltx4 flNoise128; + FourVectors fvNoise; + + fvNoise.DuplicateVector( Coord ); + flNoise128 = NoiseSIMD( fvNoise ); + float flNoise = SubFloat( flNoise128, 0 ); + + *( (int *) &flNoise) &= nAbsVal; + + ValueScale = ( flAbsScale *( fMax - fMin ) ); + ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) ); + + if ( m_bAbsValInv ) + { + flNoise = 1.0 - flNoise; + } + + float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) ); + flInitialNoise = clamp(flInitialNoise, 0.0f, (float) INT_MAX ); + + //Global strength scale brought in by operator fade in/fade out/oscillate + float flEmissionRate = flInitialNoise * flCurStrength; + if ( flControlPointScale != 0.0f ) + { + flEmissionRate *= flControlPointScale; + } + + if ( flEmissionRate == 0.0f ) + return 0; + + if ( !C_OP_NoiseEmitter::MayCreateMoreParticles( pParticles, pContext ) ) + return 0; + + float flStartTime = m_flStartTime + pCtx->m_flTimeOffset; + if ( pParticles->m_flCurTime < flStartTime ) + return 0; + + Assert( flEmissionRate != 0.0f ); + + // determine our previous and current draw times and clamp them to start time and emission duration + float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt; + float flCurrDrawTime = pParticles->m_flCurTime; + + if ( !IsInfinitelyEmitting() ) + { + if ( flPrevDrawTime < flStartTime ) + { + flPrevDrawTime = flStartTime; + } + if ( flCurrDrawTime > flStartTime + m_flEmissionDuration ) + { + flCurrDrawTime = flStartTime + m_flEmissionDuration; + } + } + + float flDeltaTime = flCurrDrawTime - flPrevDrawTime; + + //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float + float flActualParticlesToEmit = flEmissionRate * flDeltaTime; + + //Add emitted particle to float counter to allow for fractional emission + pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit; + + //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame + int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar; + + //Add emitted particles to running int total. + pCtx->m_nTotalEmittedSoFar += nParticlesToEmit; + + + if ( nParticlesToEmit == 0 ) + return 0; + + // We're only allowed to emit so many particles, though.. + // If we run out of room, only emit the last N particles + int nActualParticlesToEmit = nParticlesToEmit; + int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles; + if ( nAllowedParticlesToEmit < nParticlesToEmit ) + { + nActualParticlesToEmit = nAllowedParticlesToEmit; + //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit; + } + if ( nActualParticlesToEmit == 0 ) + return 0; + + int nStartParticle = pParticles->m_nActiveParticles; + pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles ); + + float flTimeStampStep = ( flCurrDrawTime - flPrevDrawTime ) / ( nActualParticlesToEmit ); + float flTimeStep = flPrevDrawTime + flTimeStampStep; + + // Set the particle creation time to the exact sub-frame particle emission time + // !! speed!! do sse init here + for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ ) + { + float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i ); + flTimeStep = min( flTimeStep, flCurrDrawTime ); + *pTimeStamp = flTimeStep; + flTimeStep += flTimeStampStep; + } + + return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK; +} + + +void AddBuiltInParticleEmitters( void ) +{ + REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_ContinuousEmitter ); + REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_InstantaneousEmitter ); + REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_NoiseEmitter ); +} + |