summaryrefslogtreecommitdiff
path: root/particles/builtin_initializers.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /particles/builtin_initializers.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'particles/builtin_initializers.cpp')
-rw-r--r--particles/builtin_initializers.cpp4741
1 files changed, 4741 insertions, 0 deletions
diff --git a/particles/builtin_initializers.cpp b/particles/builtin_initializers.cpp
new file mode 100644
index 0000000..bdfde7b
--- /dev/null
+++ b/particles/builtin_initializers.cpp
@@ -0,0 +1,4741 @@
+//========= 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 "dmxloader/dmxelement.h"
+#include "psheet.h"
+#include "bspflags.h"
+#include "const.h"
+#include "particles_internal.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+void CParticleOperatorInstance::InitScalarAttributeRandomRangeBlock(
+ int attr_num, float fMin, float fMax,
+ CParticleCollection *pParticles, int start_block, int n_blocks ) const
+{
+ size_t attr_stride;
+ fltx4 *pAttr = pParticles->GetM128AttributePtrForWrite( attr_num, &attr_stride );
+ pAttr += attr_stride * start_block;
+ fltx4 val0 = ReplicateX4( fMin );
+ fltx4 val_d = ReplicateX4( fMax - fMin );
+ int nRandContext = GetSIMDRandContext();
+ while( n_blocks-- )
+ {
+ *( pAttr ) = AddSIMD( val0, MulSIMD( RandSIMD( nRandContext ), val_d ) );
+ pAttr += attr_stride;
+ }
+ ReleaseSIMDRandContext( nRandContext );
+
+}
+
+void CParticleOperatorInstance::InitScalarAttributeRandomRangeExpBlock(
+ int attr_num, float fMin, float fMax, float fExp,
+ CParticleCollection *pParticles, int start_block, int n_blocks ) const
+{
+ size_t attr_stride;
+ fltx4 *pAttr = pParticles->GetM128AttributePtrForWrite( attr_num, &attr_stride );
+ pAttr += attr_stride * start_block;
+ fltx4 val0 = ReplicateX4( fMin );
+ fltx4 val_d = ReplicateX4( fMax - fMin );
+ //fltx4 val_e = ReplicateX4( fExp );
+ int nExp = (int)(4.0f * fExp);
+ int nRandContext = GetSIMDRandContext();
+ while( n_blocks-- )
+ {
+ *( pAttr ) = AddSIMD( val0, MulSIMD( Pow_FixedPoint_Exponent_SIMD( RandSIMD( nRandContext ), nExp ), val_d ) );
+ pAttr += attr_stride;
+ }
+ ReleaseSIMDRandContext( nRandContext );
+}
+
+void CParticleOperatorInstance::AddScalarAttributeRandomRangeBlock(
+ int nAttributeId, float fMin, float fMax, float fExp,
+ CParticleCollection *pParticles, int nStartBlock, int nBlockCount, bool bRandomlyInvert ) const
+{
+ size_t nAttrStride;
+ fltx4 *pAttr = pParticles->GetM128AttributePtrForWrite( nAttributeId, &nAttrStride );
+ pAttr += nAttrStride * nStartBlock;
+ fltx4 val0 = ReplicateX4( fMin );
+ fltx4 val_d = ReplicateX4( fMax - fMin );
+ int nRandContext = GetSIMDRandContext();
+ if ( !bRandomlyInvert )
+ {
+ if ( fExp != 1.0f )
+ {
+ int nExp = (int)(4.0f * fExp);
+ while( nBlockCount-- )
+ {
+ *( pAttr ) = AddSIMD( *pAttr, AddSIMD( val0, MulSIMD( Pow_FixedPoint_Exponent_SIMD( RandSIMD( nRandContext ), nExp ), val_d ) ) );
+ pAttr += nAttrStride;
+ }
+ }
+ else
+ {
+ while( nBlockCount-- )
+ {
+ *pAttr = AddSIMD( *pAttr, AddSIMD( val0, MulSIMD( RandSIMD( nRandContext ), val_d ) ) );
+ pAttr += nAttrStride;
+ }
+ }
+ }
+ else
+ {
+ fltx4 fl4NegOne = ReplicateX4( -1.0f );
+ if ( fExp != 1.0f )
+ {
+ int nExp = (int)(4.0f * fExp);
+ while( nBlockCount-- )
+ {
+ fltx4 fl4RandVal = AddSIMD( val0, MulSIMD( Pow_FixedPoint_Exponent_SIMD( RandSIMD( nRandContext ), nExp ), val_d ) );
+ fltx4 fl4Sign = MaskedAssign( CmpGeSIMD( RandSIMD( nRandContext ), Four_PointFives ), Four_Ones, fl4NegOne );
+ *pAttr = AddSIMD( *pAttr, MulSIMD( fl4RandVal, fl4Sign ) );
+ pAttr += nAttrStride;
+ }
+ }
+ else
+ {
+ while( nBlockCount-- )
+ {
+ fltx4 fl4RandVal = AddSIMD( val0, MulSIMD( RandSIMD( nRandContext ), val_d ) );
+ fltx4 fl4Sign = MaskedAssign( CmpGeSIMD( RandSIMD( nRandContext ), Four_PointFives ), Four_Ones, fl4NegOne );
+ *pAttr = AddSIMD( *pAttr, MulSIMD( fl4RandVal, fl4Sign ) );
+ pAttr += nAttrStride;
+ }
+ }
+ }
+ ReleaseSIMDRandContext( nRandContext );
+}
+
+
+class C_INIT_CreateOnModel : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateOnModel );
+
+ int m_nControlPointNumber;
+ int m_nForceInModel;
+ float m_flHitBoxScale;
+ Vector m_vecDirectionBias;
+
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK |
+ PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK | PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK;
+
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateOnModel, "Position on Model Random", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateOnModel )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "force to be inside model", "0", int, m_nForceInModel )
+ DMXELEMENT_UNPACK_FIELD( "hitbox scale", "1.0", int, m_flHitBoxScale )
+ DMXELEMENT_UNPACK_FIELD( "direction bias", "0 0 0", Vector, m_vecDirectionBias )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateOnModel )
+
+void C_INIT_CreateOnModel::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ pParticles->UpdateHitBoxInfo( m_nControlPointNumber );
+ while( nParticleCount )
+ {
+ Vector vecPnts[100]; // minimize stack usage
+ Vector vecUVW[100];
+ int nHitBoxIndex[100];
+ int nToDo = min( (int)ARRAYSIZE( vecPnts ), nParticleCount );
+
+ Assert( m_nControlPointNumber <= pParticles->GetHighestControlPoint() );
+
+ g_pParticleSystemMgr->Query()->GetRandomPointsOnControllingObjectHitBox(
+ pParticles, m_nControlPointNumber,
+ nToDo, m_flHitBoxScale, m_nForceInModel, vecPnts, m_vecDirectionBias, vecUVW,
+ nHitBoxIndex );
+
+ for( int i=0; i<nToDo; i++)
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ float *pHitboxRelXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ, start_p );
+ int *pHitboxIndex = pParticles->GetIntAttributePtrForWrite( PARTICLE_ATTRIBUTE_HITBOX_INDEX, start_p );
+ start_p++;
+
+ Vector randpos = vecPnts[i];
+ xyz[0] = randpos.x;
+ xyz[4] = randpos.y;
+ xyz[8] = randpos.z;
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ pxyz[0] = randpos.x;
+ pxyz[4] = randpos.y;
+ pxyz[8] = randpos.z;
+ }
+ if ( pHitboxRelXYZ && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK ) )
+ {
+ pHitboxRelXYZ[0] = vecUVW[i].x;
+ pHitboxRelXYZ[4] = vecUVW[i].y;
+ pHitboxRelXYZ[8] = vecUVW[i].z;
+ }
+ if ( pHitboxIndex && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK ) )
+ {
+ *pHitboxIndex = nHitBoxIndex[i];
+ }
+
+ }
+ nParticleCount -= nToDo;
+ }
+}
+
+
+static inline void RandomPointOnUnitSphere( int nRandContext, FourVectors &out )
+{
+ // generate 4 random points on the unit sphere. uses Marsaglia (1972) method from
+ // http://mathworld.wolfram.com/SpherePointPicking.html
+
+ fltx4 f4x1 = SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ); // -1..1
+ fltx4 f4x2 = SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ); // -1..1
+ fltx4 f4x1SQ = MulSIMD( f4x1, f4x1 );
+ fltx4 f4x2SQ = MulSIMD( f4x2, f4x2 );
+ fltx4 badMask = CmpGeSIMD( AddSIMD( f4x1SQ, f4x2SQ ), Four_Ones );
+ while( IsAnyNegative( badMask ) )
+ {
+ f4x1 = MaskedAssign( badMask, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ), f4x1 );
+ f4x2 = MaskedAssign( badMask, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ), f4x2 );
+ f4x1SQ = MulSIMD( f4x1, f4x1 );
+ f4x2SQ = MulSIMD( f4x2, f4x2 );
+ badMask = CmpGeSIMD( AddSIMD( f4x1SQ, f4x2SQ ), Four_Ones );
+ }
+ // now, we have 2 points on the unit circle
+ fltx4 f4OuterArea = SqrtEstSIMD( SubSIMD( Four_Ones, SubSIMD( f4x1SQ, f4x2SQ ) ) );
+ out.x = MulSIMD( AddSIMD( f4x1, f4x1 ), f4OuterArea );
+ out.y = MulSIMD( AddSIMD( f4x2, f4x2 ), f4OuterArea );
+ out.z = SubSIMD( Four_Ones, MulSIMD( Four_Twos, AddSIMD( f4x1, f4x2 ) ) );
+}
+
+static inline void RandomPointInUnitSphere( int nRandContext, FourVectors &out )
+{
+ // generate 4 random points inside the unit sphere. uses rejection method.
+ out.x = SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ); // -1..1
+ out.y = SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ); // -1..1
+ out.z = SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ); // -1..1
+ fltx4 f4xSQ = MulSIMD( out.x, out.x );
+ fltx4 f4ySQ = MulSIMD( out.y, out.y );
+ fltx4 f4zSQ = MulSIMD( out.z, out.z );
+ fltx4 badMask = CmpGtSIMD( AddSIMD( AddSIMD( f4xSQ, f4ySQ ), f4zSQ ), Four_Ones );
+ while( IsAnyNegative( badMask ) )
+ {
+ out.x = MaskedAssign( badMask, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ), out.x );
+ out.y = MaskedAssign( badMask, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ), out.y );
+ out.z = MaskedAssign( badMask, SubSIMD( MulSIMD( Four_Twos, RandSIMD( nRandContext ) ), Four_Ones ), out.z );
+ f4xSQ = MulSIMD( out.x, out.x );
+ f4ySQ = MulSIMD( out.y, out.y );
+ f4zSQ = MulSIMD( out.z, out.z );
+ badMask = CmpGeSIMD( AddSIMD( AddSIMD( f4xSQ, f4ySQ ), f4zSQ ), Four_Ones );
+ }
+}
+
+
+
+class C_INIT_CreateWithinSphere : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateWithinSphere );
+
+ float m_fRadiusMin;
+ float m_fRadiusMax;
+ Vector m_vecDistanceBias, m_vecDistanceBiasAbs;
+ int m_nControlPointNumber;
+ float m_fSpeedMin;
+ float m_fSpeedMax;
+ float m_fSpeedRandExp;
+ bool m_bLocalCoords;
+ bool m_bDistanceBiasAbs;
+ bool m_bUseHighestEndCP;
+ bool m_bDistanceBias;
+ float m_flEndCPGrowthTime;
+
+ Vector m_LocalCoordinateSystemSpeedMin;
+ Vector m_LocalCoordinateSystemSpeedMax;
+ int m_nCreateInModel;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ if ( !m_bUseHighestEndCP )
+ return 1ULL << m_nControlPointNumber;
+ return ~( ( 1ULL << m_nControlPointNumber ) - 1 );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ m_bDistanceBias = ( m_vecDistanceBias.x != 1.0f ) || ( m_vecDistanceBias.y != 1.0f ) || ( m_vecDistanceBias.z != 1.0f );
+ m_bDistanceBiasAbs = ( m_vecDistanceBiasAbs.x != 0.0f ) || ( m_vecDistanceBiasAbs.y != 0.0f ) || ( m_vecDistanceBiasAbs.z != 0.0f );
+ }
+
+ void Render( CParticleCollection *pParticles ) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateWithinSphere, "Position Within Sphere Random", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateWithinSphere )
+ DMXELEMENT_UNPACK_FIELD( "distance_min", "0", float, m_fRadiusMin )
+ DMXELEMENT_UNPACK_FIELD( "distance_max", "0", float, m_fRadiusMax )
+ DMXELEMENT_UNPACK_FIELD( "distance_bias", "1 1 1", Vector, m_vecDistanceBias )
+ DMXELEMENT_UNPACK_FIELD( "distance_bias_absolute_value", "0 0 0", Vector, m_vecDistanceBiasAbs )
+ DMXELEMENT_UNPACK_FIELD( "bias in local system", "0", bool, m_bLocalCoords )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "speed_min", "0", float, m_fSpeedMin )
+ DMXELEMENT_UNPACK_FIELD( "speed_max", "0", float, m_fSpeedMax )
+ DMXELEMENT_UNPACK_FIELD( "speed_random_exponent", "1", float, m_fSpeedRandExp )
+ DMXELEMENT_UNPACK_FIELD( "speed_in_local_coordinate_system_min", "0 0 0", Vector, m_LocalCoordinateSystemSpeedMin )
+ DMXELEMENT_UNPACK_FIELD( "speed_in_local_coordinate_system_max", "0 0 0", Vector, m_LocalCoordinateSystemSpeedMax )
+ DMXELEMENT_UNPACK_FIELD( "create in model", "0", int, m_nCreateInModel )
+ DMXELEMENT_UNPACK_FIELD( "randomly distribute to highest supplied Control Point", "0", bool, m_bUseHighestEndCP )
+ DMXELEMENT_UNPACK_FIELD( "randomly distribution growth time", "0", float, m_flEndCPGrowthTime )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateWithinSphere )
+
+
+ConVar r_sse_s( "r_sse_s", "1", 0, "sse ins for particle sphere create" );
+
+void C_INIT_CreateWithinSphere::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ int nCurrentControlPoint = m_nControlPointNumber;
+ if ( m_bUseHighestEndCP )
+ {
+ //hack for growth time instead of using strength as currenly initializers don't support it.
+ float flStrength = 1.0;
+ if ( m_flEndCPGrowthTime != 0.0f )
+ {
+ flStrength = min ( pParticles->m_flCurTime, m_flEndCPGrowthTime ) / m_flEndCPGrowthTime ;
+ }
+ int nHighestControlPoint = floor ( pParticles->GetHighestControlPoint() * flStrength );
+ nCurrentControlPoint = pParticles->RandomInt( m_nControlPointNumber, nHighestControlPoint );
+ }
+ Vector randpos, randDir;
+ for( int nTryCtr = 0 ; nTryCtr < 10; nTryCtr++ )
+ {
+ float flLength = pParticles->RandomVectorInUnitSphere( &randpos );
+
+ // Absolute value and biasing for creating hemispheres and ovoids.
+ if ( m_bDistanceBiasAbs )
+ {
+ if ( m_vecDistanceBiasAbs.x != 0.0f )
+ {
+ randpos.x = fabs(randpos.x);
+ }
+ if ( m_vecDistanceBiasAbs.y != 0.0f )
+ {
+ randpos.y = fabs(randpos.y);
+ }
+ if ( m_vecDistanceBiasAbs.z != 0.0f )
+ {
+ randpos.z = fabs(randpos.z);
+ }
+ }
+ randpos *= m_vecDistanceBias;
+ randpos.NormalizeInPlace();
+
+
+ randDir = randpos;
+ randpos *= Lerp( flLength, m_fRadiusMin, m_fRadiusMax );
+
+ if ( !m_bDistanceBias || !m_bLocalCoords )
+ {
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( nCurrentControlPoint, *ct, &vecControlPoint );
+ randpos += vecControlPoint;
+ }
+ else
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( nCurrentControlPoint, *ct, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ VectorTransform( randpos, mat, vecTransformLocal );
+ randpos = vecTransformLocal;
+ }
+
+ // now, force to be in model if we can
+ if (
+ ( m_nCreateInModel == 0 ) ||
+ (g_pParticleSystemMgr->Query()->MovePointInsideControllingObject(
+ pParticles, pParticles->m_ControlPoints[nCurrentControlPoint].m_pObject, &randpos ) ) )
+ break;
+ }
+
+ xyz[0] = randpos.x;
+ xyz[4] = randpos.y;
+ xyz[8] = randpos.z;
+
+ // FIXME: Remove this into a speed setting initializer
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ Vector poffset(0,0,0);
+ if ( m_fSpeedMax > 0.0 )
+ {
+ float rand_speed = pParticles->RandomFloatExp( m_fSpeedMin, m_fSpeedMax, m_fSpeedRandExp );
+ poffset.x -= rand_speed * randDir.x;
+ poffset.y -= rand_speed * randDir.y;
+ poffset.z -= rand_speed * randDir.z;
+ }
+ poffset -=
+ pParticles->RandomFloat( m_LocalCoordinateSystemSpeedMin.x, m_LocalCoordinateSystemSpeedMax.x )*
+ pParticles->m_ControlPoints[ nCurrentControlPoint ].m_ForwardVector;
+ poffset -=
+ pParticles->RandomFloat( m_LocalCoordinateSystemSpeedMin.y, m_LocalCoordinateSystemSpeedMax.y )*
+ pParticles->m_ControlPoints[ nCurrentControlPoint ].m_RightVector;
+ poffset -=
+ pParticles->RandomFloat( m_LocalCoordinateSystemSpeedMin.z, m_LocalCoordinateSystemSpeedMax.z )*
+ pParticles->m_ControlPoints[ nCurrentControlPoint ].m_UpVector;
+
+ poffset *= pParticles->m_flPreviousDt;
+ randpos += poffset;
+ pxyz[0] = randpos.x;
+ pxyz[4] = randpos.y;
+ pxyz[8] = randpos.z;
+ }
+ }
+}
+
+void C_INIT_CreateWithinSphere::InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+{
+ // sse-favorable settings
+ bool bMustUseScalar = m_bUseHighestEndCP || m_nCreateInModel;
+ if ( m_bDistanceBias && m_bLocalCoords )
+ bMustUseScalar = true;
+
+ if ( ( !bMustUseScalar ) &&
+ // (( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) == 0 ) &&
+ r_sse_s.GetInt() )
+ {
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ pXYZ += start_block;
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ pPrevXYZ += start_block;
+ CM128AttributeIterator pCT( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ pCT += start_block;
+
+ // now, calculate the terms we need for interpolating control points
+ FourVectors v4PrevControlPointPosition;
+ v4PrevControlPointPosition.DuplicateVector( pParticles->m_ControlPoints[m_nControlPointNumber].m_PrevPosition );
+ FourVectors v4ControlPointDelta;
+ v4ControlPointDelta.DuplicateVector( pParticles->m_ControlPoints[m_nControlPointNumber].m_Position );
+ v4ControlPointDelta -= v4PrevControlPointPosition;
+
+ float flOODT = ( pParticles->m_flDt > 0.0 ) ? ( 1.0 / pParticles->m_flDt ) : 0.0;
+ fltx4 fl4OODt = ReplicateX4( flOODT );
+ fltx4 fl4PrevTime = ReplicateX4( pParticles->m_flCurTime - pParticles->m_flDt );
+ int nContext = GetSIMDRandContext();
+
+ FourVectors v4DistanceBias;
+ v4DistanceBias.DuplicateVector( m_vecDistanceBias );
+ FourVectors v4ConditionalAbsMask;
+ for( int nComp = 0 ; nComp < 3; nComp++ )
+ {
+ v4ConditionalAbsMask[nComp] = ( m_vecDistanceBiasAbs[nComp] > 0 ) ?
+ LoadAlignedSIMD( ( const float *) g_SIMD_clear_signmask ) :
+ LoadAlignedSIMD( ( const float *) g_SIMD_AllOnesMask );
+ }
+ fltx4 fl4RadiusMin = ReplicateX4( m_fRadiusMin );
+ fltx4 fl4RadiusSpread = ReplicateX4( m_fRadiusMax - m_fRadiusMin );
+ int nPowSSEMask = 4.0 * m_fSpeedRandExp;
+
+ bool bDoRandSpeed =
+ ( m_fSpeedMax > 0. ) ||
+ ( m_LocalCoordinateSystemSpeedMax.x != 0 ) ||
+ ( m_LocalCoordinateSystemSpeedMax.y != 0 ) ||
+ ( m_LocalCoordinateSystemSpeedMax.z != 0 ) ||
+ ( m_LocalCoordinateSystemSpeedMin.x != 0 ) ||
+ ( m_LocalCoordinateSystemSpeedMin.y != 0 ) ||
+ ( m_LocalCoordinateSystemSpeedMin.z != 0 );
+
+
+ fltx4 fl4SpeedMin = ReplicateX4( m_fSpeedMin );
+ fltx4 fl4SpeedRange = ReplicateX4( m_fSpeedMax - m_fSpeedMin );
+
+ fltx4 fl4LocalSpeedMinX = ReplicateX4( m_LocalCoordinateSystemSpeedMin.x );
+ fltx4 fl4LocalSpeedXSpread = ReplicateX4( m_LocalCoordinateSystemSpeedMax.x -
+ m_LocalCoordinateSystemSpeedMin.x );
+ fltx4 fl4LocalSpeedMinY = ReplicateX4( m_LocalCoordinateSystemSpeedMin.y );
+ fltx4 fl4LocalSpeedYSpread = ReplicateX4( m_LocalCoordinateSystemSpeedMax.y -
+ m_LocalCoordinateSystemSpeedMin.y );
+ fltx4 fl4LocalSpeedMinZ = ReplicateX4( m_LocalCoordinateSystemSpeedMin.z );
+ fltx4 fl4LocalSpeedZSpread = ReplicateX4( m_LocalCoordinateSystemSpeedMax.z -
+ m_LocalCoordinateSystemSpeedMin.z );
+
+ FourVectors v4CPForward;
+ v4CPForward.DuplicateVector( pParticles->m_ControlPoints[m_nControlPointNumber].m_ForwardVector );
+ FourVectors v4CPUp;
+ v4CPUp.DuplicateVector( pParticles->m_ControlPoints[m_nControlPointNumber].m_UpVector );
+ FourVectors v4CPRight;
+ v4CPRight.DuplicateVector( pParticles->m_ControlPoints[m_nControlPointNumber].m_RightVector );
+
+ fltx4 fl4PreviousDt = ReplicateX4( pParticles->m_flPreviousDt );
+
+ while( n_blocks-- )
+ {
+ FourVectors v4RandPos;
+ RandomPointInUnitSphere( nContext, v4RandPos );
+
+ fltx4 fl4Length = v4RandPos.length();
+
+ // conditional absolute value
+ v4RandPos.x = AndSIMD( v4RandPos.x, v4ConditionalAbsMask.x );
+ v4RandPos.y = AndSIMD( v4RandPos.y, v4ConditionalAbsMask.y );
+ v4RandPos.z = AndSIMD( v4RandPos.z, v4ConditionalAbsMask.z );
+
+ v4RandPos *= v4DistanceBias;
+ v4RandPos.VectorNormalizeFast();
+
+ FourVectors v4randDir = v4RandPos;
+
+ // lerp radius
+ v4RandPos *= AddSIMD( fl4RadiusMin, MulSIMD( fl4Length, fl4RadiusSpread ) );
+ v4RandPos += v4PrevControlPointPosition;
+
+ FourVectors cpnt = v4ControlPointDelta;
+ cpnt *= MulSIMD( SubSIMD( *pCT, fl4PrevTime ), fl4OODt );
+ v4RandPos += cpnt;
+
+ *(pXYZ) = v4RandPos;
+
+ if ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK )
+ {
+ if ( bDoRandSpeed )
+ {
+ fltx4 fl4Rand_speed = Pow_FixedPoint_Exponent_SIMD( RandSIMD( nContext ), nPowSSEMask );
+ fl4Rand_speed = AddSIMD( fl4SpeedMin, MulSIMD( fl4SpeedRange, fl4Rand_speed ) );
+ v4randDir *= fl4Rand_speed;
+
+ // local speed
+ FourVectors v4LocalOffset = v4CPForward;
+ v4LocalOffset *= AddSIMD( fl4LocalSpeedMinX,
+ MulSIMD( fl4LocalSpeedXSpread, RandSIMD( nContext ) ) );
+ v4randDir += v4LocalOffset;
+
+ v4LocalOffset = v4CPRight;
+ v4LocalOffset *= AddSIMD( fl4LocalSpeedMinY,
+ MulSIMD( fl4LocalSpeedYSpread, RandSIMD( nContext ) ) );
+ v4randDir += v4LocalOffset;
+
+
+ v4LocalOffset = v4CPUp;
+ v4LocalOffset *= AddSIMD( fl4LocalSpeedMinZ,
+ MulSIMD( fl4LocalSpeedZSpread, RandSIMD( nContext ) ) );
+ v4randDir += v4LocalOffset;
+ v4randDir *= fl4PreviousDt;
+ v4RandPos -= v4randDir;
+ }
+ *(pPrevXYZ) = v4RandPos;
+
+ }
+
+
+
+ ++pXYZ;
+ ++pPrevXYZ;
+ ++pCT;
+ }
+ ReleaseSIMDRandContext( nContext );
+
+ }
+ else
+ CParticleOperatorInstance::InitNewParticlesBlock( pParticles, start_block, n_blocks, nAttributeWriteMask, pContext );
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Render visualization
+//-----------------------------------------------------------------------------
+void C_INIT_CreateWithinSphere::Render( CParticleCollection *pParticles ) const
+{
+ Vector vecOrigin;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecOrigin );
+ RenderWireframeSphere( vecOrigin, m_fRadiusMin, 16, 8, Color( 192, 192, 0, 255 ), false );
+ RenderWireframeSphere( vecOrigin, m_fRadiusMax, 16, 8, Color( 128, 128, 0, 255 ), false );
+}
+
+
+
+
+class C_INIT_CreateWithinBox : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateWithinBox );
+
+ Vector m_vecMin;
+ Vector m_vecMax;
+ int m_nControlPointNumber;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void Render( CParticleCollection *pParticles ) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateWithinBox, "Position Within Box Random", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateWithinBox )
+ DMXELEMENT_UNPACK_FIELD( "min", "0 0 0", Vector, m_vecMin )
+ DMXELEMENT_UNPACK_FIELD( "max", "0 0 0", Vector, m_vecMax )
+ DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateWithinBox )
+
+
+void C_INIT_CreateWithinBox::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ int nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ Vector randpos;
+ pParticles->RandomVector( m_vecMin, m_vecMax, &randpos );
+
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( nControlPointNumber, *ct, &vecControlPoint );
+ randpos += vecControlPoint;
+
+ xyz[0] = randpos.x;
+ xyz[4] = randpos.y;
+ xyz[8] = randpos.z;
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ pxyz[0] = randpos.x;
+ pxyz[4] = randpos.y;
+ pxyz[8] = randpos.z;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Render visualization
+//-----------------------------------------------------------------------------
+void C_INIT_CreateWithinBox::Render( CParticleCollection *pParticles ) const
+{
+ Vector vecOrigin;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecOrigin );
+ RenderWireframeBox( vecOrigin, vec3_angle, m_vecMin, m_vecMax, Color( 192, 192, 0, 255 ), false );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Position Offset Initializer
+// offsets initial position of particles within a random vector range,
+// while still respecting spherical/conical spacial and velocity initialization
+//-----------------------------------------------------------------------------
+class C_INIT_PositionOffset : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_PositionOffset );
+
+ Vector m_OffsetMin;
+ Vector m_OffsetMax;
+ int m_nControlPointNumber;
+ bool m_bLocalCoords;
+ bool m_bProportional;
+
+ 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 | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void Render( CParticleCollection *pParticles ) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_PositionOffset, "Position Modify Offset Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_PositionOffset )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "offset min", "0 0 0", Vector, m_OffsetMin )
+ DMXELEMENT_UNPACK_FIELD( "offset max", "0 0 0", Vector, m_OffsetMax )
+ DMXELEMENT_UNPACK_FIELD( "offset in local space 0/1", "0", bool, m_bLocalCoords )
+ DMXELEMENT_UNPACK_FIELD( "offset proportional to radius 0/1", "0", bool, m_bProportional )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_PositionOffset )
+
+
+void C_INIT_PositionOffset::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ const float *radius = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, start_p );
+
+ Vector randpos;
+
+ if ( m_bProportional )
+ {
+ pParticles->RandomVector( (m_OffsetMin * *radius), (m_OffsetMax * *radius), &randpos );
+ }
+ else
+ {
+ pParticles->RandomVector( m_OffsetMin, m_OffsetMax, &randpos );
+ }
+
+ if ( m_bLocalCoords )
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, *ct, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ VectorRotate( randpos, mat, vecTransformLocal );
+ randpos = vecTransformLocal;
+ }
+
+ xyz[0] += randpos.x;
+ xyz[4] += randpos.y;
+ xyz[8] += randpos.z;
+ pxyz[0] += randpos.x;
+ pxyz[4] += randpos.y;
+ pxyz[8] += randpos.z;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Render visualization
+//-----------------------------------------------------------------------------
+void C_INIT_PositionOffset::Render( CParticleCollection *pParticles ) const
+{
+ Vector vecOrigin (0,0,0);
+ Vector vecMinExtent = m_OffsetMin;
+ Vector vecMaxExtent = m_OffsetMax;
+ if ( m_bLocalCoords )
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &mat );
+ VectorRotate( m_OffsetMin, mat, vecMinExtent );
+ VectorRotate( m_OffsetMax, mat, vecMaxExtent );
+ }
+ else
+ {
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecOrigin );
+ }
+ RenderWireframeBox( vecOrigin, vec3_angle, vecMinExtent , vecMaxExtent , Color( 192, 192, 0, 255 ), false );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Velocity-based Operators
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Random velocity initializer
+//-----------------------------------------------------------------------------
+class C_INIT_VelocityRandom : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_VelocityRandom );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ if ( m_bHasLocalSpeed )
+ return 1ULL << m_nControlPointNumber;
+ return 0;
+ }
+
+ virtual bool InitMultipleOverride() { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ m_bHasLocalSpeed = ( m_LocalCoordinateSystemSpeedMin != vec3_origin ) || ( m_LocalCoordinateSystemSpeedMax != vec3_origin );
+ if ( m_fSpeedMax < m_fSpeedMin )
+ {
+ V_swap( m_fSpeedMin, m_fSpeedMax );
+ }
+ }
+
+private:
+ int m_nControlPointNumber;
+ float m_fSpeedMin;
+ float m_fSpeedMax;
+ Vector m_LocalCoordinateSystemSpeedMin;
+ Vector m_LocalCoordinateSystemSpeedMax;
+ bool m_bHasLocalSpeed;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_VelocityRandom, "Velocity Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_VelocityRandom )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "random_speed_min", "0", float, m_fSpeedMin )
+ DMXELEMENT_UNPACK_FIELD( "random_speed_max", "0", float, m_fSpeedMax )
+ DMXELEMENT_UNPACK_FIELD( "speed_in_local_coordinate_system_min", "0 0 0", Vector, m_LocalCoordinateSystemSpeedMin )
+ DMXELEMENT_UNPACK_FIELD( "speed_in_local_coordinate_system_max", "0 0 0", Vector, m_LocalCoordinateSystemSpeedMax )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_VelocityRandom )
+
+
+void C_INIT_VelocityRandom::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ Vector vecVelocity( 0.0f, 0.0f, 0.0f );
+ if ( m_bHasLocalSpeed )
+ {
+ Vector vecRandomSpeed, vecForward, vecUp, vecRight;
+ pParticles->RandomVector( m_LocalCoordinateSystemSpeedMin, m_LocalCoordinateSystemSpeedMax, &vecRandomSpeed );
+ pParticles->GetControlPointOrientationAtTime( m_nControlPointNumber, *ct, &vecForward, &vecRight, &vecUp );
+ VectorMA( vecVelocity, vecRandomSpeed.x, vecForward, vecVelocity );
+ VectorMA( vecVelocity, -vecRandomSpeed.y, vecRight, vecVelocity );
+ VectorMA( vecVelocity, vecRandomSpeed.z, vecUp, vecVelocity );
+ }
+
+ if ( m_fSpeedMax > 0.0f )
+ {
+ Vector vecRandomSpeed;
+ pParticles->RandomVector( m_fSpeedMin, m_fSpeedMax, &vecRandomSpeed );
+ vecVelocity += vecRandomSpeed;
+ }
+
+ vecVelocity *= pParticles->m_flPreviousDt;
+ pxyz[0] -= vecVelocity.x;
+ pxyz[4] -= vecVelocity.y;
+ pxyz[8] -= vecVelocity.z;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Initial Velocity Noise Operator
+//-----------------------------------------------------------------------------
+class C_INIT_InitialVelocityNoise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_InitialVelocityNoise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual bool InitMultipleOverride() { return true; }
+
+ Vector m_vecAbsVal, m_vecAbsValInv, m_vecOffsetLoc;
+ float m_flOffset;
+ Vector m_vecOutputMin;
+ Vector m_vecOutputMax;
+ float m_flNoiseScale, m_flNoiseScaleLoc;
+ int nRemainingBlocks, m_nControlPointNumber;
+ bool m_bLocalSpace;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_InitialVelocityNoise, "Velocity Noise", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_InitialVelocityNoise )
+ DMXELEMENT_UNPACK_FIELD( "Control Point Number","0",int,m_nControlPointNumber)
+ DMXELEMENT_UNPACK_FIELD( "Time Noise Coordinate Scale","1",float,m_flNoiseScale)
+ DMXELEMENT_UNPACK_FIELD( "Spatial Noise Coordinate Scale","0.01",float,m_flNoiseScaleLoc)
+ DMXELEMENT_UNPACK_FIELD( "Time Coordinate Offset","0", float, m_flOffset )
+ DMXELEMENT_UNPACK_FIELD( "Spatial Coordinate Offset","0 0 0", Vector, m_vecOffsetLoc )
+ DMXELEMENT_UNPACK_FIELD( "Absolute Value","0 0 0", Vector, m_vecAbsVal )
+ DMXELEMENT_UNPACK_FIELD( "Invert Abs Value","0 0 0", Vector, m_vecAbsValInv )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0 0 0", Vector, m_vecOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1 1 1", Vector, m_vecOutputMax )
+ DMXELEMENT_UNPACK_FIELD( "Apply Velocity in Local Space (0/1)","0", bool, m_bLocalSpace )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_InitialVelocityNoise );
+
+
+void C_INIT_InitialVelocityNoise::InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+{
+ float flAbsScaleX, flAbsScaleY, flAbsScaleZ;
+ fltx4 fl4AbsValX, fl4AbsValY, fl4AbsValZ;
+ fl4AbsValX = CmpEqSIMD( Four_Zeros, Four_Zeros );
+ fl4AbsValY = fl4AbsValX;
+ fl4AbsValZ = fl4AbsValX;
+ flAbsScaleX = 0.5;
+ flAbsScaleY = 0.5;
+ flAbsScaleZ = 0.5;
+
+ // Set up single if check for absolute value inversion inside the loop
+ bool m_bNoiseAbs = ( m_vecAbsValInv.x != 0.0f ) || ( m_vecAbsValInv.y != 0.0f ) || ( m_vecAbsValInv.z != 0.0f );
+ // Set up values for more optimal absolute value calculations inside the loop
+ if ( m_vecAbsVal.x != 0.0f )
+ {
+ fl4AbsValX = LoadAlignedSIMD( (float *) g_SIMD_clear_signmask );
+ flAbsScaleX = 1.0;
+ }
+ if ( m_vecAbsVal.y != 0.0f )
+ {
+ fl4AbsValY = LoadAlignedSIMD( (float *) g_SIMD_clear_signmask );
+ flAbsScaleY = 1.0;
+ }
+ if ( m_vecAbsVal.z != 0.0f )
+ {
+ fl4AbsValZ = LoadAlignedSIMD( (float *) g_SIMD_clear_signmask );
+ flAbsScaleZ = 1.0;
+ }
+
+ float ValueScaleX, ValueScaleY, ValueScaleZ, ValueBaseX, ValueBaseY, ValueBaseZ;
+
+ ValueScaleX = ( flAbsScaleX *(m_vecOutputMax.x-m_vecOutputMin.x ) );
+ ValueBaseX = (m_vecOutputMin.x+ ( ( 1.0 - flAbsScaleX ) *( m_vecOutputMax.x-m_vecOutputMin.x ) ) );
+
+ ValueScaleY = ( flAbsScaleY *(m_vecOutputMax.y-m_vecOutputMin.y ) );
+ ValueBaseY = (m_vecOutputMin.y+ ( ( 1.0 - flAbsScaleY ) *( m_vecOutputMax.y-m_vecOutputMin.y ) ) );
+
+ ValueScaleZ = ( flAbsScaleZ *(m_vecOutputMax.z-m_vecOutputMin.z ) );
+ ValueBaseZ = (m_vecOutputMin.z+ ( ( 1.0 - flAbsScaleZ ) *( m_vecOutputMax.z-m_vecOutputMin.z ) ) );
+
+ fltx4 fl4ValueBaseX = ReplicateX4( ValueBaseX );
+ fltx4 fl4ValueBaseY = ReplicateX4( ValueBaseY );
+ fltx4 fl4ValueBaseZ = ReplicateX4( ValueBaseZ );
+
+ fltx4 fl4ValueScaleX = ReplicateX4( ValueScaleX );
+ fltx4 fl4ValueScaleY = ReplicateX4( ValueScaleY );
+ fltx4 fl4ValueScaleZ = ReplicateX4( ValueScaleZ );
+
+ float CoordScale = m_flNoiseScale;
+ float CoordScaleLoc = m_flNoiseScaleLoc;
+
+ Vector ofs_y = Vector( 100000.5, 300000.25, 9000000.75 );
+ Vector ofs_z = Vector( 110000.25, 310000.75, 9100000.5 );
+
+ size_t attr_stride;
+
+ const FourVectors *xyz = pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &attr_stride );
+ xyz += attr_stride * start_block;
+ FourVectors *pxyz = pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, &attr_stride );
+ pxyz += attr_stride * start_block;
+ const fltx4 *pCreationTime = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &attr_stride );
+ pCreationTime += attr_stride * start_block;
+
+ // setup
+ fltx4 fl4Offset = ReplicateX4( m_flOffset );
+ FourVectors fvOffsetLoc;
+ fvOffsetLoc.DuplicateVector( m_vecOffsetLoc );
+ CParticleSIMDTransformation CPTransform;
+ float flCreationTime = SubFloat( *pCreationTime, 0 );
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, flCreationTime, &CPTransform );
+
+ while( n_blocks-- )
+ {
+ FourVectors fvCoordLoc = *xyz;
+ fvCoordLoc += fvOffsetLoc;
+
+ FourVectors fvCoord;
+ fvCoord.x = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoord.y = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoord.z = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoordLoc *= CoordScaleLoc;
+ fvCoord *= CoordScale;
+ fvCoord += fvCoordLoc;
+
+ FourVectors fvCoord2 = fvCoord;
+ FourVectors fvOffsetTemp;
+ fvOffsetTemp.DuplicateVector( ofs_y );
+ fvCoord2 += fvOffsetTemp;
+ FourVectors fvCoord3 = fvCoord;
+ fvOffsetTemp.DuplicateVector( ofs_z );
+ fvCoord3 += fvOffsetTemp;
+
+ fltx4 fl4NoiseX;
+ fltx4 fl4NoiseY;
+ fltx4 fl4NoiseZ;
+
+ fl4NoiseX = NoiseSIMD( fvCoord );
+
+ fl4NoiseY = NoiseSIMD( fvCoord2 );
+
+ fl4NoiseZ = NoiseSIMD( fvCoord3 );
+
+ fl4NoiseX = AndSIMD ( fl4NoiseX, fl4AbsValX );
+ fl4NoiseY = AndSIMD ( fl4NoiseY, fl4AbsValY );
+ fl4NoiseZ = AndSIMD ( fl4NoiseZ, fl4AbsValZ );
+
+ if ( m_bNoiseAbs )
+ {
+ if ( m_vecAbsValInv.x != 0.0f )
+ {
+ fl4NoiseX = SubSIMD( Four_Ones, fl4NoiseX );
+ }
+
+ if ( m_vecAbsValInv.y != 0.0f )
+ {
+ fl4NoiseY = SubSIMD( Four_Ones, fl4NoiseY );
+ }
+ if ( m_vecAbsValInv.z != 0.0f )
+ {
+ fl4NoiseZ = SubSIMD( Four_Ones, fl4NoiseZ );
+ }
+ }
+
+ FourVectors fvOffset;
+
+ fvOffset.x = AddSIMD( fl4ValueBaseX, ( MulSIMD( fl4ValueScaleX , fl4NoiseX ) ) );
+ fvOffset.y = AddSIMD( fl4ValueBaseY, ( MulSIMD( fl4ValueScaleY , fl4NoiseY ) ) );
+ fvOffset.z = AddSIMD( fl4ValueBaseZ, ( MulSIMD( fl4ValueScaleZ , fl4NoiseZ ) ) );
+
+ fvOffset *= pParticles->m_flPreviousDt;
+
+ if ( m_bLocalSpace )
+ {
+ CPTransform.VectorRotate( fvOffset );
+ }
+
+ *pxyz -= fvOffset;
+
+ xyz += attr_stride;
+ pxyz += attr_stride;
+ pCreationTime += attr_stride;
+
+ }
+}
+
+
+void C_INIT_InitialVelocityNoise::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ float flAbsScaleX, flAbsScaleY, flAbsScaleZ;
+ int nAbsValX, nAbsValY, nAbsValZ;
+ nAbsValX = 0xffffffff;
+ nAbsValY = 0xffffffff;
+ nAbsValZ = 0xffffffff;
+ flAbsScaleX = 0.5;
+ flAbsScaleY = 0.5;
+ flAbsScaleZ = 0.5;
+ // Set up single if check for absolute value inversion inside the loop
+ bool m_bNoiseAbs = ( m_vecAbsValInv.x != 0.0f ) || ( m_vecAbsValInv.y != 0.0f ) || ( m_vecAbsValInv.z != 0.0f );
+ // Set up values for more optimal absolute value calculations inside the loop
+ if ( m_vecAbsVal.x != 0.0f )
+ {
+ nAbsValX = 0x7fffffff;
+ flAbsScaleX = 1.0;
+ }
+ if ( m_vecAbsVal.y != 0.0f )
+ {
+ nAbsValY = 0x7fffffff;
+ flAbsScaleY = 1.0;
+ }
+ if ( m_vecAbsVal.z != 0.0f )
+ {
+ nAbsValZ = 0x7fffffff;
+ flAbsScaleZ = 1.0;
+ }
+
+ float ValueScaleX, ValueScaleY, ValueScaleZ, ValueBaseX, ValueBaseY, ValueBaseZ;
+
+ ValueScaleX = ( flAbsScaleX *(m_vecOutputMax.x-m_vecOutputMin.x ) );
+ ValueBaseX = (m_vecOutputMin.x+ ( ( 1.0 - flAbsScaleX ) *( m_vecOutputMax.x-m_vecOutputMin.x ) ) );
+
+ ValueScaleY = ( flAbsScaleY *(m_vecOutputMax.y-m_vecOutputMin.y ) );
+ ValueBaseY = (m_vecOutputMin.y+ ( ( 1.0 - flAbsScaleY ) *( m_vecOutputMax.y-m_vecOutputMin.y ) ) );
+
+ ValueScaleZ = ( flAbsScaleZ *(m_vecOutputMax.z-m_vecOutputMin.z ) );
+ ValueBaseZ = (m_vecOutputMin.z+ ( ( 1.0 - flAbsScaleZ ) *( m_vecOutputMax.z-m_vecOutputMin.z ) ) );
+
+
+ float CoordScale = m_flNoiseScale;
+ float CoordScaleLoc = m_flNoiseScaleLoc;
+
+ Vector ofs_y = Vector( 100000.5, 300000.25, 9000000.75 );
+ Vector ofs_z = Vector( 110000.25, 310000.75, 9100000.5 );
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+
+ Vector Coord, Coord2, Coord3, CoordLoc;
+ SetVectorFromAttribute( CoordLoc, xyz );
+ CoordLoc += m_vecOffsetLoc;
+
+ float Offset = m_flOffset;
+ Coord = Vector ( (*pCreationTime + Offset), (*pCreationTime + Offset), (*pCreationTime + Offset) );
+
+ Coord *= CoordScale;
+ CoordLoc *= CoordScaleLoc;
+ Coord += CoordLoc;
+
+ Coord2 = ( Coord );
+ Coord3 = ( Coord );
+
+ fltx4 flNoise128;
+ FourVectors fvNoise;
+
+ fvNoise.DuplicateVector( Coord );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoiseX = SubFloat( flNoise128, 0 );
+
+ fvNoise.DuplicateVector( Coord2 + ofs_y );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoiseY = SubFloat( flNoise128, 0 );
+
+ fvNoise.DuplicateVector( Coord3 + ofs_z );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoiseZ = SubFloat( flNoise128, 0 );
+
+ *( (int *) &flNoiseX) &= nAbsValX;
+ *( (int *) &flNoiseY) &= nAbsValY;
+ *( (int *) &flNoiseZ) &= nAbsValZ;
+
+ if ( m_bNoiseAbs )
+ {
+ if ( m_vecAbsValInv.x != 0.0f )
+ {
+ flNoiseX = 1.0 - flNoiseX;
+ }
+
+ if ( m_vecAbsValInv.y != 0.0f )
+ {
+ flNoiseY = 1.0 - flNoiseY;
+ }
+ if ( m_vecAbsValInv.z != 0.0f )
+ {
+ flNoiseZ = 1.0 - flNoiseZ;
+ }
+ }
+
+ Vector poffset;
+ poffset.x = ( ValueBaseX + ( ValueScaleX * flNoiseX ) );
+ poffset.y = ( ValueBaseY + ( ValueScaleY * flNoiseY ) );
+ poffset.z = ( ValueBaseZ + ( ValueScaleZ * flNoiseZ ) );
+
+ poffset *= pParticles->m_flPreviousDt;
+
+ if ( m_bLocalSpace )
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, *pCreationTime, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ VectorRotate( poffset, mat, vecTransformLocal );
+ poffset = vecTransformLocal;
+ }
+ pxyz[0] -= poffset.x;
+ pxyz[4] -= poffset.y;
+ pxyz[8] -= poffset.z;
+ }
+}
+
+
+
+
+class C_INIT_RandomLifeTime : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomLifeTime );
+
+ float m_fLifetimeMin;
+ float m_fLifetimeMax;
+ float m_fLifetimeRandExponent;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const;
+
+ void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ if ( m_fLifetimeRandExponent != 1.0f )
+ {
+ InitScalarAttributeRandomRangeExpBlock( PARTICLE_ATTRIBUTE_LIFE_DURATION,
+ m_fLifetimeMin, m_fLifetimeMax, m_fLifetimeRandExponent,
+ pParticles, start_block, n_blocks );
+ }
+ else
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_LIFE_DURATION,
+ m_fLifetimeMin, m_fLifetimeMax, pParticles, start_block, n_blocks );
+ }
+
+ }
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomLifeTime, "Lifetime Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomLifeTime )
+ DMXELEMENT_UNPACK_FIELD( "lifetime_min", "0", float, m_fLifetimeMin )
+ DMXELEMENT_UNPACK_FIELD( "lifetime_max", "0", float, m_fLifetimeMax )
+ DMXELEMENT_UNPACK_FIELD( "lifetime_random_exponent", "1", float, m_fLifetimeRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomLifeTime )
+
+void C_INIT_RandomLifeTime::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *dtime = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+ *dtime = pParticles->RandomFloatExp( m_fLifetimeMin, m_fLifetimeMax, m_fLifetimeRandExponent );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Random radius
+//-----------------------------------------------------------------------------
+class C_INIT_RandomRadius : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomRadius );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const;
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ if ( m_flRadiusRandExponent != 1.0f )
+ {
+ InitScalarAttributeRandomRangeExpBlock( PARTICLE_ATTRIBUTE_RADIUS,
+ m_flRadiusMin, m_flRadiusMax, m_flRadiusRandExponent,
+ pParticles, start_block, n_blocks );
+ }
+ else
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_RADIUS,
+ m_flRadiusMin, m_flRadiusMax,
+ pParticles, start_block, n_blocks );
+ }
+
+ }
+
+ float m_flRadiusMin;
+ float m_flRadiusMax;
+ float m_flRadiusRandExponent;
+};
+
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomRadius, "Radius Random", OPERATOR_PI_RADIUS );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRadius )
+ DMXELEMENT_UNPACK_FIELD( "radius_min", "1", float, m_flRadiusMin )
+ DMXELEMENT_UNPACK_FIELD( "radius_max", "1", float, m_flRadiusMax )
+ DMXELEMENT_UNPACK_FIELD( "radius_random_exponent", "1", float, m_flRadiusRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRadius )
+
+void C_INIT_RandomRadius::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *r = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_RADIUS, start_p );
+ *r = pParticles->RandomFloatExp( m_flRadiusMin, m_flRadiusMax, m_flRadiusRandExponent );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Random alpha
+//-----------------------------------------------------------------------------
+class C_INIT_RandomAlpha : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomAlpha );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flAlphaMin = m_nAlphaMin / 255.0f;
+ m_flAlphaMax = m_nAlphaMax / 255.0f;
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ if ( m_flAlphaRandExponent != 1.0f )
+ {
+ InitScalarAttributeRandomRangeExpBlock( PARTICLE_ATTRIBUTE_ALPHA,
+ m_flAlphaMin, m_flAlphaMax, m_flAlphaRandExponent,
+ pParticles, start_block, n_blocks );
+ }
+ else
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_ALPHA,
+ m_flAlphaMin, m_flAlphaMax,
+ pParticles, start_block, n_blocks );
+ }
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *pAlpha = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_ALPHA, start_p );
+ *pAlpha = pParticles->RandomFloatExp( m_flAlphaMin, m_flAlphaMax, m_flAlphaRandExponent );
+ }
+ }
+
+ int m_nAlphaMin;
+ int m_nAlphaMax;
+ float m_flAlphaMin;
+ float m_flAlphaMax;
+ float m_flAlphaRandExponent;
+};
+
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomAlpha, "Alpha Random", OPERATOR_PI_ALPHA );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomAlpha )
+ DMXELEMENT_UNPACK_FIELD( "alpha_min", "255", int, m_nAlphaMin )
+ DMXELEMENT_UNPACK_FIELD( "alpha_max", "255", int, m_nAlphaMax )
+ DMXELEMENT_UNPACK_FIELD( "alpha_random_exponent", "1", float, m_flAlphaRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomAlpha )
+
+
+//-----------------------------------------------------------------------------
+// Random rotation
+//-----------------------------------------------------------------------------
+class CGeneralRandomRotation : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToInit( void ) const = 0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return (1 << GetAttributeToInit() );
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flRadians = m_flDegrees * ( M_PI / 180.0f );
+ m_flRadiansMin = m_flDegreesMin * ( M_PI / 180.0f );
+ m_flRadiansMax = m_flDegreesMax * ( M_PI / 180.0f );
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ if ( m_flRotationRandExponent != 1.0f )
+ {
+ InitScalarAttributeRandomRangeExpBlock( GetAttributeToInit(),
+ m_flRadiansMin, m_flRadiansMax, m_flRotationRandExponent,
+ pParticles, start_block, n_blocks );
+ }
+ else
+ {
+ InitScalarAttributeRandomRangeBlock( GetAttributeToInit(),
+ m_flRadiansMin, m_flRadiansMax,
+ pParticles, start_block, n_blocks );
+ }
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *drot = pParticles->GetFloatAttributePtrForWrite( GetAttributeToInit(), start_p );
+ *drot = m_flRadians + pParticles->RandomFloatExp( m_flRadiansMin, m_flRadiansMax, m_flRotationRandExponent );
+ }
+ }
+
+ // User-specified range
+ float m_flDegreesMin;
+ float m_flDegreesMax;
+ float m_flDegrees;
+
+ // Converted range
+ float m_flRadiansMin;
+ float m_flRadiansMax;
+ float m_flRadians;
+ float m_flRotationRandExponent;
+};
+
+
+class CAddGeneralRandomRotation : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToInit( void ) const = 0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return (1 << GetAttributeToInit() );
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return (1 << GetAttributeToInit() );
+ }
+
+ virtual bool InitMultipleOverride() { return true; }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flRadians = m_flDegrees * ( M_PI / 180.0f );
+ m_flRadiansMin = m_flDegreesMin * ( M_PI / 180.0f );
+ m_flRadiansMax = m_flDegreesMax * ( M_PI / 180.0f );
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ AddScalarAttributeRandomRangeBlock( GetAttributeToInit(),
+ m_flRadiansMin, m_flRadiansMax, m_flRotationRandExponent,
+ pParticles, start_block, n_blocks, m_bRandomlyFlipDirection );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ if ( !m_bRandomlyFlipDirection )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *pAttr = pParticles->GetFloatAttributePtrForWrite( GetAttributeToInit(), start_p );
+ *pAttr += m_flRadians + pParticles->RandomFloatExp( m_flRadiansMin, m_flRadiansMax, m_flRotationRandExponent );
+ }
+ }
+ else
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *pAttr = pParticles->GetFloatAttributePtrForWrite( GetAttributeToInit(), start_p );
+ float flSpeed = m_flRadians + pParticles->RandomFloatExp( m_flRadiansMin, m_flRadiansMax, m_flRotationRandExponent );
+ bool bFlip = ( pParticles->RandomFloat( -1.0f, 1.0f ) >= 0.0f );
+ *pAttr += bFlip ? -flSpeed : flSpeed;
+ }
+ }
+ }
+
+ // User-specified range
+ float m_flDegreesMin;
+ float m_flDegreesMax;
+ float m_flDegrees;
+
+ // Converted range
+ float m_flRadiansMin;
+ float m_flRadiansMax;
+ float m_flRadians;
+ float m_flRotationRandExponent;
+ bool m_bRandomlyFlipDirection;
+};
+
+
+//-----------------------------------------------------------------------------
+// Random rotation
+//-----------------------------------------------------------------------------
+class C_INIT_RandomRotation : public CGeneralRandomRotation
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomRotation );
+
+ virtual int GetAttributeToInit( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomRotation, "Rotation Random", OPERATOR_PI_ROTATION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRotation )
+ DMXELEMENT_UNPACK_FIELD( "rotation_initial", "0", float, m_flDegrees )
+ DMXELEMENT_UNPACK_FIELD( "rotation_offset_min", "0", float, m_flDegreesMin )
+ DMXELEMENT_UNPACK_FIELD( "rotation_offset_max", "360", float, m_flDegreesMax )
+ DMXELEMENT_UNPACK_FIELD( "rotation_random_exponent", "1", float, m_flRotationRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRotation )
+
+
+//-----------------------------------------------------------------------------
+// Random rotation speed
+//-----------------------------------------------------------------------------
+class C_INIT_RandomRotationSpeed : public CAddGeneralRandomRotation
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomRotationSpeed );
+
+ virtual int GetAttributeToInit( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_SPEED;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomRotationSpeed, "Rotation Speed Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRotationSpeed )
+ DMXELEMENT_UNPACK_FIELD( "rotation_speed_constant", "0", float, m_flDegrees )
+ DMXELEMENT_UNPACK_FIELD( "rotation_speed_random_min", "0", float, m_flDegreesMin )
+ DMXELEMENT_UNPACK_FIELD( "rotation_speed_random_max", "360", float, m_flDegreesMax )
+ DMXELEMENT_UNPACK_FIELD( "rotation_speed_random_exponent", "1", float, m_flRotationRandExponent )
+ DMXELEMENT_UNPACK_FIELD( "randomly_flip_direction", "1", bool, m_bRandomlyFlipDirection )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomRotationSpeed )
+
+
+//-----------------------------------------------------------------------------
+// Random yaw
+//-----------------------------------------------------------------------------
+class C_INIT_RandomYaw : public CGeneralRandomRotation
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomYaw );
+
+ virtual int GetAttributeToInit( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_YAW;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomYaw, "Rotation Yaw Random", OPERATOR_PI_YAW );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomYaw )
+ DMXELEMENT_UNPACK_FIELD( "yaw_initial", "0", float, m_flDegrees )
+ DMXELEMENT_UNPACK_FIELD( "yaw_offset_min", "0", float, m_flDegreesMin )
+ DMXELEMENT_UNPACK_FIELD( "yaw_offset_max", "360", float, m_flDegreesMax )
+ DMXELEMENT_UNPACK_FIELD( "yaw_random_exponent", "1", float, m_flRotationRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomYaw )
+
+
+//-----------------------------------------------------------------------------
+// Random color
+//-----------------------------------------------------------------------------
+class C_INIT_RandomColor : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomColor );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ struct C_OP_RandomColorContext_t
+ {
+ Vector m_vPrevPosition;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RandomColorContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RandomColorContext_t *pCtx=reinterpret_cast<C_OP_RandomColorContext_t *>( pContext );
+ pCtx->m_vPrevPosition = vec3_origin;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flNormColorMin[0] = (float) m_ColorMin[0] / 255.0f;
+ m_flNormColorMin[1] = (float) m_ColorMin[1] / 255.0f;
+ m_flNormColorMin[2] = (float) m_ColorMin[2] / 255.0f;
+
+ m_flNormColorMax[0] = (float) m_ColorMax[0] / 255.0f;
+ m_flNormColorMax[1] = (float) m_ColorMax[1] / 255.0f;
+ m_flNormColorMax[2] = (float) m_ColorMax[2] / 255.0f;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ C_OP_RandomColorContext_t *pCtx=reinterpret_cast<C_OP_RandomColorContext_t *>( pContext );
+
+ Color tint( 255, 255, 255, 255 );
+
+ // If we're factoring in luminosity or tint, then get our lighting info for this position
+ if ( m_flTintPerc )
+ {
+ if ( pParticles->m_pParent && pParticles->m_pParent->m_LocalLightingCP == m_nTintCP )
+ {
+ tint = pParticles->m_pParent->m_LocalLighting;
+ }
+ else
+ {
+ // FIXME: Really, we want the emission point for each particle, but for now, we do it more cheaply
+ // Get our control point
+ Vector vecOrigin;
+ pParticles->GetControlPointAtTime( m_nTintCP, pParticles->m_flCurTime, &vecOrigin );
+
+ if ( ( ( pCtx->m_vPrevPosition - vecOrigin ).Length() >= m_flUpdateThreshold ) || ( pParticles->m_LocalLightingCP == -1 ) )
+ {
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( vecOrigin, tint );
+ pParticles->m_LocalLighting = tint;
+ pParticles->m_LocalLightingCP = m_nTintCP;
+ pCtx->m_vPrevPosition = vecOrigin;
+ }
+ else
+ tint = pParticles->m_LocalLighting;
+
+ }
+ tint[0] = max ( m_TintMin[0], min( tint[0], m_TintMax[0] ) );
+ tint[1] = max ( m_TintMin[1], min( tint[1], m_TintMax[1] ) );
+ tint[2] = max ( m_TintMin[2], min( tint[2], m_TintMax[2] ) );
+ }
+
+ float randomPerc;
+ float *pColor;
+ for( ; nParticleCount--; start_p++ )
+ {
+ pColor = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_TINT_RGB, start_p );
+
+ randomPerc = pParticles->RandomFloat( 0.0f, 1.0f );
+
+ // Randomly choose a range between the two colors
+ pColor[0] = m_flNormColorMin[0] + ( ( m_flNormColorMax[0] - m_flNormColorMin[0] ) * randomPerc );
+ pColor[4] = m_flNormColorMin[1] + ( ( m_flNormColorMax[1] - m_flNormColorMin[1] ) * randomPerc );
+ pColor[8] = m_flNormColorMin[2] + ( ( m_flNormColorMax[2] - m_flNormColorMin[2] ) * randomPerc );
+
+ // Tint the particles
+ if ( m_flTintPerc )
+ {
+ pColor[0] = Lerp( m_flTintPerc, (float) pColor[0], (float) tint.r() / 255.0f );
+ pColor[4] = Lerp( m_flTintPerc, (float) pColor[4], (float) tint.g() / 255.0f );
+ pColor[8] = Lerp( m_flTintPerc, (float) pColor[8], (float) tint.b() / 255.0f );
+ }
+ }
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ C_OP_RandomColorContext_t *pCtx=reinterpret_cast<C_OP_RandomColorContext_t *>( pContext );
+
+ Color tint( 255, 255, 255, 255 );
+
+ size_t attr_stride;
+
+ FourVectors *pColor = pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_TINT_RGB, &attr_stride );
+
+ pColor += attr_stride * start_block;
+
+ FourVectors fvColorMin;
+ fvColorMin.DuplicateVector( Vector (m_flNormColorMin[0], m_flNormColorMin[1], m_flNormColorMin[2] ) );
+ FourVectors fvColorWidth;
+ fvColorWidth.DuplicateVector( Vector (m_flNormColorMax[0] - m_flNormColorMin[0], m_flNormColorMax[1] - m_flNormColorMin[1], m_flNormColorMax[2] - m_flNormColorMin[2] ) );
+
+ int nRandContext = GetSIMDRandContext();
+
+ // If we're factoring in luminosity or tint, then get our lighting info for this position
+ if ( m_flTintPerc )
+ {
+ if ( pParticles->m_pParent && pParticles->m_pParent->m_LocalLightingCP == m_nTintCP )
+ {
+ tint = pParticles->m_pParent->m_LocalLighting;
+ }
+ else
+ {
+ // FIXME: Really, we want the emission point for each particle, but for now, we do it more cheaply
+ // Get our control point
+ Vector vecOrigin;
+ pParticles->GetControlPointAtTime( m_nTintCP, pParticles->m_flCurTime, &vecOrigin );
+
+ if ( ( ( pCtx->m_vPrevPosition - vecOrigin ).Length() >= m_flUpdateThreshold ) || ( pParticles->m_LocalLightingCP == -1 ) )
+ {
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( vecOrigin, tint );
+ pParticles->m_LocalLighting = tint;
+ pParticles->m_LocalLightingCP = m_nTintCP;
+ pCtx->m_vPrevPosition = vecOrigin;
+ }
+ else
+ tint = pParticles->m_LocalLighting;
+ }
+
+ tint[0] = max ( m_TintMin[0], min( tint[0], m_TintMax[0] ) );
+ tint[1] = max ( m_TintMin[1], min( tint[1], m_TintMax[1] ) );
+ tint[2] = max ( m_TintMin[2], min( tint[2], m_TintMax[2] ) );
+
+ FourVectors fvTint;
+ fvTint.DuplicateVector( Vector ( tint[0], tint[1], tint[2] ) );
+ fltx4 fl4Divisor = ReplicateX4( 1.0f / 255.0f );
+ fvTint *= fl4Divisor;
+ fltx4 fl4TintPrc = ReplicateX4( m_flTintPerc );
+
+ while( n_blocks-- )
+ {
+ FourVectors fvColor = fvColorWidth;
+ FourVectors fvColor2 = fvTint;
+ fvColor *= RandSIMD( nRandContext );
+ fvColor += fvColorMin;
+ fvColor2 -= fvColor;
+ fvColor2 *= fl4TintPrc;
+ fvColor2 += fvColor;
+ *pColor = fvColor2;
+ pColor += attr_stride;
+ }
+ }
+ else
+ {
+ while( n_blocks-- )
+ {
+ FourVectors fvColor = fvColorWidth;
+ fvColor *= RandSIMD( nRandContext );
+ fvColor += fvColorMin;
+ *pColor = fvColor;
+ pColor += attr_stride;
+ }
+ }
+ ReleaseSIMDRandContext( nRandContext );
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nTintCP;
+ }
+
+ float m_flNormColorMin[3];
+ float m_flNormColorMax[3];
+ Color m_ColorMin;
+ Color m_ColorMax;
+ Color m_TintMin;
+ Color m_TintMax;
+ float m_flTintPerc;
+ float m_flUpdateThreshold;
+ int m_nTintCP;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomColor, "Color Random", OPERATOR_PI_TINT_RGB );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomColor )
+ DMXELEMENT_UNPACK_FIELD( "color1", "255 255 255 255", Color, m_ColorMin )
+ DMXELEMENT_UNPACK_FIELD( "color2", "255 255 255 255", Color, m_ColorMax )
+ DMXELEMENT_UNPACK_FIELD( "tint_perc", "0.0", float, m_flTintPerc )
+ DMXELEMENT_UNPACK_FIELD( "tint control point", "0", int, m_nTintCP )
+ DMXELEMENT_UNPACK_FIELD( "tint clamp min", "0 0 0 0", Color, m_TintMin )
+ DMXELEMENT_UNPACK_FIELD( "tint clamp max", "255 255 255 255", Color, m_TintMax )
+ DMXELEMENT_UNPACK_FIELD( "tint update movement threshold", "32", float, m_flUpdateThreshold )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomColor )
+
+
+//-----------------------------------------------------------------------------
+// Trail Length
+//-----------------------------------------------------------------------------
+class C_INIT_RandomTrailLength : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomTrailLength );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TRAIL_LENGTH_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ if ( m_flLengthRandExponent != 1.0f )
+ {
+ InitScalarAttributeRandomRangeExpBlock( PARTICLE_ATTRIBUTE_TRAIL_LENGTH,
+ m_flMinLength, m_flMaxLength, m_flLengthRandExponent,
+ pParticles, start_block, n_blocks );
+ }
+ else
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_TRAIL_LENGTH,
+ m_flMinLength, m_flMaxLength,
+ pParticles, start_block, n_blocks );
+ }
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ float *pLength;
+ for( ; nParticleCount--; start_p++ )
+ {
+ pLength = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, start_p );
+ *pLength = pParticles->RandomFloatExp( m_flMinLength, m_flMaxLength, m_flLengthRandExponent );
+ }
+ }
+
+ float m_flMinLength;
+ float m_flMaxLength;
+ float m_flLengthRandExponent;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomTrailLength, "Trail Length Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomTrailLength )
+ DMXELEMENT_UNPACK_FIELD( "length_min", "0.1", float, m_flMinLength )
+ DMXELEMENT_UNPACK_FIELD( "length_max", "0.1", float, m_flMaxLength )
+ DMXELEMENT_UNPACK_FIELD( "length_random_exponent", "1", float, m_flLengthRandExponent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomTrailLength )
+
+//-----------------------------------------------------------------------------
+// Random sequence
+//-----------------------------------------------------------------------------
+class C_INIT_RandomSequence : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomSequence );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ // TODO: Validate the ranges here!
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER,
+ m_nSequenceMin, m_nSequenceMax,
+ pParticles, start_block, n_blocks );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ float *pSequence;
+ for( ; nParticleCount--; start_p++ )
+ {
+ pSequence = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, start_p );
+ *pSequence = pParticles->RandomInt( m_nSequenceMin, m_nSequenceMax );
+ }
+ }
+
+ int m_nSequenceMin;
+ int m_nSequenceMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomSequence, "Sequence Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomSequence )
+ DMXELEMENT_UNPACK_FIELD( "sequence_min", "0", int, m_nSequenceMin )
+ DMXELEMENT_UNPACK_FIELD( "sequence_max", "0", int, m_nSequenceMax )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomSequence )
+
+
+//-----------------------------------------------------------------------------
+// Position Warp Initializer
+// Scales initial position and velocity of particles within a random vector range
+//-----------------------------------------------------------------------------
+class C_INIT_PositionWarp : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_PositionOffset );
+
+ Vector m_vecWarpMin;
+ Vector m_vecWarpMax;
+ int m_nControlPointNumber;
+ float m_flWarpTime, m_flWarpStartTime;
+ bool m_bInvertWarp;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_PositionWarp, "Position Modify Warp Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_PositionWarp )
+ DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "warp min", "1 1 1", Vector, m_vecWarpMin )
+ DMXELEMENT_UNPACK_FIELD( "warp max", "1 1 1", Vector, m_vecWarpMax )
+ DMXELEMENT_UNPACK_FIELD( "warp transition time (treats min/max as start/end sizes)", "0", float , m_flWarpTime )
+ DMXELEMENT_UNPACK_FIELD( "warp transition start time", "0", float , m_flWarpStartTime )
+ DMXELEMENT_UNPACK_FIELD( "reverse warp (0/1)", "0", bool , m_bInvertWarp )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_PositionWarp )
+
+
+void C_INIT_PositionWarp::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ Vector vecWarpStart = m_vecWarpMin;
+ Vector vecWarpEnd = m_vecWarpMax;
+
+ if ( m_bInvertWarp )
+ {
+ vecWarpStart = m_vecWarpMax;
+ vecWarpEnd = m_vecWarpMin;
+ }
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+
+ Vector randpos;
+
+ if ( m_flWarpTime != 0.0f )
+ {
+ float flWarpEnd = m_flWarpStartTime + m_flWarpTime;
+ float flPercentage = RemapValClamped( *ct, m_flWarpStartTime, flWarpEnd, 0.0, 1.0 );
+ VectorLerp( vecWarpStart, vecWarpEnd, flPercentage, randpos );
+ }
+ else
+ {
+ pParticles->RandomVector( m_vecWarpMin, m_vecWarpMax, &randpos );
+ }
+
+
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, *ct, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ Vector vecParticlePosition, vecParticlePosition_prev ;
+ SetVectorFromAttribute( vecParticlePosition, xyz );
+ SetVectorFromAttribute( vecParticlePosition_prev, pxyz );
+ // rotate particles from world space into local
+ VectorITransform( vecParticlePosition, mat, vecTransformLocal );
+ // multiply position by desired amount
+ vecTransformLocal.x *= randpos.x;
+ vecTransformLocal.y *= randpos.y;
+ vecTransformLocal.z *= randpos.z;
+ // rotate back into world space
+ VectorTransform( vecTransformLocal, mat, vecParticlePosition );
+ // rinse, repeat
+ VectorITransform( vecParticlePosition_prev, mat, vecTransformLocal );
+ vecTransformLocal.x *= randpos.x;
+ vecTransformLocal.y *= randpos.y;
+ vecTransformLocal.z *= randpos.z;
+ VectorTransform( vecTransformLocal, mat, vecParticlePosition_prev );
+ // set positions into floats
+ SetVectorAttribute( xyz, vecParticlePosition );
+ SetVectorAttribute( pxyz, vecParticlePosition_prev );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// noise initializer
+//-----------------------------------------------------------------------------
+class C_INIT_CreationNoise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreationNoise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const;
+
+ virtual bool IsScrubSafe() { return true; }
+ int m_nFieldOutput;
+ bool m_bAbsVal, m_bAbsValInv;
+ float m_flOffset;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ float m_flNoiseScale, m_flNoiseScaleLoc;
+ Vector m_vecOffsetLoc;
+ float m_flWorldTimeScale;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreationNoise, "Remap Noise to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreationNoise )
+ DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","0.1",float,m_flNoiseScale)
+ DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","0.001",float,m_flNoiseScaleLoc)
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset )
+ DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc )
+ DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal )
+ DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+ DMXELEMENT_UNPACK_FIELD( "world time noise coordinate scale","0", float, m_flWorldTimeScale )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreationNoise );
+
+
+
+
+void C_INIT_CreationNoise::InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+{
+ float flAbsScale;
+ fltx4 fl4AbsVal;
+ fl4AbsVal = CmpEqSIMD( Four_Zeros, Four_Zeros );
+ flAbsScale = 0.5;
+
+ // Set up values for more optimal absolute value calculations inside the loop
+ if ( m_bAbsVal )
+ {
+ fl4AbsVal = LoadAlignedSIMD( (float *) g_SIMD_clear_signmask );
+ flAbsScale = 1.0;
+ }
+
+ 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 );
+ }
+
+ float CoordScale = m_flNoiseScale;
+ float CoordScaleLoc = m_flNoiseScaleLoc;
+
+ float ValueScale, ValueBase;
+ ValueScale = ( flAbsScale *( fMax - fMin ) );
+ ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );
+
+ fltx4 fl4ValueBase = ReplicateX4( ValueBase );
+ fltx4 fl4ValueScale = ReplicateX4( ValueScale );
+
+ size_t attr_stride;
+
+ fltx4 *pAttr = pParticles->GetM128AttributePtrForWrite( m_nFieldOutput, &attr_stride );
+ pAttr += attr_stride * start_block;
+ const FourVectors *pxyz = pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &attr_stride );
+ pxyz += attr_stride * start_block;
+ const fltx4 *pCreationTime = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &attr_stride );
+ pCreationTime += attr_stride * start_block;
+
+ //setup
+ fltx4 fl4Offset = ReplicateX4( m_flOffset );
+ FourVectors fvOffsetLoc;
+ fvOffsetLoc.DuplicateVector( m_vecOffsetLoc );
+ FourVectors fvCoordBase;
+ fvCoordBase.x = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoordBase.y = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoordBase.z = AddSIMD(*pCreationTime, fl4Offset);
+ fvCoordBase *= CoordScale;
+
+ while( n_blocks-- )
+ {
+ FourVectors fvCoordLoc = *pxyz;
+ fvCoordLoc += fvOffsetLoc;
+ FourVectors fvCoord = fvCoordBase;
+ fvCoordLoc *= CoordScaleLoc;
+ fvCoord += fvCoordLoc;
+
+ fltx4 fl4Noise;
+
+ fl4Noise = NoiseSIMD( fvCoord );
+
+ fl4Noise = AndSIMD ( fl4Noise, fl4AbsVal );
+
+ if ( m_bAbsValInv )
+ {
+ fl4Noise = SubSIMD( Four_Ones, fl4Noise );
+ }
+
+ fltx4 fl4InitialNoise;
+
+ fl4InitialNoise = AddSIMD( fl4ValueBase, ( MulSIMD( fl4ValueScale, fl4Noise ) ) );
+
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & (1 << m_nFieldOutput ) )
+ {
+ fl4InitialNoise = MinSIMD( Four_Ones, fl4InitialNoise );
+ fl4InitialNoise = MaxSIMD( Four_Zeros, fl4InitialNoise );
+ }
+
+ *( pAttr ) = fl4InitialNoise;
+
+ pAttr += attr_stride;
+ pxyz += attr_stride;
+
+ }
+}
+
+
+
+void C_INIT_CreationNoise::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ float flAbsScale;
+ int nAbsVal;
+ nAbsVal = 0xffffffff;
+ flAbsScale = 0.5;
+ if ( m_bAbsVal )
+ {
+ nAbsVal = 0x7fffffff;
+ flAbsScale = 1.0;
+ }
+
+ float fMin = m_flOutputMin;
+ float fMax = m_flOutputMax;
+
+ if ( ATTRIBUTES_WHICH_ARE_ANGLES & (1 << m_nFieldOutput ) )
+ {
+ fMin *= ( M_PI / 180.0f );
+ fMax *= ( M_PI / 180.0f );
+ }
+
+ float CoordScale = m_flNoiseScale;
+ float CoordScaleLoc = m_flNoiseScaleLoc;
+
+ float ValueScale, ValueBase;
+ ValueScale = ( flAbsScale *( fMax - fMin ) );
+ ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );
+
+ Vector CoordLoc, CoordWorldTime, CoordBase;
+ const float *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float Offset = m_flOffset;
+ CoordBase = Vector ( (*pCreationTime + Offset), (*pCreationTime + Offset), (*pCreationTime + Offset) );
+ CoordBase *= CoordScale;
+ CoordWorldTime = Vector( (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale) );
+ CoordBase += CoordWorldTime;
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *pxyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pAttr = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, start_p );
+
+ Vector Coord = CoordBase;
+
+ CoordLoc.x = pxyz[0];
+ CoordLoc.y = pxyz[4];
+ CoordLoc.z = pxyz[8];
+ CoordLoc += m_vecOffsetLoc;
+
+ CoordLoc *= CoordScaleLoc;
+ Coord += CoordLoc;
+
+ fltx4 flNoise128;
+ FourVectors fvNoise;
+
+ fvNoise.DuplicateVector( Coord );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoise = SubFloat( flNoise128, 0 );
+
+ *( (int *) &flNoise) &= nAbsVal;
+
+ if ( m_bAbsValInv )
+ {
+ flNoise = 1.0 - flNoise;
+ }
+
+ float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) );
+
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & (1 << m_nFieldOutput ) )
+ {
+ flInitialNoise = clamp(flInitialNoise, 0.0f, 1.0f );
+ }
+
+ *( pAttr ) = flInitialNoise;
+ }
+}
+
+
+
+
+
+
+class C_INIT_CreateAlongPath : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateAlongPath );
+
+ float m_fMaxDistance;
+ struct CPathParameters m_PathParams;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ uint64 nStartMask = ( 1ULL << m_PathParams.m_nStartControlPointNumber ) - 1;
+ uint64 nEndMask = ( 1ULL << ( m_PathParams.m_nEndControlPointNumber + 1 ) ) - 1;
+ return nEndMask & (~nStartMask);
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_PathParams.ClampControlPointIndices();
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateAlongPath, "Position Along Path Random", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateAlongPath )
+ 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 )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateAlongPath )
+
+
+void C_INIT_CreateAlongPath::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+
+ Vector StartPnt, MidP, EndPnt;
+ pParticles->CalculatePathValues( m_PathParams, *ct, &StartPnt, &MidP, &EndPnt);
+
+ float t=pParticles->RandomFloat( 0.0, 1.0 );
+
+ Vector randpos;
+ pParticles->RandomVector( -m_fMaxDistance, m_fMaxDistance, &randpos );
+
+ // 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;
+
+ Pnt+=randpos;
+
+ xyz[0] = Pnt.x;
+ xyz[4] = Pnt.y;
+ xyz[8] = Pnt.z;
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ pxyz[0] = Pnt.x;
+ pxyz[4] = Pnt.y;
+ pxyz[8] = Pnt.z;
+ }
+ }
+}
+
+
+
+
+
+class C_INIT_MoveBetweenPoints : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_MoveBetweenPoints );
+
+ float m_flSpeedMin, m_flSpeedMax;
+ float m_flEndSpread;
+ float m_flStartOffset;
+ int m_nEndControlPointNumber;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nEndControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_MoveBetweenPoints, "Move Particles Between 2 Control Points", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_MoveBetweenPoints )
+ DMXELEMENT_UNPACK_FIELD( "minimum speed", "1", float, m_flSpeedMin )
+ DMXELEMENT_UNPACK_FIELD( "maximum speed", "1", float, m_flSpeedMax )
+ DMXELEMENT_UNPACK_FIELD( "end spread", "0", float, m_flEndSpread )
+ DMXELEMENT_UNPACK_FIELD( "start offset", "0", float, m_flStartOffset )
+ DMXELEMENT_UNPACK_FIELD( "end control point", "1", int, m_nEndControlPointNumber )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_MoveBetweenPoints )
+
+
+void C_INIT_MoveBetweenPoints::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ bool bMoveStartPnt = ( m_flStartOffset > 0.0 );
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pPrevXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+
+ float *dtime = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+
+
+ Vector StartPnt( pxyz[0], pxyz[4], pxyz[8] );
+
+ Vector vecControlPoint;
+
+ pParticles->GetControlPointAtTime( m_nEndControlPointNumber, *ct, &vecControlPoint );
+
+ Vector randpos(0,0,0);
+
+ if ( m_flEndSpread > 0.0 )
+ {
+ pParticles->RandomVectorInUnitSphere( &randpos );
+ randpos *= m_flEndSpread;
+ }
+
+ vecControlPoint += randpos;
+
+ Vector vDelta = vecControlPoint - StartPnt;
+ float flLen = VectorLength( vDelta );
+
+ if ( bMoveStartPnt )
+ {
+ StartPnt += ( m_flStartOffset/(flLen+FLT_EPSILON) ) * vDelta;
+ vDelta = vecControlPoint - StartPnt;
+ flLen = VectorLength( vDelta );
+ }
+
+ float flVel = pParticles->RandomFloat( m_flSpeedMin, m_flSpeedMax );
+
+ *dtime = flLen/( flVel+FLT_EPSILON);
+
+ Vector poffset = vDelta * (flVel/flLen ) ;
+
+ poffset *= pParticles->m_flPreviousDt;
+
+ if ( bMoveStartPnt )
+ {
+ pxyz[0] = StartPnt.x;
+ pxyz[1] = StartPnt.y;
+ pxyz[2] = StartPnt.z;
+ }
+
+ pPrevXYZ[0] = pxyz[0] - poffset.x;
+ pPrevXYZ[4] = pxyz[4] - poffset.y;
+ pPrevXYZ[8] = pxyz[8] - poffset.z;
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Remap Scalar Initializer
+//-----------------------------------------------------------------------------
+class C_INIT_RemapScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RemapScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 1 << m_nFieldInput;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ int m_nFieldInput;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bScaleInitialRange;
+ bool m_bActiveRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RemapScalar, "Remap Initial Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RemapScalar )
+ 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_USERDATA( "input field", "8", 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 )
+ 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_INIT_RemapScalar )
+
+void C_INIT_RemapScalar::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, 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 );
+ }
+
+ // FIXME: SSE-ize
+ for( ; nParticleCount--; start_p++ )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ // using raw creation time to map to emitter lifespan
+ float flLifeTime = *pCreationTime;
+
+ float flInput;
+ if ( ATTRIBUTES_WHICH_ARE_INTS & ( 1 << m_nFieldInput ) )
+ {
+ const int *pInput = pParticles->GetIntAttributePtr( m_nFieldInput, start_p );
+ flInput = float( *pInput );
+ }
+ else
+ {
+ const float *pInput = pParticles->GetFloatAttributePtr( m_nFieldInput, start_p );
+ flInput = *pInput;
+ }
+
+ // only use within start/end time frame and, if set, active input range
+ if ( ( ( ( flLifeTime < m_flStartTime ) || ( flLifeTime >= m_flEndTime ) ) && ( ( m_flStartTime != -1.0f) && ( m_flEndTime != -1.0f) ) ) || ( m_bActiveRange && ( flInput < m_flInputMin || flInput > m_flInputMax ) ) )
+ continue;
+
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, start_p );
+ 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;
+ }
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Inherit Velocity Initializer
+// Causes particles to inherit the velocity of their CP at spawn
+//
+//-----------------------------------------------------------------------------
+class C_INIT_InheritVelocity : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_InheritVelocity );
+
+ int m_nControlPointNumber;
+ float m_flVelocityScale;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_InheritVelocity, "Velocity Inherit from Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_InheritVelocity )
+DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "velocity scale", "1", float, m_flVelocityScale )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_InheritVelocity )
+
+
+void C_INIT_InheritVelocity::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *ct, &vecControlPoint );
+ Vector vecControlPointPrev;
+ pParticles->GetControlPointAtPrevTime( m_nControlPointNumber, &vecControlPointPrev );
+
+ Vector vecDeltaPos = (vecControlPoint - vecControlPointPrev);
+ //Vector vecDeltaPos = (vecControlPoint - vecControlPointPrev) * pParticles->m_flDt;
+ vecDeltaPos.x *= m_flVelocityScale;
+ vecDeltaPos.y *= m_flVelocityScale;
+ vecDeltaPos.z *= m_flVelocityScale;
+
+ xyz[0] += vecDeltaPos.x;
+ xyz[4] += vecDeltaPos.y;
+ xyz[8] += vecDeltaPos.z;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Pre-Age Noise
+// Sets particle creation time back to treat newly spawned particle as if
+// part of its life has already elapsed.
+//-----------------------------------------------------------------------------
+class C_INIT_AgeNoise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_AgeNoise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ bool m_bAbsVal, m_bAbsValInv;
+ float m_flOffset;
+ float m_flAgeMin;
+ float m_flAgeMax;
+ float m_flNoiseScale, m_flNoiseScaleLoc;
+ Vector m_vecOffsetLoc;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_AgeNoise, "Lifetime Pre-Age Noise", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_AgeNoise )
+DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","1.0",float,m_flNoiseScale)
+DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","1.0",float,m_flNoiseScaleLoc)
+DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset )
+DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc )
+DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal )
+DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv )
+DMXELEMENT_UNPACK_FIELD( "start age minimum","0", float, m_flAgeMin )
+DMXELEMENT_UNPACK_FIELD( "start age maximum","1", float, m_flAgeMax )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_AgeNoise );
+
+void C_INIT_AgeNoise::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ float flAbsScale;
+ int nAbsVal;
+ nAbsVal = 0xffffffff;
+ flAbsScale = 0.5;
+ if ( m_bAbsVal )
+ {
+ nAbsVal = 0x7fffffff;
+ flAbsScale = 1.0;
+ }
+
+ float fMin = m_flAgeMin;
+ float fMax = m_flAgeMax;
+
+ float CoordScale = m_flNoiseScale;
+ float CoordScaleLoc = m_flNoiseScaleLoc;
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *pxyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ const float *pLifespan = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+ float *pAttr = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+
+ float ValueScale, ValueBase;
+
+ Vector Coord, CoordLoc;
+ CoordLoc.x = pxyz[0];
+ CoordLoc.y = pxyz[4];
+ CoordLoc.z = pxyz[8];
+ CoordLoc += m_vecOffsetLoc;
+
+ float Offset = m_flOffset;
+ Coord = Vector ( (*pCreationTime + Offset), (*pCreationTime + Offset), (*pCreationTime + Offset) );
+ Coord *= CoordScale;
+ CoordLoc *= CoordScaleLoc;
+ Coord += CoordLoc;
+
+ fltx4 flNoise128;
+ FourVectors fvNoise;
+
+ fvNoise.DuplicateVector( Coord );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoise = SubFloat( flNoise128, 0 );
+
+ *( (int *) &flNoise) &= nAbsVal;
+
+ ValueScale = ( flAbsScale *( fMax - fMin ) );
+ ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );
+
+ if ( m_bAbsValInv )
+ {
+ flNoise = 1.0 - flNoise;
+ }
+
+ float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) );
+
+
+ flInitialNoise = clamp(flInitialNoise, 0.0f, 1.0f );
+ flInitialNoise *= *pLifespan;
+
+ *( pAttr ) = *pCreationTime - flInitialNoise;
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// LifeTime Sequence Length
+//-----------------------------------------------------------------------------
+class C_INIT_SequenceLifeTime : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_SequenceLifeTime );
+
+ float m_flFramerate;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_SequenceLifeTime, "Lifetime From Sequence", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_SequenceLifeTime )
+DMXELEMENT_UNPACK_FIELD( "Frames Per Second", "30", float, m_flFramerate )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_SequenceLifeTime )
+
+void C_INIT_SequenceLifeTime::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ if ( ( m_flFramerate != 0.0f ) && ( pParticles->m_Sheet() ) )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *flSequence = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, start_p );
+ float *dtime = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+ int nSequence = *flSequence;
+
+ if ( pParticles->m_Sheet()->m_flFrameSpan[nSequence] != 0 )
+ {
+ *dtime = pParticles->m_Sheet()->m_flFrameSpan[nSequence] / m_flFramerate;
+ }
+ else
+ {
+ *dtime = 1.0;
+ }
+ }
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Create In Hierarchy
+//-----------------------------------------------------------------------------
+class C_INIT_CreateInHierarchy : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateInHierarchy );
+
+ float m_fMaxDistance;
+ float m_flGrowthTime;
+ //float m_flTraceDist;
+ float m_flDesiredMidPoint;
+ int m_nOrientation;
+ float m_flBulgeFactor;
+ int m_nDesiredEndPoint;
+ int m_nDesiredStartPoint;
+ bool m_bUseHighestEndCP;
+ Vector m_vecDistanceBias, m_vecDistanceBiasAbs;
+ bool m_bDistanceBias, m_bDistanceBiasAbs;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ uint64 nStartMask = ( 1ULL << m_nDesiredStartPoint ) - 1;
+ uint64 nEndMask = m_bUseHighestEndCP ? 0xFFFFFFFFFFFFFFFFll : ( 1ULL << ( m_nDesiredEndPoint + 1 ) ) - 1;
+ return nEndMask & (~nStartMask);
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ //fixme - confirm CPs
+ // m_PathParams.ClampControlPointIndices();
+ m_bDistanceBias = ( m_vecDistanceBias.x != 1.0f ) || ( m_vecDistanceBias.y != 1.0f ) || ( m_vecDistanceBias.z != 1.0f );
+ m_bDistanceBiasAbs = ( m_vecDistanceBiasAbs.x != 0.0f ) || ( m_vecDistanceBiasAbs.y != 0.0f ) || ( m_vecDistanceBiasAbs.z != 0.0f );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateInHierarchy, "Position In CP Hierarchy", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateInHierarchy )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance", "0", float, m_fMaxDistance )
+ DMXELEMENT_UNPACK_FIELD( "bulge", "0", float, m_flBulgeFactor )
+ DMXELEMENT_UNPACK_FIELD( "start control point number", "0", int, m_nDesiredStartPoint )
+ DMXELEMENT_UNPACK_FIELD( "end control point number", "1", int, m_nDesiredEndPoint )
+ DMXELEMENT_UNPACK_FIELD( "bulge control 0=random 1=orientation of start pnt 2=orientation of end point", "0", int, m_nOrientation )
+ DMXELEMENT_UNPACK_FIELD( "mid point position", "0.5", float, m_flDesiredMidPoint )
+ DMXELEMENT_UNPACK_FIELD( "growth time", "0.0", float, m_flGrowthTime )
+ //DMXELEMENT_UNPACK_FIELD( "trace distance for optional culling", "0.0", float, m_flTraceDist )
+ DMXELEMENT_UNPACK_FIELD( "use highest supplied end point", "0", bool, m_bUseHighestEndCP )
+ DMXELEMENT_UNPACK_FIELD( "distance_bias", "1 1 1", Vector, m_vecDistanceBias )
+ DMXELEMENT_UNPACK_FIELD( "distance_bias_absolute_value", "0 0 0", Vector, m_vecDistanceBiasAbs )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateInHierarchy )
+
+
+void C_INIT_CreateInHierarchy::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ int nEndCP;
+ float flGrowth;
+ struct CPathParameters PathParams;
+ PathParams.m_flBulge = m_flBulgeFactor;
+ PathParams.m_nBulgeControl = m_nOrientation;
+ PathParams.m_flMidPoint = m_flDesiredMidPoint;
+ int nRealEndPoint;
+
+ if ( m_bUseHighestEndCP )
+ {
+ nRealEndPoint = pParticles->GetHighestControlPoint();
+ }
+ else
+ {
+ nRealEndPoint = m_nDesiredEndPoint;
+ }
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ if ( ( pParticles->m_flCurTime <= m_flGrowthTime ) && ( nRealEndPoint > 0 ) )
+ {
+ float flCurrentEndCP = RemapValClamped( *ct, 0.0f, m_flGrowthTime, min( m_nDesiredStartPoint + 1, nRealEndPoint ), nRealEndPoint );
+ nEndCP = pParticles->RandomInt( min( m_nDesiredStartPoint + 1, (int)flCurrentEndCP ), flCurrentEndCP );
+
+ // clamp growth to the appropriate values...
+ float flEndTime = flCurrentEndCP / float(nRealEndPoint) ;
+ flGrowth = RemapValClamped( *ct, 0.0f, m_flGrowthTime, 0.0, flEndTime );
+ }
+ else
+ {
+ int nLowestStartPoint = min( m_nDesiredStartPoint + 1, nRealEndPoint );
+ nEndCP = pParticles->RandomInt( nLowestStartPoint, nRealEndPoint );
+ flGrowth = 1.0;
+ }
+
+
+ PathParams.m_nStartControlPointNumber = pParticles->m_ControlPoints[nEndCP].m_nParent;
+ PathParams.m_nEndControlPointNumber = nEndCP;
+ Vector StartPnt, MidP, EndPnt;
+
+ pParticles->CalculatePathValues( PathParams, *ct, &StartPnt, &MidP, &EndPnt);
+ EndPnt *= flGrowth;
+
+ float t=pParticles->RandomFloat( 0.0, 1.0 );
+
+ Vector randpos;
+ pParticles->RandomVector( -m_fMaxDistance, m_fMaxDistance, &randpos );
+
+ if ( m_bDistanceBiasAbs )
+ {
+ if ( m_vecDistanceBiasAbs.x != 0.0f )
+ {
+ randpos.x = fabs(randpos.x);
+ }
+ if ( m_vecDistanceBiasAbs.y != 0.0f )
+ {
+ randpos.y = fabs(randpos.y);
+ }
+ if ( m_vecDistanceBiasAbs.z != 0.0f )
+ {
+ randpos.z = fabs(randpos.z);
+ }
+ }
+ randpos *= m_vecDistanceBias;
+
+ // 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;
+
+ Pnt+=randpos;
+ // Optional Culling based on configurable trace distance. Failing particle are destroyed
+ //disabled for now.
+ //if ( m_flTraceDist != 0.0f )
+ //{
+ // // Trace down
+ // Vector TraceDir=Vector(0, 0, -1);
+ // // now set the trace distance
+ // // note - probably need to offset Pnt upwards for some fudge factor on irregular surfaces
+ // CBaseTrace tr;
+ // Vector RayStart=Pnt;
+ // float flRadius = m_flTraceDist;
+ // g_pParticleSystemMgr->Query()->TraceLine( RayStart, ( RayStart + ( TraceDir * flRadius ) ), MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
+ // if ( tr.fraction == 1.0 )
+ // {
+ // //If the trace hit nothing, kill the particle.
+ // pParticles->KillParticle( start_p );
+ // }
+ // else
+ // {
+ // //If we hit something, set particle position to collision position
+ // Pnt += tr.endpos;
+ // //FIXME - if we add a concept of a particle normal (for example, aligned quads or decals, set it here)
+ // }
+ //}
+
+ xyz[0] = Pnt.x;
+ xyz[4] = Pnt.y;
+ xyz[8] = Pnt.z;
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ pxyz[0] = Pnt.x;
+ pxyz[4] = Pnt.y;
+ pxyz[8] = Pnt.z;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Remap initial Scalar to Vector Initializer
+//-----------------------------------------------------------------------------
+class C_INIT_RemapScalarToVector : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RemapScalarToVector );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 1 << m_nFieldInput;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ int m_nFieldInput;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ Vector m_vecOutputMin;
+ Vector m_vecOutputMax;
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bScaleInitialRange;
+ int m_nControlPointNumber;
+ bool m_bLocalCoords;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RemapScalarToVector, "Remap Scalar to Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RemapScalarToVector )
+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_USERDATA( "input field", "8", 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", "0", 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 )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+DMXELEMENT_UNPACK_FIELD( "use local system", "1", bool, m_bLocalCoords )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RemapScalarToVector )
+
+void C_INIT_RemapScalarToVector::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ const float *pCreationTime;
+ // FIXME: SSE-ize
+ for( ; nParticleCount--; start_p++ )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ // 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;
+
+ const float *pInput = pParticles->GetFloatAttributePtr( m_nFieldInput, start_p );
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, start_p );
+ Vector vecOutput = vec3_origin;
+ vecOutput.x = RemapValClamped( *pInput, m_flInputMin, m_flInputMax, m_vecOutputMin.x, m_vecOutputMax.x );
+ vecOutput.y = RemapValClamped( *pInput, m_flInputMin, m_flInputMax, m_vecOutputMin.y, m_vecOutputMax.y );
+ vecOutput.z = RemapValClamped( *pInput, m_flInputMin, m_flInputMax, m_vecOutputMin.z, m_vecOutputMax.z );
+
+
+ if ( m_nFieldOutput == 0 )
+ {
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ if ( !m_bLocalCoords )
+ {
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *pCreationTime, &vecControlPoint );
+ vecOutput += vecControlPoint;
+ Vector vecOutputPrev = vecOutput;
+ if ( m_bScaleInitialRange )
+ {
+ Vector vecScaleInitial;
+ Vector vecScaleInitialPrev;
+ SetVectorFromAttribute ( vecScaleInitial, pOutput );
+ SetVectorFromAttribute ( vecScaleInitialPrev, pxyz );
+ vecOutput *= vecScaleInitial;
+ vecOutputPrev *= vecScaleInitialPrev;
+ }
+ SetVectorAttribute( pOutput, vecOutput );
+ SetVectorAttribute( pxyz, vecOutputPrev );
+ }
+ else
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, *pCreationTime, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ VectorTransform( vecOutput, mat, vecTransformLocal );
+ vecOutput = vecTransformLocal;
+ Vector vecOutputPrev = vecOutput;
+ if ( m_bScaleInitialRange )
+ {
+ Vector vecScaleInitial;
+ Vector vecScaleInitialPrev;
+ SetVectorFromAttribute ( vecScaleInitial, pOutput );
+ SetVectorFromAttribute ( vecScaleInitialPrev, pxyz );
+ vecOutput *= vecScaleInitial;
+ vecOutputPrev *= vecScaleInitialPrev;
+ }
+ SetVectorAttribute( pOutput, vecOutput );
+ SetVectorAttribute( pxyz, vecOutput );
+ }
+ }
+ else
+ {
+ if ( m_bScaleInitialRange )
+ {
+ Vector vecScaleInitial;
+ SetVectorFromAttribute ( vecScaleInitial, pOutput );
+ vecOutput *= vecScaleInitial;
+ }
+ SetVectorAttribute( pOutput, vecOutput );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Create particles sequentially along a path
+//-----------------------------------------------------------------------------
+struct SequentialPathContext_t
+{
+ int m_nParticleCount;
+ float m_flStep;
+ int m_nCountAmount;
+};
+class C_INIT_CreateSequentialPath : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateSequentialPath );
+
+ float m_fMaxDistance;
+ float m_flNumToAssign;
+ bool m_bLoop;
+ struct CPathParameters m_PathParams;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ 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
+ {
+ SequentialPathContext_t *pCtx = reinterpret_cast<SequentialPathContext_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( SequentialPathContext_t );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateSequentialPath, "Position Along Path Sequential", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateSequentialPath )
+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 )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateSequentialPath )
+
+
+void C_INIT_CreateSequentialPath::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
+ SequentialPathContext_t *pCtx = reinterpret_cast<SequentialPathContext_t *>( pContext );
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ Vector StartPnt, MidP, EndPnt;
+ pParticles->CalculatePathValues( m_PathParams, *ct, &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;
+
+ Vector randpos;
+ pParticles->RandomVector( -m_fMaxDistance, m_fMaxDistance, &randpos );
+
+ // 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;
+
+ Pnt+=randpos;
+
+ xyz[0] = Pnt.x;
+ xyz[4] = Pnt.y;
+ xyz[8] = Pnt.z;
+ if ( pxyz && ( nAttributeWriteMask & PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ) )
+ {
+ pxyz[0] = Pnt.x;
+ pxyz[4] = Pnt.y;
+ pxyz[8] = Pnt.z;
+ }
+ pCtx->m_nParticleCount += pCtx->m_nCountAmount;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Initial Repulsion Velocity - repulses the particles from nearby surfaces
+// on spawn
+//-----------------------------------------------------------------------------
+class C_INIT_InitialRepulsionVelocity : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_InitialRepulsionVelocity );
+
+ 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_RADIUS_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ Vector m_vecOutputMin;
+ Vector m_vecOutputMax;
+ int nRemainingBlocks;
+ int m_nControlPointNumber;
+ bool m_bPerParticle;
+ bool m_bTranslate;
+ bool m_bProportional;
+ float m_flTraceLength;
+ bool m_bPerParticleTR;
+ bool m_bInherit;
+ int m_nChildCP;
+ int m_nChildGroupID;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_InitialRepulsionVelocity, "Velocity Repulse from World", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_InitialRepulsionVelocity )
+DMXELEMENT_UNPACK_FIELD( "minimum velocity","0 0 0", Vector, m_vecOutputMin )
+DMXELEMENT_UNPACK_FIELD( "maximum velocity","1 1 1", Vector, m_vecOutputMax )
+DMXELEMENT_UNPACK_FIELD_STRING( "collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "Per Particle World Collision Tests", "0", bool, m_bPerParticle )
+DMXELEMENT_UNPACK_FIELD( "Use radius for Per Particle Trace Length", "0", bool, m_bPerParticleTR )
+DMXELEMENT_UNPACK_FIELD( "Offset instead of accelerate", "0", bool, m_bTranslate )
+DMXELEMENT_UNPACK_FIELD( "Offset proportional to radius 0/1", "0", bool, m_bProportional )
+DMXELEMENT_UNPACK_FIELD( "Trace Length", "64.0", float, m_flTraceLength )
+DMXELEMENT_UNPACK_FIELD( "Inherit from Parent", "0", bool, m_bInherit )
+DMXELEMENT_UNPACK_FIELD( "control points to broadcast to children (n + 1)", "-1", int, m_nChildCP )
+DMXELEMENT_UNPACK_FIELD( "Child Group ID to affect", "0", int, m_nChildGroupID )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_InitialRepulsionVelocity );
+
+
+void C_INIT_InitialRepulsionVelocity::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+
+ Vector d[6];
+
+ //All cardinal directions
+ d[0] = Vector( 1, 0, 0 );
+ d[1] = Vector( -1, 0, 0 );
+ d[2] = Vector( 0, 1, 0 );
+ d[3] = Vector( 0, -1, 0 );
+ d[4] = Vector( 0, 0, 1 );
+ d[5] = Vector( 0, 0, -1 );
+
+ //Init the results
+ Vector resultDirection;
+ float resultForce;
+ if ( m_bPerParticle )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *radius = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, start_p );
+ Vector vecCurrentPos;
+ SetVectorFromAttribute( vecCurrentPos, pxyz );
+
+ resultDirection.Init();
+ resultForce = 0.0f;
+
+ //Get the aggregate force vector
+ for ( int i = 0; i < 6; i++ )
+ {
+ //Press out
+ float flTraceDistance = m_flTraceLength;
+ if ( m_bPerParticleTR )
+ {
+ flTraceDistance = *radius;
+ }
+ Vector endpos = vecCurrentPos + ( d[i] * flTraceDistance );
+
+ //Trace into the world
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecCurrentPos, endpos, CONTENTS_SOLID, NULL, m_nCollisionGroupNumber, &tr );
+
+ //Push back a proportional amount to the probe
+ d[i] = -d[i] * (1.0f-tr.fraction);
+
+ assert(( 1.0f - tr.fraction ) >= 0.0f );
+
+ resultForce += 1.0f-tr.fraction;
+ resultDirection += d[i];
+ }
+
+ //If we've hit nothing, then point up
+ if ( resultDirection == vec3_origin )
+ {
+ resultDirection = Vector( 0, 0, 1 );
+ resultForce = 0.0f;
+ }
+
+ //Just return the direction
+ VectorNormalize( resultDirection );
+ resultDirection *= resultForce;
+
+ Vector vecRepulsionAmount;
+
+ vecRepulsionAmount.x = Lerp( resultForce, m_vecOutputMin.x, m_vecOutputMax.x );
+ vecRepulsionAmount.y = Lerp( resultForce, m_vecOutputMin.y, m_vecOutputMax.y );
+ vecRepulsionAmount.z = Lerp( resultForce, m_vecOutputMin.z, m_vecOutputMax.z );
+
+
+ vecRepulsionAmount *= resultDirection;
+
+
+ if ( m_bProportional )
+ {
+ vecRepulsionAmount *= *radius;
+ }
+
+ pxyz[0] += vecRepulsionAmount.x;
+ pxyz[4] += vecRepulsionAmount.y;
+ pxyz[8] += vecRepulsionAmount.z;
+
+ if ( m_bTranslate )
+ {
+ pxyz_prev[0] += vecRepulsionAmount.x;
+ pxyz_prev[4] += vecRepulsionAmount.y;
+ pxyz_prev[8] += vecRepulsionAmount.z;
+ }
+ }
+ }
+ else
+ {
+
+ Vector vecRepulsionAmount;
+
+ if ( m_bInherit )
+ {
+ float *ct = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *ct, &resultDirection );
+ Vector vecPassedForce;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber+1, *ct, &vecPassedForce );
+
+ vecRepulsionAmount.x = Lerp( vecPassedForce.x, m_vecOutputMin.x, m_vecOutputMax.x );
+ vecRepulsionAmount.y = Lerp( vecPassedForce.x, m_vecOutputMin.y, m_vecOutputMax.y );
+ vecRepulsionAmount.z = Lerp( vecPassedForce.x, m_vecOutputMin.z, m_vecOutputMax.z );
+
+ vecRepulsionAmount *= resultDirection;
+ }
+ else
+ {
+ float *ct = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *ct, &vecControlPoint );
+
+ Vector vecCurrentPos = vecControlPoint;
+
+ resultDirection.Init();
+ resultForce = 0.0f;
+
+ //Get the aggregate force vector
+ for ( int i = 0; i < 6; i++ )
+ {
+ //Press out
+ Vector endpos = vecCurrentPos + ( d[i] * m_flTraceLength );
+
+ //Trace into the world
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecCurrentPos, endpos, CONTENTS_SOLID, NULL, m_nCollisionGroupNumber, &tr );
+
+ //Push back a proportional amount to the probe
+ d[i] = -d[i] * (1.0f-tr.fraction);
+
+ assert(( 1.0f - tr.fraction ) >= 0.0f );
+
+ resultForce += 1.0f-tr.fraction;
+ resultDirection += d[i];
+ }
+
+ //If we've hit nothing, then point up
+ if ( resultDirection == vec3_origin )
+ {
+ resultDirection = Vector( 0, 0, 1 );
+ resultForce = 0.0f;
+ }
+
+ //Just return the direction
+ VectorNormalize( resultDirection );
+ resultDirection *= resultForce;
+
+ vecRepulsionAmount.x = Lerp( resultForce, m_vecOutputMin.x, m_vecOutputMax.x );
+ vecRepulsionAmount.y = Lerp( resultForce, m_vecOutputMin.y, m_vecOutputMax.y );
+ vecRepulsionAmount.z = Lerp( resultForce, m_vecOutputMin.z, m_vecOutputMax.z );
+
+ vecRepulsionAmount *= resultDirection;
+
+ if ( m_nChildCP != -1 )
+ {
+ for( CParticleCollection *pChild = pParticles->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
+ {
+ if ( pChild->GetGroupID() == m_nChildGroupID )
+ {
+ Vector vecPassForce = Vector(resultForce, 0, 0);
+ pChild->SetControlPoint( m_nChildCP, resultDirection );
+ pChild->SetControlPoint( m_nChildCP+1, vecPassForce );
+ }
+ }
+ }
+ }
+
+ for( ; nParticleCount--; start_p++ )
+ {
+
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *radius = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, start_p );
+
+ if ( m_bProportional )
+ {
+ vecRepulsionAmount *= *radius;
+ }
+
+ pxyz[0] += vecRepulsionAmount.x;
+ pxyz[4] += vecRepulsionAmount.y;
+ pxyz[8] += vecRepulsionAmount.z;
+
+ if ( m_bTranslate )
+ {
+ pxyz_prev[0] += vecRepulsionAmount.x;
+ pxyz_prev[4] += vecRepulsionAmount.y;
+ pxyz_prev[8] += vecRepulsionAmount.z;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Random Yaw Flip
+//-----------------------------------------------------------------------------
+class C_INIT_RandomYawFlip : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomYawFlip );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_YAW_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const;
+
+ float m_flPercent;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomYawFlip, "Rotation Yaw Flip Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomYawFlip )
+DMXELEMENT_UNPACK_FIELD( "Flip Percentage", ".5", float, m_flPercent )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomYawFlip )
+
+void C_INIT_RandomYawFlip::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ for( ; nParticleCount--; start_p++ )
+ {
+ float flChance = pParticles->RandomFloat( 0.0, 1.0 );
+ if ( flChance < m_flPercent )
+ {
+ float flRadians = 180 * ( M_PI / 180.0f );
+ float *drot = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_YAW, start_p );
+ *drot += flRadians;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Random second sequence
+//-----------------------------------------------------------------------------
+class C_INIT_RandomSecondSequence : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RandomSecondSequence );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ // TODO: Validate the ranges here!
+ }
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+ {
+ InitScalarAttributeRandomRangeBlock( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1,
+ m_nSequenceMin, m_nSequenceMax,
+ pParticles, start_block, n_blocks );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p, int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+ {
+ float *pSequence;
+ for( ; nParticleCount--; start_p++ )
+ {
+ pSequence = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, start_p );
+ *pSequence = pParticles->RandomInt( m_nSequenceMin, m_nSequenceMax );
+ }
+ }
+
+ int m_nSequenceMin;
+ int m_nSequenceMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RandomSecondSequence, "Sequence Two Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomSecondSequence )
+ DMXELEMENT_UNPACK_FIELD( "sequence_min", "0", int, m_nSequenceMin )
+ DMXELEMENT_UNPACK_FIELD( "sequence_max", "0", int, m_nSequenceMax )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RandomSecondSequence )
+
+
+
+//-----------------------------------------------------------------------------
+// Remap CP to Scalar Initializer
+//-----------------------------------------------------------------------------
+class C_INIT_RemapCPtoScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RemapCPtoScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nCPInput;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nField = int (clamp (m_nField, 0, 2));
+ }
+
+ 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_INIT_RemapCPtoScalar, "Remap Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_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_INIT_RemapCPtoScalar )
+
+void C_INIT_RemapCPtoScalar::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, 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;
+ float *ct = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ pParticles->GetControlPointAtTime( m_nCPInput, *ct, &vecControlPoint );
+
+ float flInput = vecControlPoint[m_nField];
+
+ // FIXME: SSE-ize
+ for( ; nParticleCount--; start_p++ )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ // 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, start_p );
+ 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;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Remap CP to Vector Initializer
+//-----------------------------------------------------------------------------
+class C_INIT_RemapCPtoVector : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_RemapCPtoVector );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ uint64 nMask = ( 1ULL << m_nCPInput );
+ if ( m_nLocalSpaceCP != -1 )
+ {
+ nMask |= ( 1ULL << m_nLocalSpaceCP );
+ }
+ return nMask;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nField = int (clamp (m_nField, 0, 2));
+ }
+
+ int m_nCPInput;
+ int m_nFieldOutput;
+ int m_nField;
+ Vector m_vInputMin;
+ Vector m_vInputMax;
+ Vector m_vOutputMin;
+ Vector m_vOutputMax;
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bScaleInitialRange;
+ bool m_bOffset;
+ bool m_bAccelerate;
+ int m_nLocalSpaceCP;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_RemapCPtoVector, "Remap Control Point to Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_RemapCPtoVector )
+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 0 0", Vector, m_vInputMin )
+DMXELEMENT_UNPACK_FIELD( "input maximum","0 0 0", Vector, m_vInputMax )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "0", int, m_nFieldOutput, "intchoice particlefield_vector" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0 0 0", Vector, m_vOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","0 0 0", Vector, m_vOutputMax )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+DMXELEMENT_UNPACK_FIELD( "offset position","0", bool, m_bOffset )
+DMXELEMENT_UNPACK_FIELD( "accelerate position","0", bool, m_bAccelerate )
+DMXELEMENT_UNPACK_FIELD( "local space CP","-1", int, m_nLocalSpaceCP )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_RemapCPtoVector )
+
+void C_INIT_RemapCPtoVector::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ Vector vecControlPoint;
+ pParticles->GetControlPointAtTime( m_nCPInput, pParticles->m_flCurTime, &vecControlPoint );
+ Vector vOutputMinLocal = m_vOutputMin;
+ Vector vOutputMaxLocal = m_vOutputMax;
+ if ( m_nLocalSpaceCP != -1 )
+ {
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nLocalSpaceCP, pParticles->m_flCurTime, &mat );
+ Vector vecTransformLocal = vec3_origin;
+ VectorRotate( vOutputMinLocal, mat, vecTransformLocal );
+ vOutputMinLocal = vecTransformLocal;
+ VectorRotate( vOutputMaxLocal, mat, vecTransformLocal );
+ vOutputMaxLocal = vecTransformLocal;
+ }
+
+ // FIXME: SSE-ize
+ for( ; nParticleCount--; start_p++ )
+ {
+ const float *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ // 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, start_p );
+
+ Vector vOutput;
+ vOutput.x = RemapValClamped( vecControlPoint.x, m_vInputMin.x, m_vInputMax.x, vOutputMinLocal.x, vOutputMaxLocal.x );
+ vOutput.y = RemapValClamped( vecControlPoint.y, m_vInputMin.y, m_vInputMax.y, vOutputMinLocal.y, vOutputMaxLocal.y );
+ vOutput.z = RemapValClamped( vecControlPoint.z, m_vInputMin.z, m_vInputMax.z, vOutputMinLocal.z, vOutputMaxLocal.z );
+
+ if ( m_bScaleInitialRange )
+ {
+ Vector vOrgValue;
+ SetVectorFromAttribute ( vOrgValue, pOutput );
+ vOutput *= vOrgValue;
+ }
+ if ( m_nFieldOutput == 6 )
+ {
+ pOutput[0] = max( 0.0f, min( vOutput.x, 1.0f) );
+ pOutput[4] = max( 0.0f, min( vOutput.y, 1.0f) );
+ pOutput[8] = max( 0.0f, min( vOutput.z, 1.0f) );
+ }
+ else
+ {
+ float *pXYZ_Prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ Vector vXYZPrev;
+ if ( m_bAccelerate )
+ {
+ if ( m_bOffset )
+ {
+ Vector vOrgValue;
+ SetVectorFromAttribute ( vOrgValue, pOutput );
+ SetVectorFromAttribute ( vXYZPrev, pXYZ_Prev );
+ vOutput += vOrgValue;
+ vXYZPrev += vOutput;
+ vOutput += vOutput * pParticles->m_flDt;
+ SetVectorAttribute ( pOutput, vOutput );
+ SetVectorAttribute ( pXYZ_Prev, vXYZPrev );
+ }
+ else
+ {
+ vOutput *= pParticles->m_flDt;
+ SetVectorAttribute ( pOutput, vOutput );
+ }
+
+ }
+ else
+ {
+ vXYZPrev = vOutput;
+ if ( m_bOffset )
+ {
+ Vector vOrgValue;
+ SetVectorFromAttribute ( vOrgValue, pOutput );
+ SetVectorFromAttribute ( vXYZPrev, pXYZ_Prev );
+ vOutput += vOrgValue;
+ vXYZPrev += vOutput;
+
+ }
+ SetVectorAttribute ( pOutput, vOutput );
+ SetVectorAttribute ( pXYZ_Prev, vXYZPrev );
+ }
+ }
+ }
+}
+
+
+
+class C_INIT_CreateFromParentParticles : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateFromParentParticles );
+
+ struct ParentParticlesContext_t
+ {
+ int m_nCurrentParentParticle;
+ };
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ ParentParticlesContext_t *pCtx = reinterpret_cast<ParentParticlesContext_t *>( pContext );
+ pCtx->m_nCurrentParentParticle = 0;
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( ParentParticlesContext_t );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ float m_flVelocityScale;
+ bool m_bRandomDistribution;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateFromParentParticles, "Position From Parent Particles", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateFromParentParticles )
+DMXELEMENT_UNPACK_FIELD( "Inherited Velocity Scale","0", float, m_flVelocityScale )
+DMXELEMENT_UNPACK_FIELD( "Random Parent Particle Distribution","0", bool, m_bRandomDistribution )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateFromParentParticles )
+
+void C_INIT_CreateFromParentParticles::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ if ( !pParticles->m_pParent )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ SetVectorAttribute( xyz, vec3_origin );
+ SetVectorAttribute( pxyz, vec3_origin );
+ }
+ return;
+ }
+ ParentParticlesContext_t *pCtx = reinterpret_cast<ParentParticlesContext_t *>( pContext );
+ int nActiveParticles = pParticles->m_pParent->m_nActiveParticles;
+
+
+ if ( nActiveParticles == 0 )
+ {
+ while( nParticleCount-- )
+ {
+ float *lifespan = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+ *lifespan = 0.0f;
+ start_p++;
+ }
+ return;
+ }
+
+ nActiveParticles = max ( 0, nActiveParticles - 1 );
+
+ for( ; nParticleCount--; start_p++ )
+ {
+ if ( m_bRandomDistribution )
+ {
+ pCtx->m_nCurrentParentParticle = pParticles->RandomInt( 0, nActiveParticles );
+ }
+ else if ( pCtx->m_nCurrentParentParticle > nActiveParticles )
+ {
+ pCtx->m_nCurrentParentParticle = 0;
+ }
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ const float *ct = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, start_p );
+ const float *pParent_xyz = pParticles->m_pParent->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, pCtx->m_nCurrentParentParticle );
+ const float *pParent_pxyz = pParticles->m_pParent->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, pCtx->m_nCurrentParentParticle );
+
+ Vector vecParentXYZ;
+ Vector vecParentPrevXYZ;
+ Vector vecScaledXYZ;
+
+ float flPrevTime = pParticles->m_flCurTime - pParticles->m_flDt;
+ float flSubFrame = RemapValClamped( *ct, flPrevTime, pParticles->m_flCurTime, 0, 1 );
+
+
+ vecParentXYZ.x = pParent_xyz[0];
+ vecParentXYZ.y = pParent_xyz[4];
+ vecParentXYZ.z = pParent_xyz[8];
+ vecParentPrevXYZ.x = pParent_pxyz[0];
+ vecParentPrevXYZ.y = pParent_pxyz[4];
+ vecParentPrevXYZ.z = pParent_pxyz[8];
+
+ VectorLerp( vecParentPrevXYZ, vecParentXYZ, flSubFrame, vecParentXYZ );
+ VectorLerp( vecParentXYZ, vecParentPrevXYZ, m_flVelocityScale, vecScaledXYZ );
+ SetVectorAttribute( pxyz, vecScaledXYZ );
+ SetVectorAttribute( xyz, vecParentXYZ );
+
+ pCtx->m_nCurrentParentParticle++;
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Distance to CP Initializer
+//-----------------------------------------------------------------------------
+class C_INIT_DistanceToCPInit : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_DistanceToCPInit );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nStartCP;
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ 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 ) );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ int m_nStartCP;
+ 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_INIT_DistanceToCPInit, "Remap Initial Distance to Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_DistanceToCPInit )
+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_INIT_DistanceToCPInit )
+
+void C_INIT_DistanceToCPInit::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, 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( ; nParticleCount--; start_p++ )
+ {
+ Vector vecPosition2;
+ const float *pXYZ = pParticles->GetFloatAttributePtr(PARTICLE_ATTRIBUTE_XYZ, start_p );
+ 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->GetFloatAttributePtr( m_nFieldOutput, start_p );
+ flOutput = *pInitialOutput * flOutput;
+ }
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, start_p );
+
+ *pOutput = flOutput;
+ }
+}
+
+
+
+
+class C_INIT_LifespanFromVelocity : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_LifespanFromVelocity );
+
+ Vector m_vecComponentScale;
+ float m_flTraceOffset;
+ float m_flMaxTraceLength;
+ float m_flTraceTolerance;
+ int m_nCollisionGroupNumber;
+ int m_nMaxPlanes;
+ int m_nAllowedPlanes;
+ char m_CollisionGroupName[128];
+
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ void InitializeContextData( CParticleCollection *pParticles,
+ void *pContext ) const
+ {
+ }
+
+ size_t GetRequiredContextBytes( ) const
+ {
+ return sizeof( CWorldCollideContextData );
+ }
+
+ bool InitMultipleOverride ( void ) { return true; }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nAllowedPlanes = ( min ( MAX_WORLD_PLANAR_CONSTRAINTS, m_nMaxPlanes ) - 1 );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+
+ virtual void InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_LifespanFromVelocity, "Lifetime from Time to Impact", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_LifespanFromVelocity )
+DMXELEMENT_UNPACK_FIELD_STRING( "trace collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "maximum trace length", "1024", float, m_flMaxTraceLength )
+DMXELEMENT_UNPACK_FIELD( "trace offset", "0", float, m_flTraceOffset )
+DMXELEMENT_UNPACK_FIELD( "trace recycle tolerance", "64", float, m_flTraceTolerance )
+DMXELEMENT_UNPACK_FIELD( "maximum points to cache", "16", int, m_nMaxPlanes )
+DMXELEMENT_UNPACK_FIELD( "bias distance", "1 1 1", Vector, m_vecComponentScale )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_LifespanFromVelocity )
+
+
+void C_INIT_LifespanFromVelocity::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ CWorldCollideContextData **ppCtx;
+ if ( pParticles->m_pParent )
+ ppCtx = &( pParticles->m_pParent->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+ else
+ ppCtx = &( pParticles->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+
+ CWorldCollideContextData *pCtx = NULL;
+ if ( ! *ppCtx )
+ {
+ *ppCtx = new CWorldCollideContextData;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_nNumFixedPlanes = 0;
+ }
+ pCtx = *ppCtx;
+
+ float flTol = m_flTraceTolerance * m_flTraceTolerance;
+
+ //Trace length takes the max trace and subtracts the offset to get the actual total.
+ float flTotalTraceDist = m_flMaxTraceLength - m_flTraceOffset;
+
+ //Offset percentage to account for if we've hit something within the offset (but not spawn) area
+ float flOffsetPct = m_flMaxTraceLength / ( flTotalTraceDist + FLT_EPSILON );
+
+ FourVectors v4ComponentScale;
+ v4ComponentScale.DuplicateVector( m_vecComponentScale );
+ while( nParticleCount-- )
+ {
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pPrevXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ float *dtime = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+
+
+ Vector vecXYZ( pxyz[0], pxyz[4], pxyz[8] );
+ Vector vecXYZ_Prev( pPrevXYZ[0], pPrevXYZ[4], pPrevXYZ[8] );
+
+ //Calculate velocity and account for frame delta time
+ Vector vDelta = vecXYZ - vecXYZ_Prev;
+ float flVelocity = VectorLength( vDelta );
+ flVelocity /= pParticles->m_flPreviousDt;
+
+ fltx4 fl4TraceOffset = ReplicateX4( m_flTraceOffset );
+
+ //Normalize the delta and get the offset to use from the normalized delta times the offset
+ VectorNormalize( vDelta );
+ Vector vecOffset = vDelta * m_flTraceOffset;
+
+ Vector vecStartPnt = vecXYZ + vecOffset;
+ Vector vecEndPnt = ( vDelta * flTotalTraceDist ) + vecStartPnt;
+
+ // Use SIMD section to interface with plane cache, even though we're not SIMD here
+ // Test versus existing Data
+ FourVectors fvStartPnt;
+ fvStartPnt.DuplicateVector( vecStartPnt );
+ FourVectors fvEndPnt;
+ fvEndPnt.DuplicateVector( vecEndPnt );
+ FourVectors v4PointOnPlane;
+ FourVectors v4PlaneNormal;
+ FourVectors v4Delta;
+ fltx4 fl4ClosestDist = Four_FLT_MAX;
+ for( int i = 0 ; i < pCtx->m_nActivePlanes; i++ )
+ {
+ if ( pCtx->m_bPlaneActive[i] )
+ {
+ fltx4 fl4TrialDistance = MaxSIMD(
+ fvStartPnt.DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ),
+ fvEndPnt.DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ) );
+ // If the trial distance is closer than the existing closest, replace.
+ if ( !IsAllGreaterThan( fl4TrialDistance, fl4ClosestDist ) )
+ {
+ fl4ClosestDist = fl4TrialDistance;
+ v4PointOnPlane = pCtx->m_PointOnPlane[i];
+ }
+ }
+ }
+ fl4ClosestDist = fabs( fl4ClosestDist );
+ // If we're outside the tolerance range, do a new trace and store it.
+ if ( IsAllGreaterThan( fl4ClosestDist, ReplicateX4( flTol ) ) )
+ {
+ //replace this with fast raycaster when available
+ CBaseTrace tr;
+ tr.plane.normal = vec3_invalid;
+ g_pParticleSystemMgr->Query()->TraceLine( vecStartPnt, vecEndPnt, CONTENTS_SOLID, NULL , m_nCollisionGroupNumber, &tr );
+
+ //Set the lifespan to 0 if we start solid, our trace distance is 0, or we hit within the offset area
+ if ( ( tr.fraction < ( 1 - flOffsetPct ) ) || tr.startsolid || flTotalTraceDist == 0.0f )
+ {
+ *dtime = 0.0f;
+ fl4TraceOffset = ReplicateX4( 0.0f );
+ fvStartPnt.DuplicateVector( vec3_origin );
+ v4PointOnPlane.DuplicateVector( vec3_origin );
+ }
+ else
+ {
+ int nIndex = pCtx->m_nNumFixedPlanes;
+ Vector vPointOnPlane = vecStartPnt + ( tr.fraction * ( vecEndPnt - vecStartPnt ) ) ;
+ pCtx->m_bPlaneActive[nIndex] = true;
+ pCtx->m_PointOnPlane[nIndex].DuplicateVector( vPointOnPlane );
+ pCtx->m_PlaneNormal[nIndex].DuplicateVector( tr.plane.normal );
+ pCtx->m_TraceStartPnt[nIndex].DuplicateVector( vecStartPnt );
+ pCtx->m_TraceEndPnt[nIndex].DuplicateVector( vecEndPnt );
+
+ fvStartPnt.DuplicateVector( vecStartPnt );
+ v4PointOnPlane.DuplicateVector( vPointOnPlane );
+
+ pCtx->m_nNumFixedPlanes = pCtx->m_nNumFixedPlanes + 1;
+ if ( pCtx->m_nNumFixedPlanes > m_nAllowedPlanes )
+ pCtx->m_nNumFixedPlanes = 0;
+ pCtx->m_nActivePlanes = min( m_nAllowedPlanes, pCtx->m_nActivePlanes + 1 );
+ }
+ }
+
+ fvStartPnt -= v4PointOnPlane;
+ //Scale components to remove undesired axis
+ fvStartPnt *= v4ComponentScale;
+ //Find the length of the trace
+ //Need to use the adjusted value of the trace length and collision point to account for the offset
+ fltx4 fl4Dist = AddSIMD ( fvStartPnt.length(), fl4TraceOffset );
+ flVelocity += FLT_EPSILON;
+ //Divide by Velocity to get Lifespan
+ *dtime = SubFloat( fl4Dist, 0) / flVelocity;
+
+ }
+}
+
+
+void C_INIT_LifespanFromVelocity::InitNewParticlesBlock( CParticleCollection *pParticles,
+ int start_block, int n_blocks, int nAttributeWriteMask,
+ void *pContext ) const
+{
+ CWorldCollideContextData **ppCtx;
+ if ( pParticles->m_pParent )
+ ppCtx = &( pParticles->m_pParent->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+ else
+ ppCtx = &( pParticles->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+
+ CWorldCollideContextData *pCtx = NULL;
+ if ( ! *ppCtx )
+ {
+ *ppCtx = new CWorldCollideContextData;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_nNumFixedPlanes = 0;
+ }
+ pCtx = *ppCtx;
+
+ float flTol = m_flTraceTolerance * m_flTraceTolerance;
+
+ size_t attr_stride;
+
+ FourVectors *pXYZ = pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, &attr_stride );
+ pXYZ += attr_stride * start_block;
+ FourVectors *pPrev_XYZ = pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, &attr_stride );
+ pPrev_XYZ += attr_stride * start_block;
+ fltx4 *pLifespan = pParticles->GetM128AttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, &attr_stride );
+ pLifespan += attr_stride * start_block;
+
+ //Trace length takes the max trace and subtracts the offset to get the actual total.
+ float flTotalTraceDist = m_flMaxTraceLength - m_flTraceOffset;
+ fltx4 fl4TotalTraceDist = ReplicateX4( flTotalTraceDist );
+
+ //Offset percentage to account for if we've hit something within the offset (but not spawn) area
+ float flOffsetPct = m_flMaxTraceLength / ( flTotalTraceDist + FLT_EPSILON );
+
+ fltx4 fl4PrevDT = ReplicateX4( 1.0f / pParticles->m_flPreviousDt );
+
+ FourVectors v4ComponentScale;
+ v4ComponentScale.DuplicateVector( m_vecComponentScale );
+
+ while( n_blocks-- )
+ {
+ // Determine Velocity
+ FourVectors fvDelta = *pXYZ;
+ fvDelta -= *pPrev_XYZ;
+ fltx4 fl4Velocity = fvDelta.length();
+ fl4Velocity = MulSIMD ( fl4Velocity, fl4PrevDT );
+
+ fltx4 fl4TraceOffset = ReplicateX4( m_flTraceOffset );
+
+ //Normalize the delta and get the offset to use from the normalized delta times the offset
+ FourVectors fvDeltaNormalized = fvDelta;
+ fvDeltaNormalized.VectorNormalizeFast();
+ FourVectors fvOffset = fvDeltaNormalized;
+ fvOffset *= m_flTraceOffset;
+
+ //Start/Endpoints for our traces
+ FourVectors fvStartPnt = *pXYZ;
+ fvStartPnt += fvOffset;
+ FourVectors fvEndPnt = fvDeltaNormalized;
+ fvEndPnt *= fl4TotalTraceDist;
+ fvEndPnt += fvStartPnt;
+
+ // Test versus existing Data
+ FourVectors v4PointOnPlane;
+ FourVectors v4PlaneNormal;
+ fltx4 fl4ClosestDist = Four_FLT_MAX;
+ for( int i = 0 ; i < pCtx->m_nActivePlanes; i++ )
+ {
+ if ( pCtx->m_bPlaneActive[i] )
+ {
+ fltx4 fl4TrialDistance = MaxSIMD(
+ fvStartPnt.DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ),
+ fvEndPnt.DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ) );
+ fltx4 fl4Nearestmask = CmpLeSIMD( fl4TrialDistance, fl4ClosestDist );
+ fl4ClosestDist = MaskedAssign( fl4ClosestDist, fl4TrialDistance, fl4Nearestmask );
+ v4PointOnPlane.x = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].x, v4PointOnPlane.x );
+ v4PointOnPlane.y = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].y, v4PointOnPlane.y );
+ v4PointOnPlane.z = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].z, v4PointOnPlane.z );
+ }
+ }
+
+ // If we're outside the tolerance range, do a new trace and store it.
+ fltx4 fl4OutOfRange = CmpGtSIMD( fl4ClosestDist, ReplicateX4( flTol ) );
+ if ( IsAnyNegative( fl4OutOfRange ) )
+ {
+ int nMask = TestSignSIMD( fl4OutOfRange );
+ for(int i=0; i < 4; i++ )
+ {
+ if ( nMask & ( 1 << i ) )
+ {
+ Vector start = fvStartPnt.Vec( i );
+ Vector end = fvEndPnt.Vec( i );
+
+ //replace this with fast raycaster when available
+ CBaseTrace tr;
+ tr.plane.normal = vec3_invalid;
+ g_pParticleSystemMgr->Query()->TraceLine( start, end, CONTENTS_SOLID, NULL , m_nCollisionGroupNumber, &tr );
+
+ //Set the lifespan to 0 if we start solid, our trace distance is 0, or we hit within the offset area
+ if ( ( tr.fraction < ( 1 - flOffsetPct ) ) || tr.startsolid || flTotalTraceDist == 0.0f )
+ {
+ SubFloat( fvStartPnt.x, i ) = 0.0f;
+ SubFloat( fvStartPnt.y, i ) = 0.0f;
+ SubFloat( fvStartPnt.z, i ) = 0.0f;
+ SubFloat( v4PointOnPlane.x, i ) = 0.0f;
+ SubFloat( v4PointOnPlane.y, i ) = 0.0f;
+ SubFloat( v4PointOnPlane.z, i ) = 0.0f;
+ SubFloat( fl4TraceOffset, i ) = 0.0f;
+ }
+ else
+ {
+ int nIndex = pCtx->m_nNumFixedPlanes;
+ Vector vPointOnPlane = start + ( tr.fraction * ( end - start ) ) ;
+ SubFloat( v4PointOnPlane.x, i ) = vPointOnPlane.x;
+ SubFloat( v4PointOnPlane.y, i ) = vPointOnPlane.y;
+ SubFloat( v4PointOnPlane.z, i ) = vPointOnPlane.z;
+ pCtx->m_bPlaneActive[nIndex] = true;
+ pCtx->m_PointOnPlane[nIndex].DuplicateVector( vPointOnPlane );
+ pCtx->m_PlaneNormal[nIndex].DuplicateVector( tr.plane.normal );
+ pCtx->m_TraceStartPnt[nIndex].DuplicateVector( start );
+ pCtx->m_TraceEndPnt[nIndex].DuplicateVector( end );
+ pCtx->m_nNumFixedPlanes = pCtx->m_nNumFixedPlanes + 1;
+ if ( pCtx->m_nNumFixedPlanes > m_nAllowedPlanes )
+ pCtx->m_nNumFixedPlanes = 0;
+ pCtx->m_nActivePlanes = min( m_nAllowedPlanes, pCtx->m_nActivePlanes + 1 );
+ }
+ }
+ }
+ }
+
+ //Find the length of the trace
+ fvStartPnt -= v4PointOnPlane;
+ fvStartPnt *= v4ComponentScale;
+ //Need to use the adjusted value of the trace length and collision point to account for the offset
+ fltx4 fl4Dist = AddSIMD ( fvStartPnt.length(), fl4TraceOffset );
+ fl4Velocity = AddSIMD( fl4Velocity, Four_Epsilons );
+ //Divide by Velocity to get Lifespan
+ *pLifespan = DivSIMD( fl4Dist, fl4Velocity );
+
+ pXYZ += attr_stride;
+ pPrev_XYZ += attr_stride;
+ pLifespan += attr_stride;
+ }
+}
+
+
+
+
+
+class C_INIT_CreateFromPlaneCache : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_INIT_CreateFromPlaneCache );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ size_t GetRequiredContextBytes( ) const
+ {
+ return sizeof( CWorldCollideContextData );
+ }
+
+ void InitNewParticlesScalar( CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask,
+ void *pContext) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_INIT_CreateFromPlaneCache, "Position from Parent Cache", OPERATOR_PI_POSITION );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateFromPlaneCache )
+END_PARTICLE_OPERATOR_UNPACK( C_INIT_CreateFromPlaneCache )
+
+void C_INIT_CreateFromPlaneCache::InitNewParticlesScalar(
+ CParticleCollection *pParticles, int start_p,
+ int nParticleCount, int nAttributeWriteMask, void *pContext ) const
+{
+ if ( !pParticles->m_pParent )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+
+ SetVectorAttribute( xyz, vec3_origin );
+ SetVectorAttribute( pxyz, vec3_origin );
+ }
+ return;
+ }
+
+
+ CWorldCollideContextData **ppCtx;
+ if ( pParticles->m_pParent )
+ ppCtx = &( pParticles->m_pParent->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+ else
+ ppCtx = &( pParticles->m_pCollisionCacheData[COLLISION_MODE_INITIAL_TRACE_DOWN] );
+
+ CWorldCollideContextData *pCtx = NULL;
+ if ( ! *ppCtx )
+ {
+ *ppCtx = new CWorldCollideContextData;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_nNumFixedPlanes = 0;
+ FourVectors fvEmpty;
+ fvEmpty.DuplicateVector( vec3_origin );
+ (*ppCtx)->m_PointOnPlane[0] = fvEmpty;
+ }
+ pCtx = *ppCtx;
+ if ( pCtx->m_nActivePlanes > 0 )
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ int nIndex = pParticles->RandomInt( 0, pCtx->m_nActivePlanes - 1 );
+ if ( pCtx->m_PlaneNormal[nIndex].Vec( 0 ) == vec3_invalid )
+ {
+ float *plifespan = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, start_p );
+ *plifespan = 0.0f;
+ }
+ else
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ FourVectors fvPoint = pCtx->m_PointOnPlane[nIndex];
+ Vector vPoint = fvPoint.Vec( 0 );
+ SetVectorAttribute( xyz, vPoint );
+ SetVectorAttribute( pxyz, vPoint );
+ }
+ }
+ }
+ else
+ {
+ for( ; nParticleCount--; start_p++ )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, start_p );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, start_p );
+ SetVectorAttribute( xyz, vec3_origin );
+ SetVectorAttribute( pxyz, vec3_origin );
+ }
+ }
+}
+
+
+
+
+
+//
+//
+//
+//
+
+//-----------------------------------------------------------------------------
+// Purpose: Add all operators to be considered active, here
+//-----------------------------------------------------------------------------
+void AddBuiltInParticleInitializers( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateAlongPath );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_MoveBetweenPoints );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateWithinSphere );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_VelocityRandom );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateOnModel );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateWithinBox );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomRotationSpeed );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomLifeTime );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomAlpha );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomRadius );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomRotation );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomYaw );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomColor );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomTrailLength );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomSequence );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_PositionOffset );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_PositionWarp );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreationNoise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_InitialVelocityNoise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RemapScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_InheritVelocity );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_AgeNoise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_SequenceLifeTime );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateInHierarchy );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RemapScalarToVector );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateSequentialPath );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_InitialRepulsionVelocity );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomYawFlip );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RandomSecondSequence );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RemapCPtoScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_RemapCPtoVector );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateFromParentParticles );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_DistanceToCPInit );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_LifespanFromVelocity );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_INITIALIZER, C_INIT_CreateFromPlaneCache );
+}
+