summaryrefslogtreecommitdiff
path: root/particles/builtin_particle_ops.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'particles/builtin_particle_ops.cpp')
-rw-r--r--particles/builtin_particle_ops.cpp4584
1 files changed, 4584 insertions, 0 deletions
diff --git a/particles/builtin_particle_ops.cpp b/particles/builtin_particle_ops.cpp
new file mode 100644
index 0000000..0d8acdf
--- /dev/null
+++ b/particles/builtin_particle_ops.cpp
@@ -0,0 +1,4584 @@
+//========= 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 "tier2/renderutils.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "studio.h"
+#include "bspflags.h"
+#include "tier0/vprof.h"
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#if MEASURE_PARTICLE_PERF
+
+#if VPROF_LEVEL > 0
+#define START_OP float flOpStartTime = Plat_FloatTime(); VPROF_ENTER_SCOPE(pOp->GetDefinition()->GetName())
+#else
+#define START_OP float flOpStartTime = Plat_FloatTime();
+#endif
+
+#if VPROF_LEVEL > 0
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->GetDefinition(); \
+ pDef->RecordExecutionTime( flETime ); \
+} \
+ VPROF_EXIT_SCOPE()
+#else
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->GetDefinition(); \
+ pDef->RecordExecutionTime( flETime ); \
+}
+#endif
+#else
+#define START_OP
+#define END_OP
+#endif
+
+//-----------------------------------------------------------------------------
+// Standard movement operator
+//-----------------------------------------------------------------------------
+class C_OP_BasicMovement : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_BasicMovement );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Vector m_Gravity;
+ float m_fDrag;
+ int m_nMaxConstraintPasses;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_BasicMovement, "Movement Basic", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_BasicMovement )
+ DMXELEMENT_UNPACK_FIELD( "gravity", "0 0 0", Vector, m_Gravity )
+ DMXELEMENT_UNPACK_FIELD( "drag", "0", float, m_fDrag )
+ DMXELEMENT_UNPACK_FIELD( "max constraint passes", "3", int, m_nMaxConstraintPasses )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_BasicMovement )
+
+
+#define MAXIMUM_NUMBER_OF_CONSTRAINTS 100
+//#define CHECKALL 1
+
+#ifdef NDEBUG
+#define CHECKSYSTEM( p ) 0
+#else
+#ifdef CHECKALL
+static void CHECKSYSTEM( CParticleCollection *pParticles )
+{
+// Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Assert( IsFinite( xyz[0] ) );
+ Assert( IsFinite( xyz[4] ) );
+ Assert( IsFinite( xyz[8] ) );
+ Assert( IsFinite( xyz_prev[0] ) );
+ Assert( IsFinite( xyz_prev[4] ) );
+ Assert( IsFinite( xyz_prev[8] ) );
+ }
+}
+#else
+#define CHECKSYSTEM( p ) 0
+#endif
+#endif
+
+void C_OP_BasicMovement::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator prev_xyz( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ C4VAttributeWriteIterator xyz( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ // fltx4 adj_dt = ReplicateX4( (1.0-m_fDrag) * ( pParticles->m_flDt / pParticles->m_flPreviousDt ) );
+ fltx4 adj_dt = ReplicateX4( ( pParticles->m_flDt / pParticles->m_flPreviousDt ) * ExponentialDecay( (1.0f-max(0.f,m_fDrag)), (1.0f/30.0f), pParticles->m_flDt ) );
+
+ size_t nForceStride=0;
+ Vector acc = m_Gravity;
+ fltx4 accFactorX = ReplicateX4( acc.x );
+ fltx4 accFactorY = ReplicateX4( acc.y );
+ fltx4 accFactorZ = ReplicateX4( acc.z );
+
+ int nAccumulators = pParticles->m_pDef->m_ForceGenerators.Count();
+
+ FourVectors PerParticleForceAccumulator[MAX_PARTICLES_IN_A_SYSTEM / 4]; // xbox fixme - memory
+
+ FourVectors *pAccOut = PerParticleForceAccumulator;
+ if (nAccumulators)
+ {
+ // we do have per particle force accumulators
+ nForceStride = 1;
+ int nblocks = pParticles->m_nPaddedActiveParticles;
+ for(int i=0;i<nblocks;i++)
+ {
+ pAccOut->x = accFactorX;
+ pAccOut->y = accFactorY;
+ pAccOut->z = accFactorZ;
+ pAccOut++;
+ }
+ // now, call all force accumulators
+ for(int i=0;i < nAccumulators ; i++ )
+ {
+ float flStrengthOp;
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_ForceGenerators[i];
+ if ( pParticles->CheckIfOperatorShouldRun( pOp, &flStrengthOp ))
+ {
+ START_OP;
+ pParticles->m_pDef->m_ForceGenerators[i]->AddForces(
+ PerParticleForceAccumulator,
+ pParticles,
+ nblocks,
+ flStrengthOp,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nForceGeneratorsCtxOffsets[i] );
+ END_OP;
+ }
+ }
+ }
+ else
+ {
+ pAccOut->x = accFactorX;
+ pAccOut->y = accFactorY;
+ pAccOut->z = accFactorZ;
+ // we just have gravity
+ }
+
+ CHECKSYSTEM( pParticles );
+ fltx4 DtSquared = ReplicateX4( pParticles->m_flDt * pParticles->m_flDt );
+ int ctr = pParticles->m_nPaddedActiveParticles;
+ FourVectors *pAccIn = PerParticleForceAccumulator;
+ do
+ {
+ accFactorX = MulSIMD( pAccIn->x, DtSquared );
+ accFactorY = MulSIMD( pAccIn->y, DtSquared );
+ accFactorZ = MulSIMD( pAccIn->z, DtSquared );
+
+ // we will write prev xyz, and swap prev and cur at the end
+ prev_xyz->x = AddSIMD( xyz->x,
+ AddSIMD( accFactorX, MulSIMD( adj_dt, SubSIMD( xyz->x, prev_xyz->x ) ) ) );
+ prev_xyz->y = AddSIMD( xyz->y,
+ AddSIMD( accFactorY, MulSIMD( adj_dt, SubSIMD( xyz->y, prev_xyz->y ) ) ) );
+ prev_xyz->z = AddSIMD( xyz->z,
+ AddSIMD( accFactorZ, MulSIMD( adj_dt, SubSIMD( xyz->z, prev_xyz->z ) ) ) );
+ CHECKSYSTEM( pParticles );
+ ++prev_xyz;
+ ++xyz;
+ pAccIn += nForceStride;
+ } while (--ctr);
+
+ CHECKSYSTEM( pParticles );
+ pParticles->SwapPosAndPrevPos();
+ // now, enforce constraints
+ int nConstraints = pParticles->m_pDef->m_Constraints.Count();
+ if ( nConstraints && pParticles->m_nPaddedActiveParticles )
+ {
+ bool bConstraintSatisfied[ MAXIMUM_NUMBER_OF_CONSTRAINTS ];
+ bool bFinalConstraint[ MAXIMUM_NUMBER_OF_CONSTRAINTS ];
+ for(int i=0;i<nConstraints; i++)
+ {
+ bFinalConstraint[i] = pParticles->m_pDef->m_Constraints[i]->IsFinalConstraint();
+
+ bConstraintSatisfied[i] = false;
+ pParticles->m_pDef->m_Constraints[i]->SetupConstraintPerFrameData(
+ pParticles, pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i] );
+ }
+
+ // constraints get to see their own per psystem per op random #s
+ for(int p=0; p < m_nMaxConstraintPasses ; p++ )
+ {
+// int nSaveOffset=pParticles->m_nOperatorRandomSampleOffset;
+ for(int i=0;i<nConstraints; i++)
+ {
+// pParticles->m_nOperatorRandomSampleOffset += 23;
+ if ( ! bConstraintSatisfied[i] )
+ {
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_Constraints[i];
+ bConstraintSatisfied[i] = true;
+ if ( ( !bFinalConstraint[i] ) && ( pParticles->CheckIfOperatorShouldRun( pOp ) ) )
+ {
+ START_OP;
+ bool bDidSomething = pOp->EnforceConstraint(
+ 0, pParticles->m_nPaddedActiveParticles, pParticles,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i],
+ pParticles->m_nActiveParticles );
+ END_OP;
+ CHECKSYSTEM( pParticles );
+ if ( bDidSomething )
+ {
+ // other constraints now not satisfied, maybe
+ for( int j=0; j<nConstraints; j++)
+ {
+ if ( i != j )
+ {
+ bConstraintSatisfied[ j ] = false;
+ }
+ }
+ }
+ }
+ }
+ }
+// pParticles->m_nOperatorRandomSampleOffset = nSaveOffset;
+ }
+ // now, run final constraints
+ for(int i=0;i<nConstraints; i++)
+ {
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_Constraints[i];
+ if ( ( bFinalConstraint[i] ) &&
+ ( pParticles->CheckIfOperatorShouldRun(
+ pOp ) ) )
+ {
+ START_OP;
+ pOp->EnforceConstraint(
+ 0, pParticles->m_nPaddedActiveParticles, pParticles,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i],
+ pParticles->m_nActiveParticles );
+ END_OP;
+ CHECKSYSTEM( pParticles );
+ }
+ }
+ }
+ CHECKSYSTEM( pParticles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Fade and kill operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeAndKill : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeAndKill );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flStartFadeInTime;
+ float m_flEndFadeInTime;
+ float m_flStartFadeOutTime;
+ float m_flEndFadeOutTime;
+ float m_flStartAlpha;
+ float m_flEndAlpha;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeAndKill, "Alpha Fade and Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeAndKill )
+ DMXELEMENT_UNPACK_FIELD( "start_alpha","1", float, m_flStartAlpha )
+ DMXELEMENT_UNPACK_FIELD( "end_alpha","0", float, m_flEndAlpha )
+ DMXELEMENT_UNPACK_FIELD( "start_fade_in_time","0", float, m_flStartFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "end_fade_in_time","0.5", float, m_flEndFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "start_fade_out_time","0.5", float, m_flStartFadeOutTime )
+ DMXELEMENT_UNPACK_FIELD( "end_fade_out_time","1", float, m_flEndFadeOutTime )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeAndKill )
+
+void C_OP_FadeAndKill::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+{
+ // Cache off and validate values
+ if ( m_flEndFadeInTime < m_flStartFadeInTime )
+ {
+ m_flEndFadeInTime = m_flStartFadeInTime;
+ }
+ if ( m_flEndFadeOutTime < m_flStartFadeOutTime )
+ {
+ m_flEndFadeOutTime = m_flStartFadeOutTime;
+ }
+
+ if ( m_flStartFadeOutTime < m_flStartFadeInTime )
+ {
+ V_swap( m_flStartFadeInTime, m_flStartFadeOutTime );
+ }
+
+ if ( m_flEndFadeOutTime < m_flEndFadeInTime )
+ {
+ V_swap( m_flEndFadeInTime, m_flEndFadeOutTime );
+ }
+}
+
+void C_OP_FadeAndKill::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+
+ fltx4 fl4StartFadeInTime = ReplicateX4( m_flStartFadeInTime );
+ fltx4 fl4StartFadeOutTime = ReplicateX4( m_flStartFadeOutTime );
+ fltx4 fl4EndFadeInTime = ReplicateX4( m_flEndFadeInTime );
+ fltx4 fl4EndFadeOutTime = ReplicateX4( m_flEndFadeOutTime );
+ fltx4 fl4EndAlpha = ReplicateX4( m_flEndAlpha );
+ fltx4 fl4StartAlpha = ReplicateX4( m_flStartAlpha );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ fltx4 fl4FadeInDuration = ReplicateX4( m_flEndFadeInTime - m_flStartFadeInTime );
+ fltx4 fl4OOFadeInDuration = ReciprocalEstSIMD( fl4FadeInDuration );
+
+ fltx4 fl4FadeOutDuration = ReplicateX4( m_flEndFadeOutTime - m_flStartFadeOutTime );
+ fltx4 fl4OOFadeOutDuration = ReciprocalEstSIMD( fl4FadeOutDuration );
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4Age = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4ParticleLifeTime = *pLifeDuration;
+ fltx4 fl4KillMask = CmpGeSIMD( fl4Age, *pLifeDuration ); // takes care of lifeduration = 0 div 0
+ fl4Age = MulSIMD( fl4Age, ReciprocalEstSIMD( fl4ParticleLifeTime ) ); // age 0..1
+ fltx4 fl4FadingInMask = AndNotSIMD( fl4KillMask,
+ AndSIMD(
+ CmpLeSIMD( fl4StartFadeInTime, fl4Age ), CmpGtSIMD(fl4EndFadeInTime, fl4Age ) ) );
+ fltx4 fl4FadingOutMask = AndNotSIMD( fl4KillMask,
+ AndSIMD(
+ CmpLeSIMD( fl4StartFadeOutTime, fl4Age ), CmpGtSIMD(fl4EndFadeOutTime, fl4Age ) ) );
+ if ( IsAnyNegative( fl4FadingInMask ) )
+ {
+ fltx4 fl4Goal = MulSIMD( *pInitialAlpha, fl4StartAlpha );
+ fltx4 fl4NewAlpha = SimpleSplineRemapValWithDeltasClamped( fl4Age, fl4StartFadeInTime, fl4FadeInDuration, fl4OOFadeInDuration,
+ fl4Goal, SubSIMD( *pInitialAlpha, fl4Goal ) );
+
+ *pAlpha = MaskedAssign( fl4FadingInMask, fl4NewAlpha, *pAlpha );
+ }
+ if ( IsAnyNegative( fl4FadingOutMask ) )
+ {
+ fltx4 fl4Goal = MulSIMD( *pInitialAlpha, fl4EndAlpha );
+ fltx4 fl4NewAlpha = SimpleSplineRemapValWithDeltasClamped( fl4Age, fl4StartFadeOutTime, fl4FadeOutDuration, fl4OOFadeOutDuration,
+ *pInitialAlpha, SubSIMD( fl4Goal, *pInitialAlpha ) );
+ *pAlpha = MaskedAssign( fl4FadingOutMask, fl4NewAlpha, *pAlpha );
+ }
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fade In Operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeIn : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeIn );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flFadeInTimeMin;
+ float m_flFadeInTimeMax;
+ float m_flFadeInTimeExp;
+ bool m_bProportional;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeIn, "Alpha Fade In Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeIn )
+ DMXELEMENT_UNPACK_FIELD( "fade in time min",".25", float, m_flFadeInTimeMin )
+ DMXELEMENT_UNPACK_FIELD( "fade in time max",".25", float, m_flFadeInTimeMax )
+ DMXELEMENT_UNPACK_FIELD( "fade in time exponent","1", float, m_flFadeInTimeExp )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1","1", bool, m_bProportional )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeIn )
+
+
+void C_OP_FadeIn::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ C4IAttributeIterator pParticleID( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ int nSSEFixedExponent = m_flFadeInTimeExp*4.0;
+
+ fltx4 FadeTimeMin = ReplicateX4( m_flFadeInTimeMin );
+ fltx4 FadeTimeWidth = ReplicateX4( m_flFadeInTimeMax - m_flFadeInTimeMin );
+
+ do
+ {
+ fltx4 FadeInTime= Pow_FixedPoint_Exponent_SIMD(
+ pParticles->RandomFloat( *pParticleID, nRandomOffset ),
+ nSSEFixedExponent);
+ FadeInTime = AddSIMD( FadeTimeMin, MulSIMD( FadeTimeWidth, FadeInTime ) );
+
+ // Find our life percentage
+ fltx4 flLifeTime = SubSIMD( CurTime, *pCreationTime );
+ if ( m_bProportional )
+ {
+ flLifeTime =
+ MaxSIMD( Four_Zeros,
+ MinSIMD( Four_Ones,
+ MulSIMD( flLifeTime, ReciprocalEstSIMD( *pLifeDuration ) ) ) );
+ }
+
+ fltx4 ApplyMask = CmpGtSIMD( FadeInTime, flLifeTime );
+ if ( IsAnyNegative( ApplyMask ) )
+ {
+ // Fading in
+ fltx4 NewAlpha =
+ SimpleSplineRemapValWithDeltasClamped(
+ flLifeTime, Four_Zeros,
+ FadeInTime, ReciprocalEstSIMD( FadeInTime ),
+ Four_Zeros, *pInitialAlpha );
+ *( pAlpha ) = MaskedAssign( ApplyMask, NewAlpha, *( pAlpha ) );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ ++pParticleID;
+ } while( --nCtr );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Fade Out Operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeOut : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeOut );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ float flBias = ( m_flFadeBias != 0.0f ) ? m_flFadeBias : 0.5f;
+ m_fl4BiasParam = PreCalcBiasParameter( ReplicateX4( flBias ) );
+ if ( m_flFadeOutTimeMin == 0.0f && m_flFadeOutTimeMax == 0.0f )
+ {
+ m_flFadeOutTimeMin = m_flFadeOutTimeMax = FLT_EPSILON;
+ }
+ }
+
+ float m_flFadeOutTimeMin;
+ float m_flFadeOutTimeMax;
+ float m_flFadeOutTimeExp;
+ float m_flFadeBias;
+ fltx4 m_fl4BiasParam;
+ bool m_bProportional;
+ bool m_bEaseInAndOut;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeOut, "Alpha Fade Out Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeOut )
+ DMXELEMENT_UNPACK_FIELD( "fade out time min",".25", float, m_flFadeOutTimeMin )
+ DMXELEMENT_UNPACK_FIELD( "fade out time max",".25", float, m_flFadeOutTimeMax )
+ DMXELEMENT_UNPACK_FIELD( "fade out time exponent","1", float, m_flFadeOutTimeExp )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1","1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "ease in and out","1", bool, m_bEaseInAndOut )
+ DMXELEMENT_UNPACK_FIELD( "fade bias", "0.5", float, m_flFadeBias )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeOut )
+
+
+void C_OP_FadeOut::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ C4IAttributeIterator pParticleID( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ int nSSEFixedExponent = m_flFadeOutTimeExp*4.0;
+
+ fltx4 FadeTimeMin = ReplicateX4( m_flFadeOutTimeMin );
+ fltx4 FadeTimeWidth = ReplicateX4( m_flFadeOutTimeMax - m_flFadeOutTimeMin );
+
+ do
+ {
+ fltx4 fl4FadeOutTime = Pow_FixedPoint_Exponent_SIMD(
+ pParticles->RandomFloat( *pParticleID, nRandomOffset ),
+ nSSEFixedExponent );
+ fl4FadeOutTime = AddSIMD( FadeTimeMin, MulSIMD( FadeTimeWidth, fl4FadeOutTime ) );
+
+ fltx4 fl4Lifespan;
+
+ // Find our life percentage
+ fltx4 fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4LifeDuration = *pLifeDuration;
+
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( fl4LifeTime, ReciprocalEstSIMD( fl4LifeDuration ) );
+ fl4FadeOutTime = SubSIMD( Four_Ones, fl4FadeOutTime );
+ fl4Lifespan = SubSIMD ( Four_Ones, fl4FadeOutTime );
+ }
+ else
+ {
+ fl4FadeOutTime = SubSIMD( *pLifeDuration, fl4FadeOutTime );
+ fl4Lifespan = SubSIMD( *pLifeDuration, fl4FadeOutTime ) ;
+ }
+
+ fltx4 ApplyMask = CmpLtSIMD( fl4FadeOutTime, fl4LifeTime );
+ if ( IsAnyNegative( ApplyMask ) )
+ {
+ // Fading out
+ fltx4 NewAlpha;
+ if ( m_bEaseInAndOut )
+ {
+ NewAlpha = SimpleSplineRemapValWithDeltasClamped(
+ fl4LifeTime, fl4FadeOutTime,
+ fl4Lifespan, ReciprocalEstSIMD( fl4Lifespan ),
+ *pInitialAlpha, SubSIMD ( Four_Zeros, *pInitialAlpha ) );
+ NewAlpha = MaxSIMD( Four_Zeros, NewAlpha );
+ }
+ else
+ {
+ fltx4 fl4Frac = MulSIMD( SubSIMD( fl4LifeTime, fl4FadeOutTime ), ReciprocalEstSIMD( fl4Lifespan ) );
+ fl4Frac = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, fl4Frac ) );
+ fl4Frac = BiasSIMD( fl4Frac, m_fl4BiasParam );
+ fl4Frac = SubSIMD( Four_Ones, fl4Frac );
+ NewAlpha = MulSIMD( *pInitialAlpha, fl4Frac );
+ }
+
+ *( pAlpha ) = MaskedAssign( ApplyMask, NewAlpha, *( pAlpha ) );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ ++pParticleID;
+ } while( --nCtr );
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Oscillating Scalar operator
+// performs an oscillation operation on any scalar (fade, radius, etc.)
+//-----------------------------------------------------------------------------
+class C_OP_OscillateScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OscillateScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nField;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_RateMin;
+ float m_RateMax;
+ float m_FrequencyMin;
+ float m_FrequencyMax;
+ int m_nField;
+ bool m_bProportional, m_bProportionalOp;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flOscMult;
+ float m_flOscAdd;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OscillateScalar, "Oscillate Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateScalar )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "oscillation field", "7", int, m_nField, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate min", "0", float, m_RateMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate max", "0", float, m_RateMax )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency min", "1", float, m_FrequencyMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency max", "1", float, m_FrequencyMax )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1", "1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "start time min", "0", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start time max", "0", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end time min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end time max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start/end proportional", "1", bool, m_bProportionalOp )
+ DMXELEMENT_UNPACK_FIELD( "oscillation multiplier", "2", float, m_flOscMult )
+ DMXELEMENT_UNPACK_FIELD( "oscillation start phase", ".5", float, m_flOscAdd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateScalar )
+
+void C_OP_OscillateScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4IAttributeIterator pParticleId ( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ CM128AttributeWriteIterator pOscField ( m_nField, pParticles) ;
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 fl4OscVal;
+
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength * pParticles->m_flDt );
+
+ fltx4 fl4CosFactorMultiplier = ReplicateX4( m_flOscMult );
+ fltx4 fl4CosFactorAdd = ReplicateX4( m_flOscAdd );
+
+ fltx4 fl4CosFactor = AddSIMD( MulSIMD( fl4CosFactorMultiplier, fl4CurTime ), fl4CosFactorAdd );
+ fltx4 fl4CosFactorProp = fl4CosFactorMultiplier;
+
+ fltx4 fl4StartTimeMin = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4StartTimeWidth = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4EndTimeMin = ReplicateX4( m_flEndTime_min );
+ fltx4 fl4EndTimeWidth = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+
+ fltx4 fl4FrequencyMin = ReplicateX4( m_FrequencyMin );
+ fltx4 fl4FrequencyWidth = ReplicateX4( m_FrequencyMax - m_FrequencyMin );
+ fltx4 fl4RateMin = ReplicateX4( m_RateMin );
+ fltx4 fl4RateWidth = ReplicateX4( m_RateMax - m_RateMin );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime;
+ if ( m_bProportionalOp )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ }
+ else
+ {
+ fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ }
+
+ fltx4 fl4StartTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 11);
+ fl4StartTime = AddSIMD( fl4StartTimeMin, MulSIMD( fl4StartTimeWidth, fl4StartTime ) );
+ fltx4 fl4EndTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 12);
+ fl4EndTime = AddSIMD( fl4EndTimeMin, MulSIMD( fl4EndTimeWidth, fl4EndTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4Frequency = pParticles->RandomFloat( *pParticleId, nRandomOffset );
+ fl4Frequency = AddSIMD( fl4FrequencyMin, MulSIMD( fl4FrequencyWidth, fl4Frequency ) );
+ fltx4 fl4Rate= pParticles->RandomFloat( *pParticleId, nRandomOffset + 1);
+ fl4Rate = AddSIMD( fl4RateMin, MulSIMD( fl4RateWidth, fl4Rate ) );
+ fltx4 fl4Cos;
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) );
+ fl4Cos = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fl4LifeTime, fl4Frequency )), fl4CosFactorAdd );
+ }
+ else
+ {
+ fl4Cos = MulSIMD( fl4CosFactor, fl4Frequency );
+ }
+ fltx4 fl4OscMultiplier = MulSIMD( fl4Rate, fl4ScaleFactor);
+ fl4OscVal = AddSIMD ( *pOscField, MulSIMD ( fl4OscMultiplier, SinEst01SIMD( fl4Cos ) ) );
+ if ( m_nField == 7)
+ {
+ *pOscField = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fl4OscVal, Four_Ones), Four_Zeros ), *pOscField );
+ }
+ else
+ {
+ *pOscField = MaskedAssign( fl4GoodMask, fl4OscVal, *pOscField );
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pOscField;
+ ++pParticleId;
+ } while (--nCtr );
+
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Oscillating Vector operator
+// performs an oscillation operation on any vector (location, tint)
+//-----------------------------------------------------------------------------
+class C_OP_OscillateVector : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OscillateVector );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nField;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Vector m_RateMin;
+ Vector m_RateMax;
+ Vector m_FrequencyMin;
+ Vector m_FrequencyMax;
+ int m_nField;
+ bool m_bProportional, m_bProportionalOp;
+ bool m_bAccelerator;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flOscMult;
+ float m_flOscAdd;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OscillateVector, "Oscillate Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateVector )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "oscillation field", "0", int, m_nField, "intchoice particlefield_vector" )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate min", "0 0 0", Vector, m_RateMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate max", "0 0 0", Vector, m_RateMax )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency min", "1 1 1", Vector, m_FrequencyMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency max", "1 1 1", Vector, m_FrequencyMax )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1", "1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "start time min", "0", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start time max", "0", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end time min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end time max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start/end proportional", "1", bool, m_bProportionalOp )
+ DMXELEMENT_UNPACK_FIELD( "oscillation multiplier", "2", float, m_flOscMult )
+ DMXELEMENT_UNPACK_FIELD( "oscillation start phase", ".5", float, m_flOscAdd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateVector )
+
+
+void C_OP_OscillateVector::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4IAttributeIterator pParticleId ( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ C4VAttributeWriteIterator pOscField ( m_nField, pParticles) ;
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ FourVectors fvOscVal;
+
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength * pParticles->m_flDt );
+
+ fltx4 fl4CosFactorMultiplier = ReplicateX4( m_flOscMult );
+ fltx4 fl4CosFactorAdd = ReplicateX4( m_flOscAdd );
+
+ fltx4 fl4CosFactor = AddSIMD( MulSIMD( fl4CosFactorMultiplier, fl4CurTime ), fl4CosFactorAdd );
+ fltx4 fl4CosFactorProp = fl4CosFactorMultiplier;
+
+ fltx4 fl4StartTimeMin = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4StartTimeWidth = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4EndTimeMin = ReplicateX4( m_flEndTime_min );
+ fltx4 fl4EndTimeWidth = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+
+ FourVectors fvFrequencyMin;
+ fvFrequencyMin.DuplicateVector( m_FrequencyMin );
+ FourVectors fvFrequencyWidth;
+ fvFrequencyWidth.DuplicateVector( m_FrequencyMax - m_FrequencyMin );
+ FourVectors fvRateMin;
+ fvRateMin.DuplicateVector( m_RateMin );
+ FourVectors fvRateWidth;
+ fvRateWidth.DuplicateVector( m_RateMax - m_RateMin );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime;
+ if ( m_bProportionalOp )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ }
+ else
+ {
+ fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ }
+
+ fltx4 fl4StartTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 11);
+ fl4StartTime = AddSIMD( fl4StartTimeMin, MulSIMD( fl4StartTimeWidth, fl4StartTime ) );
+ fltx4 fl4EndTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 12);
+ fl4EndTime = AddSIMD( fl4EndTimeMin, MulSIMD( fl4EndTimeWidth, fl4EndTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ FourVectors fvFrequency;
+ fvFrequency.x = pParticles->RandomFloat( *pParticleId, nRandomOffset + 8 );
+ fvFrequency.y = pParticles->RandomFloat( *pParticleId, nRandomOffset + 12 );
+ fvFrequency.z = pParticles->RandomFloat( *pParticleId, nRandomOffset + 15 );
+ fvFrequency.VProduct( fvFrequencyWidth );
+ fvFrequency += fvFrequencyMin;
+
+ FourVectors fvRate;
+ fvRate.x = pParticles->RandomFloat( *pParticleId, nRandomOffset + 3);
+ fvRate.y = pParticles->RandomFloat( *pParticleId, nRandomOffset + 7);
+ fvRate.z = pParticles->RandomFloat( *pParticleId, nRandomOffset + 9);
+
+ //fvRate = AddSIMD( fvRateMin, MulSIMD( fvRateWidth, fvRate ) );
+ fvRate.VProduct( fvRateWidth );
+ fvRate += fvRateMin;
+
+ FourVectors fvCos;
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) );
+ fvCos.x = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.x, fl4LifeTime )), fl4CosFactorAdd );
+ fvCos.y = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.y, fl4LifeTime )), fl4CosFactorAdd );
+ fvCos.z = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.z, fl4LifeTime )), fl4CosFactorAdd );
+ }
+ else
+ {
+ //fvCos = MulSIMD( fl4CosFactor, fvFrequency );
+ fvCos.x = MulSIMD( fvFrequency.x, fl4CosFactor );
+ fvCos.y = MulSIMD( fvFrequency.y, fl4CosFactor );
+ fvCos.z = MulSIMD( fvFrequency.z, fl4CosFactor );
+ }
+
+ FourVectors fvOscMultiplier;
+ fvOscMultiplier.x = MulSIMD( fvRate.x, fl4ScaleFactor);
+ fvOscMultiplier.y = MulSIMD( fvRate.y, fl4ScaleFactor);
+ fvOscMultiplier.z = MulSIMD( fvRate.z, fl4ScaleFactor);
+
+ FourVectors fvOutput = *pOscField;
+
+ fvOscVal.x = AddSIMD ( fvOutput.x, MulSIMD ( fvOscMultiplier.x, SinEst01SIMD( fvCos.x ) ) );
+ fvOscVal.y = AddSIMD ( fvOutput.y, MulSIMD ( fvOscMultiplier.y, SinEst01SIMD( fvCos.y ) ) );
+ fvOscVal.z = AddSIMD ( fvOutput.z, MulSIMD ( fvOscMultiplier.z, SinEst01SIMD( fvCos.z ) ) );
+
+ if ( m_nField == 6)
+ {
+ pOscField->x = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.x, Four_Ones), Four_Zeros ), fvOutput.x );
+ pOscField->y = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.y, Four_Ones), Four_Zeros ), fvOutput.y );
+ pOscField->z = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.z, Four_Ones), Four_Zeros ), fvOutput.z );
+ }
+ else
+ {
+ pOscField->x = MaskedAssign( fl4GoodMask, fvOscVal.x, fvOutput.x );
+ pOscField->y = MaskedAssign( fl4GoodMask, fvOscVal.y, fvOutput.y );
+ pOscField->z = MaskedAssign( fl4GoodMask, fvOscVal.z, fvOutput.z );
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pOscField;
+ ++pParticleId;
+ } while (--nCtr );
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Remap Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 1 << m_nFieldInput;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldInput;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapScalar, "Remap Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapScalar )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "input field", "7", int, m_nFieldInput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+ DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapScalar )
+
+void C_OP_RemapScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *pInput = pParticles->GetFloatAttributePtr( m_nFieldInput, i );
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( *pInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// noise Operator
+//-----------------------------------------------------------------------------
+class C_OP_Noise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Noise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ fltx4 m_fl4NoiseScale;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Noise, "Noise Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Noise )
+ DMXELEMENT_UNPACK_FLTX4( "noise coordinate scale", "0.1", m_fl4NoiseScale)
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Noise );
+
+void C_OP_Noise::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeWriteIterator pAttr( m_nFieldOutput, pParticles );
+
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ fltx4 CoordScale=m_fl4NoiseScale;
+
+ float fMin = m_flOutputMin;
+ float fMax = m_flOutputMax;
+
+ if ( ATTRIBUTES_WHICH_ARE_ANGLES & (1 << m_nFieldOutput ) )
+ {
+ fMin *= ( M_PI / 180.0f );
+ fMax *= ( M_PI / 180.0f );
+ }
+ // calculate coefficients. noise retuns -1..1
+ fltx4 ValueScale=ReplicateX4( 0.5*(fMax-fMin ) );
+ fltx4 ValueBase=ReplicateX4( fMin + 0.5*( fMax - fMin ) );
+ int nActive = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors Coord = *pXYZ;
+ Coord *= CoordScale;
+ *( pAttr )=AddSIMD( ValueBase, MulSIMD( ValueScale, NoiseSIMD( Coord ) ) );
+
+ ++pAttr;
+ ++pXYZ;
+ } while( --nActive );
+}
+//-----------------------------------------------------------------------------
+// vector noise Operator
+//-----------------------------------------------------------------------------
+class C_OP_VectorNoise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VectorNoise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ Vector m_vecOutputMin;
+ Vector m_vecOutputMax;
+ fltx4 m_fl4NoiseScale;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VectorNoise, "Noise Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VectorNoise )
+ DMXELEMENT_UNPACK_FLTX4( "noise coordinate scale", "0.1", m_fl4NoiseScale)
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "6", int, m_nFieldOutput, "intchoice particlefield_vector" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0 0 0", Vector, m_vecOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1 1 1", Vector, m_vecOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VectorNoise );
+
+void C_OP_VectorNoise::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator pAttr( m_nFieldOutput, pParticles );
+
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ fltx4 CoordScale = m_fl4NoiseScale;
+
+ // calculate coefficients. noise retuns -1..1
+ fltx4 ValueScaleX = ReplicateX4( 0.5*(m_vecOutputMax.x-m_vecOutputMin.x ) );
+ fltx4 ValueBaseX = ReplicateX4(m_vecOutputMin.x+0.5*( m_vecOutputMax.x-m_vecOutputMin.x ) );
+
+ fltx4 ValueScaleY = ReplicateX4( 0.5*(m_vecOutputMax.y-m_vecOutputMin.y ) );
+ fltx4 ValueBaseY = ReplicateX4(m_vecOutputMin.y+0.5*( m_vecOutputMax.y-m_vecOutputMin.y ) );
+
+ fltx4 ValueScaleZ = ReplicateX4( 0.5*(m_vecOutputMax.z-m_vecOutputMin.z ) );
+ fltx4 ValueBaseZ = ReplicateX4(m_vecOutputMin.z+0.5*( m_vecOutputMax.z-m_vecOutputMin.z ) );
+
+ FourVectors ofs_y;
+ ofs_y.DuplicateVector( Vector( 100000.5, 300000.25, 9000000.75 ) );
+ FourVectors ofs_z;
+ ofs_z.DuplicateVector( Vector( 110000.25, 310000.75, 9100000.5 ) );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i+=4 )
+ {
+ FourVectors Coord = *pXYZ;
+ Coord *= CoordScale;
+ pAttr->x=AddSIMD( ValueBaseX, MulSIMD( ValueScaleX, NoiseSIMD( Coord ) ) );
+ Coord += ofs_y;
+ pAttr->y=AddSIMD( ValueBaseY, MulSIMD( ValueScaleY, NoiseSIMD( Coord ) ) );
+ Coord += ofs_z;
+ pAttr->z=AddSIMD( ValueBaseZ, MulSIMD( ValueScaleZ, NoiseSIMD( Coord ) ) );
+
+ ++pAttr;
+ ++pXYZ;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Decay Operator (Lifespan limiter - kills dead particles)
+//-----------------------------------------------------------------------------
+class C_OP_Decay : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Decay );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Decay, "Lifespan Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Decay )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Decay )
+
+
+void C_OP_Decay::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+
+ fltx4 fl4KillMask = CmpLeSIMD( fl4LifeDuration, Four_Zeros );
+
+ fltx4 fl4Age = SubSIMD( fl4CurTime, *pCreationTime );
+
+ fl4KillMask = OrSIMD( fl4KillMask, CmpGeSIMD( fl4Age, fl4LifeDuration ) );
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Lifespan Minimum Velocity Decay Operator (kills particles if they cease moving)
+//-----------------------------------------------------------------------------
+class C_OP_VelocityDecay : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VelocityDecay );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flMinVelocity;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VelocityDecay, "Lifespan Minimum Velocity Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityDecay )
+ DMXELEMENT_UNPACK_FIELD( "minimum velocity","1", float, m_flMinVelocity )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityDecay )
+
+
+void C_OP_VelocityDecay::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ fltx4 fl4MinVelocity = ReplicateX4( m_flMinVelocity );
+ fltx4 fl4Dt = ReplicateX4( pParticles->m_flDt );
+
+ CM128AttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ CM128AttributeIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4KillMask = CmpLeSIMD( DivSIMD ( SubSIMD( *pXYZ, *pPrevXYZ ), fl4Dt ), fl4MinVelocity );
+
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+ }
+ ++pXYZ;
+ ++pPrevXYZ;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Random Cull Operator - Randomly culls particles before their lifespan
+//-----------------------------------------------------------------------------
+class C_OP_Cull : public CParticleOperatorInstance
+{
+ float m_flCullPerc;
+ float m_flCullStart;
+ float m_flCullEnd;
+ float m_flCullExp;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_Cull );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Cull, "Cull Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Cull )
+ DMXELEMENT_UNPACK_FIELD( "Cull Start Time", "0", float, m_flCullStart )
+ DMXELEMENT_UNPACK_FIELD( "Cull End Time", "1", float, m_flCullEnd )
+ DMXELEMENT_UNPACK_FIELD( "Cull Time Exponent", "1", float, m_flCullExp )
+ DMXELEMENT_UNPACK_FIELD( "Cull Percentage", "0.5", float, m_flCullPerc )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Cull )
+
+
+void C_OP_Cull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ const float *pCreationTime;
+ const float *pLifeDuration;
+ float flLifeTime;
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ pLifeDuration = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, i );
+ int nParticleId = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_PARTICLE_ID, i );
+ float flCullRank = pParticles->RandomFloat( nParticleId + nRandomOffset + 15, 0.0f, 1.0f);
+ float flCullTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 12, m_flCullStart, m_flCullEnd, m_flCullExp );
+
+ if ( flCullRank > ( m_flCullPerc * flStrength ) )
+ {
+ continue;
+ }
+ // Find our life percentage
+ flLifeTime = clamp( ( pParticles->m_flCurTime - *pCreationTime ) / ( *pLifeDuration ), 0.0f, 1.0f );
+ if ( flLifeTime >= m_flCullStart && flLifeTime <= m_flCullEnd && flLifeTime >= flCullTime )
+ {
+ pParticles->KillParticle( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// generic spin operator
+//-----------------------------------------------------------------------------
+class CGeneralSpin : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToSpin( void ) const =0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ if ( m_nSpinRateDegrees != 0.0 )
+ return (1 << GetAttributeToSpin() );
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_fSpinRateRadians = (float) m_nSpinRateDegrees * ( M_PI / 180.0f );
+ m_fSpinRateMinRadians = (float) m_nSpinRateMinDegrees * ( M_PI / 180.0f );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nSpinRateDegrees;
+ int m_nSpinRateMinDegrees;
+ float m_fSpinRateRadians;
+ float m_fSpinRateStopTime;
+ float m_fSpinRateMinRadians;
+};
+
+void CGeneralSpin::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ float fCurSpinRate = m_fSpinRateRadians * flStrength;
+
+ if ( fCurSpinRate == 0.0 )
+ return;
+
+ float dt = pParticles->m_flDt;
+ float drot = dt * fabs( fCurSpinRate * 2.0f * M_PI );
+ if ( m_fSpinRateStopTime == 0.0f )
+ {
+ drot = fmod( drot, (float)(2.0f * M_PI) );
+ }
+ if ( fCurSpinRate < 0.0f )
+ {
+ drot = -drot;
+ }
+ fltx4 Rot_Add = ReplicateX4( drot );
+ fltx4 Pi_2 = ReplicateX4( 2.0*M_PI );
+ fltx4 nPi_2 = ReplicateX4( -2.0*M_PI );
+
+ // FIXME: This is wrong
+ fltx4 minSpeedRadians = ReplicateX4( dt * fabs( m_fSpinRateMinRadians * 2.0f * M_PI ) );
+
+ fltx4 now = pParticles->m_fl4CurTime;
+ fltx4 SpinRateStopTime = ReplicateX4( m_fSpinRateStopTime );
+
+ CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+
+ CM128AttributeWriteIterator pRot( GetAttributeToSpin(), pParticles );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i+=4 )
+ {
+ // HACK: Rather than redo this, I'm simply remapping the stop time into the percentage of lifetime, rather than seconds
+ fltx4 LifeSpan = *pLifeDuration;
+ fltx4 SpinFadePerc = Four_Zeros;
+ fltx4 OOSpinFadeRate = Four_Zeros;
+ if ( m_fSpinRateStopTime )
+ {
+ SpinFadePerc = MulSIMD( LifeSpan, SpinRateStopTime );
+ OOSpinFadeRate = DivSIMD( Four_Ones, SpinFadePerc );
+ }
+
+ fltx4 Age = SubSIMD( now, *pCreationTimeStamp );
+ fltx4 RScale = MaxSIMD( Four_Zeros,
+ SubSIMD( Four_Ones, MulSIMD( Age, OOSpinFadeRate ) ) );
+
+ // Cap the rotation at a minimum speed
+ fltx4 deltaRot = MulSIMD( Rot_Add, RScale );
+ fltx4 Tooslow = CmpLeSIMD( deltaRot, minSpeedRadians );
+ deltaRot = OrSIMD( AndSIMD( Tooslow, minSpeedRadians ), AndNotSIMD( Tooslow, deltaRot ) );
+ fltx4 NewRot = AddSIMD( *pRot, deltaRot );
+
+ // now, cap at +/- 2*pi
+ fltx4 Toobig =CmpGeSIMD( NewRot, Pi_2 );
+ fltx4 Toosmall = CmpLeSIMD( NewRot, nPi_2 );
+
+ NewRot = OrSIMD( AndSIMD( Toobig, SubSIMD( NewRot, Pi_2 ) ),
+ AndNotSIMD( Toobig, NewRot ) );
+
+ NewRot = OrSIMD( AndSIMD( Toosmall, AddSIMD( NewRot, Pi_2 ) ),
+ AndNotSIMD( Toosmall, NewRot ) );
+
+ *( pRot )= NewRot;
+
+ ++pRot;
+ ++pCreationTimeStamp;
+ ++pLifeDuration;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// generic spin operator, version 2. Uses rotation_speed
+//-----------------------------------------------------------------------------
+class CSpinUpdateBase : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToSpin( void ) const =0;
+ virtual int GetSpinSpeedAttribute( void ) const =0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return (1 << GetAttributeToSpin() );
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return ( 1 << GetAttributeToSpin() ) | ( 1 << GetSpinSpeedAttribute() ) |
+ PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+};
+
+void CSpinUpdateBase::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pRotationSpeed( GetSpinSpeedAttribute(), pParticles );
+ CM128AttributeWriteIterator pRot( GetAttributeToSpin(), pParticles );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ fltx4 fl4Dt = ReplicateX4( pParticles->m_flDt );
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i += 4 )
+ {
+ fltx4 fl4SimTime = MinSIMD( fl4Dt, SubSIMD( fl4CurTime, *pCreationTimeStamp ) );
+ fl4SimTime = MulSIMD( fl4SimTime, fl4ScaleFactor );
+ *pRot = MaddSIMD( fl4SimTime, *pRotationSpeed, *pRot );
+
+ ++pRot;
+ ++pRotationSpeed;
+ ++pCreationTimeStamp;
+ }
+}
+
+
+class C_OP_Spin : public CGeneralSpin
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Spin );
+
+ int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Spin, "Rotation Spin Roll", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Spin )
+ DMXELEMENT_UNPACK_FIELD( "spin_rate_degrees", "0", int, m_nSpinRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "spin_stop_time", "0", float, m_fSpinRateStopTime )
+ DMXELEMENT_UNPACK_FIELD( "spin_rate_min", "0", int, m_nSpinRateMinDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Spin )
+
+class C_OP_SpinUpdate : public CSpinUpdateBase
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SpinUpdate );
+
+ virtual int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION;
+ }
+
+ virtual int GetSpinSpeedAttribute( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_SPEED;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SpinUpdate, "Rotation Basic", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SpinUpdate )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SpinUpdate )
+
+class C_OP_SpinYaw : public CGeneralSpin
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SpinYaw );
+
+ int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_YAW;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SpinYaw, "Rotation Spin Yaw", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SpinYaw )
+ DMXELEMENT_UNPACK_FIELD( "yaw_rate_degrees", "0", int, m_nSpinRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "yaw_stop_time", "0", float, m_fSpinRateStopTime )
+ DMXELEMENT_UNPACK_FIELD( "yaw_rate_min", "0", int, m_nSpinRateMinDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SpinYaw )
+
+
+
+//-----------------------------------------------------------------------------
+// Size changing operator
+//-----------------------------------------------------------------------------
+class C_OP_InterpolateRadius : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_InterpolateRadius );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flBias = ( m_flBias != 0.0f ) ? m_flBias : 0.5f;
+ m_fl4BiasParam = PreCalcBiasParameter( ReplicateX4( m_flBias ) );
+ }
+
+ float m_flStartTime;
+ float m_flEndTime;
+ float m_flStartScale;
+ float m_flEndScale;
+ bool m_bEaseInAndOut;
+ float m_flBias;
+ fltx4 m_fl4BiasParam;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_InterpolateRadius, "Radius Scale", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InterpolateRadius )
+ DMXELEMENT_UNPACK_FIELD( "start_time", "0", float, m_flStartTime )
+ DMXELEMENT_UNPACK_FIELD( "end_time", "1", float, m_flEndTime )
+ DMXELEMENT_UNPACK_FIELD( "radius_start_scale", "1", float, m_flStartScale )
+ DMXELEMENT_UNPACK_FIELD( "radius_end_scale", "1", float, m_flEndScale )
+ DMXELEMENT_UNPACK_FIELD( "ease_in_and_out", "0", bool, m_bEaseInAndOut )
+ DMXELEMENT_UNPACK_FIELD( "scale_bias", "0.5", float, m_flBias )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_InterpolateRadius )
+
+void C_OP_InterpolateRadius::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_flEndTime <= m_flStartTime )
+ return;
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128AttributeWriteIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+ CM128InitialAttributeIterator pInitialRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+
+ fltx4 fl4StartTime = ReplicateX4( m_flStartTime );
+ fltx4 fl4EndTime = ReplicateX4( m_flEndTime );
+ fltx4 fl4OOTimeWidth = ReciprocalSIMD( SubSIMD( fl4EndTime, fl4StartTime ) );
+
+ fltx4 fl4ScaleWidth = ReplicateX4( m_flEndScale - m_flStartScale );
+ fltx4 fl4StartScale = ReplicateX4( m_flStartScale );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+ if ( m_bEaseInAndOut )
+ {
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( SimpleSpline( fl4FadeWindow ), fl4ScaleWidth ) );
+ // !!speed!! - can anyone really tell the diff between spline and lerp here?
+ *pRadius = MaskedAssign(
+ fl4GoodMask, MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ else
+ {
+ if ( m_flBias == 0.5f ) // no bias case
+ {
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( fl4FadeWindow, fl4ScaleWidth ) );
+ *pRadius = MaskedAssign( fl4GoodMask, MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ else
+ {
+ // use rational approximation to bias
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+#ifdef FP_EXCEPTIONS_ENABLED
+ // Wherever fl4GoodMask is zero we need to ensure that fl4FadeWindow is not zero
+ // to avoid 0/0 divides in BiasSIMD. Setting those elements to fl4EndTime
+ // should do the trick...
+ fl4FadeWindow = OrSIMD( AndSIMD( fl4GoodMask, fl4EndTime ), AndNotSIMD( fl4GoodMask, fl4EndTime ) );
+#endif
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( BiasSIMD( fl4FadeWindow, m_fl4BiasParam ), fl4ScaleWidth ) );
+ *pRadius = MaskedAssign(
+ fl4GoodMask,
+ MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Color Fade
+//-----------------------------------------------------------------------------
+class C_OP_ColorInterpolate : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ColorInterpolate );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flColorFade[0] = m_ColorFade[0] / 255.0f;
+ m_flColorFade[1] = m_ColorFade[1] / 255.0f;
+ m_flColorFade[2] = m_ColorFade[2] / 255.0f;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Color m_ColorFade;
+ float m_flColorFade[3];
+ float m_flFadeStartTime;
+ float m_flFadeEndTime;
+ bool m_bEaseInOut;
+};
+
+
+
+void C_OP_ColorInterpolate::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator pColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4VInitialAttributeIterator pInitialColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ if ( m_flFadeEndTime == m_flFadeStartTime )
+ return;
+
+ fltx4 ooInRange = ReplicateX4( 1.0 / ( m_flFadeEndTime - m_flFadeStartTime ) );
+
+ fltx4 curTime = pParticles->m_fl4CurTime;
+ fltx4 lowRange = ReplicateX4( m_flFadeStartTime );
+
+ fltx4 targetR = ReplicateX4( m_flColorFade[0] );
+ fltx4 targetG = ReplicateX4( m_flColorFade[1] );
+ fltx4 targetB = ReplicateX4( m_flColorFade[2] );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+ if ( m_bEaseInOut )
+ {
+ do
+ {
+ fltx4 goodMask = CmpGtSIMD( *pLifeDuration, Four_Zeros );
+ if ( IsAnyNegative( goodMask ) )
+ {
+ fltx4 flLifeTime = DivSIMD( SubSIMD( curTime, *pCreationTime ), *pLifeDuration );
+
+ fltx4 T = MulSIMD( SubSIMD( flLifeTime, lowRange ), ooInRange );
+ T = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, T ) );
+ T = SimpleSpline( T );
+ pColor->x = MaskedAssign( goodMask, AddSIMD( pInitialColor->x, MulSIMD( T, SubSIMD( targetR, pInitialColor->x ) ) ), pColor->x );
+ pColor->y = MaskedAssign( goodMask, AddSIMD( pInitialColor->y, MulSIMD( T, SubSIMD( targetG, pInitialColor->y ) ) ), pColor->y );
+ pColor->z = MaskedAssign( goodMask, AddSIMD( pInitialColor->z, MulSIMD( T, SubSIMD( targetB, pInitialColor->z ) ) ), pColor->z );
+ }
+ ++pColor;
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialColor;
+
+ } while( --nCtr );
+ }
+ else
+ {
+ do
+ {
+ fltx4 goodMask = CmpGtSIMD( *pLifeDuration, Four_Zeros );
+ if ( IsAnyNegative( goodMask ) )
+ {
+ fltx4 flLifeTime = DivSIMD( SubSIMD( curTime, *pCreationTime ), *pLifeDuration );
+
+ fltx4 T = MulSIMD( SubSIMD( flLifeTime, lowRange ), ooInRange );
+ T = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, T ) );
+
+ pColor->x = MaskedAssign( goodMask, AddSIMD( pInitialColor->x, MulSIMD( T, SubSIMD( targetR, pInitialColor->x ) ) ), pColor->x );
+ pColor->y = MaskedAssign( goodMask, AddSIMD( pInitialColor->y, MulSIMD( T, SubSIMD( targetG, pInitialColor->y ) ) ), pColor->y );
+ pColor->z = MaskedAssign( goodMask, AddSIMD( pInitialColor->z, MulSIMD( T, SubSIMD( targetB, pInitialColor->z ) ) ), pColor->z );
+ }
+ ++pColor;
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialColor;
+
+ } while( --nCtr );
+ }
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ColorInterpolate, "Color Fade", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ColorInterpolate )
+ DMXELEMENT_UNPACK_FIELD( "color_fade", "255 255 255 255", Color, m_ColorFade )
+ DMXELEMENT_UNPACK_FIELD( "fade_start_time", "0", float, m_flFadeStartTime )
+ DMXELEMENT_UNPACK_FIELD( "fade_end_time", "1", float, m_flFadeEndTime )
+ DMXELEMENT_UNPACK_FIELD( "ease_in_and_out", "1", bool, m_bEaseInOut )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ColorInterpolate )
+
+
+//-----------------------------------------------------------------------------
+// Position Lock to Control Point
+// Locks all particles to the specified control point
+// Useful for making particles move with their emitter and so forth
+//-----------------------------------------------------------------------------
+class C_OP_PositionLock : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_PositionLock );
+
+ struct C_OP_PositionLockContext_t
+ {
+ Vector m_vPrevPosition;
+ matrix3x4_t m_matPrevTransform;
+ };
+
+ int m_nControlPointNumber;
+ Vector m_vPrevPosition;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flStartTime_exp;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flEndTime_exp;
+ float m_flRange;
+ bool m_bLockRot;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_PositionLockContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+ pCtx->m_vPrevPosition = vec3_origin;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &pCtx->m_matPrevTransform );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PositionLock , "Movement Lock to Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PositionLock )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_min", "1", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_max", "1", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_exponent", "1", float, m_flStartTime_exp )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_exponent", "1", float, m_flEndTime_exp )
+ DMXELEMENT_UNPACK_FIELD( "distance fade range", "0", float, m_flRange )
+ DMXELEMENT_UNPACK_FIELD( "lock rotation", "0", bool, m_bLockRot )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PositionLock )
+
+#ifdef OLD_NON_SSE_POSLOCK_FOR_TESTING
+void C_OP_PositionLock::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // At initialization, set prevposition to the control point to prevent random placements/velocities
+
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+
+ if ( pCtx->m_vPrevPosition == Vector (0, 0, 0) )
+
+ {
+ pCtx->m_vPrevPosition = vecControlPoint;
+ }
+
+ // Control point movement delta
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ Vector vecPrevCPPos = pCtx->m_vPrevPosition;
+
+ const float *pCreationTime;
+ const float *pLifeDuration;
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ pLifeDuration = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, i );
+ float flLifeTime = *pLifeDuration != 0.0f ? clamp( ( pParticles->m_flCurTime - *pCreationTime ) / ( *pLifeDuration ), 0.0f, 1.0f ) : 0.0f;
+ if ( *pCreationTime >= ( pParticles->m_flCurTime - pParticles->m_flDt ) )
+ {
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *pCreationTime, &vecPrevCPPos );
+ }
+
+ Vector vDelta = vecControlPoint - vecPrevCPPos;
+ vDelta *= flStrength;
+
+ // clamp activity to start/end time
+ int nParticleId = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_PARTICLE_ID, i );
+ float flStartTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 9, m_flStartTime_min, m_flStartTime_max, m_flStartTime_exp );
+ float flEndTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 10, m_flEndTime_min, m_flEndTime_max, m_flEndTime_exp );
+
+ // bias attachedness by fadeout
+ float flLockScale = SimpleSplineRemapValClamped( flLifeTime, flStartTime, flEndTime, 1.0f, 0.0f );
+
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecParticlePosition, vecParticlePosition_prev ;
+ SetVectorFromAttribute( vecParticlePosition, xyz );
+ SetVectorFromAttribute( vecParticlePosition_prev, xyz_prev );
+ float flDampenAmount = 1;
+ if ( m_flRange != 0 )
+ {
+ Vector ofs;
+ ofs = (vecParticlePosition + ( vDelta * flLockScale ) ) - vecControlPoint;
+ float flDistance = ofs.Length();
+ flDampenAmount = SimpleSplineRemapValClamped( flDistance, 0, m_flRange, 1.0f, 0.0f );
+ flDampenAmount = Bias( flDampenAmount, .2 );
+ }
+ Vector vParticleDelta = vDelta * flLockScale * flDampenAmount;
+
+
+ vecParticlePosition += vParticleDelta;
+ vecParticlePosition_prev += vParticleDelta;
+ SetVectorAttribute( xyz, vecParticlePosition );
+ SetVectorAttribute( xyz_prev, vecParticlePosition_prev );
+ }
+
+ // Store off the control point position for the next delta computation
+ pCtx->m_vPrevPosition = vecControlPoint;
+
+
+};
+
+#else
+void C_OP_PositionLock::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // At initialization, set prevposition to the control point to prevent random placements/velocities
+
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+
+ if ( pCtx->m_vPrevPosition == Vector (0, 0, 0) )
+
+ {
+ pCtx->m_vPrevPosition = vecControlPoint;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &pCtx->m_matPrevTransform );
+ }
+
+ Vector vDelta;
+ matrix3x4_t matCurrentTransform;
+ matrix3x4_t matTransformLock;
+
+ if ( m_bLockRot )
+ {
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &matCurrentTransform );
+ matrix3x4_t matPrev;
+ //if ( MatricesAreEqual ( matCurrentTransform, pCtx->m_matPrevTransform ) )
+ // return;
+ MatrixInvert( pCtx->m_matPrevTransform, matPrev );
+ MatrixMultiply( matCurrentTransform, matPrev, matTransformLock);
+ }
+
+ int nContext = GetSIMDRandContext();
+
+ // Control point movement delta - not full transform
+ vDelta = vecControlPoint - pCtx->m_vPrevPosition;
+ //if ( vDelta == vec3_origin && !m_bLockRot )
+ // return;
+
+ vDelta *= flStrength;
+ FourVectors v4Delta;
+ v4Delta.DuplicateVector( vDelta );
+
+ FourVectors v4ControlPoint;
+ v4ControlPoint.DuplicateVector( vecControlPoint );
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ fltx4 fl4_Dt = ReplicateX4( pParticles->m_flDt );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ bool bUseRange = ( m_flRange != 0.0 );
+ fltx4 fl4OORange;
+ if ( bUseRange )
+ fl4OORange = ReplicateX4( 1.0 / m_flRange );
+
+ fltx4 fl4BiasParm = PreCalcBiasParameter( ReplicateX4( 0.2 ) );
+ if ( m_flStartTime_min >= 1.0 ) // always locked on
+ {
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ do
+ {
+ fltx4 fl4ParticleAge = SubSIMD( pParticles->m_fl4CurTime, *pCreationTime);
+ fltx4 fl4CreationFrameBias = MinSIMD( fl4ParticleAge, fl4_Dt );
+ fl4CreationFrameBias = MulSIMD( DivSIMD( Four_Ones, fl4_Dt ), fl4CreationFrameBias );
+ FourVectors v4ScaledDelta = v4Delta;
+ v4ScaledDelta *= fl4CreationFrameBias;
+
+ fltx4 fl4LockStrength = ReplicateX4( flStrength );
+ // ok, some of these particles should be moved
+ if ( bUseRange )
+ {
+ FourVectors ofs = *pXYZ;
+ ofs += v4ScaledDelta;
+ ofs -= v4ControlPoint;
+ fltx4 fl4Dist = ofs.length();
+ fl4Dist = BiasSIMD( MinSIMD( Four_Ones, MulSIMD( fl4Dist, fl4OORange ) ), fl4BiasParm );
+ v4ScaledDelta *= SubSIMD( Four_Ones, fl4Dist );
+ fl4LockStrength = SubSIMD( Four_Ones, MulSIMD ( fl4Dist, fl4LockStrength ) );
+ }
+ if ( m_bLockRot )
+ {
+ fl4LockStrength = MulSIMD( fl4LockStrength, fl4CreationFrameBias );
+ FourVectors fvCurPos = *pXYZ;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvCurPos.TransformBy( matTransformLock );
+ fvPrevPos.TransformBy( matTransformLock );
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4LockStrength;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4LockStrength;
+ *(pXYZ) += fvCurPos;
+ *(pPrevXYZ) += fvPrevPos;
+ }
+ else
+ {
+ *(pXYZ) += v4ScaledDelta;
+ *(pPrevXYZ) += v4ScaledDelta;
+ }
+ ++pCreationTime;
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+ }
+ else
+ {
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ fltx4 fl4StartRange = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4StartBias = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4EndRange = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+ fltx4 fl4EndBias = ReplicateX4( m_flEndTime_min );
+ int nSSEStartExponent = m_flStartTime_exp * 4.0;
+ int nSSEEndExponent = m_flEndTime_exp * 4.0;
+ do
+ {
+
+ fltx4 fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4CreationFrameBias = MinSIMD( fl4LifeTime, fl4_Dt );
+ fl4CreationFrameBias = MulSIMD( DivSIMD( Four_Ones, fl4_Dt ), fl4CreationFrameBias );
+
+ FourVectors v4ScaledDelta = v4Delta;
+ v4ScaledDelta *= fl4CreationFrameBias;
+
+ fl4LifeTime = MaxSIMD( Four_Zeros, MinSIMD( Four_Ones,
+ MulSIMD( fl4LifeTime, ReciprocalEstSIMD( *pLifeDuration ) ) ) );
+ fltx4 fl4StartTime = Pow_FixedPoint_Exponent_SIMD( RandSIMD( nContext ), nSSEStartExponent );
+ fl4StartTime = AddSIMD( fl4StartBias, MulSIMD( fl4StartTime, fl4StartRange ) );
+
+ fltx4 fl4EndTime = Pow_FixedPoint_Exponent_SIMD( RandSIMD( nContext ), nSSEEndExponent );
+ fl4EndTime = AddSIMD( fl4EndBias, MulSIMD( fl4EndTime, fl4EndRange ) );
+
+ // now, determine "lockedness"
+ fltx4 fl4LockScale = DivSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), SubSIMD( fl4EndTime, fl4StartTime ) );
+ fl4LockScale = SubSIMD( Four_Ones, MaxSIMD( Four_Zeros, MinSIMD( Four_Ones, fl4LockScale ) ) );
+ if ( IsAnyNegative( CmpGtSIMD( fl4LockScale, Four_Zeros ) ) )
+ {
+ //fl4LockScale = MulSIMD( fl4LockScale, fl4CreationFrameBias );
+ v4ScaledDelta *= fl4LockScale;
+ fltx4 fl4LockStrength = fl4LockScale ;
+ // ok, some of these particles should be moved
+ if ( bUseRange )
+ {
+ FourVectors ofs = *pXYZ;
+ ofs += v4ScaledDelta;
+ ofs -= v4ControlPoint;
+ fltx4 fl4Dist = ofs.length();
+ fl4Dist = BiasSIMD( MinSIMD( Four_Ones, MulSIMD( fl4Dist, fl4OORange ) ), fl4BiasParm );
+ v4ScaledDelta *= SubSIMD( Four_Ones, fl4Dist );
+ fl4LockStrength = SubSIMD( Four_Ones, MulSIMD ( fl4Dist, fl4LockStrength ) );
+ }
+ if ( m_bLockRot )
+ {
+ fl4LockStrength = MulSIMD( fl4LockStrength, fl4CreationFrameBias );
+ FourVectors fvCurPos = *pXYZ;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvCurPos.TransformBy( matTransformLock );
+ fvPrevPos.TransformBy( matTransformLock );
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4LockStrength;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4LockStrength;
+ *(pXYZ) += fvCurPos;
+ *(pPrevXYZ) += fvPrevPos;
+ }
+ else
+ {
+ *(pXYZ) += v4ScaledDelta;
+ *(pPrevXYZ) += v4ScaledDelta;
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+ }
+ // Store off the control point position for the next delta computation
+ pCtx->m_vPrevPosition = vecControlPoint;
+ pCtx->m_matPrevTransform = matCurrentTransform;
+ ReleaseSIMDRandContext( nContext );
+};
+#endif
+
+
+
+
+//-----------------------------------------------------------------------------
+// Controlpoint Light
+// Determines particle color/fakes lighting using the influence of control
+// points
+//-----------------------------------------------------------------------------
+class C_OP_ControlpointLight : public CParticleOperatorInstance
+{
+ float m_flScale;
+ LightDesc_t m_LightNode1, m_LightNode2, m_LightNode3, m_LightNode4;
+ int m_nControlPoint1, m_nControlPoint2, m_nControlPoint3, m_nControlPoint4;
+ Vector m_vecCPOffset1, m_vecCPOffset2, m_vecCPOffset3, m_vecCPOffset4;
+ float m_LightFiftyDist1, m_LightZeroDist1, m_LightFiftyDist2, m_LightZeroDist2,
+ m_LightFiftyDist3, m_LightZeroDist3, m_LightFiftyDist4, m_LightZeroDist4;
+ Color m_LightColor1, m_LightColor2, m_LightColor3, m_LightColor4;
+ bool m_bLightType1, m_bLightType2, m_bLightType3, m_bLightType4, m_bLightDynamic1,
+ m_bLightDynamic2, m_bLightDynamic3, m_bLightDynamic4, m_bUseNormal, m_bUseHLambert,
+ m_bLightActive1, m_bLightActive2, m_bLightActive3, m_bLightActive4,
+ m_bClampLowerRange, m_bClampUpperRange;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_ControlpointLight );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPoint1 ) | ( 1ULL << m_nControlPoint2 ) |
+ ( 1ULL << m_nControlPoint3 ) | ( 1ULL << m_nControlPoint4 );
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_LightNode1.m_Color[0] = m_LightColor1[0] / 255.0f;
+ m_LightNode1.m_Color[1] = m_LightColor1[1] / 255.0f;
+ m_LightNode1.m_Color[2] = m_LightColor1[2] / 255.0f;
+ m_LightNode2.m_Color[0] = m_LightColor2[0] / 255.0f;
+ m_LightNode2.m_Color[1] = m_LightColor2[1] / 255.0f;
+ m_LightNode2.m_Color[2] = m_LightColor2[2] / 255.0f;
+ m_LightNode3.m_Color[0] = m_LightColor3[0] / 255.0f;
+ m_LightNode3.m_Color[1] = m_LightColor3[1] / 255.0f;
+ m_LightNode3.m_Color[2] = m_LightColor3[2] / 255.0f;
+ m_LightNode4.m_Color[0] = m_LightColor4[0] / 255.0f;
+ m_LightNode4.m_Color[1] = m_LightColor4[1] / 255.0f;
+ m_LightNode4.m_Color[2] = m_LightColor4[2] / 255.0f;
+ m_LightNode1.m_Range = 0;
+ m_LightNode2.m_Range = 0;
+ m_LightNode3.m_Range = 0;
+ m_LightNode4.m_Range = 0;
+ m_LightNode1.m_Falloff=5.0;
+ m_LightNode2.m_Falloff=5.0;
+ m_LightNode3.m_Falloff=5.0;
+ m_LightNode4.m_Falloff=5.0;
+ m_LightNode1.m_Attenuation0 = 0;
+ m_LightNode1.m_Attenuation1 = 0;
+ m_LightNode1.m_Attenuation2 = 1;
+ m_LightNode2.m_Attenuation0 = 0;
+ m_LightNode2.m_Attenuation1 = 0;
+ m_LightNode2.m_Attenuation2 = 1;
+ m_LightNode3.m_Attenuation0 = 0;
+ m_LightNode3.m_Attenuation1 = 0;
+ m_LightNode3.m_Attenuation2 = 1;
+ m_LightNode4.m_Attenuation0 = 0;
+ m_LightNode4.m_Attenuation1 = 0;
+ m_LightNode4.m_Attenuation2 = 1;
+
+ if ( !m_bLightType1 )
+ {
+ m_LightNode1.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode1.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType2 )
+ {
+ m_LightNode2.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode2.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType3 )
+ {
+ m_LightNode3.m_Type = MATERIAL_LIGHT_POINT;
+ }
+
+ else
+ {
+ m_LightNode3.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType4 )
+ {
+ m_LightNode4.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode4.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightDynamic1 && ( m_LightColor1 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive1 = true;
+ }
+ else
+ {
+ m_bLightActive1 = false;
+ }
+ if ( !m_bLightDynamic2 && ( m_LightColor2 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive2 = true;
+ }
+ else
+ {
+ m_bLightActive2 = false;
+ }
+ if ( !m_bLightDynamic3 && ( m_LightColor3 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive3 = true;
+ }
+ else
+ {
+ m_bLightActive3 = false;
+ }
+ if ( !m_bLightDynamic4 && ( m_LightColor4 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive4 = true;
+ }
+ else
+ {
+ m_bLightActive4 = false;
+ }
+ m_LightNode1.SetupNewStyleAttenuation ( m_LightFiftyDist1, m_LightZeroDist1 );
+ m_LightNode2.SetupNewStyleAttenuation ( m_LightFiftyDist2, m_LightZeroDist2 );
+ m_LightNode3.SetupNewStyleAttenuation ( m_LightFiftyDist3, m_LightZeroDist3 );
+ m_LightNode4.SetupNewStyleAttenuation ( m_LightFiftyDist4, m_LightZeroDist4 );
+
+ }
+
+ void Render( CParticleCollection *pParticles ) const;
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ControlpointLight, "Color Light from Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ControlpointLight )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Control Point", "0", int, m_nControlPoint1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Control Point Offset", "0 0 0", Vector, m_vecCPOffset1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Type 0=Point 1=Spot", "0", bool, m_bLightType1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Color", "0 0 0 255", Color, m_LightColor1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Dynamic Light", "0", bool, m_bLightDynamic1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Direction", "0 0 0", Vector, m_LightNode1.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 50% Distance", "100", float, m_LightFiftyDist1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 0% Distance", "200", float, m_LightZeroDist1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Spot Inner Cone", "30.0", float, m_LightNode1.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Spot Outer Cone", "45.0", float, m_LightNode1.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Control Point", "0", int, m_nControlPoint2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Control Point Offset", "0 0 0", Vector, m_vecCPOffset2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Type 0=Point 1=Spot", "0", bool, m_bLightType2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Color", "0 0 0 255", Color, m_LightColor2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Dynamic Light", "0", bool, m_bLightDynamic2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Direction", "0 0 0", Vector, m_LightNode2.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 50% Distance", "100", float, m_LightFiftyDist2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 0% Distance", "200", float, m_LightZeroDist2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Spot Inner Cone", "30.0", float, m_LightNode2.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Spot Outer Cone", "45.0", float, m_LightNode2.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Control Point", "0", int, m_nControlPoint3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Control Point Offset", "0 0 0", Vector, m_vecCPOffset3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Type 0=Point 1=Spot", "0", bool, m_bLightType3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Color", "0 0 0 255", Color, m_LightColor3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Dynamic Light", "0", bool, m_bLightDynamic3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Direction", "0 0 0", Vector, m_LightNode3.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 50% Distance", "100", float, m_LightFiftyDist3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 0% Distance", "200", float, m_LightZeroDist3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Spot Inner Cone", "30.0", float, m_LightNode3.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Spot Outer Cone", "45.0", float, m_LightNode3.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Control Point", "0", int, m_nControlPoint4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Control Point Offset", "0 0 0", Vector, m_vecCPOffset4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Type 0=Point 1=Spot", "0", bool, m_bLightType4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Color", "0 0 0 255", Color, m_LightColor4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Dynamic Light", "0", bool, m_bLightDynamic4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Direction", "0 0 0", Vector, m_LightNode4.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 50% Distance", "100", float, m_LightFiftyDist4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 0% Distance", "200", float, m_LightZeroDist4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Spot Inner Cone", "30.0", float, m_LightNode4.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Spot Outer Cone", "45.0", float, m_LightNode4.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Initial Color Bias", "0.0", float, m_flScale )
+ DMXELEMENT_UNPACK_FIELD( "Clamp Minimum Light Value to Initial Color", "0", bool, m_bClampLowerRange )
+ DMXELEMENT_UNPACK_FIELD( "Clamp Maximum Light Value to Initial Color", "0", bool, m_bClampUpperRange )
+ DMXELEMENT_UNPACK_FIELD( "Compute Normals From Control Points", "0", bool, m_bUseNormal )
+ DMXELEMENT_UNPACK_FIELD( "Half-Lambert Normals", "1", bool, m_bUseHLambert )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ControlpointLight )
+
+void C_OP_ControlpointLight::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ //Set up location of each light - this needs to be done every time as the CP's can move
+ Vector vecLocation1, vecLocation2, vecLocation3, vecLocation4;
+ vecLocation1 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint1 );
+ vecLocation2 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint2 );
+ vecLocation3 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint3 );
+ vecLocation4 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint4 );
+
+
+ LightDesc_t LightNode1 = m_LightNode1;
+ LightDesc_t LightNode2 = m_LightNode2;
+ LightDesc_t LightNode3 = m_LightNode3;
+ LightDesc_t LightNode4 = m_LightNode3;
+
+ // Apply any offsets
+ LightNode1.m_Position = vecLocation1 + m_vecCPOffset1;
+ LightNode2.m_Position = vecLocation2 + m_vecCPOffset2;
+ LightNode3.m_Position = vecLocation3 + m_vecCPOffset3;
+ LightNode4.m_Position = vecLocation4 + m_vecCPOffset4;
+
+
+ C4VAttributeIterator pInitialColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ C4VAttributeWriteIterator pColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+
+ // Set up lighting conditions and attenuation
+ if ( m_bLightDynamic1 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode1.m_Position, lc );
+ LightNode1.m_Color[0] = lc[0] / 255.0f;
+ LightNode1.m_Color[1] = lc[1] / 255.0f;
+ LightNode1.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic2 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode2.m_Position, lc );
+ LightNode2.m_Color[0] = lc[0] / 255.0f;
+ LightNode2.m_Color[1] = lc[1] / 255.0f;
+ LightNode2.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic3 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode3.m_Position, lc );
+ LightNode3.m_Color[0] = lc[0] / 255.0f;
+ LightNode3.m_Color[1] = lc[1] / 255.0f;
+ LightNode3.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic4 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode4.m_Position, lc );
+ LightNode4.m_Color[0] = lc[0] / 255.0f;
+ LightNode4.m_Color[1] = lc[1] / 255.0f;
+ LightNode4.m_Color[2] = lc[2] / 255.0f;
+ }
+ LightNode1.RecalculateDerivedValues();
+ LightNode2.RecalculateDerivedValues();
+ LightNode3.RecalculateDerivedValues();
+ LightNode4.RecalculateDerivedValues();
+
+ FourVectors vScale;
+ vScale.DuplicateVector( Vector(m_flScale, m_flScale, m_flScale) );
+
+ if ( m_bUseNormal )
+ {
+ FourVectors vCPPosition1, vCPPosition2, vCPPosition3, vCPPosition4;
+ //vCPPosition1.DuplicateVector( LightNode1.m_Position );
+ vCPPosition1.DuplicateVector( vecLocation1 );
+ vCPPosition2.DuplicateVector( vecLocation2 );
+ vCPPosition3.DuplicateVector( vecLocation3 );
+ vCPPosition4.DuplicateVector( vecLocation4 );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors vLighting = vScale;
+ vLighting *= *pInitialColor;
+ FourVectors vNormal = *pXYZ;
+ vNormal -= vCPPosition1;
+ vNormal.VectorNormalizeFast();
+ LightNode1.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition2;
+ vNormal.VectorNormalizeFast();
+ LightNode2.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition3;
+ vNormal.VectorNormalizeFast();
+ LightNode3.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition4;
+ vNormal.VectorNormalizeFast();
+ LightNode4.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+
+ if ( m_bClampLowerRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MaxSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MaxSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MaxSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MaxSIMD( vLighting.x, Four_Zeros );
+ vLighting.y = MaxSIMD( vLighting.y, Four_Zeros );
+ vLighting.z = MaxSIMD( vLighting.z, Four_Zeros );
+ }
+ if ( m_bClampUpperRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MinSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MinSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MinSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MinSIMD( vLighting.x, Four_Ones );
+ vLighting.y = MinSIMD( vLighting.y, Four_Ones );
+ vLighting.z = MinSIMD( vLighting.z, Four_Ones );
+ }
+
+ *pColor = vLighting;
+
+ ++pColor;
+ ++pXYZ;
+ ++pInitialColor;
+ } while (--nCtr);
+ }
+ else
+ {
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors vLighting = vScale;
+ vLighting *= *pInitialColor;
+
+ LightNode1.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode2.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode3.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode4.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+
+
+ if ( m_bClampLowerRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MaxSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MaxSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MaxSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MaxSIMD( vLighting.x, Four_Zeros );
+ vLighting.y = MaxSIMD( vLighting.y, Four_Zeros );
+ vLighting.z = MaxSIMD( vLighting.z, Four_Zeros );
+ }
+ if ( m_bClampUpperRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MinSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MinSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MinSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MinSIMD( vLighting.x, Four_Ones );
+ vLighting.y = MinSIMD( vLighting.y, Four_Ones );
+ vLighting.z = MinSIMD( vLighting.z, Four_Ones );
+ }
+
+
+ *pColor = vLighting;
+
+ ++pColor;
+ ++pXYZ;
+ ++pInitialColor;
+ } while (--nCtr);
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Render visualization
+//-----------------------------------------------------------------------------
+void C_OP_ControlpointLight::Render( CParticleCollection *pParticles ) const
+{
+ Vector vecOrigin1 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint1 );
+ vecOrigin1 += m_vecCPOffset1;
+ Vector vecOrigin2 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint2 );
+ vecOrigin2 += m_vecCPOffset2;
+ Vector vecOrigin3 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint3 );
+ vecOrigin3 += m_vecCPOffset3;
+ Vector vecOrigin4 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint4 );
+ vecOrigin4 += m_vecCPOffset4;
+
+ Color LightColor1Outer;
+ LightColor1Outer[0] = m_LightColor1[0] / 2.0f;
+ LightColor1Outer[1] = m_LightColor1[1] / 2.0f;
+ LightColor1Outer[2] = m_LightColor1[2] / 2.0f;
+ LightColor1Outer[3] = 255;
+ Color LightColor2Outer;
+ LightColor2Outer[0] = m_LightColor2[0] / 2.0f;
+ LightColor2Outer[1] = m_LightColor2[1] / 2.0f;
+ LightColor2Outer[2] = m_LightColor2[2] / 2.0f;
+ LightColor2Outer[3] = 255;
+ Color LightColor3Outer;
+ LightColor3Outer[0] = m_LightColor3[0] / 2.0f;
+ LightColor3Outer[1] = m_LightColor3[1] / 2.0f;
+ LightColor3Outer[2] = m_LightColor3[2] / 2.0f;
+ LightColor3Outer[3] = 255;
+ Color LightColor4Outer;
+ LightColor4Outer[0] = m_LightColor4[0] / 2.0f;
+ LightColor4Outer[1] = m_LightColor4[1] / 2.0f;
+ LightColor4Outer[2] = m_LightColor4[2] / 2.0f;
+ LightColor4Outer[3] = 255;
+ if ( m_bLightActive1 )
+ {
+ RenderWireframeSphere( vecOrigin1, m_LightFiftyDist1, 16, 8, m_LightColor1, false );
+ RenderWireframeSphere( vecOrigin1, m_LightZeroDist1, 16, 8, LightColor1Outer, false );
+ }
+ if ( m_bLightActive2 )
+ {
+ RenderWireframeSphere( vecOrigin2, m_LightFiftyDist2, 16, 8, m_LightColor2, false );
+ RenderWireframeSphere( vecOrigin2, m_LightZeroDist2, 16, 8, LightColor2Outer, false );
+ }
+ if ( m_bLightActive3 )
+ {
+ RenderWireframeSphere( vecOrigin3, m_LightFiftyDist3, 16, 8, m_LightColor3, false );
+ RenderWireframeSphere( vecOrigin3, m_LightZeroDist3, 16, 8, LightColor3Outer, false );
+ }
+ if ( m_bLightActive4 )
+ {
+ RenderWireframeSphere( vecOrigin4, m_LightFiftyDist4, 16, 8, m_LightColor4, false );
+ RenderWireframeSphere( vecOrigin4, m_LightZeroDist4, 16, 8, LightColor4Outer, false );
+ }
+
+}
+
+
+
+// set child controlpoints - copy the positions of our particles to the control points of a child
+class C_OP_SetChildControlPoints : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetChildControlPoints );
+
+ int m_nChildGroupID;
+ int m_nFirstControlPoint;
+ int m_nNumControlPoints;
+ int m_nFirstSourcePoint;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetChildControlPoints, "Set child control points from particle positions", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetChildControlPoints )
+ DMXELEMENT_UNPACK_FIELD( "Group ID to affect", "0", int, m_nChildGroupID )
+ DMXELEMENT_UNPACK_FIELD( "First control point to set", "0", int, m_nFirstControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "# of control points to set", "1", int, m_nNumControlPoints )
+ DMXELEMENT_UNPACK_FIELD( "first particle to copy", "0", int, m_nFirstSourcePoint )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetChildControlPoints )
+
+
+void C_OP_SetChildControlPoints::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ int nFirst=max(0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nFirstControlPoint ) );
+ int nToSet=min( pParticles->m_nActiveParticles-m_nFirstSourcePoint, m_nNumControlPoints );
+ nToSet=min( nToSet, MAX_PARTICLE_CONTROL_POINTS-nFirst );
+ if ( nToSet )
+ {
+ for( CParticleCollection *pChild = pParticles->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
+ {
+ if ( pChild->GetGroupID() == m_nChildGroupID )
+ {
+ for( int p=0; p < nToSet; p++ )
+ {
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, p + m_nFirstSourcePoint );
+ Vector cPnt( pXYZ[0], pXYZ[4], pXYZ[8] );
+ pChild->SetControlPoint( p+nFirst, cPnt );
+ }
+ }
+ }
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Set Control Point Positions
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointPositions : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointPositions );
+
+ bool m_bUseWorldLocation;
+ int m_nCP1, m_nCP1Parent;
+ int m_nCP2, m_nCP2Parent;
+ int m_nCP3, m_nCP3Parent;
+ int m_nCP4, m_nCP4Parent;
+ Vector m_vecCP1Pos, m_vecCP2Pos, m_vecCP3Pos, m_vecCP4Pos;
+ int m_nHeadLocation;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointPositions, "Set Control Point Positions", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointPositions )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Number", "1", int, m_nCP1 )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Parent", "0", int, m_nCP1Parent )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Location", "128 0 0", Vector, m_vecCP1Pos )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Number", "2", int, m_nCP2 )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Parent", "0", int, m_nCP2Parent )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Location", "0 128 0", Vector, m_vecCP2Pos )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Number", "3", int, m_nCP3 )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Parent", "0", int, m_nCP3Parent )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Location", "-128 0 0", Vector, m_vecCP3Pos )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Number", "4", int, m_nCP4 )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Parent", "0", int, m_nCP4Parent )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Location", "0 -128 0", Vector, m_vecCP4Pos )
+ DMXELEMENT_UNPACK_FIELD( "Set positions in world space", "0", bool, m_bUseWorldLocation )
+ DMXELEMENT_UNPACK_FIELD( "Control Point to offset positions from", "0", int, m_nHeadLocation )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointPositions )
+
+void C_OP_SetControlPointPositions::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( !m_bUseWorldLocation )
+ {
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nHeadLocation );
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nHeadLocation, pParticles->m_flCurTime, &mat );
+ Vector vecTransformLocal = vec3_origin;
+
+ VectorTransform( m_vecCP1Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP1, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP1, m_nCP1Parent );
+
+ VectorTransform( m_vecCP2Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP2, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP2, m_nCP2Parent );
+
+ VectorTransform( m_vecCP3Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP3, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP3, m_nCP3Parent );
+
+ VectorTransform( m_vecCP4Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP4, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP4, m_nCP4Parent );
+ }
+ else
+ {
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos );
+ pParticles->SetControlPointParent( m_nCP1, m_nCP1Parent );
+ pParticles->SetControlPoint( m_nCP2, m_vecCP2Pos );
+ pParticles->SetControlPointParent( m_nCP2, m_nCP2Parent );
+ pParticles->SetControlPoint( m_nCP3, m_vecCP3Pos );
+ pParticles->SetControlPointParent( m_nCP3, m_nCP3Parent );
+ pParticles->SetControlPoint( m_nCP4, m_vecCP4Pos );
+ pParticles->SetControlPointParent( m_nCP4, m_nCP4Parent );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Dampen Movement Relative to Control Point
+// The closer a particle is the the assigned control point, the less
+// it can move
+//-----------------------------------------------------------------------------
+class C_OP_DampenToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DampenToCP );
+
+ int m_nControlPointNumber;
+ float m_flRange, m_flScale;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPointNumber );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DampenToCP , "Movement Dampen Relative to Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DampenToCP )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "falloff range", "100", float, m_flRange )
+DMXELEMENT_UNPACK_FIELD( "dampen scale", "1", float, m_flScale )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DampenToCP )
+
+void C_OP_DampenToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_flRange <= 0.0f )
+ return;
+
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecParticlePosition, vecParticlePosition_prev, vParticleDelta ;
+
+ SetVectorFromAttribute( vecParticlePosition, xyz );
+ SetVectorFromAttribute( vecParticlePosition_prev, xyz_prev );
+ Vector ofs;
+ ofs = vecParticlePosition - vecControlPoint;
+ float flDistance = ofs.Length();
+ float flDampenAmount;
+ if ( flDistance > m_flRange )
+ {
+ continue;
+ }
+ else
+ {
+ flDampenAmount = flDistance / m_flRange;
+ flDampenAmount = pow( flDampenAmount, m_flScale);
+ }
+
+ vParticleDelta = vecParticlePosition - vecParticlePosition_prev;
+ Vector vParticleDampened = vParticleDelta * flDampenAmount;
+ vecParticlePosition = vecParticlePosition_prev + vParticleDampened;
+ Vector vecParticlePositionOrg;
+ SetVectorFromAttribute( vecParticlePositionOrg, xyz );
+ VectorLerp (vecParticlePositionOrg, vecParticlePosition, flStrength, vecParticlePosition );
+ SetVectorAttribute( xyz, vecParticlePosition );
+ }
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Distance Between CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_DistanceBetweenCPs : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DistanceBetweenCPs );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nStartCP ) | ( 1ULL << m_nEndCP );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nStartCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartCP ) );
+ m_nEndCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndCP ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ int m_nStartCP;
+ int m_nEndCP;
+ bool m_bLOS;
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ float m_flMaxTraceLength;
+ float m_flLOSScale;
+ bool m_bScaleInitialRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DistanceBetweenCPs, "Remap Distance Between Two Control Points to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceBetweenCPs )
+DMXELEMENT_UNPACK_FIELD( "distance minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "distance maximum","128", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "starting control point","0", int, m_nStartCP )
+DMXELEMENT_UNPACK_FIELD( "ending control point","1", int, m_nEndCP )
+DMXELEMENT_UNPACK_FIELD( "ensure line of sight","0", bool, m_bLOS )
+DMXELEMENT_UNPACK_FIELD_STRING( "LOS collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "Maximum Trace Length", "-1", float, m_flMaxTraceLength )
+DMXELEMENT_UNPACK_FIELD( "LOS Failure Scalar", "0", float, m_flLOSScale )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceBetweenCPs )
+
+void C_OP_DistanceBetweenCPs::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint1 = pParticles->GetControlPointAtCurrentTime( m_nStartCP );
+ Vector vecControlPoint2 = pParticles->GetControlPointAtCurrentTime( m_nEndCP );
+ Vector vecDelta = vecControlPoint1 - vecControlPoint2;
+ float flDistance = vecDelta.Length();
+
+
+ if ( m_bLOS )
+ {
+ Vector vecEndPoint = vecControlPoint2;
+ if ( m_flMaxTraceLength != -1.0f && m_flMaxTraceLength < flDistance )
+ {
+ VectorNormalize(vecEndPoint);
+ vecEndPoint *= m_flMaxTraceLength;
+ vecEndPoint += vecControlPoint1;
+ }
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecControlPoint1, vecEndPoint, MASK_OPAQUE_AND_NPCS, NULL, m_nCollisionGroupNumber, &tr );
+ if (tr.fraction != 1.0f)
+ {
+ flDistance *= tr.fraction * m_flLOSScale;
+ }
+
+ }
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ const float *pInitialOutput = pParticles->GetInitialFloatAttributePtr( m_nFieldOutput, i );
+ flOutput = *pInitialOutput * flOutput;
+ }
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Distance to CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_DistanceToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DistanceToCP );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nStartCP ) | ( 1ULL << m_nEndCP );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nStartCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartCP ) );
+ m_nEndCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndCP ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ int m_nStartCP;
+ int m_nEndCP;
+ bool m_bLOS;
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ float m_flMaxTraceLength;
+ float m_flLOSScale;
+ bool m_bScaleInitialRange;
+ bool m_bActiveRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DistanceToCP, "Remap Distance to Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceToCP )
+DMXELEMENT_UNPACK_FIELD( "distance minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "distance maximum","128", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "control point","0", int, m_nStartCP )
+DMXELEMENT_UNPACK_FIELD( "ensure line of sight","0", bool, m_bLOS )
+DMXELEMENT_UNPACK_FIELD_STRING( "LOS collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "Maximum Trace Length", "-1", float, m_flMaxTraceLength )
+DMXELEMENT_UNPACK_FIELD( "LOS Failure Scalar", "0", float, m_flLOSScale )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+DMXELEMENT_UNPACK_FIELD( "only active within specified distance","0", bool, m_bActiveRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceToCP )
+
+void C_OP_DistanceToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint1 = pParticles->GetControlPointAtCurrentTime( m_nStartCP );
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ Vector vecPosition2;
+ const float *pXYZ = pParticles->GetFloatAttributePtr(PARTICLE_ATTRIBUTE_XYZ, i );
+ vecPosition2 = Vector(pXYZ[0], pXYZ[4], pXYZ[8]);
+ Vector vecDelta = vecControlPoint1 - vecPosition2;
+ float flDistance = vecDelta.Length();
+ if ( m_bActiveRange && ( flDistance < m_flInputMin || flDistance > m_flInputMax ) )
+ {
+ continue;
+ }
+ if ( m_bLOS )
+ {
+ Vector vecEndPoint = vecPosition2;
+ if ( m_flMaxTraceLength != -1.0f && m_flMaxTraceLength < flDistance )
+ {
+ VectorNormalize(vecEndPoint);
+ vecEndPoint *= m_flMaxTraceLength;
+ vecEndPoint += vecControlPoint1;
+ }
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecControlPoint1, vecEndPoint, MASK_OPAQUE_AND_NPCS, NULL , m_nCollisionGroupNumber, &tr );
+ if (tr.fraction != 1.0f)
+ {
+ flDistance *= tr.fraction * m_flLOSScale;
+ }
+
+ }
+
+ float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ const float *pInitialOutput = pParticles->GetInitialFloatAttributePtr( m_nFieldOutput, i );
+ flOutput = *pInitialOutput * flOutput;
+ }
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ //float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ //float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ //*pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Assign CP to Player
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointToPlayer : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointToPlayer );
+
+ int m_nCP1;
+
+ Vector m_vecCP1Pos;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCP1 = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nCP1 ) );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointToPlayer, "Set Control Point To Player", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToPlayer )
+DMXELEMENT_UNPACK_FIELD( "Control Point Number", "1", int, m_nCP1 )
+DMXELEMENT_UNPACK_FIELD( "Control Point Offset", "0 0 0", Vector, m_vecCP1Pos )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToPlayer )
+
+void C_OP_SetControlPointToPlayer::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecClientPos =g_pParticleSystemMgr->Query()->GetLocalPlayerPos();
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos + vecClientPos );
+ Vector vecForward;
+ Vector vecRight;
+ Vector vecUp;
+ g_pParticleSystemMgr->Query()->GetLocalPlayerEyeVectors( &vecForward, &vecRight, &vecUp);
+ pParticles->SetControlPointOrientation( m_nCP1, vecForward, vecRight, vecUp );
+}
+
+
+
+
+
+//-------------------------
+// Emits particles from particles
+//NOT FINISHED
+//-------------------------
+class C_OP_PerParticleEmitter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_PerParticleEmitter );
+
+ struct C_OP_PerParticleEmitterContext_t
+ {
+ float m_flTotalActualParticlesSoFar;
+ int m_nTotalEmittedSoFar;
+ bool m_bStoppedEmission;
+ };
+
+ int m_nChildGroupID;
+ bool m_bInheritVelocity;
+ float m_flEmitRate;
+ float m_flVelocityScale;
+ float m_flStartTime;
+ float m_flEmissionDuration;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ | PARTICLE_ATTRIBUTE_PREV_XYZ;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_flEmitRate < 0.0f )
+ {
+ m_flEmitRate = 0.0f;
+ }
+ if ( m_flEmissionDuration < 0.0f )
+ {
+ m_flEmissionDuration = 0.0f;
+ }
+ }
+
+ inline bool IsInfinitelyEmitting() const
+ {
+ return ( m_flEmissionDuration == 0.0f );
+ }
+
+ virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx = reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ if ( pCtx->m_bStoppedEmission )
+ return false;
+
+ if ( m_flEmitRate <= 0.0f )
+ return false;
+
+ if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( m_flStartTime + m_flEmissionDuration ) )
+ return false;
+
+ return true;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx=reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ pCtx->m_flTotalActualParticlesSoFar = 0.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_bStoppedEmission = false;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PerParticleEmitter, "Per Particle Emitter", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PerParticleEmitter )
+DMXELEMENT_UNPACK_FIELD( "Group ID to affect", "1", int, m_nChildGroupID )
+DMXELEMENT_UNPACK_FIELD( "Inherit Velocity", "0", int, m_bInheritVelocity )
+DMXELEMENT_UNPACK_FIELD( "Emission Rate", "100", float, m_flEmitRate )
+DMXELEMENT_UNPACK_FIELD( "Velocity Scale", "0", int, m_flVelocityScale )
+DMXELEMENT_UNPACK_FIELD( "Emission Start Time", "0", float, m_flStartTime )
+DMXELEMENT_UNPACK_FIELD( "Emission Duration", "0", float, m_flEmissionDuration )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PerParticleEmitter )
+
+
+void C_OP_PerParticleEmitter::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ for( CParticleCollection *pChild = pParticles->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
+ {
+ if ( pChild->GetGroupID() == m_nChildGroupID )
+ {
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx=reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *pXYZ_Prev = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Vector vecParticlePosition, vecParticlePosition_prev, vParticleDelta ;
+
+ vecParticlePosition = Vector ( pXYZ[0], pXYZ[4], pXYZ[8] );
+ vecParticlePosition_prev = Vector ( pXYZ_Prev[0], pXYZ_Prev[4], pXYZ_Prev[8] );
+ vParticleDelta = vecParticlePosition - vecParticlePosition_prev;
+
+ float flEmissionRate = m_flEmitRate * flStrength;
+
+ if ( m_flVelocityScale != 0.0f )
+ {
+ float flVelocity = vParticleDelta.Length();
+ flEmissionRate *= flVelocity * m_flVelocityScale * pParticles->m_flDt;
+ }
+
+ if ( flEmissionRate == 0.0f )
+ continue;
+
+ if ( !C_OP_PerParticleEmitter::MayCreateMoreParticles( pChild, pContext ) )
+ continue;
+
+ 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 < m_flStartTime )
+ {
+ flPrevDrawTime = m_flStartTime;
+ }
+ if ( flCurrDrawTime > m_flStartTime + m_flEmissionDuration )
+ {
+ flCurrDrawTime = m_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;
+ int nParticlesEmitted = pCtx->m_nTotalEmittedSoFar;
+ //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 )
+ continue;
+
+ // 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 = pChild->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
+ if ( nAllowedParticlesToEmit < nParticlesToEmit )
+ {
+ nActualParticlesToEmit = nAllowedParticlesToEmit;
+ }
+ if ( nActualParticlesToEmit == 0 )
+ continue;
+
+ int nStartParticle = pChild->m_nActiveParticles;
+ pChild->SetNActiveParticles( nActualParticlesToEmit + pChild->m_nActiveParticles );
+
+
+ float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
+ float flTimeStep = flPrevDrawTime + flTimeStampStep;
+ Vector vecMoveStampStep = vParticleDelta / nActualParticlesToEmit ;
+ Vector vecMoveStep = vecParticlePosition_prev + vecMoveStampStep ;
+
+ if ( nParticlesEmitted != pChild->m_nActiveParticles )
+ {
+
+ uint32 nInittedMask = ( PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK );
+ // init newly emitted particles
+ pChild->InitializeNewParticles( nParticlesEmitted, pChild->m_nActiveParticles - nParticlesEmitted, nInittedMask );
+ //CHECKSYSTEM( this );
+ }
+
+ // Set the particle creation time to the exact sub-frame particle emission time
+ // !! speed!! do sse init here
+ for( int j = nStartParticle; j < nStartParticle + nActualParticlesToEmit; j++ )
+ {
+ float *pTimeStamp = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, j );
+ flTimeStep = min( flTimeStep, flCurrDrawTime );
+ *pTimeStamp = flTimeStep;
+ flTimeStep += flTimeStampStep;
+ float *pXYZ_Child = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, j );
+ float *pXYZ_Prev_Child = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, j );
+ Vector vecChildXYZ;
+ SetVectorFromAttribute ( vecChildXYZ, pXYZ_Child);
+ vecChildXYZ = vecMoveStep;
+ SetVectorAttribute ( pXYZ_Child, vecChildXYZ);
+ vecMoveStep += vecMoveStampStep;
+ if ( m_bInheritVelocity )
+ {
+ *pXYZ_Prev_Child = *pXYZ_Prev;
+ }
+ else
+ {
+ *pXYZ_Prev_Child = *pXYZ_Child;
+ }
+
+ }
+
+ }
+ }
+ }
+}
+
+
+class C_OP_LockToBone : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_LockToBone );
+
+ int m_nControlPointNumber;
+ float m_flLifeTimeFadeStart;
+ float m_flLifeTimeFadeEnd;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ int ret= PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK |
+ PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK | PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK;
+ ret |= PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ return ret;
+
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPointNumber );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_LockToBone , "Movement Lock to Bone", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_LockToBone )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "lifetime fade start", "0", float, m_flLifeTimeFadeStart )
+ DMXELEMENT_UNPACK_FIELD( "lifetime fade end", "0", float, m_flLifeTimeFadeEnd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_LockToBone )
+
+void C_OP_LockToBone::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ pParticles->UpdateHitBoxInfo( m_nControlPointNumber );
+ if ( pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].CurAndPrevValid() )
+ {
+ float flAgeThreshold = m_flLifeTimeFadeEnd;
+ if ( flAgeThreshold <= 0.0 )
+ flAgeThreshold = 1.0e20;
+ float flIScale = 0.0;
+ if ( m_flLifeTimeFadeEnd > m_flLifeTimeFadeStart )
+ flIScale = 1.0/( m_flLifeTimeFadeEnd - m_flLifeTimeFadeStart );
+
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *pXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *pPrevXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ const float *pUVW = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ, i );
+ const int nBoxIndex = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_HITBOX_INDEX, i );
+ float const *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+
+ float flAge = pParticles->m_flCurTime -*pCreationTime;
+
+ if ( flAge < flAgeThreshold )
+ {
+ if (
+ ( nBoxIndex < pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_nNumHitBoxes ) &&
+ ( nBoxIndex < pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_nNumPrevHitBoxes ) &&
+ ( nBoxIndex >= 0 )
+ )
+ {
+ Vector vecParticlePosition;
+ ModelHitBoxInfo_t const &hb = pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_pHitBoxes[ nBoxIndex ];
+ vecParticlePosition.x = Lerp( pUVW[0], hb.m_vecBoxMins.x, hb.m_vecBoxMaxes.x );
+ vecParticlePosition.y = Lerp( pUVW[4], hb.m_vecBoxMins.y, hb.m_vecBoxMaxes.y );
+ vecParticlePosition.z = Lerp( pUVW[8], hb.m_vecBoxMins.z, hb.m_vecBoxMaxes.z );
+ Vector vecWorldPosition;
+ VectorTransform( vecParticlePosition, hb.m_Transform, vecWorldPosition );
+
+ Vector vecPrevParticlePosition;
+ ModelHitBoxInfo_t phb = pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_pPrevBoxes[ nBoxIndex ];
+ vecPrevParticlePosition.x = Lerp( pUVW[0], phb.m_vecBoxMins.x, phb.m_vecBoxMaxes.x );
+ vecPrevParticlePosition.y = Lerp( pUVW[4], phb.m_vecBoxMins.y, phb.m_vecBoxMaxes.y );
+ vecPrevParticlePosition.z = Lerp( pUVW[8], phb.m_vecBoxMins.z, phb.m_vecBoxMaxes.z );
+ Vector vecPrevWorldPosition;
+ VectorTransform( vecPrevParticlePosition, phb.m_Transform, vecPrevWorldPosition );
+
+ Vector Delta = vecWorldPosition-vecPrevWorldPosition;
+
+ if ( flAge > m_flLifeTimeFadeStart )
+ Delta *= flStrength * ( 1.0- ( ( flAge - m_flLifeTimeFadeStart ) * flIScale ) );
+
+ Vector xyz;
+ SetVectorFromAttribute( xyz, pXYZ );
+ xyz += Delta;
+ SetVectorAttribute( pXYZ, xyz );
+
+ Vector prevxyz;
+ SetVectorFromAttribute( prevxyz, pPrevXYZ );
+ prevxyz += Delta;
+ SetVectorAttribute( pPrevXYZ, prevxyz );
+ }
+ }
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Plane Cull Operator - cull particles on the "wrong" side of a plane
+//-----------------------------------------------------------------------------
+class C_OP_PlaneCull : public CParticleOperatorInstance
+{
+ int m_nPlaneControlPoint;
+ Vector m_vecPlaneDirection;
+ float m_flPlaneOffset;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_PlaneCull );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nPlaneControlPoint );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PlaneCull, "Cull when crossing plane", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PlaneCull )
+ DMXELEMENT_UNPACK_FIELD( "Control Point for point on plane", "0", int, m_nPlaneControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "Cull plane offset", "0", float, m_flPlaneOffset )
+ DMXELEMENT_UNPACK_FIELD( "Plane Normal", "0 0 1", Vector, m_vecPlaneDirection )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PlaneCull )
+
+void C_OP_PlaneCull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ // setup vars
+ FourVectors v4N ;
+ v4N.DuplicateVector( m_vecPlaneDirection );
+ v4N.VectorNormalize();
+ FourVectors v4Pnt;
+ v4Pnt.DuplicateVector( pParticles->GetControlPointAtCurrentTime( m_nPlaneControlPoint ) );
+ FourVectors ofs = v4N;
+ ofs *= ReplicateX4( m_flPlaneOffset );
+ v4Pnt -= ofs;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ FourVectors f4PlaneRel = (*pXYZ );
+ f4PlaneRel -= v4Pnt;
+ fltx4 fl4PlaneEq = ( f4PlaneRel * v4N );
+ if ( IsAnyNegative( fl4PlaneEq ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4PlaneEq );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+
+ }
+ ++pXYZ;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Model Cull Operator - cull particles inside or outside of a brush/animated model
+//-----------------------------------------------------------------------------
+class C_OP_ModelCull : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ModelCull );
+
+ int m_nControlPointNumber;
+ bool m_bBoundBox;
+ bool m_bCullOutside;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ModelCull , "Cull relative to model", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ModelCull )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "use only bounding box", "0", bool, m_bBoundBox )
+DMXELEMENT_UNPACK_FIELD( "cull outside instead of inside", "0", bool, m_bCullOutside )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ModelCull )
+
+void C_OP_ModelCull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ pParticles->UpdateHitBoxInfo( m_nControlPointNumber );
+ if ( pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].CurAndPrevValid() )
+ {
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *pXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ Vector vecParticlePosition;
+
+ SetVectorFromAttribute( vecParticlePosition, pXYZ );
+
+ bool bInside = g_pParticleSystemMgr->Query()->IsPointInControllingObjectHitBox( pParticles, m_nControlPointNumber, vecParticlePosition, m_bBoundBox );
+ if ( ( bInside && m_bCullOutside ) || ( !bInside && !m_bCullOutside ))
+ continue;
+
+ pParticles->KillParticle(i);
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Assign CP to Center
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointToCenter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointToCenter );
+
+ int m_nCP1;
+
+ Vector m_vecCP1Pos;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCP1 = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nCP1 ) );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointToCenter, "Set Control Point To Particles' Center", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToCenter )
+DMXELEMENT_UNPACK_FIELD( "Control Point Number to Set", "1", int, m_nCP1 )
+DMXELEMENT_UNPACK_FIELD( "Center Offset", "0 0 0", Vector, m_vecCP1Pos )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToCenter )
+
+void C_OP_SetControlPointToCenter::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ Vector vecMinBounds;
+ Vector vecMaxBounds;
+
+ pParticles->GetBounds( &vecMinBounds, &vecMaxBounds );
+
+ Vector vecCenter = ( ( vecMinBounds + vecMaxBounds ) / 2 );
+
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos + vecCenter );
+}
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Velocity Match a group of particles
+//-----------------------------------------------------------------------------
+class C_OP_VelocityMatchingForce : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VelocityMatchingForce );
+
+ float m_flDirScale;
+ float m_flSpdScale;
+ int m_nCPBroadcast;
+
+ struct VelocityMatchingForceContext_t
+ {
+ Vector m_vecAvgVelocity;
+ float m_flAvgSpeed;
+ };
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ VelocityMatchingForceContext_t *pCtx = reinterpret_cast<VelocityMatchingForceContext_t *>( pContext );
+ pCtx->m_vecAvgVelocity = vec3_origin;
+ pCtx->m_flAvgSpeed = 0;
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( VelocityMatchingForceContext_t );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VelocityMatchingForce , "Movement Match Particle Velocities", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityMatchingForce )
+DMXELEMENT_UNPACK_FIELD( "Direction Matching Strength", "0.25", float, m_flDirScale )
+DMXELEMENT_UNPACK_FIELD( "Speed Matching Strength", "0.25", float, m_flSpdScale )
+DMXELEMENT_UNPACK_FIELD( "Control Point to Broadcast Speed and Direction To", "-1", int, m_nCPBroadcast )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityMatchingForce )
+
+void C_OP_VelocityMatchingForce::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ VelocityMatchingForceContext_t *pCtx = reinterpret_cast<VelocityMatchingForceContext_t *>( pContext );
+
+ Vector vecVelocityAvg = vec3_origin;
+ float flAvgSpeed = 0;
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, xyz_prev );
+ Vector vecVelocityCur = ( ( vecXYZ - vecPXYZ ) / pParticles->m_flDt );
+ vecVelocityAvg += vecVelocityCur;
+ float flSpeed = vecVelocityCur.Length();
+ flAvgSpeed += flSpeed;
+
+ if ( pCtx->m_vecAvgVelocity != vec3_origin )
+ {
+ Vector vecScaledXYZ;
+ VectorNormalizeFast(vecVelocityCur);
+ VectorLerp( vecVelocityCur, pCtx->m_vecAvgVelocity, m_flDirScale, vecScaledXYZ );
+ VectorNormalizeFast(vecScaledXYZ);
+ flSpeed = Lerp ( m_flSpdScale, flSpeed, pCtx->m_flAvgSpeed );
+ vecScaledXYZ *= flSpeed;
+ vecScaledXYZ = ( ( vecScaledXYZ * pParticles->m_flDt ) + vecPXYZ );
+ SetVectorAttribute( xyz, vecScaledXYZ );
+ }
+ }
+
+ VectorNormalizeFast( vecVelocityAvg );
+ pCtx->m_vecAvgVelocity = vecVelocityAvg;
+ pCtx->m_flAvgSpeed = ( flAvgSpeed / pParticles->m_nActiveParticles );
+ if ( m_nCPBroadcast != -1 )
+ {
+ pParticles->SetControlPoint( m_nCPBroadcast, Vector ( pCtx->m_flAvgSpeed, pCtx->m_flAvgSpeed, pCtx->m_flAvgSpeed ) );
+ pParticles->SetControlPointForwardVector( m_nCPBroadcast, pCtx->m_vecAvgVelocity );
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Orient to heading
+//-----------------------------------------------------------------------------
+class C_OP_OrientTo2dDirection : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OrientTo2dDirection );
+
+ float m_flRotOffset;
+ float m_flSpinStrength;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OrientTo2dDirection , "Rotation Orient to 2D Direction", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OrientTo2dDirection )
+DMXELEMENT_UNPACK_FIELD( "Rotation Offset", "0", float, m_flRotOffset )
+DMXELEMENT_UNPACK_FIELD( "Spin Strength", "1", float, m_flSpinStrength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OrientTo2dDirection )
+
+void C_OP_OrientTo2dDirection::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ float flRotOffset = m_flRotOffset * ( M_PI / 180.0f );
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ float *roll = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+ vecPXYZ.x = xyz_prev[0];
+ vecPXYZ.y = xyz_prev[4];
+ vecPXYZ.z = xyz_prev[8];
+ Vector vecVelocityCur = ( vecXYZ - vecPXYZ );
+
+ vecVelocityCur.z = 0.0f;
+ VectorNormalizeFast ( vecVelocityCur );
+
+ float flCurRot = *roll;
+
+ float flVelRot = atan2(vecVelocityCur.y, vecVelocityCur.x ) + M_PI;
+
+ flVelRot += flRotOffset;
+
+ float flRotation = Lerp ( m_flSpinStrength, flCurRot, flVelRot );
+ *roll = flRotation;
+ }
+
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Orient relative to CP
+//-----------------------------------------------------------------------------
+class C_OP_Orient2DRelToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Orient2DRelToCP );
+
+ float m_flRotOffset;
+ float m_flSpinStrength;
+ int m_nCP;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK ;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nCP );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Orient2DRelToCP , "Rotation Orient Relative to CP", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Orient2DRelToCP )
+DMXELEMENT_UNPACK_FIELD( "Rotation Offset", "0", float, m_flRotOffset )
+DMXELEMENT_UNPACK_FIELD( "Spin Strength", "1", float, m_flSpinStrength )
+DMXELEMENT_UNPACK_FIELD( "Control Point", "0", int, m_nCP )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Orient2DRelToCP )
+
+void C_OP_Orient2DRelToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ float flRotOffset = m_flRotOffset * ( M_PI / 180.0f );
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *roll = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, i );
+
+ Vector vecXYZ;
+ Vector vecCP;
+ vecCP = pParticles->GetControlPointAtCurrentTime( m_nCP );
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+
+ Vector vecVelocityCur = ( vecXYZ - vecCP );
+
+ vecVelocityCur.z = 0.0f;
+ VectorNormalizeFast ( vecVelocityCur );
+
+ float flCurRot = *roll;
+
+ float flVelRot = atan2(vecVelocityCur.y, vecVelocityCur.x ) + M_PI;
+
+ flVelRot += flRotOffset;
+
+ float flRotation = Lerp ( m_flSpinStrength, flCurRot, flVelRot );
+ *roll = flRotation;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Max Velocity - clamps the maximum velocity of a particle
+//-----------------------------------------------------------------------------
+class C_OP_MaxVelocity : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MaxVelocity );
+
+ float m_flMaxVelocity;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MaxVelocity , "Movement Max Velocity", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MaxVelocity )
+DMXELEMENT_UNPACK_FIELD( "Maximum Velocity", "0", float, m_flMaxVelocity )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MaxVelocity )
+
+void C_OP_MaxVelocity::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, xyz_prev );
+ Vector vecVelocityCur = ( ( vecXYZ - vecPXYZ ) );
+ float flSpeed = vecVelocityCur.Length();
+ VectorNormalizeFast( vecVelocityCur );
+ float flMaxVelocityNormalized = m_flMaxVelocity * pParticles->m_flDt;
+ vecVelocityCur *= min( flSpeed, flMaxVelocityNormalized);
+ vecXYZ = vecPXYZ + vecVelocityCur;
+ SetVectorAttribute( xyz, vecXYZ );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Maintain position along a path
+//-----------------------------------------------------------------------------
+struct SequentialPositionContext_t
+{
+ int m_nParticleCount;
+ float m_flStep;
+ int m_nCountAmount;
+};
+
+class C_OP_MaintainSequentialPath : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MaintainSequentialPath );
+
+ float m_fMaxDistance;
+ float m_flNumToAssign;
+ bool m_bLoop;
+ float m_flCohesionStrength;
+ struct CPathParameters m_PathParams;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ uint64 nStartMask = ( 1ULL << m_PathParams.m_nStartControlPointNumber ) - 1;
+ uint64 nEndMask = ( 1ULL << ( m_PathParams.m_nEndControlPointNumber + 1 ) ) - 1;
+ return nEndMask & (~nStartMask);
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ SequentialPositionContext_t *pCtx = reinterpret_cast<SequentialPositionContext_t *>( pContext );
+ pCtx->m_nParticleCount = 0;
+ if ( m_flNumToAssign > 1.0f )
+ {
+ pCtx->m_flStep = 1.0f / ( m_flNumToAssign - 1 );
+ }
+ else
+ {
+ pCtx->m_flStep = 0.0f;
+ }
+ pCtx->m_nCountAmount = 1;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_PathParams.ClampControlPointIndices();
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( SequentialPositionContext_t );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MaintainSequentialPath, "Movement Maintain Position Along Path", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainSequentialPath )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance", "0", float, m_fMaxDistance )
+ DMXELEMENT_UNPACK_FIELD( "bulge", "0", float, m_PathParams.m_flBulge )
+ DMXELEMENT_UNPACK_FIELD( "start control point number", "0", int, m_PathParams.m_nStartControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "end control point number", "0", int, m_PathParams.m_nEndControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "bulge control 0=random 1=orientation of start pnt 2=orientation of end point", "0", int, m_PathParams.m_nBulgeControl )
+ DMXELEMENT_UNPACK_FIELD( "mid point position", "0.5", float, m_PathParams.m_flMidPoint )
+ DMXELEMENT_UNPACK_FIELD( "particles to map from start to end", "100", float, m_flNumToAssign )
+ DMXELEMENT_UNPACK_FIELD( "restart behavior (0 = bounce, 1 = loop )", "1", bool, m_bLoop )
+ DMXELEMENT_UNPACK_FIELD( "cohesion strength", "1", float, m_flCohesionStrength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainSequentialPath )
+
+
+void C_OP_MaintainSequentialPath::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
+ SequentialPositionContext_t *pCtx = reinterpret_cast<SequentialPositionContext_t *>( pContext );
+
+ float fl_Cohesion = ( 1 - m_flCohesionStrength );
+
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector StartPnt, MidP, EndPnt;
+ pParticles->CalculatePathValues( m_PathParams, pParticles->m_flCurTime, &StartPnt, &MidP, &EndPnt);
+ if ( pCtx->m_nParticleCount >= m_flNumToAssign || pCtx->m_nParticleCount < 0 )
+ {
+ if ( m_bLoop )
+ {
+ pCtx->m_nParticleCount = 0;
+ }
+ else
+ {
+ pCtx->m_nCountAmount *= -1;
+ pCtx->m_nParticleCount = min ( pCtx->m_nParticleCount, (int)( m_flNumToAssign - 1) );
+ pCtx->m_nParticleCount = max ( pCtx->m_nParticleCount, 1 );
+ }
+ }
+
+ float t= pCtx->m_nParticleCount * pCtx->m_flStep;
+
+
+ // form delta terms needed for quadratic bezier
+ Vector Delta0=MidP-StartPnt;
+ Vector Delta1 = EndPnt-MidP;
+
+ Vector L0 = StartPnt+t*Delta0;
+ Vector L1 = MidP+t*Delta1;
+
+ Vector Pnt = L0+(L1-L0)*t;
+
+ // Allow an offset distance and position lerp
+ Vector vecXYZ;
+ Vector vecPXYZ;
+
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, pxyz );
+
+ vecXYZ -= Pnt;
+ vecPXYZ -= Pnt;
+
+ float flXYZOffset = min (vecXYZ.Length(), m_fMaxDistance );
+ float flPXYZOffset = min (vecPXYZ.Length(), m_fMaxDistance );
+
+ VectorNormalizeFast( vecXYZ );
+ vecXYZ *= flXYZOffset * fl_Cohesion;
+ VectorNormalizeFast( vecPXYZ );
+ vecPXYZ *= flPXYZOffset * fl_Cohesion;
+
+ vecXYZ += Pnt;
+ vecPXYZ += Pnt;
+
+ xyz[0] = vecXYZ.x;
+ xyz[4] = vecXYZ.y;
+ xyz[8] = vecXYZ.z;
+ pxyz[0] = vecPXYZ.x;
+ pxyz[4] = vecPXYZ.y;
+ pxyz[8] = vecPXYZ.z;
+
+ pCtx->m_nParticleCount += pCtx->m_nCountAmount;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Remap Dot Product to Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapDotProductToScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapDotProductToScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nInputCP1 ) | ( 1ULL << m_nInputCP2 );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nInputCP1;
+ int m_nInputCP2;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ bool m_bUseParticleVelocity;
+ bool m_bScaleInitialRange;
+ bool m_bActiveRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapDotProductToScalar, "Remap Dot Product to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapDotProductToScalar )
+ DMXELEMENT_UNPACK_FIELD( "use particle velocity for first input", "0", bool, m_bUseParticleVelocity )
+ DMXELEMENT_UNPACK_FIELD( "first input control point", "0", int, m_nInputCP1 )
+ DMXELEMENT_UNPACK_FIELD( "second input control point", "0", int, m_nInputCP2 )
+ DMXELEMENT_UNPACK_FIELD( "input minimum (-1 to 1)","0", float, m_flInputMin )
+ DMXELEMENT_UNPACK_FIELD( "input maximum (-1 to 1)","1", float, m_flInputMax )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+ DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+ DMXELEMENT_UNPACK_FIELD( "only active within specified input range","0", bool, m_bActiveRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapDotProductToScalar )
+
+void C_OP_RemapDotProductToScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+
+ Vector vecInput1;
+ Vector vecInput2;
+
+ CParticleSIMDTransformation pXForm1;
+ CParticleSIMDTransformation pXForm2;
+ pParticles->GetControlPointTransformAtTime( m_nInputCP1, pParticles->m_flCurTime, &pXForm1 );
+ pParticles->GetControlPointTransformAtTime( m_nInputCP2, pParticles->m_flCurTime, &pXForm2 );
+
+ vecInput1 = pXForm1.m_v4Fwd.Vec( 0 );
+ vecInput2 = pXForm2.m_v4Fwd.Vec( 0 );
+
+ float flInput = DotProduct( vecInput1, vecInput2 );
+
+ // only use within start/end time frame and, if set, active input range
+ if ( ( m_bActiveRange && !m_bUseParticleVelocity && ( flInput < m_flInputMin || flInput > m_flInputMax ) ) )
+ return;
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ if ( m_bUseParticleVelocity )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Vector vecXYZ;
+ Vector vecPXYZ;
+
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+ vecPXYZ.x = pxyz[0];
+ vecPXYZ.y = pxyz[4];
+ vecPXYZ.z = pxyz[8];
+
+ vecInput1 = vecXYZ - vecPXYZ;
+ VectorNormalizeFast( vecInput1 );
+
+ float flInputDot = DotProduct( vecInput1, vecInput2 );
+
+ // only use within start/end time frame and, if set, active input range
+ if ( ( m_bActiveRange && (flInputDot < m_flInputMin || flInputDot > m_flInputMax ) ) )
+ continue;
+ }
+
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( flInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ flOutput *= *pOutput;
+ }
+ if ( ATTRIBUTES_WHICH_ARE_INTS & ( 1 << m_nFieldOutput ) )
+ {
+ *pOutput = int ( flOutput );
+ }
+ else
+ {
+ *pOutput = flOutput;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Remap CP to Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapCPtoScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapCPtoScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nCPInput;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nField = int (clamp (m_nField, 0, 2));
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nCPInput;
+ int m_nFieldOutput;
+ int m_nField;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bScaleInitialRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapCPtoScalar, "Remap Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapCPtoScalar )
+DMXELEMENT_UNPACK_FIELD( "emitter lifetime start time (seconds)", "-1", float, m_flStartTime )
+DMXELEMENT_UNPACK_FIELD( "emitter lifetime end time (seconds)", "-1", float, m_flEndTime )
+DMXELEMENT_UNPACK_FIELD( "input control point number", "0", int, m_nCPInput )
+DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD( "input field 0-2 X/Y/Z","0", int, m_nField )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapCPtoScalar )
+
+void C_OP_RemapCPtoScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ const float *pCreationTime;
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nCPInput );
+
+ float flInput = vecControlPoint[m_nField];
+
+ // FIXME: SSE-ize
+ for( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ // using raw creation time to map to emitter lifespan
+ float flLifeTime = *pCreationTime;
+
+ // only use within start/end time frame
+ if ( ( ( flLifeTime < m_flStartTime ) || ( flLifeTime >= m_flEndTime ) ) && ( ( m_flStartTime != -1.0f) && ( m_flEndTime != -1.0f) ) )
+ continue;
+
+
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( flInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ flOutput = *pOutput * flOutput;
+ }
+ if ( ATTRIBUTES_WHICH_ARE_INTS & ( 1 << m_nFieldOutput ) )
+ {
+ *pOutput = int ( flOutput );
+ }
+ else
+ {
+ *pOutput = flOutput;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Rotate Particle around axis
+//-----------------------------------------------------------------------------
+class C_OP_MovementRotateParticleAroundAxis : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MovementRotateParticleAroundAxis );
+
+ Vector m_vecRotAxis;
+ float m_flRotRate;
+ int m_nCP;
+ bool m_bLocalSpace;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nCP;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef )
+ {
+ VectorNormalize( m_vecRotAxis );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MovementRotateParticleAroundAxis , "Movement Rotate Particle Around Axis", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MovementRotateParticleAroundAxis )
+DMXELEMENT_UNPACK_FIELD( "Rotation Axis", "0 0 1", Vector, m_vecRotAxis )
+DMXELEMENT_UNPACK_FIELD( "Rotation Rate", "180", float, m_flRotRate )
+DMXELEMENT_UNPACK_FIELD( "Control Point", "0", int, m_nCP )
+DMXELEMENT_UNPACK_FIELD( "Use Local Space", "0", bool, m_bLocalSpace )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MovementRotateParticleAroundAxis )
+
+void C_OP_MovementRotateParticleAroundAxis::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ float flRotRate = m_flRotRate * pParticles->m_flDt;
+
+ matrix3x4_t matRot;
+
+ Vector vecRotAxis = m_vecRotAxis;
+
+ if ( m_bLocalSpace )
+ {
+ matrix3x4_t matLocalCP;
+ pParticles->GetControlPointTransformAtCurrentTime( m_nCP, &matLocalCP );
+ VectorRotate( m_vecRotAxis, matLocalCP, vecRotAxis );
+ }
+
+ MatrixBuildRotationAboutAxis ( vecRotAxis, flRotRate, matRot );
+
+ Vector vecCPPos = pParticles->GetControlPointAtCurrentTime( m_nCP );
+
+ FourVectors fvCPPos;
+ fvCPPos.DuplicateVector( vecCPPos );
+
+ fltx4 fl4Strength = ReplicateX4( flStrength );
+
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors fvCurPos = *pXYZ;
+ fvCurPos -= fvCPPos;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvPrevPos -= fvCPPos;
+
+ fvCurPos.RotateBy( matRot );
+ fvPrevPos.RotateBy( matRot );
+
+ fvCurPos += fvCPPos;
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4Strength;
+ *pXYZ += fvCurPos;
+ fvPrevPos += fvCPPos;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4Strength;
+ *pPrevXYZ += fvPrevPos;
+
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+
+};
+
+//-----------------------------------------------------------------------------
+// Remap Speed to CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapSpeedtoCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapSpeedtoCP );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nInControlPointNumber ) | ( 1ULL << m_nOutControlPointNumber );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+ virtual void InitParams(CParticleSystemDefinition *pDef )
+ {
+ // Safety for bogus input->output feedback loop
+ if ( m_nInControlPointNumber == m_nOutControlPointNumber )
+ m_nOutControlPointNumber = -1;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nInControlPointNumber;
+ int m_nOutControlPointNumber;
+ int m_nField;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapSpeedtoCP, "Remap CP Speed to CP", OPERATOR_GENERIC );
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapSpeedtoCP )
+DMXELEMENT_UNPACK_FIELD( "input control point", "0", int, m_nInControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD( "output control point", "-1", int, m_nOutControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "Output field 0-2 X/Y/Z","0", int, m_nField )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapSpeedtoCP );
+
+void C_OP_RemapSpeedtoCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_nOutControlPointNumber >= 0 )
+ {
+ Vector vecPrevPos;
+ pParticles->GetControlPointAtPrevTime( m_nInControlPointNumber, &vecPrevPos );
+ Vector vecDelta;
+ vecDelta = pParticles->GetControlPointAtCurrentTime( m_nInControlPointNumber ) - vecPrevPos;
+ float flSpeed = vecDelta.Length() / pParticles->m_flPreviousDt;
+ float flOutput = RemapValClamped( flSpeed, m_flInputMin, m_flInputMax, m_flOutputMin, m_flOutputMax );
+
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nOutControlPointNumber );
+ vecControlPoint[m_nField] = flOutput;
+ pParticles->SetControlPoint( m_nOutControlPointNumber, vecControlPoint );
+ }
+}
+
+
+void AddBuiltInParticleOperators( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_BasicMovement );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Decay );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VelocityDecay );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeAndKill );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeIn );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeOut );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Spin );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SpinUpdate );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SpinYaw );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OrientTo2dDirection );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Orient2DRelToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_InterpolateRadius );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ColorInterpolate );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OscillateScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OscillateVector );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DampenToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_PositionLock );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_LockToBone );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DistanceBetweenCPs );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DistanceToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointToPlayer );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointToCenter );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetChildControlPoints );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointPositions );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_PlaneCull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ModelCull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Cull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ControlpointLight );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Noise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VectorNoise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VelocityMatchingForce );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MaxVelocity );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MaintainSequentialPath );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapDotProductToScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapCPtoScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MovementRotateParticleAroundAxis );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapSpeedtoCP );
+}
+