diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /particles/builtin_initializers.cpp | |
| download | archived-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.cpp | 4741 |
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 ); +} + |