summaryrefslogtreecommitdiff
path: root/particles
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /particles
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'particles')
-rw-r--r--particles/addbuiltin_ops.cpp42
-rw-r--r--particles/builtin_constraints.cpp1109
-rw-r--r--particles/builtin_initializers.cpp4741
-rw-r--r--particles/builtin_particle_emitters.cpp876
-rw-r--r--particles/builtin_particle_forces.cpp544
-rw-r--r--particles/builtin_particle_ops.cpp4584
-rw-r--r--particles/builtin_particle_render_ops.cpp2416
-rw-r--r--particles/particle_sort.cpp194
-rw-r--r--particles/particles.cpp3881
-rw-r--r--particles/particles.vpc32
-rw-r--r--particles/particles_internal.h52
-rw-r--r--particles/psheet.cpp146
-rw-r--r--particles/psheet.h84
-rw-r--r--particles/random_floats.h537
14 files changed, 19238 insertions, 0 deletions
diff --git a/particles/addbuiltin_ops.cpp b/particles/addbuiltin_ops.cpp
new file mode 100644
index 0000000..b583b6a
--- /dev/null
+++ b/particles/addbuiltin_ops.cpp
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+void AddBuiltInParticleOperators( void );
+void AddBuiltInParticleRenderers( void );
+void AddBuiltInParticleInitializers( void );
+void AddBuiltInParticleEmitters( void );
+void AddBuiltInParticleForceGenerators( void );
+void AddBuiltInParticleConstraints( void );
+
+void CParticleSystemMgr::AddBuiltinSimulationOperators( void )
+{
+ static bool s_DidAddSim = false;
+ if ( ! s_DidAddSim )
+ {
+ s_DidAddSim = true;
+ AddBuiltInParticleOperators();
+ AddBuiltInParticleInitializers();
+ AddBuiltInParticleEmitters();
+ AddBuiltInParticleForceGenerators();
+ AddBuiltInParticleConstraints();
+ }
+}
+
+void CParticleSystemMgr::AddBuiltinRenderingOperators( void )
+{
+ static bool s_DidAddRenderers = false;
+ if ( ! s_DidAddRenderers )
+ {
+ s_DidAddRenderers = true;
+ AddBuiltInParticleRenderers();
+ }
+}
diff --git a/particles/builtin_constraints.cpp b/particles/builtin_constraints.cpp
new file mode 100644
index 0000000..5658972
--- /dev/null
+++ b/particles/builtin_constraints.cpp
@@ -0,0 +1,1109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "mathlib/halton.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"
+
+
+class C_OP_ConstrainDistance : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ConstrainDistance );
+
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+
+ bool EnforceConstraint( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext,
+ int nNumValidParticlesInLastChunk ) const;
+
+ float m_fMinDistance, m_fMaxDistance;
+ int m_nControlPointNumber;
+ Vector m_CenterOffset;
+ bool m_bGlobalCenter;
+
+};
+
+#ifdef NDEBUG
+#define CHECKSYSTEM( p ) 0
+#else
+static void CHECKSYSTEM( CParticleCollection *pParticles )
+{
+// Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Assert( IsFinite( xyz[0] ) );
+ Assert( IsFinite( xyz[4] ) );
+ Assert( IsFinite( xyz[8] ) );
+ Assert( IsFinite( xyz_prev[0] ) );
+ Assert( IsFinite( xyz_prev[4] ) );
+ Assert( IsFinite( xyz_prev[8] ) );
+ }
+}
+#endif
+
+bool C_OP_ConstrainDistance::EnforceConstraint( int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const
+{
+ size_t nStride;
+ FourVectors *pXYZ=pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ,
+ &nStride );
+ pXYZ += nStride * nStartBlock;
+ fltx4 SIMDMinDist=ReplicateX4( m_fMinDistance );
+ fltx4 SIMDMaxDist=ReplicateX4( m_fMaxDistance );
+ fltx4 SIMDMinDist2=ReplicateX4( m_fMinDistance*m_fMinDistance );
+ fltx4 SIMDMaxDist2=ReplicateX4( m_fMaxDistance*m_fMaxDistance );
+
+ Vector vecCenter;
+ if ( m_bGlobalCenter )
+ vecCenter = m_CenterOffset;
+ else
+ {
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecCenter );
+ vecCenter += pParticles->TransformAxis( m_CenterOffset, true, m_nControlPointNumber );
+ }
+ FourVectors Center;
+ Center.DuplicateVector( vecCenter );
+
+ bool bChangedSomething = false;
+ do
+ {
+ FourVectors pts = *(pXYZ);
+ pts -= Center;
+ fltx4 dist_squared= pts * pts;
+ fltx4 TooFarMask = CmpGtSIMD( dist_squared, SIMDMaxDist2 );
+ fltx4 TooCloseMask = CmpLtSIMD( dist_squared, SIMDMinDist2 );
+ fltx4 NeedAdjust = OrSIMD( TooFarMask, TooCloseMask );
+ if ( IsAnyNegative( NeedAdjust ) ) // any out of bounds?
+ {
+ // change squared distance into approximate rsqr root
+ fltx4 guess = ReciprocalSqrtEstSaturateSIMD(dist_squared);
+ // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2));
+ guess=MulSIMD(guess,SubSIMD(Four_Threes,MulSIMD(dist_squared,MulSIMD(guess,guess))));
+ guess=MulSIMD(Four_PointFives,guess);
+ pts *= guess;
+
+ FourVectors clamp_far=pts;
+ clamp_far *= SIMDMaxDist;
+ clamp_far += Center;
+ FourVectors clamp_near=pts;
+ clamp_near *= SIMDMinDist;
+ clamp_near += Center;
+ pts.x = MaskedAssign( TooCloseMask, clamp_near.x, MaskedAssign( TooFarMask, clamp_far.x, pXYZ->x ));
+ pts.y = MaskedAssign( TooCloseMask, clamp_near.y, MaskedAssign( TooFarMask, clamp_far.y, pXYZ->y ));
+ pts.z = MaskedAssign( TooCloseMask, clamp_near.z, MaskedAssign( TooFarMask, clamp_far.z, pXYZ->z ));
+ *(pXYZ) = pts;
+ bChangedSomething = true;
+ }
+ pXYZ += nStride;
+ } while (--nNumBlocks);
+ return bChangedSomething;
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ConstrainDistance, "Constrain distance to control point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistance )
+ DMXELEMENT_UNPACK_FIELD( "minimum distance", "0", float, m_fMinDistance )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance", "100", float, m_fMaxDistance )
+ DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "offset of center", "0 0 0", Vector, m_CenterOffset )
+ DMXELEMENT_UNPACK_FIELD( "global center point", "0", bool, m_bGlobalCenter )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistance )
+
+class C_OP_ConstrainDistanceToPath : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ConstrainDistanceToPath );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_PathParameters.m_nStartControlPointNumber ) |
+ ( 1ULL << m_PathParameters.m_nEndControlPointNumber );
+ }
+
+ bool EnforceConstraint( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const;
+
+ float m_fMinDistance;
+
+ float m_flMaxDistance0, m_flMaxDistanceMid, m_flMaxDistance1;
+ CPathParameters m_PathParameters;
+
+ float m_flTravelTime;
+
+};
+
+bool C_OP_ConstrainDistanceToPath::EnforceConstraint( int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext,
+ int nNumValidParticlesInLastChunk ) const
+{
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ pXYZ += nStartBlock;
+
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ pCreationTime += nStartBlock;
+
+
+ Vector StartPnt, EndPnt, MidP;
+
+ pParticles->CalculatePathValues( m_PathParameters, pParticles->m_flCurTime,
+ &StartPnt, &MidP, &EndPnt );
+
+ fltx4 CurTime = ReplicateX4( pParticles->m_flCurTime );
+ fltx4 TimeScale= ReplicateX4( 1.0/(max(0.001f, m_flTravelTime ) ) );
+
+ // calculate radius spline
+ bool bConstantRadius = true;
+ fltx4 Rad0=ReplicateX4(m_flMaxDistance0);
+ fltx4 Radm=Rad0;
+
+ if ( m_flMaxDistanceMid >= 0.0 )
+ {
+ bConstantRadius = ( m_flMaxDistanceMid == m_flMaxDistance0 );
+ Radm=ReplicateX4( m_flMaxDistanceMid);
+ }
+ fltx4 Rad1=Radm;
+ if ( m_flMaxDistance1 >= 0.0 )
+ {
+ bConstantRadius &= ( m_flMaxDistance1 == m_flMaxDistance0 );
+ Rad1=ReplicateX4( m_flMaxDistance1 );
+ }
+
+ fltx4 RadmMinusRad0=SubSIMD( Radm, Rad0);
+ fltx4 Rad1MinusRadm=SubSIMD( Rad1, Radm);
+
+ fltx4 SIMDMinDist=ReplicateX4( m_fMinDistance );
+ fltx4 SIMDMinDist2=ReplicateX4( m_fMinDistance*m_fMinDistance );
+
+ fltx4 SIMDMaxDist=MaxSIMD( Rad0, MaxSIMD( Radm, Rad1 ) );
+ fltx4 SIMDMaxDist2=MulSIMD( SIMDMaxDist, SIMDMaxDist);
+
+ bool bChangedSomething = false;
+ FourVectors StartP;
+ StartP.DuplicateVector( StartPnt );
+
+ FourVectors MiddleP;
+ MiddleP.DuplicateVector( MidP );
+
+ // form delta terms needed for quadratic bezier
+ FourVectors Delta0;
+ Delta0.DuplicateVector( MidP-StartPnt );
+
+ FourVectors Delta1;
+ Delta1.DuplicateVector( EndPnt-MidP );
+ do
+ {
+ fltx4 TScale=MinSIMD(
+ Four_Ones,
+ MulSIMD( TimeScale, SubSIMD( CurTime, *pCreationTime ) ) );
+
+ // bezier(a,b,c,t)=lerp( lerp(a,b,t),lerp(b,c,t),t)
+ FourVectors L0 = Delta0;
+ L0 *= TScale;
+ L0 += StartP;
+
+ FourVectors L1= Delta1;
+ L1 *= TScale;
+ L1 += MiddleP;
+
+ FourVectors Center = L1;
+ Center -= L0;
+ Center *= TScale;
+ Center += L0;
+
+ FourVectors pts = *(pXYZ);
+ pts -= Center;
+
+ // calculate radius at the point. !!speed!! - use speical case for constant radius
+
+ fltx4 dist_squared= pts * pts;
+ fltx4 TooFarMask = CmpGtSIMD( dist_squared, SIMDMaxDist2 );
+ if ( ( !bConstantRadius) && ( ! IsAnyNegative( TooFarMask ) ) )
+ {
+ // need to calculate and adjust for true radius =- we've only trivilally rejected note
+ // voodoo here - we update simdmaxdist for true radius, but not max dist^2, since
+ // that's used only for the trivial reject case, which we've already done
+ fltx4 R0=AddSIMD( Rad0, MulSIMD( RadmMinusRad0, TScale ) );
+ fltx4 R1=AddSIMD( Radm, MulSIMD( Rad1MinusRadm, TScale ) );
+ SIMDMaxDist = AddSIMD( R0, MulSIMD( SubSIMD( R1, R0 ), TScale) );
+
+ // now that we know the true radius, update our mask
+ TooFarMask = CmpGtSIMD( dist_squared, MulSIMD( SIMDMaxDist, SIMDMaxDist ) );
+ }
+
+ fltx4 TooCloseMask = CmpLtSIMD( dist_squared, SIMDMinDist2 );
+ fltx4 NeedAdjust = OrSIMD( TooFarMask, TooCloseMask );
+ if ( IsAnyNegative( NeedAdjust ) ) // any out of bounds?
+ {
+ if ( ! bConstantRadius )
+ {
+ // need to calculate and adjust for true radius =- we've only trivilally rejected
+
+ }
+
+ // change squared distance into approximate rsqr root
+ fltx4 guess=ReciprocalSqrtEstSIMD(dist_squared);
+ // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2));
+ guess=MulSIMD(guess,SubSIMD(Four_Threes,MulSIMD(dist_squared,MulSIMD(guess,guess))));
+ guess=MulSIMD(Four_PointFives,guess);
+ pts *= guess;
+
+ FourVectors clamp_far=pts;
+ clamp_far *= SIMDMaxDist;
+ clamp_far += Center;
+ FourVectors clamp_near=pts;
+ clamp_near *= SIMDMinDist;
+ clamp_near += Center;
+ pts.x = MaskedAssign( TooCloseMask, clamp_near.x, MaskedAssign( TooFarMask, clamp_far.x, pXYZ->x ));
+ pts.y = MaskedAssign( TooCloseMask, clamp_near.y, MaskedAssign( TooFarMask, clamp_far.y, pXYZ->y ));
+ pts.z = MaskedAssign( TooCloseMask, clamp_near.z, MaskedAssign( TooFarMask, clamp_far.z, pXYZ->z ));
+ *(pXYZ) = pts;
+ bChangedSomething = true;
+ }
+ ++pXYZ;
+ ++pCreationTime;
+ } while (--nNumBlocks);
+ return bChangedSomething;
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ConstrainDistanceToPath, "Constrain distance to path between two control points", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistanceToPath )
+ DMXELEMENT_UNPACK_FIELD( "minimum distance", "0", float, m_fMinDistance )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance", "100", float, m_flMaxDistance0 )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance middle", "-1", float, m_flMaxDistanceMid )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance end", "-1", float, m_flMaxDistance1 )
+ DMXELEMENT_UNPACK_FIELD( "travel time", "10", float, m_flTravelTime )
+ DMXELEMENT_UNPACK_FIELD( "random bulge", "0", float, m_PathParameters.m_flBulge )
+ DMXELEMENT_UNPACK_FIELD( "start control point number", "0", int, m_PathParameters.m_nStartControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "end control point number", "0", int, m_PathParameters.m_nEndControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "bulge control 0=random 1=orientation of start pnt 2=orientation of end point", "0", int, m_PathParameters.m_nBulgeControl )
+ DMXELEMENT_UNPACK_FIELD( "mid point position", "0.5", float, m_PathParameters.m_flMidPoint )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistanceToPath )
+
+
+
+class C_OP_PlanarConstraint : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_PlanarConstraint );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ bool EnforceConstraint( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const;
+
+ Vector m_PointOnPlane;
+ Vector m_PlaneNormal;
+ int m_nControlPointNumber;
+ bool m_bGlobalOrigin;
+ bool m_bGlobalNormal;
+
+};
+
+bool C_OP_PlanarConstraint::EnforceConstraint( int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext,
+ int nNumValidParticlesInLastChunk ) const
+{
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ pXYZ += nStartBlock;
+
+ CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+ pRadius += nStartBlock;
+
+ // now, transform and offset parameters
+ FourVectors PlaneNormal;
+ PlaneNormal.DuplicateVector(
+ pParticles->TransformAxis( m_PlaneNormal, ! m_bGlobalNormal, m_nControlPointNumber ) );
+ PlaneNormal.VectorNormalize();
+
+ FourVectors PlanePoint;
+ if ( m_bGlobalOrigin )
+ {
+ PlanePoint.DuplicateVector( m_PointOnPlane );
+ }
+ else
+ {
+ Vector ofs=pParticles->TransformAxis( m_PointOnPlane, true, m_nControlPointNumber );
+ Vector vecCenter;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber,
+ pParticles->m_flCurTime, &vecCenter );
+ PlanePoint.DuplicateVector( ofs + vecCenter );
+ }
+
+
+ bool bChangedSomething = false;
+ do
+ {
+ FourVectors pts = *pXYZ;
+ pts -= PlanePoint;
+ fltx4 PlaneEq=pts * PlaneNormal;
+ // where planeeq<0, inside
+ PlaneEq = SubSIMD( PlaneEq, *pRadius );
+ fltx4 BadPts=CmpLtSIMD( PlaneEq, Four_Zeros );
+ if ( IsAnyNegative( BadPts ) )
+ {
+ bChangedSomething = true;
+ // project points to plane surface
+ fltx4 PenetrationDistance=MinSIMD( Four_Zeros, PlaneEq );
+ FourVectors PenetrationVector = PlaneNormal;
+ PenetrationVector *= PenetrationDistance;
+ (*pXYZ) -= PenetrationVector;
+ }
+ ++pXYZ;
+ ++pRadius;
+ } while (--nNumBlocks);
+ return bChangedSomething;
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PlanarConstraint, "Prevent passing through a plane", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PlanarConstraint )
+ DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "plane point", "0 0 0", Vector, m_PointOnPlane )
+ DMXELEMENT_UNPACK_FIELD( "plane normal", "0 0 1", Vector, m_PlaneNormal )
+ DMXELEMENT_UNPACK_FIELD( "global origin", "0", bool, m_bGlobalOrigin )
+ DMXELEMENT_UNPACK_FIELD( "global normal", "0", bool, m_bGlobalNormal )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PlanarConstraint )
+
+
+
+
+static Vector s_OrientationRelativeTraceVectors[] = {
+ Vector( 0, .1962, .784929 ),
+ Vector( -.1962, 0, .784929 ),
+ Vector( .1962, 0, .784929 ),
+ Vector( 0, -.1962, .78929 ),
+};
+
+void CWorldCollideContextData::SetBaseTrace( int nIndex, Vector const &rayStart, Vector const &traceDir, int nCollisionGroup, bool bKeepMisses )
+{
+ CBaseTrace tr;
+ Vector rayEnd = rayStart + traceDir;
+ g_pParticleSystemMgr->Query()->TraceLine( rayStart, rayEnd, MASK_SOLID, NULL, nCollisionGroup, &tr );
+ if ( tr.fraction < 1.0 )
+ {
+ m_bPlaneActive[nIndex] = true;
+ m_PointOnPlane[nIndex].DuplicateVector( rayStart + tr.fraction * traceDir );
+ m_PlaneNormal[nIndex].DuplicateVector( tr.plane.normal );
+ m_TraceStartPnt[nIndex].DuplicateVector( rayStart );
+ m_TraceEndPnt[nIndex].DuplicateVector( rayEnd );
+ }
+ else
+ {
+ if ( bKeepMisses )
+ {
+ m_PlaneNormal[nIndex].x = Four_Zeros;
+ m_PlaneNormal[nIndex].y = Four_Zeros;
+ m_PlaneNormal[nIndex].z = Four_Zeros;
+ m_TraceStartPnt[nIndex].DuplicateVector( rayStart );
+ m_TraceEndPnt[nIndex].DuplicateVector( rayEnd );
+ m_bPlaneActive[nIndex] = true;
+ }
+ else
+ m_bPlaneActive[nIndex] = false;
+ }
+}
+
+void CWorldCollideContextData::CalculatePlanes( CParticleCollection *pParticles, int nCollisionMode,
+ int nCollisionGroup, Vector const *pCPOffset,
+ float flDistanceTolerance )
+{
+ // fire some rays to find the convex around the control point
+ if ( m_nActivePlanes && ( nCollisionMode == COLLISION_MODE_INITIAL_TRACE_DOWN ) )
+ return;
+ Vector rayStart = pParticles->GetControlPointAtCurrentTime( 0 ); // allow config + offset
+
+ if ( pCPOffset )
+ rayStart += *pCPOffset;
+
+ if ( ( m_flLastUpdateTime > 0. ) && ( ( rayStart - m_vecLastUpdateOrigin ).LengthSqr() < Square( flDistanceTolerance ) ) )
+ return;
+
+ m_vecLastUpdateOrigin = rayStart;
+ m_nActivePlanes = 0;
+ switch( nCollisionMode )
+ {
+ case COLLISION_MODE_INITIAL_TRACE_DOWN:
+ {
+ SetBaseTrace( 0, rayStart, 1000.0 * Vector( -1, 0, 0 ), nCollisionGroup, false );
+ m_nActivePlanes = 1;
+ m_nNumFixedPlanes = 1;
+ break;
+ }
+ case COLLISION_MODE_PER_FRAME_PLANESET:
+ {
+ int nIndexOut = 0;
+ for( int i = -1; i <= 1; i++ )
+ for( int j = -1; j <= 1; j++ )
+ for( int k = -1; k <= 1; k++ )
+ {
+ if ( i || j || k )
+ {
+ SetBaseTrace( nIndexOut++, rayStart, 1000.0 * Vector( i, j, k ), nCollisionGroup, false );
+ }
+ }
+ m_nNumFixedPlanes = nIndexOut;
+ m_nActivePlanes = nIndexOut;
+ }
+ // Long missing break. Added to Source2 in change 700053.
+ // It's a bug, but changing it now could cause regressions, so
+ // leaving it for now until someone decides it's worth fixing.
+#ifdef FP_EXCEPTIONS_ENABLED
+ // This break is necessary when exceptions are enabled because otherwise
+ // m_bPlaneActive[21] is set even though that plane is filled with
+ // NaNs. We should perhaps put this break in, but we need to do
+ // careful particle testing.
+ break;
+#endif
+
+ case COLLISION_MODE_USE_NEAREST_TRACE:
+ {
+ int nIndexOut = 0;
+ for( int i = -1; i <= 1; i++ )
+ for( int j = -1; j <= 1; j++ )
+ for( int k = -1; k <= 1; k++ )
+ {
+ if ( i || j || k )
+ {
+ SetBaseTrace( nIndexOut++, rayStart, 1000.0 * Vector( i, j, k ), nCollisionGroup, true );
+ }
+ }
+ m_nNumFixedPlanes = nIndexOut;
+ m_nActivePlanes = nIndexOut;
+ }
+ }
+}
+
+class C_OP_WorldCollideConstraint : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_WorldCollideConstraint );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << 0;
+ }
+
+ size_t GetRequiredContextBytes( ) const
+ {
+ return sizeof( CWorldCollideContextData );
+ }
+
+ bool EnforceConstraint( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const;
+
+ void SetupConstraintPerFrameData( CParticleCollection *pParticles,
+ void *pContext ) const;
+};
+
+
+void C_OP_WorldCollideConstraint::SetupConstraintPerFrameData( CParticleCollection *pParticles,
+ void *pContext ) const
+{
+ CWorldCollideContextData *pCtx =
+ reinterpret_cast<CWorldCollideContextData *>( pContext );
+ pCtx->CalculatePlanes( pParticles, COLLISION_MODE_PER_FRAME_PLANESET, COLLISION_GROUP_NONE );
+}
+
+bool C_OP_WorldCollideConstraint::EnforceConstraint( int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext,
+ int nNumValidParticlesInLastChunk ) const
+{
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ pXYZ += nStartBlock;
+
+ CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+ pRadius += nStartBlock;
+
+ CWorldCollideContextData *pCtx =
+ reinterpret_cast<CWorldCollideContextData *>( pContext );
+
+ bool bChangedSomething = false;
+ do
+ {
+ for( int i=0; i < pCtx->m_nActivePlanes; i++ )
+ {
+ if ( pCtx->m_bPlaneActive[ i ] )
+ {
+ FourVectors pts = *pXYZ;
+ pts -= pCtx->m_PointOnPlane[i];
+ fltx4 PlaneEq=pts * pCtx->m_PlaneNormal[i];
+ // where planeeq<0, inside
+ PlaneEq = SubSIMD( PlaneEq, *pRadius );
+ fltx4 BadPts=CmpLtSIMD( PlaneEq, Four_Zeros );
+ if ( IsAnyNegative( BadPts ) )
+ {
+ bChangedSomething = true;
+ // project points to plane surface
+ fltx4 PenetrationDistance=MinSIMD( Four_Zeros, PlaneEq );
+ FourVectors PenetrationVector = pCtx->m_PlaneNormal[i];
+ PenetrationVector *= PenetrationDistance;
+ (*pXYZ) -= PenetrationVector;
+ }
+ }
+ }
+ ++pXYZ;
+ ++pRadius;
+ } while (--nNumBlocks);
+ return bChangedSomething;
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_WorldCollideConstraint, "Prevent passing through static part of world", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_WorldCollideConstraint )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_WorldCollideConstraint )
+
+
+
+class C_OP_WorldTraceConstraint : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_WorldTraceConstraint );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ int nRet = PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ;
+ if ( m_bKillonContact )
+ nRet |= PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ return nRet;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << 0;
+ }
+
+ Vector m_vecCpOffset;
+ int m_nCollisionMode;
+ float m_flBounceAmount;
+ float m_flSlideAmount;
+ float m_flRadiusScale;
+ float m_flCpMovementTolerance;
+ float m_flTraceTolerance;
+
+ bool m_bKillonContact;
+
+ virtual bool IsFinalConstraint( void ) const
+ {
+ return ( m_flBounceAmount != 0. ) || ( m_flSlideAmount != 0. );
+ }
+
+ void InitializeContextData( CParticleCollection *pParticles,
+ void *pContext ) const
+ {
+ }
+
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ bool m_bBrushOnly;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
+
+ bool EnforceConstraint( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext,
+ int nNumValidParticlesInLastChunk ) const;
+ template<bool bKillOnContact, bool bCached> bool EnforceConstraintInternal( int nStartBlock,
+ int nEndBlock,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const;
+};
+
+void C_OP_WorldTraceConstraint::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+{
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+}
+
+
+struct ISectData_t
+{
+ fltx4 m_ISectT; // "t" of intersection
+ fltx4 m_LeftOverT; // "left-over" amount
+ FourVectors m_ISectNormal; // normal at intersection if any
+};
+
+
+
+static void WorldIntersectTNew( FourVectors const *pStartPnt, FourVectors const *pEndPnt,
+ int nCollisionGroup, int nMask, ISectData_t *pISectData,
+ int nCollisionMode, CWorldCollideContextData *pCtx, fltx4 const &fl4ParticleValidMask,
+ float flTolerance = 0.0 )
+{
+ pISectData->m_ISectT = Four_Zeros;
+ pISectData->m_LeftOverT = Four_Zeros;
+ pISectData->m_ISectNormal.x = Four_Zeros;
+ pISectData->m_ISectNormal.y = Four_Zeros;
+ pISectData->m_ISectNormal.z = Four_Zeros;
+
+ if ( pCtx )
+ {
+ pISectData->m_ISectT = Four_Twos;
+ // do simd interseciton against planes
+ if ( nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE )
+ {
+ // find which of our traces is closest to our start / end points
+ pISectData->m_ISectT = Four_Twos; // no hit
+
+ 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(
+ pStartPnt->DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ),
+ pEndPnt->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 );
+ v4PlaneNormal.x = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].x, v4PlaneNormal.x );
+ v4PlaneNormal.y = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].y, v4PlaneNormal.y );
+ v4PlaneNormal.z = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].z, v4PlaneNormal.z );
+ }
+ }
+ fltx4 fl4OutOfRange = AndSIMD( fl4ParticleValidMask,
+ CmpGtSIMD( fl4ClosestDist, ReplicateX4( flTolerance ) ) );
+ if ( IsAnyNegative( fl4OutOfRange ) )
+ {
+ nMask = TestSignSIMD( fl4OutOfRange );
+ for(int i=0; i < 4; i++ )
+ {
+ if ( nMask & ( 1 << i ) )
+ {
+ Vector start = pStartPnt->Vec( i );
+ Vector delta = pEndPnt->Vec( i ) - start;
+
+ float ln = delta.Length();
+
+ float traceScale = max( 5.0, 300.0 / ( ln + .01 ) );
+
+ Vector end = start + delta * traceScale;
+
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( start, end,
+ nMask, NULL, nCollisionGroup, &tr );
+
+ if ( tr.fraction < 1.0 )
+ {
+ SubFloat( v4PointOnPlane.x, i ) = start.x + ( tr.fraction * ( end.x - start.x ) );
+ SubFloat( v4PointOnPlane.y, i ) = start.y + ( tr.fraction * ( end.y - start.y ) );
+ SubFloat( v4PointOnPlane.z, i ) = start.z + ( tr.fraction * ( end.z - start.z ) );
+ SubFloat( v4PlaneNormal.x, i ) = tr.plane.normal.x;
+ SubFloat( v4PlaneNormal.y, i ) = tr.plane.normal.y;
+ SubFloat( v4PlaneNormal.z, i ) = tr.plane.normal.z;
+ }
+ else
+ {
+ // no hit. a normal of 0 will prevent the crossing check from ever
+ // finding a crossing, since it will check for (p - origin ) dot normal
+ // < 0
+ SubFloat( v4PlaneNormal.x, i ) = 0;
+ SubFloat( v4PlaneNormal.y, i ) = 0;
+ SubFloat( v4PlaneNormal.z, i ) = 0;
+ }
+ }
+ }
+ }
+ FourVectors v4StartD = *pStartPnt;
+ FourVectors v4EndD = *pEndPnt;
+ v4StartD -= v4PointOnPlane;
+ v4EndD -= v4PointOnPlane;
+ fltx4 fl4StartDist = v4StartD * v4PlaneNormal;
+ fltx4 fl4EndDist = v4EndD * v4PlaneNormal;
+ fltx4 fl4CrossMask = AndSIMD( CmpGeSIMD( fl4StartDist, Four_Zeros ), CmpLtSIMD( fl4EndDist, Four_Zeros ) );
+ fl4CrossMask = AndSIMD( fl4CrossMask, fl4ParticleValidMask );
+ if ( IsAnyNegative( fl4CrossMask ) )
+ {
+ // a hit!
+ fltx4 fl4T = DivSIMD( fl4StartDist, SubSIMD( fl4StartDist, fl4EndDist ) );
+ fl4CrossMask = AndSIMD( fl4CrossMask, CmpLtSIMD( fl4T, pISectData->m_ISectT ) );
+ if ( IsAnyNegative( fl4CrossMask ) )
+ {
+ pISectData->m_ISectT = MaskedAssign( fl4CrossMask, fl4T, pISectData->m_ISectT );
+ pISectData->m_ISectNormal.x = MaskedAssign( fl4CrossMask, v4PlaneNormal.x, pISectData->m_ISectNormal.x );
+ pISectData->m_ISectNormal.y = MaskedAssign( fl4CrossMask, v4PlaneNormal.y, pISectData->m_ISectNormal.y );
+ pISectData->m_ISectNormal.z = MaskedAssign( fl4CrossMask, v4PlaneNormal.z, pISectData->m_ISectNormal.z );
+ }
+ }
+ }
+ pISectData->m_LeftOverT = MaxSIMD( Four_Zeros, SubSIMD( Four_Ones, pISectData->m_ISectT ) );
+ }
+}
+
+static void WorldIntersectT( FourVectors const *pStartPnt, FourVectors const *pEndPnt,
+ int nCollisionGroup, int nMask, ISectData_t *pISectData,
+ CWorldCollideContextData *pCtx )
+{
+ pISectData->m_ISectT = Four_Zeros;
+ pISectData->m_LeftOverT = Four_Zeros;
+ pISectData->m_ISectNormal.x = Four_Zeros;
+ pISectData->m_ISectNormal.y = Four_Zeros;
+ pISectData->m_ISectNormal.z = Four_Zeros;
+
+ if ( pCtx )
+ {
+ pISectData->m_ISectT = Four_Twos;
+ // do simd interseciton against planes
+ for( int i=0 ; i < pCtx->m_nActivePlanes; i++ )
+ {
+ if ( pCtx->m_bPlaneActive[ i ] )
+ {
+ FourVectors v4StartD = *pStartPnt;
+ FourVectors v4EndD = *pEndPnt;
+ v4StartD -= pCtx->m_PointOnPlane[i];
+ v4EndD -= pCtx->m_PointOnPlane[i];
+ fltx4 fl4StartDist = v4StartD * pCtx->m_PlaneNormal[i];
+ fltx4 fl4EndDist = v4EndD * pCtx->m_PlaneNormal[i];
+ fltx4 fl4CrossMask = AndSIMD( CmpGeSIMD( fl4StartDist, Four_Zeros ), CmpLtSIMD( fl4EndDist, Four_Zeros ) );
+ if ( IsAnyNegative( fl4CrossMask ) )
+ {
+#ifdef FP_EXCEPTIONS_ENABLED
+ // Wherever fl4CrossMask is zero we need to ensure that fl4StartDist does
+ // not equal fl4EndDist to avoid divide-by-zero.
+ //fl4FadeWindow = OrSIMD( AndSIMD( fl4GoodMask, fl4EndTime ), AndNotSIMD( fl4GoodMask, fl4EndTime ) );
+ fl4EndDist = AddSIMD( fl4EndDist, AndNotSIMD( fl4CrossMask, Four_Ones ) );
+#endif
+ // a hit!
+ fltx4 fl4T = DivSIMD( fl4StartDist, SubSIMD( fl4StartDist, fl4EndDist ) );
+ fl4CrossMask = AndSIMD( fl4CrossMask, CmpLtSIMD( fl4T, pISectData->m_ISectT ) );
+ if ( IsAnyNegative( fl4CrossMask ) )
+ {
+ pISectData->m_ISectT = MaskedAssign( fl4CrossMask, fl4T, pISectData->m_ISectT );
+ pISectData->m_ISectNormal.x = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].x, pISectData->m_ISectNormal.x );
+ pISectData->m_ISectNormal.y = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].y, pISectData->m_ISectNormal.y );
+ pISectData->m_ISectNormal.z = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].z, pISectData->m_ISectNormal.z );
+ }
+ }
+ }
+ }
+ pISectData->m_LeftOverT = MaxSIMD( Four_Zeros, SubSIMD( Four_Ones, pISectData->m_ISectT ) );
+ }
+ else
+ {
+ // assumes they don't start solid
+ for(int i=0; i < 4; i++ )
+ {
+ Vector start=pStartPnt->Vec( i );
+ Vector end=pEndPnt->Vec( i );
+ Assert( start.IsValid() );
+ Assert( end.IsValid() );
+
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( start, end,
+ nMask, NULL, nCollisionGroup, &tr );
+
+ SubFloat( pISectData->m_ISectT, i ) = tr.fraction;
+ if ( tr.startsolid )
+ {
+ SubFloat( pISectData->m_LeftOverT, i ) = 0; // don't bounce if stuck
+ }
+ else
+ {
+ SubFloat( pISectData->m_LeftOverT, i ) = 1.0 - tr.fraction;
+ }
+ SubFloat( pISectData->m_ISectNormal.x, i ) = tr.plane.normal.x;
+ SubFloat( pISectData->m_ISectNormal.y, i ) = tr.plane.normal.y;
+ SubFloat( pISectData->m_ISectNormal.z, i ) = tr.plane.normal.z;
+ }
+ }
+}
+
+bool C_OP_WorldTraceConstraint::EnforceConstraint( int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const
+{
+ if ( m_nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE )
+ {
+ if ( m_bKillonContact )
+ return EnforceConstraintInternal<true, true>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
+ else
+ return EnforceConstraintInternal<false, true>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
+ }
+ else
+ {
+ if ( m_bKillonContact )
+ return EnforceConstraintInternal<true, false>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
+ else
+ return EnforceConstraintInternal<false, false>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
+ }
+}
+
+template<bool bKillonContact, bool bCached> bool C_OP_WorldTraceConstraint::EnforceConstraintInternal(
+ int nStartBlock,
+ int nNumBlocks,
+ CParticleCollection *pParticles,
+ void *pContext, int nNumValidParticlesInLastChunk ) const
+{
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ pPrevXYZ += nStartBlock;
+
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ pXYZ += nStartBlock;
+
+ CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+ pRadius += nStartBlock;
+
+ CM128AttributeWriteIterator pLifetime;
+
+ if ( bKillonContact )
+ {
+ pLifetime.Init( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ pLifetime += nStartBlock;
+ }
+
+
+ fltx4 bounceScale = ReplicateX4( m_flBounceAmount );
+
+ fltx4 slideScale = ReplicateX4( m_flSlideAmount );
+
+ bool bBouncingOrSliding = ( m_flBounceAmount != 0.0 ) || ( m_flSlideAmount != 0.0 );
+
+ fltx4 radAdjustScale = ReplicateX4( m_flRadiusScale );
+
+ bool bChangedSomething = false;
+
+ int nMask = MASK_SOLID;
+
+ if ( m_bBrushOnly )
+ nMask = MASK_SOLID_BRUSHONLY;
+
+
+ CWorldCollideContextData **ppCtx;
+ if ( pParticles->m_pParent )
+ ppCtx = &( pParticles->m_pParent->m_pCollisionCacheData[m_nCollisionMode] );
+ else
+ ppCtx = &( pParticles->m_pCollisionCacheData[m_nCollisionMode] );
+
+ CWorldCollideContextData *pCtx = NULL;
+ if ( ( m_nCollisionMode == COLLISION_MODE_PER_FRAME_PLANESET ) ||
+ ( m_nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE ) ||
+ ( m_nCollisionMode == COLLISION_MODE_INITIAL_TRACE_DOWN ) )
+ {
+ if ( ! *ppCtx )
+ {
+ *ppCtx = new CWorldCollideContextData;
+ (*ppCtx)->m_nActivePlanes = 0;
+ (*ppCtx)->m_flLastUpdateTime = -1.0;
+ }
+ pCtx = *ppCtx;
+ if ( pCtx->m_flLastUpdateTime != pParticles->m_flCurTime )
+ {
+ pCtx->CalculatePlanes( pParticles, m_nCollisionMode, m_nCollisionGroupNumber, &m_vecCpOffset, m_flCpMovementTolerance );
+ pCtx->m_flLastUpdateTime = pParticles->m_flCurTime;
+ }
+ }
+ float flTol = m_flTraceTolerance * m_flTraceTolerance;
+ do
+ {
+ // compute radius adjust factor for intersection
+
+ fltx4 radiusFactor = MulSIMD( *pRadius, radAdjustScale );
+
+ // compute movement delta
+ FourVectors delta = *pXYZ;
+ delta -= *pPrevXYZ;
+
+
+ // now, add two components - the non-intersecting movement vector, and the
+ // then the movement vector with the components normal to the plane removed.
+ FourVectors deltanormalized = delta;
+ fltx4 len2 = delta * delta;
+
+ fltx4 bBadDeltas = CmpLeSIMD( len2, Four_Zeros );
+
+ len2 = ReciprocalSqrtEstSIMD( len2 );
+
+ deltanormalized *= AndNotSIMD( bBadDeltas, len2 );
+
+
+ FourVectors endPnt = *pXYZ;
+
+ FourVectors radadjust = deltanormalized;
+ radadjust *= radiusFactor;
+
+ endPnt += radadjust;
+
+ ISectData_t iData;
+
+ if ( bCached )
+ {
+ fltx4 fl4TailMask;
+ if ( nNumBlocks > 1 )
+ fl4TailMask = LoadAlignedIntSIMD( g_SIMD_AllOnesMask );
+ else
+ fl4TailMask = LoadAlignedIntSIMD( g_SIMD_SkipTailMask[nNumValidParticlesInLastChunk] );
+
+ WorldIntersectTNew( pPrevXYZ, &endPnt, m_nCollisionGroupNumber, nMask, &iData, m_nCollisionMode, pCtx, fl4TailMask, flTol );
+ }
+ else
+ WorldIntersectT( pPrevXYZ, &endPnt, m_nCollisionGroupNumber, nMask, &iData, pCtx );
+
+
+ fltx4 didhit = CmpLtSIMD( iData.m_ISectT, Four_Ones );
+ // mask off zero-length deltas
+ didhit = AndNotSIMD( bBadDeltas, didhit );
+
+ if ( IsAnyNegative( didhit ) ) // any penetration?
+ {
+
+ bChangedSomething = true;
+ if ( bKillonContact )
+ {
+ *pLifetime = MaskedAssign( didhit, Four_Zeros, *pLifetime );
+ }
+ else
+ {
+ FourVectors newPnt = delta;
+ newPnt *= iData.m_ISectT;
+ newPnt += *pPrevXYZ;
+
+ if ( bBouncingOrSliding )
+ {
+ // need to compute movement due to sliding and bouncing, and add it to the point,
+ // and also compute the new velocity, adjust prev pnt to reflect that new velocity
+
+ FourVectors bouncePart = VectorReflect( deltanormalized, iData.m_ISectNormal );
+ bouncePart *= bounceScale;
+ FourVectors newVel = bouncePart;
+
+ bouncePart *= iData.m_LeftOverT;
+ newPnt += bouncePart;
+
+ FourVectors slidePart = VectorSlide( delta, iData.m_ISectNormal );
+ slidePart *= slideScale;
+ newVel += slidePart;
+
+ slidePart *= iData.m_LeftOverT;
+
+ newPnt += slidePart;
+
+ FourVectors newPrev = newPnt;
+ newPrev -= newVel;
+ pPrevXYZ->x = MaskedAssign( didhit, newPrev.x, pPrevXYZ->x );
+ pPrevXYZ->y = MaskedAssign( didhit, newPrev.y, pPrevXYZ->y );
+ pPrevXYZ->z = MaskedAssign( didhit, newPrev.z, pPrevXYZ->z );
+ }
+ pXYZ->x = MaskedAssign( didhit, newPnt.x, pXYZ->x );
+ pXYZ->y = MaskedAssign( didhit, newPnt.y, pXYZ->y );
+ pXYZ->z = MaskedAssign( didhit, newPnt.z, pXYZ->z );
+ }
+
+ CHECKSYSTEM( pParticles );
+ }
+ ++pXYZ;
+ ++pPrevXYZ;
+ ++pRadius;
+ if ( bKillonContact )
+ ++pLifetime;
+ } while (--nNumBlocks);
+ return bChangedSomething;
+}
+
+
+
+DEFINE_PARTICLE_OPERATOR( C_OP_WorldTraceConstraint, "Collision via traces", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_WorldTraceConstraint )
+ DMXELEMENT_UNPACK_FIELD( "collision mode", "0", int, m_nCollisionMode )
+ DMXELEMENT_UNPACK_FIELD( "amount of bounce", "0", float, m_flBounceAmount )
+ DMXELEMENT_UNPACK_FIELD( "amount of slide", "0", float, m_flSlideAmount )
+ DMXELEMENT_UNPACK_FIELD( "radius scale", "1", float, m_flRadiusScale )
+ DMXELEMENT_UNPACK_FIELD( "brush only", "0", bool, m_bBrushOnly )
+ DMXELEMENT_UNPACK_FIELD_STRING( "collision group", "NONE", m_CollisionGroupName )
+ DMXELEMENT_UNPACK_FIELD( "control point offset for fast collisions", "0 0 0", Vector, m_vecCpOffset )
+ DMXELEMENT_UNPACK_FIELD( "control point movement distance tolerance", "5", float, m_flCpMovementTolerance )
+ DMXELEMENT_UNPACK_FIELD( "kill particle on collision", "0", bool, m_bKillonContact )
+ DMXELEMENT_UNPACK_FIELD( "trace accuracy tolerance", "24", float, m_flTraceTolerance )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_WorldTraceConstraint )
+
+void AddBuiltInParticleConstraints( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_ConstrainDistance );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_PlanarConstraint );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_WorldCollideConstraint );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_WorldTraceConstraint );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_ConstrainDistanceToPath );
+}
+
+
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 );
+}
+
diff --git a/particles/builtin_particle_emitters.cpp b/particles/builtin_particle_emitters.cpp
new file mode 100644
index 0000000..8e1c728
--- /dev/null
+++ b/particles/builtin_particle_emitters.cpp
@@ -0,0 +1,876 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern int g_nParticle_Multiplier;
+
+//-----------------------------------------------------------------------------
+// Emits particles immediately
+//-----------------------------------------------------------------------------
+struct InstantaneousEmitterContext_t
+{
+ int m_nRemainingParticles;
+ int m_ActualParticlesToEmit;
+ float m_flTimeOffset;
+ bool m_bReadScaleFactor;
+};
+
+class C_OP_InstantaneousEmitter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ if ( m_nScaleControlPoint >= 0 )
+ return ( 1ULL << m_nScaleControlPoint );
+ return 0;
+ }
+
+ virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const;
+
+ // unpack structure will be applied by creator. add extra initialization needed here
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_nMinParticlesToEmit >= 0 )
+ {
+ if ( m_nMinParticlesToEmit > m_nParticlesToEmit )
+ {
+ V_swap( m_nParticlesToEmit, m_nMinParticlesToEmit );
+ }
+ }
+
+ if ( m_nPerFrameNum < 0 )
+ {
+ m_nPerFrameNum = INT_MAX;
+ }
+ m_nScaleControlPointField = clamp( m_nScaleControlPointField, 0, 2 );
+ }
+
+ virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly )
+ {
+ pCtx->m_nRemainingParticles = 0;
+ }
+ }
+ virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly )
+ {
+ pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
+ SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
+ }
+ }
+
+ // Called when the SFM wants to skip forward in time
+ virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
+ {
+ // NOTE: This is a bit of a hack. We're saying that if we're skipping more than two seconds, that we're
+ // probably not going to bother emitting at all. Really, this would have to know the maximum
+ // lifetime of the child particles and only skip if past that.
+
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( flTime > ( flStartTime + 2.0f ) )
+ {
+ pCtx->m_nRemainingParticles = 0;
+ }
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ if ( m_nMinParticlesToEmit >= 0 )
+ {
+ pCtx->m_ActualParticlesToEmit = pParticles->RandomInt( m_nMinParticlesToEmit, m_nParticlesToEmit );
+ }
+ else
+ {
+ pCtx->m_ActualParticlesToEmit = m_nParticlesToEmit;
+ }
+ pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
+ pCtx->m_flTimeOffset = 0.0f;
+ pCtx->m_bReadScaleFactor = false;
+ }
+
+ virtual void Restart( CParticleCollection *pParticles, void *pContext )
+ {
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
+ pCtx->m_flTimeOffset = pParticles->m_flCurTime;
+ pCtx->m_bReadScaleFactor = false;
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( InstantaneousEmitterContext_t );
+ }
+
+ virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
+ {
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ return !(pCtx->m_nRemainingParticles <= 0);
+ }
+
+ int m_nParticlesToEmit;
+ int m_nMinParticlesToEmit;
+ float m_flStartTime;
+ int m_nPerFrameNum;
+ int m_nScaleControlPoint;
+ int m_nScaleControlPointField;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter, "emit_instantaneously", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter )
+ DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
+ DMXELEMENT_UNPACK_FIELD( "num_to_emit_minimum", "-1", int, m_nMinParticlesToEmit )
+ DMXELEMENT_UNPACK_FIELD( "num_to_emit", "100", int, m_nParticlesToEmit )
+ DMXELEMENT_UNPACK_FIELD( "maximum emission per frame", "-1", int, m_nPerFrameNum )
+ DMXELEMENT_UNPACK_FIELD( "emission count scale control point", "-1", int, m_nScaleControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "emission count scale control point field", "0", int, m_nScaleControlPointField )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter )
+
+
+uint32 C_OP_InstantaneousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const
+{
+ // Don't emit any more if the particle system has emitted all it's supposed to.
+ InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
+ if ( pCtx->m_nRemainingParticles <= 0 )
+ return 0;
+
+ // Wait until we're told to start emitting
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( pParticles->m_flCurTime < flStartTime )
+ return 0;
+
+ if ( pCtx->m_ActualParticlesToEmit == 0 )
+ return 0;
+
+ if ( ( m_nScaleControlPoint >= 0 ) && !pCtx->m_bReadScaleFactor )
+ {
+ Vector vecScale;
+ if ( flStartTime <= pParticles->m_flCurTime && flStartTime >= pParticles->m_flCurTime - pParticles->m_flPreviousDt )
+ {
+ pParticles->GetControlPointAtTime( m_nScaleControlPoint, flStartTime, &vecScale );
+ }
+ else
+ {
+ pParticles->GetControlPointAtPrevTime( m_nScaleControlPoint, &vecScale );
+ }
+
+ pCtx->m_ActualParticlesToEmit *= vecScale[m_nScaleControlPointField];
+ pCtx->m_nRemainingParticles *= vecScale[m_nScaleControlPointField];
+ pCtx->m_bReadScaleFactor = true;
+ }
+
+ pCtx->m_nRemainingParticles = max( pCtx->m_nRemainingParticles, 0 );
+
+ // NOTE: Applying the scale here because I don't believe we can sample the control point
+ // values inside
+ // We're only allowed to emit so many particles, though..
+ // If we run out of room, only emit the last N particles
+ int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
+ // Cap to the maximum emission per frame
+ int nParticlesThisFrame = min( m_nPerFrameNum, pCtx->m_nRemainingParticles );
+ nAllowedParticlesToEmit = min( nAllowedParticlesToEmit, nParticlesThisFrame );
+ int nActualParticlesToEmit = min( nAllowedParticlesToEmit, pCtx->m_ActualParticlesToEmit * g_nParticle_Multiplier );
+ pCtx->m_nRemainingParticles -= nParticlesThisFrame;
+ Assert( pCtx->m_nRemainingParticles >= 0 );
+
+ if ( nActualParticlesToEmit == 0 )
+ return 0;
+
+ int nStartParticle = pParticles->m_nActiveParticles;
+ pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
+
+ // !! speed!! do sse init here
+ for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
+ {
+ float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ *pTimeStamp = flStartTime;
+ }
+
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Emits particles over time
+//-----------------------------------------------------------------------------
+struct ContinuousEmitterContext_t
+{
+ float m_flTotalActualParticlesSoFar;
+ int m_nTotalEmittedSoFar;
+ float m_flNextEmitTime;
+ float m_flTimeOffset;
+ bool m_bStoppedEmission;
+};
+
+bool g_bDontMakeSkipToTimeTakeForever = false;
+
+
+class C_OP_ContinuousEmitter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_flEmitRate < 0.0f )
+ {
+ m_flEmitRate = 0.0f;
+ }
+ if ( m_flEmissionDuration < 0.0f )
+ {
+ m_flEmissionDuration = 0.0f;
+ }
+ m_flEmitRate *= g_nParticle_Multiplier;
+ }
+
+ virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const ;
+
+ inline bool IsInfinitelyEmitting() const
+ {
+ return ( m_flEmissionDuration == 0.0f );
+ }
+
+ virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly || IsInfinitelyEmitting() )
+ {
+ pCtx->m_bStoppedEmission = true;
+ }
+ }
+ virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly || IsInfinitelyEmitting() )
+ {
+ pCtx->m_bStoppedEmission = false;
+ SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
+ }
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ pCtx->m_flNextEmitTime = m_flStartTime;
+ pCtx->m_flTotalActualParticlesSoFar = 0.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_flTimeOffset = 0.0f;
+ pCtx->m_bStoppedEmission = false;
+ }
+
+ virtual void Restart( CParticleCollection *pParticles, void *pContext )
+ {
+ if ( !IsInfinitelyEmitting() )
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ pCtx->m_flNextEmitTime = pParticles->m_flCurTime + m_flStartTime;
+ pCtx->m_flTotalActualParticlesSoFar = 0.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_flTimeOffset = pParticles->m_flCurTime;
+ }
+ }
+
+ // Called when the SFM wants to skip forward in time
+ // Currently hacked for save/load pre-sim - correct solution is to serialize rather
+ // than skip-to-time and simulate
+ virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( flTime <= flStartTime )
+ return;
+
+ float flControlPointScale = pParticles->GetHighestControlPoint();
+ flControlPointScale *= m_flEmissionScale;
+ float flEmissionRate = m_flEmitRate;
+
+ float flEmitStrength;
+ if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
+ {
+ flEmissionRate *= flEmitStrength;
+ }
+
+ if ( flControlPointScale != 0.0f )
+ {
+ flEmissionRate *= flControlPointScale;
+ }
+
+ float flPrevDrawTime = pParticles->m_flCurTime - flTime;
+ float flCurrDrawTime = pParticles->m_flCurTime;
+
+ if ( !IsInfinitelyEmitting() )
+ {
+ if ( flPrevDrawTime < flStartTime )
+ {
+ flPrevDrawTime = flStartTime;
+ }
+ //if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
+ //{
+ // flCurrDrawTime = flStartTime + m_flEmissionDuration;
+ //}
+ }
+ float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
+ flDeltaTime = min( flDeltaTime, 4.f );
+ flPrevDrawTime = flCurrDrawTime - flDeltaTime;
+ //disabled for now
+ pCtx->m_flTotalActualParticlesSoFar = flDeltaTime * flEmissionRate;
+
+
+ //if ( !IsInfinitelyEmitting() )
+ // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
+ pCtx->m_nTotalEmittedSoFar = 0;
+ //simulate a bunch
+ int nActualParticlesToEmit = floor (pCtx->m_flTotalActualParticlesSoFar);
+ int nStartParticle = pParticles->m_nActiveParticles;
+
+ if ( pParticles->m_nMaxAllowedParticles < nStartParticle + nActualParticlesToEmit )
+ {
+ nActualParticlesToEmit = pParticles->m_nMaxAllowedParticles - nStartParticle;
+ }
+
+ pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
+
+ float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
+ float flTimeStep = flPrevDrawTime + flTimeStampStep;
+
+ // Set the particle creation time to the exact sub-frame particle emission time
+ // !! speed!! do sse init here
+ for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
+ {
+ float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ flTimeStep = min( flTimeStep, flCurrDrawTime );
+ *pTimeStamp = flTimeStep;
+ flTimeStep += flTimeStampStep;
+ }
+
+ if ( !g_bDontMakeSkipToTimeTakeForever )
+ {
+ flPrevDrawTime = max( flPrevDrawTime, flCurrDrawTime - pParticles->m_pDef->m_flNoDrawTimeToGoToSleep );
+ pParticles->m_flCurTime = flPrevDrawTime;
+ pParticles->m_fl4CurTime = ReplicateX4( flPrevDrawTime );
+ for( float i = flPrevDrawTime; i < flCurrDrawTime; i += 0.1 )
+ {
+ pParticles->Simulate( .1, false );
+ }
+ }
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( ContinuousEmitterContext_t );
+ }
+
+ virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
+ {
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+ if ( pCtx->m_bStoppedEmission )
+ return false;
+
+ if ( m_flEmitRate <= 0.0f )
+ return false;
+
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
+ return false;
+
+ return true;
+ }
+
+ float m_flEmissionDuration;
+ float m_flStartTime;
+ float m_flEmitRate;
+ float m_flTimePerEmission;
+ float m_flEmissionScale;
+ bool m_bScalePerParticle;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter, "emit_continuously", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter )
+ DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
+ DMXELEMENT_UNPACK_FIELD( "emission_rate", "100", float, m_flEmitRate )
+ DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
+ DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
+ DMXELEMENT_UNPACK_FIELD( "use parent particles for emission scaling", "0", bool, m_bScalePerParticle )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter )
+
+uint32 C_OP_ContinuousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const
+{
+ // Have we emitted all the particles we're going to emit?
+ // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
+ ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
+
+ //Allows for dynamic scaling via changes in number of control points.
+ float flControlPointScale = pParticles->GetHighestControlPoint();
+ //The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
+ flControlPointScale *= m_flEmissionScale;
+ //Global strength scale brought in by operator fade in/fade out/oscillate
+ float flEmissionRate = m_flEmitRate * flCurStrength;
+ if ( flControlPointScale != 0.0f || m_bScalePerParticle )
+ {
+ if ( m_bScalePerParticle )
+ {
+ if ( pParticles->m_pParent )
+ {
+ flControlPointScale = pParticles->m_pParent->m_nActiveParticles * m_flEmissionScale;
+ }
+ else
+ {
+ flControlPointScale = m_flEmissionScale;
+ }
+
+ }
+ flEmissionRate *= flControlPointScale;
+ }
+
+ if ( flEmissionRate == 0.0f )
+ return 0;
+
+ if ( !C_OP_ContinuousEmitter::MayCreateMoreParticles( pParticles, pContext ) )
+ return 0;
+
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( pParticles->m_flCurTime < flStartTime )
+ return 0;
+
+ Assert( flEmissionRate != 0.0f );
+
+ // determine our previous and current draw times and clamp them to start time and emission duration
+ float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
+ float flCurrDrawTime = pParticles->m_flCurTime;
+
+ if ( !IsInfinitelyEmitting() )
+ {
+ if ( flPrevDrawTime < flStartTime )
+ {
+ flPrevDrawTime = flStartTime;
+ }
+ if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
+ {
+ flCurrDrawTime = flStartTime + m_flEmissionDuration;
+ }
+ }
+
+ float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
+
+ //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
+ float flActualParticlesToEmit = flEmissionRate * flDeltaTime;
+
+ //Add emitted particle to float counter to allow for fractional emission
+ pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;
+
+ //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
+ int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;
+
+ //Add emitted particles to running int total.
+ pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;
+
+
+ if ( nParticlesToEmit == 0 )
+ return 0;
+
+ // We're only allowed to emit so many particles, though..
+ // If we run out of room, only emit the last N particles
+ int nActualParticlesToEmit = nParticlesToEmit;
+ int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
+ if ( nAllowedParticlesToEmit < nParticlesToEmit )
+ {
+ nActualParticlesToEmit = nAllowedParticlesToEmit;
+ //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
+ }
+ if ( nActualParticlesToEmit == 0 )
+ return 0;
+
+ int nStartParticle = pParticles->m_nActiveParticles;
+ pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
+
+
+ float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
+ float flTimeStep = flPrevDrawTime + flTimeStampStep;
+
+ // Set the particle creation time to the exact sub-frame particle emission time
+ // !! speed!! do sse init here
+ for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
+ {
+ float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ flTimeStep = min( flTimeStep, flCurrDrawTime );
+ *pTimeStamp = flTimeStep;
+ flTimeStep += flTimeStampStep;
+ }
+
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Noise Emitter
+//-----------------------------------------------------------------------------
+struct NoiseEmitterContext_t
+{
+ float m_flTotalActualParticlesSoFar;
+ int m_nTotalEmittedSoFar;
+ float m_flNextEmitTime;
+ float m_flTimeOffset;
+ bool m_bStoppedEmission;
+};
+
+class C_OP_NoiseEmitter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_NoiseEmitter );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_flEmitRate < 0.0f )
+ {
+ m_flEmitRate = 0.0f;
+ }
+ if ( m_flEmissionDuration < 0.0f )
+ {
+ m_flEmissionDuration = 0.0f;
+ }
+ m_flEmitRate *= g_nParticle_Multiplier;
+ }
+
+ virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const ;
+
+ inline bool IsInfinitelyEmitting() const
+ {
+ return ( m_flEmissionDuration == 0.0f );
+ }
+
+ virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly || IsInfinitelyEmitting() )
+ {
+ pCtx->m_bStoppedEmission = true;
+ }
+ }
+ virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ if ( !bInfiniteOnly || IsInfinitelyEmitting() )
+ {
+ pCtx->m_bStoppedEmission = false;
+ SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
+ }
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ pCtx->m_flNextEmitTime = m_flStartTime;
+ pCtx->m_flTotalActualParticlesSoFar = 1.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_flTimeOffset = 0.0f;
+ pCtx->m_bStoppedEmission = false;
+ }
+
+ virtual void Restart( CParticleCollection *pParticles, void *pContext )
+ {
+ if ( !IsInfinitelyEmitting() )
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ pCtx->m_flNextEmitTime = m_flStartTime + pParticles->m_flCurTime;
+ pCtx->m_flTotalActualParticlesSoFar = 1.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_flTimeOffset = pParticles->m_flCurTime;
+ }
+ }
+
+ // Called when the SFM wants to skip forward in time
+ virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( flTime <= flStartTime )
+ return;
+
+ float flControlPointScale = pParticles->GetHighestControlPoint();
+ flControlPointScale *= m_flEmissionScale;
+ float flEmissionRate = m_flEmitRate;
+
+ float flEmitStrength;
+ if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
+ {
+ flEmissionRate *= flEmitStrength;
+ }
+
+ if ( flControlPointScale != 0.0f )
+ {
+ flEmissionRate *= flControlPointScale;
+ }
+ pCtx->m_flTotalActualParticlesSoFar = ( pParticles->m_flCurTime - flStartTime ) * flEmissionRate + 1;
+
+ //if ( !IsInfinitelyEmitting() )
+ // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
+ pCtx->m_nTotalEmittedSoFar = 0;
+
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( NoiseEmitterContext_t );
+ }
+
+ virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
+ {
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+ if ( pCtx->m_bStoppedEmission )
+ return false;
+
+ if ( m_flEmitRate <= 0.0f )
+ return false;
+
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
+ return false;
+
+ return true;
+ }
+
+ float m_flEmissionDuration;
+ float m_flStartTime;
+ float m_flEmitRate;
+ float m_flTimePerEmission;
+ float m_flEmissionScale;
+ bool m_bAbsVal, m_bAbsValInv;
+ float m_flOffset;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ float m_flNoiseScale, m_flNoiseScaleLoc;
+ Vector m_vecOffsetLoc;
+ float m_flWorldTimeScale;
+};
+
+
+DEFINE_PARTICLE_OPERATOR( C_OP_NoiseEmitter, "emit noise", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter )
+ DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
+ DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
+ DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
+ DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","0.1",float,m_flNoiseScale)
+ //DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","0.001",float,m_flNoiseScaleLoc)
+ DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset )
+ //DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc )
+ DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal )
+ DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv )
+ DMXELEMENT_UNPACK_FIELD( "emission minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "emission maximum","100", float, m_flOutputMax )
+ DMXELEMENT_UNPACK_FIELD( "world time noise coordinate scale","0", float, m_flWorldTimeScale )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter )
+
+uint32 C_OP_NoiseEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
+ void *pContext ) const
+{
+ // Have we emitted all the particles we're going to emit?
+ // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
+ NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
+
+ //Allows for dynamic scaling via changes in number of control points.
+ float flControlPointScale = pParticles->GetHighestControlPoint();
+ //The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
+ flControlPointScale *= m_flEmissionScale;
+
+ float flAbsScale;
+ int nAbsVal;
+ nAbsVal = 0xffffffff;
+ flAbsScale = 0.5;
+ if ( m_bAbsVal )
+ {
+ nAbsVal = 0x7fffffff;
+ flAbsScale = 1.0;
+ }
+
+ float fMin = m_flOutputMin;
+ float fMax = m_flOutputMax;
+
+
+ float CoordScale = m_flNoiseScale;
+ //float CoordScaleLoc = m_flNoiseScaleLoc;
+
+
+ float ValueScale, ValueBase;
+
+ Vector Coord, CoordLoc, CoordWorldTime;
+ //CoordLoc.x = pxyz[0];
+ //CoordLoc.y = pxyz[4];
+ //CoordLoc.z = pxyz[8];
+ //CoordLoc += m_vecOffsetLoc;
+
+ float Offset = m_flOffset;
+ Coord = Vector ( (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset) );
+ Coord *= CoordScale;
+ //CoordLoc *= CoordScaleLoc;
+ //Coord += CoordLoc;
+
+ CoordWorldTime = Vector( (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale) );
+ Coord += CoordWorldTime;
+
+ fltx4 flNoise128;
+ FourVectors fvNoise;
+
+ fvNoise.DuplicateVector( Coord );
+ flNoise128 = NoiseSIMD( fvNoise );
+ float flNoise = SubFloat( flNoise128, 0 );
+
+ *( (int *) &flNoise) &= nAbsVal;
+
+ ValueScale = ( flAbsScale *( fMax - fMin ) );
+ ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );
+
+ if ( m_bAbsValInv )
+ {
+ flNoise = 1.0 - flNoise;
+ }
+
+ float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) );
+ flInitialNoise = clamp(flInitialNoise, 0.0f, (float) INT_MAX );
+
+ //Global strength scale brought in by operator fade in/fade out/oscillate
+ float flEmissionRate = flInitialNoise * flCurStrength;
+ if ( flControlPointScale != 0.0f )
+ {
+ flEmissionRate *= flControlPointScale;
+ }
+
+ if ( flEmissionRate == 0.0f )
+ return 0;
+
+ if ( !C_OP_NoiseEmitter::MayCreateMoreParticles( pParticles, pContext ) )
+ return 0;
+
+ float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
+ if ( pParticles->m_flCurTime < flStartTime )
+ return 0;
+
+ Assert( flEmissionRate != 0.0f );
+
+ // determine our previous and current draw times and clamp them to start time and emission duration
+ float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
+ float flCurrDrawTime = pParticles->m_flCurTime;
+
+ if ( !IsInfinitelyEmitting() )
+ {
+ if ( flPrevDrawTime < flStartTime )
+ {
+ flPrevDrawTime = flStartTime;
+ }
+ if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
+ {
+ flCurrDrawTime = flStartTime + m_flEmissionDuration;
+ }
+ }
+
+ float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
+
+ //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
+ float flActualParticlesToEmit = flEmissionRate * flDeltaTime;
+
+ //Add emitted particle to float counter to allow for fractional emission
+ pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;
+
+ //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
+ int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;
+
+ //Add emitted particles to running int total.
+ pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;
+
+
+ if ( nParticlesToEmit == 0 )
+ return 0;
+
+ // We're only allowed to emit so many particles, though..
+ // If we run out of room, only emit the last N particles
+ int nActualParticlesToEmit = nParticlesToEmit;
+ int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
+ if ( nAllowedParticlesToEmit < nParticlesToEmit )
+ {
+ nActualParticlesToEmit = nAllowedParticlesToEmit;
+ //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
+ }
+ if ( nActualParticlesToEmit == 0 )
+ return 0;
+
+ int nStartParticle = pParticles->m_nActiveParticles;
+ pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
+
+ float flTimeStampStep = ( flCurrDrawTime - flPrevDrawTime ) / ( nActualParticlesToEmit );
+ float flTimeStep = flPrevDrawTime + flTimeStampStep;
+
+ // Set the particle creation time to the exact sub-frame particle emission time
+ // !! speed!! do sse init here
+ for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
+ {
+ float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ flTimeStep = min( flTimeStep, flCurrDrawTime );
+ *pTimeStamp = flTimeStep;
+ flTimeStep += flTimeStampStep;
+ }
+
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+}
+
+
+void AddBuiltInParticleEmitters( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_ContinuousEmitter );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_InstantaneousEmitter );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_NoiseEmitter );
+}
+
diff --git a/particles/builtin_particle_forces.cpp b/particles/builtin_particle_forces.cpp
new file mode 100644
index 0000000..1495526
--- /dev/null
+++ b/particles/builtin_particle_forces.cpp
@@ -0,0 +1,544 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+
+#ifdef USE_BLOBULATOR
+// TODO: These should be in public by the time the SDK ships
+#include "../common/blobulator/Physics/PhysParticle.h"
+#include "../common/blobulator/Physics/PhysParticleCache_inl.h"
+#include "../common/blobulator/Physics/PhysTiler.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+class C_OP_RandomForce : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RandomForce );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+
+ virtual void AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const;
+
+ Vector m_MinForce;
+ Vector m_MaxForce;
+};
+
+void C_OP_RandomForce::AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const
+{
+ FourVectors box_min,box_max;
+ box_min.DuplicateVector( m_MinForce * flStrength );
+ box_max.DuplicateVector( m_MaxForce * flStrength);
+ box_max -= box_min;
+ int nContext = GetSIMDRandContext();
+ for(int i=0;i<nBlocks;i++)
+ {
+ pAccumulatedForces->x = AddSIMD(
+ pAccumulatedForces->x, AddSIMD( box_min.x, MulSIMD( box_max.x, RandSIMD( nContext) ) ) );
+ pAccumulatedForces->y = AddSIMD(
+ pAccumulatedForces->y, AddSIMD( box_min.y, MulSIMD( box_max.y, RandSIMD( nContext) ) ) );
+ pAccumulatedForces->z = AddSIMD(
+ pAccumulatedForces->z, AddSIMD( box_min.z, MulSIMD( box_max.z, RandSIMD( nContext) ) ) );
+ pAccumulatedForces++;
+ }
+ ReleaseSIMDRandContext( nContext );
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RandomForce, "random force", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RandomForce )
+ DMXELEMENT_UNPACK_FIELD( "min force", "0 0 0", Vector, m_MinForce )
+ DMXELEMENT_UNPACK_FIELD( "max force", "0 0 0", Vector, m_MaxForce )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RandomForce )
+
+class C_OP_TwistAroundAxis : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_TwistAroundAxis );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+
+ virtual void AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const;
+
+ float m_fForceAmount;
+ Vector m_TwistAxis;
+ bool m_bLocalSpace;
+};
+
+void C_OP_TwistAroundAxis::AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const
+{
+ FourVectors Twist_AxisInWorldSpace;
+ Twist_AxisInWorldSpace.DuplicateVector( pParticles->TransformAxis( m_TwistAxis, m_bLocalSpace ) );
+ Twist_AxisInWorldSpace.VectorNormalize();
+
+ Vector vecCenter;
+ pParticles->GetControlPointAtTime( 0, pParticles->m_flCurTime, &vecCenter );
+ FourVectors Center;
+ Center.DuplicateVector( vecCenter );
+ size_t nPosStride;
+ fltx4 ForceScale = ReplicateX4( m_fForceAmount * flStrength );
+ const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
+ for(int i=0;i<nBlocks;i++)
+ {
+ FourVectors ofs=*pPos;
+ ofs -= Center;
+ fltx4 bGoodLen = CmpGtSIMD( ofs*ofs, Four_Epsilons );
+ ofs.VectorNormalize();
+ FourVectors parallel_comp=ofs;
+ parallel_comp *= ( ofs*Twist_AxisInWorldSpace );
+ ofs-=parallel_comp;
+ bGoodLen = AndSIMD( bGoodLen, CmpGtSIMD( ofs*ofs, Four_Epsilons ) );
+ ofs.VectorNormalize();
+ FourVectors TangentialForce = ofs ^ Twist_AxisInWorldSpace;
+ TangentialForce *= ForceScale;
+ TangentialForce.x = AndSIMD( TangentialForce.x, bGoodLen );
+ TangentialForce.y = AndSIMD( TangentialForce.y, bGoodLen );
+ TangentialForce.z = AndSIMD( TangentialForce.z, bGoodLen );
+
+ *(pAccumulatedForces++) += TangentialForce;
+ pPos += nPosStride;
+ }
+
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_TwistAroundAxis, "twist around axis", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_TwistAroundAxis )
+ DMXELEMENT_UNPACK_FIELD( "amount of force", "0", float, m_fForceAmount )
+ DMXELEMENT_UNPACK_FIELD( "twist axis", "0 0 1", Vector, m_TwistAxis )
+ DMXELEMENT_UNPACK_FIELD( "object local space axis 0/1","0", bool, m_bLocalSpace )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_TwistAroundAxis )
+
+class C_OP_AttractToControlPoint : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_AttractToControlPoint );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+
+ virtual void AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const;
+
+ float m_fForceAmount;
+ float m_fFalloffPower;
+ int m_nControlPointNumber;
+};
+
+void C_OP_AttractToControlPoint::AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const
+{
+ int power_frac=-4.0*m_fFalloffPower; // convert to what pow_fixedpoint_exponent_simd wants
+ fltx4 fForceScale=ReplicateX4( -m_fForceAmount * flStrength );
+
+ Vector vecCenter;
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecCenter );
+ FourVectors Center;
+ Center.DuplicateVector( vecCenter );
+ size_t nPosStride;
+ const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
+
+ for(int i=0;i<nBlocks;i++)
+ {
+ FourVectors ofs=*pPos;
+ ofs -= Center;
+ fltx4 len = ofs.length();
+ ofs *= MulSIMD( fForceScale, ReciprocalSaturateSIMD( len )); // normalize and scale
+ ofs *= Pow_FixedPoint_Exponent_SIMD( len, power_frac ); // * 1/pow(dist, exponent)
+ fltx4 bGood = CmpGtSIMD( len, Four_Epsilons );
+ ofs.x = AndSIMD( bGood, ofs.x );
+ ofs.y = AndSIMD( bGood, ofs.y );
+ ofs.z = AndSIMD( bGood, ofs.z );
+ *(pAccumulatedForces++) += ofs;
+ pPos += nPosStride;
+ }
+
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_AttractToControlPoint, "Pull towards control point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_AttractToControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "amount of force", "0", float, m_fForceAmount )
+ DMXELEMENT_UNPACK_FIELD( "falloff power", "2", float, m_fFalloffPower )
+ DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_AttractToControlPoint )
+
+
+#undef USE_BLOBULATOR // TODO (Ilya): Must fix this code
+
+#ifdef USE_BLOBULATOR
+
+class C_OP_LennardJonesForce : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_LennardJonesForce );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ //m_pParticleCache = new ParticleCache(m_fInteractionRadius);
+ m_pPhysTiler = new PhysTiler(m_fInteractionRadius);
+ }
+
+ virtual void AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const;
+
+ // TODO: Have to destroy PhysTiler in destructor somewhere!!!!
+
+ //ParticleCache* m_pParticleCache;
+ PhysTiler* m_pPhysTiler;
+ float m_fInteractionRadius;
+ float m_fSurfaceTension;
+ float m_fLennardJonesRepulsion;
+ float m_fLennardJonesAttraction;
+ float m_fMaxRepulsion;
+ float m_fMaxAttraction;
+
+private:
+ //virtual void addParticleForce(PhysParticle* a, PhysParticleCacheNode* bcn, float flStrength, float ts) const;
+ virtual void addParticleForce(PhysParticle* a, PhysParticle* b, float distSq, float flStrength, float ts) const;
+};
+
+
+
+
+// TODO: I should make sure I don't have divide by zero errors.
+// TODO: ts is not used
+void C_OP_LennardJonesForce::addParticleForce(PhysParticle* a, PhysParticle* b, float distSq, float flStrength, float ts) const
+{
+ float d = sqrtf(distSq);
+
+ //========================================================
+ // based on equation of force between two molecules which is
+ // factor * ((distance/bond_length)^-7 - (distance/bond_length)^-13)
+
+ float f;
+ if(a->group == b->group) // In the same group
+ {
+ float p = a->radius * 2.0f / (d+FLT_EPSILON);
+ float p2 = p * p;
+ float p4 = p2 * p2;
+
+
+ // Surface tension:
+
+ //Notes:
+ // Can average the neighbor count between the two particles...
+ // I tried this, and discovered that rather than averaging, I can take maybe take the
+ // larger of the two neighbor counts, so the attraction between two particles on the surface will be strong, but
+ // the attraction between a particle inside and a particle on the surface will be weak. I can also try
+ // taking the min so that the attraction between a particle on the surface and a particle inside the fluid will
+ // be strong, but the attraction between two particles completely on the inside will be weak.
+ //
+ // int symmetric_neighbor_count = min(a->neighbor_count, b->neighbor_count);
+ //
+ // Can try having neighbors only cause stronger attraction (no repulsion)
+ // Can try lower exponents for the LennardJones forces.
+
+ // This is a trick to prevent single particles from floating off... the less neighbors a particle has.. the more it sticks
+ // This also tends to simulate surface tension
+ float surface_tension_modifier = ((24.0f * m_fSurfaceTension) / (a->neighbor_count + b->neighbor_count + 0.1f)) + 1.0f;
+ //float lennard_jones_force = fLennardJones * 2.0f * (p2 - (p4 * p4));
+ float lennard_jones_force = m_fLennardJonesAttraction * p2 - m_fLennardJonesRepulsion*p4;
+ f = surface_tension_modifier * lennard_jones_force;
+
+ // This is some older code:
+ //f = ((35.0f * LampScene::simulationSurfaceTension) / (a->neighbor_count + 0.1f)) * (p2 - (p4 * p4));
+ // used to be 68'
+
+
+ //float factor = (b->neighbor_count < 13 && neighbor_count < 13 ? 4.0f : 0.5f);
+ //f = factor * (p2 - (p2 * p2 * p2 * p2));
+ }
+ else
+ {
+ // This was 3.5 ... made 3.0 so particles get closer when they collide
+ if(d > a->radius * 3.0f) return;
+
+ float p = a->radius * 4.0f / d;
+ f = -1.0f * p * p;
+ }
+
+ // These checks are great to have, but are they really necessary?
+ // It might also be good to have a limit on velocity
+
+ // Attraction is a positive value.
+ // Repulsion is negative.
+ if(f < -m_fMaxRepulsion) f = -m_fMaxRepulsion;
+ if(f > m_fMaxAttraction) f = m_fMaxAttraction;
+
+ Point3D scaledr = (b->center - a->center) * (f/(d+FLT_EPSILON)); // Dividing by d scales distance down to a unit vector
+ a->force.add(scaledr);
+ b->force.subtract(scaledr);
+}
+
+void C_OP_LennardJonesForce::AddForces( FourVectors *pAccumulatedForces,
+ CParticleCollection *pParticles,
+ int nBlocks,
+ float flStrength,
+ void *pContext ) const
+{
+ int nParticles = pParticles->m_nActiveParticles; // Not sure if this is correct!
+
+ size_t nPosStride;
+ const FourVectors *pPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_XYZ, &nPosStride );
+
+ // The +4 is because particles are stored by PET in blocks of 4
+ // However, not every block is full. Thus, nParticles may be
+ // less than nBlocks*4. Could get rid of this if the swizzling/unswizzling
+ // loop were better written.
+ static SmartArray<PhysParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
+ while(imp_particles_sa.size < nParticles+4)
+ {
+ imp_particles_sa.pushAutoSize(PhysParticle());
+ }
+
+ /*
+ size_t nPrevPosStride;
+ const FourVectors *pPrevPos=pParticles->Get4VAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &nPrevPosStride );
+ */
+
+ //m_pParticleCache->beginFrame();
+ //m_pParticleCache->beginTile(nParticles);
+
+ m_pPhysTiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f));
+
+ // Unswizzle from the FourVectors format into particles
+ for(int i=0, p=0;i<nBlocks;i++)
+ {
+ FourVectors ofs=*pPos;
+
+ PhysParticle* particle = &(imp_particles_sa[p]);
+ particle->force.clear();
+ if(p < nParticles)
+ {
+ particle->center = ofs.Vec(0);
+ particle->group = 0;
+ particle->neighbor_count = 0;
+ m_pPhysTiler->insertParticle(particle);
+ }
+ p++;
+
+ particle = &(imp_particles_sa[p]);
+ particle->force.clear();
+ if(p < nParticles)
+ {
+ particle->center = ofs.Vec(1);
+ particle->group = 0;
+ particle->neighbor_count = 0;
+ m_pPhysTiler->insertParticle(particle);
+ }
+ p++;
+
+ particle = &(imp_particles_sa[p]);
+ particle->force.clear();
+ if(p < nParticles)
+ {
+ particle->center = ofs.Vec(2);
+ particle->group = 0;
+ particle->neighbor_count = 0;
+ m_pPhysTiler->insertParticle(particle);
+ }
+ p++;
+
+ particle = &(imp_particles_sa[p]);
+ particle->force.clear();
+ if(p < nParticles)
+ {
+ particle->center = ofs.Vec(3);
+ particle->group = 0;
+ particle->neighbor_count = 0;
+ m_pPhysTiler->insertParticle(particle);
+ }
+ p++;
+
+ pPos += nPosStride;
+ }
+
+ m_pPhysTiler->processTiles();
+
+
+ float timeStep = 1.0f; // This should be customizable
+ float nearNeighborInteractionRadius = 2.3f;
+ float nearNeighborInteractionRadiusSq = nearNeighborInteractionRadius * nearNeighborInteractionRadius;
+
+ PhysParticleCache* pCache = m_pPhysTiler->getParticleCache();
+
+ // Calculate number of near neighbors for each particle
+ for(int i = 0; i < nParticles; i++)
+ {
+ PhysParticle *b1 = &(imp_particles_sa[i]);
+
+ PhysParticleAndDist* node = pCache->get(b1);
+
+ while(node->particle != NULL)
+ {
+ PhysParticle* b2 = node->particle;
+
+ // Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
+ if(b1 < b2 && node->distSq < nearNeighborInteractionRadiusSq)
+ {
+ b1->neighbor_count++;
+ b2->neighbor_count++;
+
+ }
+
+ node++;
+ }
+ }
+
+ // Calculate forces on particles due to other particles
+ for(int i = 0; i < nParticles; i++)
+ {
+ PhysParticle *b1 = &(imp_particles_sa[i]);
+
+ PhysParticleAndDist* node = pCache->get(b1);
+
+ while(node->particle != NULL)
+ {
+ PhysParticle* b2 = node->particle;
+
+ // Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
+ if(b1 < b2)
+ {
+ addParticleForce(b1, b2, node->distSq, flStrength, timeStep);
+ }
+
+ node++;
+ }
+ }
+
+
+ /*
+ for(ParticleListNode* bit3 = particles; bit3; bit3 = bit3->next)
+ {
+ Particle* b = bit3->particle;
+ b->prev_group = b->group; // Set prev group
+ //b1->addDirDragForce();
+ b->move(ts); // Move the particle (it should never be used again until next iteration)
+ }
+ */
+
+ m_pPhysTiler->endFrame();
+
+ // Swizzle forces back into FourVectors format
+ for(int i=0;i<nBlocks;i++)
+ {
+ pAccumulatedForces->X(0) += imp_particles_sa[i*4].force[0];
+ pAccumulatedForces->Y(0) += imp_particles_sa[i*4].force[1];
+ pAccumulatedForces->Z(0) += imp_particles_sa[i*4].force[2];
+
+ pAccumulatedForces->X(1) += imp_particles_sa[i*4+1].force[0];
+ pAccumulatedForces->Y(1) += imp_particles_sa[i*4+1].force[1];
+ pAccumulatedForces->Z(1) += imp_particles_sa[i*4+1].force[2];
+
+ pAccumulatedForces->X(2) += imp_particles_sa[i*4+2].force[0];
+ pAccumulatedForces->Y(2) += imp_particles_sa[i*4+2].force[1];
+ pAccumulatedForces->Z(2) += imp_particles_sa[i*4+2].force[2];
+
+ pAccumulatedForces->X(3) += imp_particles_sa[i*4+3].force[0];
+ pAccumulatedForces->Y(3) += imp_particles_sa[i*4+3].force[1];
+ pAccumulatedForces->Z(3) += imp_particles_sa[i*4+3].force[2];
+
+ pAccumulatedForces++;
+ }
+
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_LennardJonesForce, "lennard jones force", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_LennardJonesForce )
+DMXELEMENT_UNPACK_FIELD( "interaction radius", "4", float, m_fInteractionRadius )
+DMXELEMENT_UNPACK_FIELD( "surface tension", "1", float, m_fSurfaceTension )
+DMXELEMENT_UNPACK_FIELD( "lennard jones attractive force", "1", float, m_fLennardJonesAttraction )
+DMXELEMENT_UNPACK_FIELD( "lennard jones repulsive force", "1", float, m_fLennardJonesRepulsion )
+DMXELEMENT_UNPACK_FIELD( "max repulsion", "100", float, m_fMaxRepulsion )
+DMXELEMENT_UNPACK_FIELD( "max attraction", "100", float, m_fMaxAttraction )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_LennardJonesForce )
+
+#endif
+
+
+void AddBuiltInParticleForceGenerators( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_RandomForce );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_TwistAroundAxis );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_AttractToControlPoint );
+ #ifdef USE_BLOBULATOR
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_FORCEGENERATOR, C_OP_LennardJonesForce );
+ #endif
+}
+
diff --git a/particles/builtin_particle_ops.cpp b/particles/builtin_particle_ops.cpp
new file mode 100644
index 0000000..0d8acdf
--- /dev/null
+++ b/particles/builtin_particle_ops.cpp
@@ -0,0 +1,4584 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier2/renderutils.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "studio.h"
+#include "bspflags.h"
+#include "tier0/vprof.h"
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#if MEASURE_PARTICLE_PERF
+
+#if VPROF_LEVEL > 0
+#define START_OP float flOpStartTime = Plat_FloatTime(); VPROF_ENTER_SCOPE(pOp->GetDefinition()->GetName())
+#else
+#define START_OP float flOpStartTime = Plat_FloatTime();
+#endif
+
+#if VPROF_LEVEL > 0
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->GetDefinition(); \
+ pDef->RecordExecutionTime( flETime ); \
+} \
+ VPROF_EXIT_SCOPE()
+#else
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->GetDefinition(); \
+ pDef->RecordExecutionTime( flETime ); \
+}
+#endif
+#else
+#define START_OP
+#define END_OP
+#endif
+
+//-----------------------------------------------------------------------------
+// Standard movement operator
+//-----------------------------------------------------------------------------
+class C_OP_BasicMovement : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_BasicMovement );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Vector m_Gravity;
+ float m_fDrag;
+ int m_nMaxConstraintPasses;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_BasicMovement, "Movement Basic", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_BasicMovement )
+ DMXELEMENT_UNPACK_FIELD( "gravity", "0 0 0", Vector, m_Gravity )
+ DMXELEMENT_UNPACK_FIELD( "drag", "0", float, m_fDrag )
+ DMXELEMENT_UNPACK_FIELD( "max constraint passes", "3", int, m_nMaxConstraintPasses )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_BasicMovement )
+
+
+#define MAXIMUM_NUMBER_OF_CONSTRAINTS 100
+//#define CHECKALL 1
+
+#ifdef NDEBUG
+#define CHECKSYSTEM( p ) 0
+#else
+#ifdef CHECKALL
+static void CHECKSYSTEM( CParticleCollection *pParticles )
+{
+// Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Assert( IsFinite( xyz[0] ) );
+ Assert( IsFinite( xyz[4] ) );
+ Assert( IsFinite( xyz[8] ) );
+ Assert( IsFinite( xyz_prev[0] ) );
+ Assert( IsFinite( xyz_prev[4] ) );
+ Assert( IsFinite( xyz_prev[8] ) );
+ }
+}
+#else
+#define CHECKSYSTEM( p ) 0
+#endif
+#endif
+
+void C_OP_BasicMovement::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator prev_xyz( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ C4VAttributeWriteIterator xyz( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ // fltx4 adj_dt = ReplicateX4( (1.0-m_fDrag) * ( pParticles->m_flDt / pParticles->m_flPreviousDt ) );
+ fltx4 adj_dt = ReplicateX4( ( pParticles->m_flDt / pParticles->m_flPreviousDt ) * ExponentialDecay( (1.0f-max(0.f,m_fDrag)), (1.0f/30.0f), pParticles->m_flDt ) );
+
+ size_t nForceStride=0;
+ Vector acc = m_Gravity;
+ fltx4 accFactorX = ReplicateX4( acc.x );
+ fltx4 accFactorY = ReplicateX4( acc.y );
+ fltx4 accFactorZ = ReplicateX4( acc.z );
+
+ int nAccumulators = pParticles->m_pDef->m_ForceGenerators.Count();
+
+ FourVectors PerParticleForceAccumulator[MAX_PARTICLES_IN_A_SYSTEM / 4]; // xbox fixme - memory
+
+ FourVectors *pAccOut = PerParticleForceAccumulator;
+ if (nAccumulators)
+ {
+ // we do have per particle force accumulators
+ nForceStride = 1;
+ int nblocks = pParticles->m_nPaddedActiveParticles;
+ for(int i=0;i<nblocks;i++)
+ {
+ pAccOut->x = accFactorX;
+ pAccOut->y = accFactorY;
+ pAccOut->z = accFactorZ;
+ pAccOut++;
+ }
+ // now, call all force accumulators
+ for(int i=0;i < nAccumulators ; i++ )
+ {
+ float flStrengthOp;
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_ForceGenerators[i];
+ if ( pParticles->CheckIfOperatorShouldRun( pOp, &flStrengthOp ))
+ {
+ START_OP;
+ pParticles->m_pDef->m_ForceGenerators[i]->AddForces(
+ PerParticleForceAccumulator,
+ pParticles,
+ nblocks,
+ flStrengthOp,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nForceGeneratorsCtxOffsets[i] );
+ END_OP;
+ }
+ }
+ }
+ else
+ {
+ pAccOut->x = accFactorX;
+ pAccOut->y = accFactorY;
+ pAccOut->z = accFactorZ;
+ // we just have gravity
+ }
+
+ CHECKSYSTEM( pParticles );
+ fltx4 DtSquared = ReplicateX4( pParticles->m_flDt * pParticles->m_flDt );
+ int ctr = pParticles->m_nPaddedActiveParticles;
+ FourVectors *pAccIn = PerParticleForceAccumulator;
+ do
+ {
+ accFactorX = MulSIMD( pAccIn->x, DtSquared );
+ accFactorY = MulSIMD( pAccIn->y, DtSquared );
+ accFactorZ = MulSIMD( pAccIn->z, DtSquared );
+
+ // we will write prev xyz, and swap prev and cur at the end
+ prev_xyz->x = AddSIMD( xyz->x,
+ AddSIMD( accFactorX, MulSIMD( adj_dt, SubSIMD( xyz->x, prev_xyz->x ) ) ) );
+ prev_xyz->y = AddSIMD( xyz->y,
+ AddSIMD( accFactorY, MulSIMD( adj_dt, SubSIMD( xyz->y, prev_xyz->y ) ) ) );
+ prev_xyz->z = AddSIMD( xyz->z,
+ AddSIMD( accFactorZ, MulSIMD( adj_dt, SubSIMD( xyz->z, prev_xyz->z ) ) ) );
+ CHECKSYSTEM( pParticles );
+ ++prev_xyz;
+ ++xyz;
+ pAccIn += nForceStride;
+ } while (--ctr);
+
+ CHECKSYSTEM( pParticles );
+ pParticles->SwapPosAndPrevPos();
+ // now, enforce constraints
+ int nConstraints = pParticles->m_pDef->m_Constraints.Count();
+ if ( nConstraints && pParticles->m_nPaddedActiveParticles )
+ {
+ bool bConstraintSatisfied[ MAXIMUM_NUMBER_OF_CONSTRAINTS ];
+ bool bFinalConstraint[ MAXIMUM_NUMBER_OF_CONSTRAINTS ];
+ for(int i=0;i<nConstraints; i++)
+ {
+ bFinalConstraint[i] = pParticles->m_pDef->m_Constraints[i]->IsFinalConstraint();
+
+ bConstraintSatisfied[i] = false;
+ pParticles->m_pDef->m_Constraints[i]->SetupConstraintPerFrameData(
+ pParticles, pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i] );
+ }
+
+ // constraints get to see their own per psystem per op random #s
+ for(int p=0; p < m_nMaxConstraintPasses ; p++ )
+ {
+// int nSaveOffset=pParticles->m_nOperatorRandomSampleOffset;
+ for(int i=0;i<nConstraints; i++)
+ {
+// pParticles->m_nOperatorRandomSampleOffset += 23;
+ if ( ! bConstraintSatisfied[i] )
+ {
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_Constraints[i];
+ bConstraintSatisfied[i] = true;
+ if ( ( !bFinalConstraint[i] ) && ( pParticles->CheckIfOperatorShouldRun( pOp ) ) )
+ {
+ START_OP;
+ bool bDidSomething = pOp->EnforceConstraint(
+ 0, pParticles->m_nPaddedActiveParticles, pParticles,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i],
+ pParticles->m_nActiveParticles );
+ END_OP;
+ CHECKSYSTEM( pParticles );
+ if ( bDidSomething )
+ {
+ // other constraints now not satisfied, maybe
+ for( int j=0; j<nConstraints; j++)
+ {
+ if ( i != j )
+ {
+ bConstraintSatisfied[ j ] = false;
+ }
+ }
+ }
+ }
+ }
+ }
+// pParticles->m_nOperatorRandomSampleOffset = nSaveOffset;
+ }
+ // now, run final constraints
+ for(int i=0;i<nConstraints; i++)
+ {
+ CParticleOperatorInstance *pOp = pParticles->m_pDef->m_Constraints[i];
+ if ( ( bFinalConstraint[i] ) &&
+ ( pParticles->CheckIfOperatorShouldRun(
+ pOp ) ) )
+ {
+ START_OP;
+ pOp->EnforceConstraint(
+ 0, pParticles->m_nPaddedActiveParticles, pParticles,
+ pParticles->m_pOperatorContextData +
+ pParticles->m_pDef->m_nConstraintsCtxOffsets[i],
+ pParticles->m_nActiveParticles );
+ END_OP;
+ CHECKSYSTEM( pParticles );
+ }
+ }
+ }
+ CHECKSYSTEM( pParticles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Fade and kill operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeAndKill : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeAndKill );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flStartFadeInTime;
+ float m_flEndFadeInTime;
+ float m_flStartFadeOutTime;
+ float m_flEndFadeOutTime;
+ float m_flStartAlpha;
+ float m_flEndAlpha;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeAndKill, "Alpha Fade and Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeAndKill )
+ DMXELEMENT_UNPACK_FIELD( "start_alpha","1", float, m_flStartAlpha )
+ DMXELEMENT_UNPACK_FIELD( "end_alpha","0", float, m_flEndAlpha )
+ DMXELEMENT_UNPACK_FIELD( "start_fade_in_time","0", float, m_flStartFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "end_fade_in_time","0.5", float, m_flEndFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "start_fade_out_time","0.5", float, m_flStartFadeOutTime )
+ DMXELEMENT_UNPACK_FIELD( "end_fade_out_time","1", float, m_flEndFadeOutTime )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeAndKill )
+
+void C_OP_FadeAndKill::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+{
+ // Cache off and validate values
+ if ( m_flEndFadeInTime < m_flStartFadeInTime )
+ {
+ m_flEndFadeInTime = m_flStartFadeInTime;
+ }
+ if ( m_flEndFadeOutTime < m_flStartFadeOutTime )
+ {
+ m_flEndFadeOutTime = m_flStartFadeOutTime;
+ }
+
+ if ( m_flStartFadeOutTime < m_flStartFadeInTime )
+ {
+ V_swap( m_flStartFadeInTime, m_flStartFadeOutTime );
+ }
+
+ if ( m_flEndFadeOutTime < m_flEndFadeInTime )
+ {
+ V_swap( m_flEndFadeInTime, m_flEndFadeOutTime );
+ }
+}
+
+void C_OP_FadeAndKill::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+
+ fltx4 fl4StartFadeInTime = ReplicateX4( m_flStartFadeInTime );
+ fltx4 fl4StartFadeOutTime = ReplicateX4( m_flStartFadeOutTime );
+ fltx4 fl4EndFadeInTime = ReplicateX4( m_flEndFadeInTime );
+ fltx4 fl4EndFadeOutTime = ReplicateX4( m_flEndFadeOutTime );
+ fltx4 fl4EndAlpha = ReplicateX4( m_flEndAlpha );
+ fltx4 fl4StartAlpha = ReplicateX4( m_flStartAlpha );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ fltx4 fl4FadeInDuration = ReplicateX4( m_flEndFadeInTime - m_flStartFadeInTime );
+ fltx4 fl4OOFadeInDuration = ReciprocalEstSIMD( fl4FadeInDuration );
+
+ fltx4 fl4FadeOutDuration = ReplicateX4( m_flEndFadeOutTime - m_flStartFadeOutTime );
+ fltx4 fl4OOFadeOutDuration = ReciprocalEstSIMD( fl4FadeOutDuration );
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4Age = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4ParticleLifeTime = *pLifeDuration;
+ fltx4 fl4KillMask = CmpGeSIMD( fl4Age, *pLifeDuration ); // takes care of lifeduration = 0 div 0
+ fl4Age = MulSIMD( fl4Age, ReciprocalEstSIMD( fl4ParticleLifeTime ) ); // age 0..1
+ fltx4 fl4FadingInMask = AndNotSIMD( fl4KillMask,
+ AndSIMD(
+ CmpLeSIMD( fl4StartFadeInTime, fl4Age ), CmpGtSIMD(fl4EndFadeInTime, fl4Age ) ) );
+ fltx4 fl4FadingOutMask = AndNotSIMD( fl4KillMask,
+ AndSIMD(
+ CmpLeSIMD( fl4StartFadeOutTime, fl4Age ), CmpGtSIMD(fl4EndFadeOutTime, fl4Age ) ) );
+ if ( IsAnyNegative( fl4FadingInMask ) )
+ {
+ fltx4 fl4Goal = MulSIMD( *pInitialAlpha, fl4StartAlpha );
+ fltx4 fl4NewAlpha = SimpleSplineRemapValWithDeltasClamped( fl4Age, fl4StartFadeInTime, fl4FadeInDuration, fl4OOFadeInDuration,
+ fl4Goal, SubSIMD( *pInitialAlpha, fl4Goal ) );
+
+ *pAlpha = MaskedAssign( fl4FadingInMask, fl4NewAlpha, *pAlpha );
+ }
+ if ( IsAnyNegative( fl4FadingOutMask ) )
+ {
+ fltx4 fl4Goal = MulSIMD( *pInitialAlpha, fl4EndAlpha );
+ fltx4 fl4NewAlpha = SimpleSplineRemapValWithDeltasClamped( fl4Age, fl4StartFadeOutTime, fl4FadeOutDuration, fl4OOFadeOutDuration,
+ *pInitialAlpha, SubSIMD( fl4Goal, *pInitialAlpha ) );
+ *pAlpha = MaskedAssign( fl4FadingOutMask, fl4NewAlpha, *pAlpha );
+ }
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fade In Operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeIn : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeIn );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flFadeInTimeMin;
+ float m_flFadeInTimeMax;
+ float m_flFadeInTimeExp;
+ bool m_bProportional;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeIn, "Alpha Fade In Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeIn )
+ DMXELEMENT_UNPACK_FIELD( "fade in time min",".25", float, m_flFadeInTimeMin )
+ DMXELEMENT_UNPACK_FIELD( "fade in time max",".25", float, m_flFadeInTimeMax )
+ DMXELEMENT_UNPACK_FIELD( "fade in time exponent","1", float, m_flFadeInTimeExp )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1","1", bool, m_bProportional )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeIn )
+
+
+void C_OP_FadeIn::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ C4IAttributeIterator pParticleID( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ int nSSEFixedExponent = m_flFadeInTimeExp*4.0;
+
+ fltx4 FadeTimeMin = ReplicateX4( m_flFadeInTimeMin );
+ fltx4 FadeTimeWidth = ReplicateX4( m_flFadeInTimeMax - m_flFadeInTimeMin );
+
+ do
+ {
+ fltx4 FadeInTime= Pow_FixedPoint_Exponent_SIMD(
+ pParticles->RandomFloat( *pParticleID, nRandomOffset ),
+ nSSEFixedExponent);
+ FadeInTime = AddSIMD( FadeTimeMin, MulSIMD( FadeTimeWidth, FadeInTime ) );
+
+ // Find our life percentage
+ fltx4 flLifeTime = SubSIMD( CurTime, *pCreationTime );
+ if ( m_bProportional )
+ {
+ flLifeTime =
+ MaxSIMD( Four_Zeros,
+ MinSIMD( Four_Ones,
+ MulSIMD( flLifeTime, ReciprocalEstSIMD( *pLifeDuration ) ) ) );
+ }
+
+ fltx4 ApplyMask = CmpGtSIMD( FadeInTime, flLifeTime );
+ if ( IsAnyNegative( ApplyMask ) )
+ {
+ // Fading in
+ fltx4 NewAlpha =
+ SimpleSplineRemapValWithDeltasClamped(
+ flLifeTime, Four_Zeros,
+ FadeInTime, ReciprocalEstSIMD( FadeInTime ),
+ Four_Zeros, *pInitialAlpha );
+ *( pAlpha ) = MaskedAssign( ApplyMask, NewAlpha, *( pAlpha ) );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ ++pParticleID;
+ } while( --nCtr );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Fade Out Operator
+//-----------------------------------------------------------------------------
+class C_OP_FadeOut : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_FadeOut );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ float flBias = ( m_flFadeBias != 0.0f ) ? m_flFadeBias : 0.5f;
+ m_fl4BiasParam = PreCalcBiasParameter( ReplicateX4( flBias ) );
+ if ( m_flFadeOutTimeMin == 0.0f && m_flFadeOutTimeMax == 0.0f )
+ {
+ m_flFadeOutTimeMin = m_flFadeOutTimeMax = FLT_EPSILON;
+ }
+ }
+
+ float m_flFadeOutTimeMin;
+ float m_flFadeOutTimeMax;
+ float m_flFadeOutTimeExp;
+ float m_flFadeBias;
+ fltx4 m_fl4BiasParam;
+ bool m_bProportional;
+ bool m_bEaseInAndOut;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_FadeOut, "Alpha Fade Out Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_FadeOut )
+ DMXELEMENT_UNPACK_FIELD( "fade out time min",".25", float, m_flFadeOutTimeMin )
+ DMXELEMENT_UNPACK_FIELD( "fade out time max",".25", float, m_flFadeOutTimeMax )
+ DMXELEMENT_UNPACK_FIELD( "fade out time exponent","1", float, m_flFadeOutTimeExp )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1","1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "ease in and out","1", bool, m_bEaseInAndOut )
+ DMXELEMENT_UNPACK_FIELD( "fade bias", "0.5", float, m_flFadeBias )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_FadeOut )
+
+
+void C_OP_FadeOut::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128InitialAttributeIterator pInitialAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeWriteIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ C4IAttributeIterator pParticleID( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ int nSSEFixedExponent = m_flFadeOutTimeExp*4.0;
+
+ fltx4 FadeTimeMin = ReplicateX4( m_flFadeOutTimeMin );
+ fltx4 FadeTimeWidth = ReplicateX4( m_flFadeOutTimeMax - m_flFadeOutTimeMin );
+
+ do
+ {
+ fltx4 fl4FadeOutTime = Pow_FixedPoint_Exponent_SIMD(
+ pParticles->RandomFloat( *pParticleID, nRandomOffset ),
+ nSSEFixedExponent );
+ fl4FadeOutTime = AddSIMD( FadeTimeMin, MulSIMD( FadeTimeWidth, fl4FadeOutTime ) );
+
+ fltx4 fl4Lifespan;
+
+ // Find our life percentage
+ fltx4 fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4LifeDuration = *pLifeDuration;
+
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( fl4LifeTime, ReciprocalEstSIMD( fl4LifeDuration ) );
+ fl4FadeOutTime = SubSIMD( Four_Ones, fl4FadeOutTime );
+ fl4Lifespan = SubSIMD ( Four_Ones, fl4FadeOutTime );
+ }
+ else
+ {
+ fl4FadeOutTime = SubSIMD( *pLifeDuration, fl4FadeOutTime );
+ fl4Lifespan = SubSIMD( *pLifeDuration, fl4FadeOutTime ) ;
+ }
+
+ fltx4 ApplyMask = CmpLtSIMD( fl4FadeOutTime, fl4LifeTime );
+ if ( IsAnyNegative( ApplyMask ) )
+ {
+ // Fading out
+ fltx4 NewAlpha;
+ if ( m_bEaseInAndOut )
+ {
+ NewAlpha = SimpleSplineRemapValWithDeltasClamped(
+ fl4LifeTime, fl4FadeOutTime,
+ fl4Lifespan, ReciprocalEstSIMD( fl4Lifespan ),
+ *pInitialAlpha, SubSIMD ( Four_Zeros, *pInitialAlpha ) );
+ NewAlpha = MaxSIMD( Four_Zeros, NewAlpha );
+ }
+ else
+ {
+ fltx4 fl4Frac = MulSIMD( SubSIMD( fl4LifeTime, fl4FadeOutTime ), ReciprocalEstSIMD( fl4Lifespan ) );
+ fl4Frac = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, fl4Frac ) );
+ fl4Frac = BiasSIMD( fl4Frac, m_fl4BiasParam );
+ fl4Frac = SubSIMD( Four_Ones, fl4Frac );
+ NewAlpha = MulSIMD( *pInitialAlpha, fl4Frac );
+ }
+
+ *( pAlpha ) = MaskedAssign( ApplyMask, NewAlpha, *( pAlpha ) );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialAlpha;
+ ++pAlpha;
+ ++pParticleID;
+ } while( --nCtr );
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Oscillating Scalar operator
+// performs an oscillation operation on any scalar (fade, radius, etc.)
+//-----------------------------------------------------------------------------
+class C_OP_OscillateScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OscillateScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nField;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_RateMin;
+ float m_RateMax;
+ float m_FrequencyMin;
+ float m_FrequencyMax;
+ int m_nField;
+ bool m_bProportional, m_bProportionalOp;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flOscMult;
+ float m_flOscAdd;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OscillateScalar, "Oscillate Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateScalar )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "oscillation field", "7", int, m_nField, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate min", "0", float, m_RateMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate max", "0", float, m_RateMax )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency min", "1", float, m_FrequencyMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency max", "1", float, m_FrequencyMax )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1", "1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "start time min", "0", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start time max", "0", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end time min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end time max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start/end proportional", "1", bool, m_bProportionalOp )
+ DMXELEMENT_UNPACK_FIELD( "oscillation multiplier", "2", float, m_flOscMult )
+ DMXELEMENT_UNPACK_FIELD( "oscillation start phase", ".5", float, m_flOscAdd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateScalar )
+
+void C_OP_OscillateScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4IAttributeIterator pParticleId ( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ CM128AttributeWriteIterator pOscField ( m_nField, pParticles) ;
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ fltx4 fl4OscVal;
+
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength * pParticles->m_flDt );
+
+ fltx4 fl4CosFactorMultiplier = ReplicateX4( m_flOscMult );
+ fltx4 fl4CosFactorAdd = ReplicateX4( m_flOscAdd );
+
+ fltx4 fl4CosFactor = AddSIMD( MulSIMD( fl4CosFactorMultiplier, fl4CurTime ), fl4CosFactorAdd );
+ fltx4 fl4CosFactorProp = fl4CosFactorMultiplier;
+
+ fltx4 fl4StartTimeMin = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4StartTimeWidth = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4EndTimeMin = ReplicateX4( m_flEndTime_min );
+ fltx4 fl4EndTimeWidth = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+
+ fltx4 fl4FrequencyMin = ReplicateX4( m_FrequencyMin );
+ fltx4 fl4FrequencyWidth = ReplicateX4( m_FrequencyMax - m_FrequencyMin );
+ fltx4 fl4RateMin = ReplicateX4( m_RateMin );
+ fltx4 fl4RateWidth = ReplicateX4( m_RateMax - m_RateMin );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime;
+ if ( m_bProportionalOp )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ }
+ else
+ {
+ fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ }
+
+ fltx4 fl4StartTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 11);
+ fl4StartTime = AddSIMD( fl4StartTimeMin, MulSIMD( fl4StartTimeWidth, fl4StartTime ) );
+ fltx4 fl4EndTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 12);
+ fl4EndTime = AddSIMD( fl4EndTimeMin, MulSIMD( fl4EndTimeWidth, fl4EndTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4Frequency = pParticles->RandomFloat( *pParticleId, nRandomOffset );
+ fl4Frequency = AddSIMD( fl4FrequencyMin, MulSIMD( fl4FrequencyWidth, fl4Frequency ) );
+ fltx4 fl4Rate= pParticles->RandomFloat( *pParticleId, nRandomOffset + 1);
+ fl4Rate = AddSIMD( fl4RateMin, MulSIMD( fl4RateWidth, fl4Rate ) );
+ fltx4 fl4Cos;
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) );
+ fl4Cos = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fl4LifeTime, fl4Frequency )), fl4CosFactorAdd );
+ }
+ else
+ {
+ fl4Cos = MulSIMD( fl4CosFactor, fl4Frequency );
+ }
+ fltx4 fl4OscMultiplier = MulSIMD( fl4Rate, fl4ScaleFactor);
+ fl4OscVal = AddSIMD ( *pOscField, MulSIMD ( fl4OscMultiplier, SinEst01SIMD( fl4Cos ) ) );
+ if ( m_nField == 7)
+ {
+ *pOscField = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fl4OscVal, Four_Ones), Four_Zeros ), *pOscField );
+ }
+ else
+ {
+ *pOscField = MaskedAssign( fl4GoodMask, fl4OscVal, *pOscField );
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pOscField;
+ ++pParticleId;
+ } while (--nCtr );
+
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Oscillating Vector operator
+// performs an oscillation operation on any vector (location, tint)
+//-----------------------------------------------------------------------------
+class C_OP_OscillateVector : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OscillateVector );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nField;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Vector m_RateMin;
+ Vector m_RateMax;
+ Vector m_FrequencyMin;
+ Vector m_FrequencyMax;
+ int m_nField;
+ bool m_bProportional, m_bProportionalOp;
+ bool m_bAccelerator;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flOscMult;
+ float m_flOscAdd;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OscillateVector, "Oscillate Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateVector )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "oscillation field", "0", int, m_nField, "intchoice particlefield_vector" )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate min", "0 0 0", Vector, m_RateMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation rate max", "0 0 0", Vector, m_RateMax )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency min", "1 1 1", Vector, m_FrequencyMin )
+ DMXELEMENT_UNPACK_FIELD( "oscillation frequency max", "1 1 1", Vector, m_FrequencyMax )
+ DMXELEMENT_UNPACK_FIELD( "proportional 0/1", "1", bool, m_bProportional )
+ DMXELEMENT_UNPACK_FIELD( "start time min", "0", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start time max", "0", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end time min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end time max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start/end proportional", "1", bool, m_bProportionalOp )
+ DMXELEMENT_UNPACK_FIELD( "oscillation multiplier", "2", float, m_flOscMult )
+ DMXELEMENT_UNPACK_FIELD( "oscillation start phase", ".5", float, m_flOscAdd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OscillateVector )
+
+
+void C_OP_OscillateVector::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4IAttributeIterator pParticleId ( PARTICLE_ATTRIBUTE_PARTICLE_ID, pParticles );
+ C4VAttributeWriteIterator pOscField ( m_nField, pParticles) ;
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ FourVectors fvOscVal;
+
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength * pParticles->m_flDt );
+
+ fltx4 fl4CosFactorMultiplier = ReplicateX4( m_flOscMult );
+ fltx4 fl4CosFactorAdd = ReplicateX4( m_flOscAdd );
+
+ fltx4 fl4CosFactor = AddSIMD( MulSIMD( fl4CosFactorMultiplier, fl4CurTime ), fl4CosFactorAdd );
+ fltx4 fl4CosFactorProp = fl4CosFactorMultiplier;
+
+ fltx4 fl4StartTimeMin = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4StartTimeWidth = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4EndTimeMin = ReplicateX4( m_flEndTime_min );
+ fltx4 fl4EndTimeWidth = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+
+ FourVectors fvFrequencyMin;
+ fvFrequencyMin.DuplicateVector( m_FrequencyMin );
+ FourVectors fvFrequencyWidth;
+ fvFrequencyWidth.DuplicateVector( m_FrequencyMax - m_FrequencyMin );
+ FourVectors fvRateMin;
+ fvRateMin.DuplicateVector( m_RateMin );
+ FourVectors fvRateWidth;
+ fvRateWidth.DuplicateVector( m_RateMax - m_RateMin );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime;
+ if ( m_bProportionalOp )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ }
+ else
+ {
+ fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ }
+
+ fltx4 fl4StartTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 11);
+ fl4StartTime = AddSIMD( fl4StartTimeMin, MulSIMD( fl4StartTimeWidth, fl4StartTime ) );
+ fltx4 fl4EndTime= pParticles->RandomFloat( *pParticleId, nRandomOffset + 12);
+ fl4EndTime = AddSIMD( fl4EndTimeMin, MulSIMD( fl4EndTimeWidth, fl4EndTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ FourVectors fvFrequency;
+ fvFrequency.x = pParticles->RandomFloat( *pParticleId, nRandomOffset + 8 );
+ fvFrequency.y = pParticles->RandomFloat( *pParticleId, nRandomOffset + 12 );
+ fvFrequency.z = pParticles->RandomFloat( *pParticleId, nRandomOffset + 15 );
+ fvFrequency.VProduct( fvFrequencyWidth );
+ fvFrequency += fvFrequencyMin;
+
+ FourVectors fvRate;
+ fvRate.x = pParticles->RandomFloat( *pParticleId, nRandomOffset + 3);
+ fvRate.y = pParticles->RandomFloat( *pParticleId, nRandomOffset + 7);
+ fvRate.z = pParticles->RandomFloat( *pParticleId, nRandomOffset + 9);
+
+ //fvRate = AddSIMD( fvRateMin, MulSIMD( fvRateWidth, fvRate ) );
+ fvRate.VProduct( fvRateWidth );
+ fvRate += fvRateMin;
+
+ FourVectors fvCos;
+ if ( m_bProportional )
+ {
+ fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) );
+ fvCos.x = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.x, fl4LifeTime )), fl4CosFactorAdd );
+ fvCos.y = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.y, fl4LifeTime )), fl4CosFactorAdd );
+ fvCos.z = AddSIMD( MulSIMD( fl4CosFactorProp, MulSIMD( fvFrequency.z, fl4LifeTime )), fl4CosFactorAdd );
+ }
+ else
+ {
+ //fvCos = MulSIMD( fl4CosFactor, fvFrequency );
+ fvCos.x = MulSIMD( fvFrequency.x, fl4CosFactor );
+ fvCos.y = MulSIMD( fvFrequency.y, fl4CosFactor );
+ fvCos.z = MulSIMD( fvFrequency.z, fl4CosFactor );
+ }
+
+ FourVectors fvOscMultiplier;
+ fvOscMultiplier.x = MulSIMD( fvRate.x, fl4ScaleFactor);
+ fvOscMultiplier.y = MulSIMD( fvRate.y, fl4ScaleFactor);
+ fvOscMultiplier.z = MulSIMD( fvRate.z, fl4ScaleFactor);
+
+ FourVectors fvOutput = *pOscField;
+
+ fvOscVal.x = AddSIMD ( fvOutput.x, MulSIMD ( fvOscMultiplier.x, SinEst01SIMD( fvCos.x ) ) );
+ fvOscVal.y = AddSIMD ( fvOutput.y, MulSIMD ( fvOscMultiplier.y, SinEst01SIMD( fvCos.y ) ) );
+ fvOscVal.z = AddSIMD ( fvOutput.z, MulSIMD ( fvOscMultiplier.z, SinEst01SIMD( fvCos.z ) ) );
+
+ if ( m_nField == 6)
+ {
+ pOscField->x = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.x, Four_Ones), Four_Zeros ), fvOutput.x );
+ pOscField->y = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.y, Four_Ones), Four_Zeros ), fvOutput.y );
+ pOscField->z = MaskedAssign( fl4GoodMask,
+ MaxSIMD( MinSIMD( fvOscVal.z, Four_Ones), Four_Zeros ), fvOutput.z );
+ }
+ else
+ {
+ pOscField->x = MaskedAssign( fl4GoodMask, fvOscVal.x, fvOutput.x );
+ pOscField->y = MaskedAssign( fl4GoodMask, fvOscVal.y, fvOutput.y );
+ pOscField->z = MaskedAssign( fl4GoodMask, fvOscVal.z, fvOutput.z );
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pOscField;
+ ++pParticleId;
+ } while (--nCtr );
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Remap Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 1 << m_nFieldInput;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldInput;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapScalar, "Remap Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapScalar )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "input field", "7", int, m_nFieldInput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+ DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapScalar )
+
+void C_OP_RemapScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *pInput = pParticles->GetFloatAttributePtr( m_nFieldInput, i );
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( *pInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// noise Operator
+//-----------------------------------------------------------------------------
+class C_OP_Noise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Noise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ fltx4 m_fl4NoiseScale;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Noise, "Noise Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Noise )
+ DMXELEMENT_UNPACK_FLTX4( "noise coordinate scale", "0.1", m_fl4NoiseScale)
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Noise );
+
+void C_OP_Noise::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeWriteIterator pAttr( m_nFieldOutput, pParticles );
+
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ fltx4 CoordScale=m_fl4NoiseScale;
+
+ float fMin = m_flOutputMin;
+ float fMax = m_flOutputMax;
+
+ if ( ATTRIBUTES_WHICH_ARE_ANGLES & (1 << m_nFieldOutput ) )
+ {
+ fMin *= ( M_PI / 180.0f );
+ fMax *= ( M_PI / 180.0f );
+ }
+ // calculate coefficients. noise retuns -1..1
+ fltx4 ValueScale=ReplicateX4( 0.5*(fMax-fMin ) );
+ fltx4 ValueBase=ReplicateX4( fMin + 0.5*( fMax - fMin ) );
+ int nActive = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors Coord = *pXYZ;
+ Coord *= CoordScale;
+ *( pAttr )=AddSIMD( ValueBase, MulSIMD( ValueScale, NoiseSIMD( Coord ) ) );
+
+ ++pAttr;
+ ++pXYZ;
+ } while( --nActive );
+}
+//-----------------------------------------------------------------------------
+// vector noise Operator
+//-----------------------------------------------------------------------------
+class C_OP_VectorNoise : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VectorNoise );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ Vector m_vecOutputMin;
+ Vector m_vecOutputMax;
+ fltx4 m_fl4NoiseScale;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VectorNoise, "Noise Vector", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VectorNoise )
+ DMXELEMENT_UNPACK_FLTX4( "noise coordinate scale", "0.1", m_fl4NoiseScale)
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "6", int, m_nFieldOutput, "intchoice particlefield_vector" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0 0 0", Vector, m_vecOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1 1 1", Vector, m_vecOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VectorNoise );
+
+void C_OP_VectorNoise::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator pAttr( m_nFieldOutput, pParticles );
+
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+ fltx4 CoordScale = m_fl4NoiseScale;
+
+ // calculate coefficients. noise retuns -1..1
+ fltx4 ValueScaleX = ReplicateX4( 0.5*(m_vecOutputMax.x-m_vecOutputMin.x ) );
+ fltx4 ValueBaseX = ReplicateX4(m_vecOutputMin.x+0.5*( m_vecOutputMax.x-m_vecOutputMin.x ) );
+
+ fltx4 ValueScaleY = ReplicateX4( 0.5*(m_vecOutputMax.y-m_vecOutputMin.y ) );
+ fltx4 ValueBaseY = ReplicateX4(m_vecOutputMin.y+0.5*( m_vecOutputMax.y-m_vecOutputMin.y ) );
+
+ fltx4 ValueScaleZ = ReplicateX4( 0.5*(m_vecOutputMax.z-m_vecOutputMin.z ) );
+ fltx4 ValueBaseZ = ReplicateX4(m_vecOutputMin.z+0.5*( m_vecOutputMax.z-m_vecOutputMin.z ) );
+
+ FourVectors ofs_y;
+ ofs_y.DuplicateVector( Vector( 100000.5, 300000.25, 9000000.75 ) );
+ FourVectors ofs_z;
+ ofs_z.DuplicateVector( Vector( 110000.25, 310000.75, 9100000.5 ) );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i+=4 )
+ {
+ FourVectors Coord = *pXYZ;
+ Coord *= CoordScale;
+ pAttr->x=AddSIMD( ValueBaseX, MulSIMD( ValueScaleX, NoiseSIMD( Coord ) ) );
+ Coord += ofs_y;
+ pAttr->y=AddSIMD( ValueBaseY, MulSIMD( ValueScaleY, NoiseSIMD( Coord ) ) );
+ Coord += ofs_z;
+ pAttr->z=AddSIMD( ValueBaseZ, MulSIMD( ValueScaleZ, NoiseSIMD( Coord ) ) );
+
+ ++pAttr;
+ ++pXYZ;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Decay Operator (Lifespan limiter - kills dead particles)
+//-----------------------------------------------------------------------------
+class C_OP_Decay : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Decay );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Decay, "Lifespan Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Decay )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Decay )
+
+
+void C_OP_Decay::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+
+ fltx4 fl4KillMask = CmpLeSIMD( fl4LifeDuration, Four_Zeros );
+
+ fltx4 fl4Age = SubSIMD( fl4CurTime, *pCreationTime );
+
+ fl4KillMask = OrSIMD( fl4KillMask, CmpGeSIMD( fl4Age, fl4LifeDuration ) );
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Lifespan Minimum Velocity Decay Operator (kills particles if they cease moving)
+//-----------------------------------------------------------------------------
+class C_OP_VelocityDecay : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VelocityDecay );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ float m_flMinVelocity;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VelocityDecay, "Lifespan Minimum Velocity Decay", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityDecay )
+ DMXELEMENT_UNPACK_FIELD( "minimum velocity","1", float, m_flMinVelocity )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityDecay )
+
+
+void C_OP_VelocityDecay::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ fltx4 fl4MinVelocity = ReplicateX4( m_flMinVelocity );
+ fltx4 fl4Dt = ReplicateX4( pParticles->m_flDt );
+
+ CM128AttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ CM128AttributeIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ fltx4 fl4KillMask = CmpLeSIMD( DivSIMD ( SubSIMD( *pXYZ, *pPrevXYZ ), fl4Dt ), fl4MinVelocity );
+
+ if ( IsAnyNegative( fl4KillMask ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4KillMask );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+ }
+ ++pXYZ;
+ ++pPrevXYZ;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Random Cull Operator - Randomly culls particles before their lifespan
+//-----------------------------------------------------------------------------
+class C_OP_Cull : public CParticleOperatorInstance
+{
+ float m_flCullPerc;
+ float m_flCullStart;
+ float m_flCullEnd;
+ float m_flCullExp;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_Cull );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Cull, "Cull Random", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Cull )
+ DMXELEMENT_UNPACK_FIELD( "Cull Start Time", "0", float, m_flCullStart )
+ DMXELEMENT_UNPACK_FIELD( "Cull End Time", "1", float, m_flCullEnd )
+ DMXELEMENT_UNPACK_FIELD( "Cull Time Exponent", "1", float, m_flCullExp )
+ DMXELEMENT_UNPACK_FIELD( "Cull Percentage", "0.5", float, m_flCullPerc )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Cull )
+
+
+void C_OP_Cull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ const float *pCreationTime;
+ const float *pLifeDuration;
+ float flLifeTime;
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ pLifeDuration = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, i );
+ int nParticleId = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_PARTICLE_ID, i );
+ float flCullRank = pParticles->RandomFloat( nParticleId + nRandomOffset + 15, 0.0f, 1.0f);
+ float flCullTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 12, m_flCullStart, m_flCullEnd, m_flCullExp );
+
+ if ( flCullRank > ( m_flCullPerc * flStrength ) )
+ {
+ continue;
+ }
+ // Find our life percentage
+ flLifeTime = clamp( ( pParticles->m_flCurTime - *pCreationTime ) / ( *pLifeDuration ), 0.0f, 1.0f );
+ if ( flLifeTime >= m_flCullStart && flLifeTime <= m_flCullEnd && flLifeTime >= flCullTime )
+ {
+ pParticles->KillParticle( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// generic spin operator
+//-----------------------------------------------------------------------------
+class CGeneralSpin : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToSpin( void ) const =0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ if ( m_nSpinRateDegrees != 0.0 )
+ return (1 << GetAttributeToSpin() );
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_fSpinRateRadians = (float) m_nSpinRateDegrees * ( M_PI / 180.0f );
+ m_fSpinRateMinRadians = (float) m_nSpinRateMinDegrees * ( M_PI / 180.0f );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nSpinRateDegrees;
+ int m_nSpinRateMinDegrees;
+ float m_fSpinRateRadians;
+ float m_fSpinRateStopTime;
+ float m_fSpinRateMinRadians;
+};
+
+void CGeneralSpin::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ float fCurSpinRate = m_fSpinRateRadians * flStrength;
+
+ if ( fCurSpinRate == 0.0 )
+ return;
+
+ float dt = pParticles->m_flDt;
+ float drot = dt * fabs( fCurSpinRate * 2.0f * M_PI );
+ if ( m_fSpinRateStopTime == 0.0f )
+ {
+ drot = fmod( drot, (float)(2.0f * M_PI) );
+ }
+ if ( fCurSpinRate < 0.0f )
+ {
+ drot = -drot;
+ }
+ fltx4 Rot_Add = ReplicateX4( drot );
+ fltx4 Pi_2 = ReplicateX4( 2.0*M_PI );
+ fltx4 nPi_2 = ReplicateX4( -2.0*M_PI );
+
+ // FIXME: This is wrong
+ fltx4 minSpeedRadians = ReplicateX4( dt * fabs( m_fSpinRateMinRadians * 2.0f * M_PI ) );
+
+ fltx4 now = pParticles->m_fl4CurTime;
+ fltx4 SpinRateStopTime = ReplicateX4( m_fSpinRateStopTime );
+
+ CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+
+ CM128AttributeWriteIterator pRot( GetAttributeToSpin(), pParticles );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i+=4 )
+ {
+ // HACK: Rather than redo this, I'm simply remapping the stop time into the percentage of lifetime, rather than seconds
+ fltx4 LifeSpan = *pLifeDuration;
+ fltx4 SpinFadePerc = Four_Zeros;
+ fltx4 OOSpinFadeRate = Four_Zeros;
+ if ( m_fSpinRateStopTime )
+ {
+ SpinFadePerc = MulSIMD( LifeSpan, SpinRateStopTime );
+ OOSpinFadeRate = DivSIMD( Four_Ones, SpinFadePerc );
+ }
+
+ fltx4 Age = SubSIMD( now, *pCreationTimeStamp );
+ fltx4 RScale = MaxSIMD( Four_Zeros,
+ SubSIMD( Four_Ones, MulSIMD( Age, OOSpinFadeRate ) ) );
+
+ // Cap the rotation at a minimum speed
+ fltx4 deltaRot = MulSIMD( Rot_Add, RScale );
+ fltx4 Tooslow = CmpLeSIMD( deltaRot, minSpeedRadians );
+ deltaRot = OrSIMD( AndSIMD( Tooslow, minSpeedRadians ), AndNotSIMD( Tooslow, deltaRot ) );
+ fltx4 NewRot = AddSIMD( *pRot, deltaRot );
+
+ // now, cap at +/- 2*pi
+ fltx4 Toobig =CmpGeSIMD( NewRot, Pi_2 );
+ fltx4 Toosmall = CmpLeSIMD( NewRot, nPi_2 );
+
+ NewRot = OrSIMD( AndSIMD( Toobig, SubSIMD( NewRot, Pi_2 ) ),
+ AndNotSIMD( Toobig, NewRot ) );
+
+ NewRot = OrSIMD( AndSIMD( Toosmall, AddSIMD( NewRot, Pi_2 ) ),
+ AndNotSIMD( Toosmall, NewRot ) );
+
+ *( pRot )= NewRot;
+
+ ++pRot;
+ ++pCreationTimeStamp;
+ ++pLifeDuration;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// generic spin operator, version 2. Uses rotation_speed
+//-----------------------------------------------------------------------------
+class CSpinUpdateBase : public CParticleOperatorInstance
+{
+protected:
+ virtual int GetAttributeToSpin( void ) const =0;
+ virtual int GetSpinSpeedAttribute( void ) const =0;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return (1 << GetAttributeToSpin() );
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return ( 1 << GetAttributeToSpin() ) | ( 1 << GetSpinSpeedAttribute() ) |
+ PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+};
+
+void CSpinUpdateBase::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pRotationSpeed( GetSpinSpeedAttribute(), pParticles );
+ CM128AttributeWriteIterator pRot( GetAttributeToSpin(), pParticles );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ fltx4 fl4Dt = ReplicateX4( pParticles->m_flDt );
+ fltx4 fl4ScaleFactor = ReplicateX4( flStrength );
+
+ int nActive = pParticles->m_nActiveParticles;
+ for( int i=0; i < nActive; i += 4 )
+ {
+ fltx4 fl4SimTime = MinSIMD( fl4Dt, SubSIMD( fl4CurTime, *pCreationTimeStamp ) );
+ fl4SimTime = MulSIMD( fl4SimTime, fl4ScaleFactor );
+ *pRot = MaddSIMD( fl4SimTime, *pRotationSpeed, *pRot );
+
+ ++pRot;
+ ++pRotationSpeed;
+ ++pCreationTimeStamp;
+ }
+}
+
+
+class C_OP_Spin : public CGeneralSpin
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Spin );
+
+ int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Spin, "Rotation Spin Roll", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Spin )
+ DMXELEMENT_UNPACK_FIELD( "spin_rate_degrees", "0", int, m_nSpinRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "spin_stop_time", "0", float, m_fSpinRateStopTime )
+ DMXELEMENT_UNPACK_FIELD( "spin_rate_min", "0", int, m_nSpinRateMinDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Spin )
+
+class C_OP_SpinUpdate : public CSpinUpdateBase
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SpinUpdate );
+
+ virtual int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION;
+ }
+
+ virtual int GetSpinSpeedAttribute( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_SPEED;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SpinUpdate, "Rotation Basic", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SpinUpdate )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SpinUpdate )
+
+class C_OP_SpinYaw : public CGeneralSpin
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SpinYaw );
+
+ int GetAttributeToSpin( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_YAW;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SpinYaw, "Rotation Spin Yaw", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SpinYaw )
+ DMXELEMENT_UNPACK_FIELD( "yaw_rate_degrees", "0", int, m_nSpinRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "yaw_stop_time", "0", float, m_fSpinRateStopTime )
+ DMXELEMENT_UNPACK_FIELD( "yaw_rate_min", "0", int, m_nSpinRateMinDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SpinYaw )
+
+
+
+//-----------------------------------------------------------------------------
+// Size changing operator
+//-----------------------------------------------------------------------------
+class C_OP_InterpolateRadius : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_InterpolateRadius );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_RADIUS_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flBias = ( m_flBias != 0.0f ) ? m_flBias : 0.5f;
+ m_fl4BiasParam = PreCalcBiasParameter( ReplicateX4( m_flBias ) );
+ }
+
+ float m_flStartTime;
+ float m_flEndTime;
+ float m_flStartScale;
+ float m_flEndScale;
+ bool m_bEaseInAndOut;
+ float m_flBias;
+ fltx4 m_fl4BiasParam;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_InterpolateRadius, "Radius Scale", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InterpolateRadius )
+ DMXELEMENT_UNPACK_FIELD( "start_time", "0", float, m_flStartTime )
+ DMXELEMENT_UNPACK_FIELD( "end_time", "1", float, m_flEndTime )
+ DMXELEMENT_UNPACK_FIELD( "radius_start_scale", "1", float, m_flStartScale )
+ DMXELEMENT_UNPACK_FIELD( "radius_end_scale", "1", float, m_flEndScale )
+ DMXELEMENT_UNPACK_FIELD( "ease_in_and_out", "0", bool, m_bEaseInAndOut )
+ DMXELEMENT_UNPACK_FIELD( "scale_bias", "0.5", float, m_flBias )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_InterpolateRadius )
+
+void C_OP_InterpolateRadius::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_flEndTime <= m_flStartTime )
+ return;
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ CM128AttributeWriteIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+ CM128InitialAttributeIterator pInitialRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+
+ fltx4 fl4StartTime = ReplicateX4( m_flStartTime );
+ fltx4 fl4EndTime = ReplicateX4( m_flEndTime );
+ fltx4 fl4OOTimeWidth = ReciprocalSIMD( SubSIMD( fl4EndTime, fl4StartTime ) );
+
+ fltx4 fl4ScaleWidth = ReplicateX4( m_flEndScale - m_flStartScale );
+ fltx4 fl4StartScale = ReplicateX4( m_flStartScale );
+
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+ if ( m_bEaseInAndOut )
+ {
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( SimpleSpline( fl4FadeWindow ), fl4ScaleWidth ) );
+ // !!speed!! - can anyone really tell the diff between spline and lerp here?
+ *pRadius = MaskedAssign(
+ fl4GoodMask, MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ else
+ {
+ if ( m_flBias == 0.5f ) // no bias case
+ {
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( fl4FadeWindow, fl4ScaleWidth ) );
+ *pRadius = MaskedAssign( fl4GoodMask, MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ else
+ {
+ // use rational approximation to bias
+ do
+ {
+ fltx4 fl4LifeDuration = *pLifeDuration;
+ fltx4 fl4GoodMask = CmpGtSIMD( fl4LifeDuration, Four_Zeros );
+ fltx4 fl4LifeTime = MulSIMD( SubSIMD( fl4CurTime, *pCreationTime ), ReciprocalEstSIMD( fl4LifeDuration ) ); // maybe need accurate div here?
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpGeSIMD( fl4LifeTime, fl4StartTime ) );
+ fl4GoodMask = AndSIMD( fl4GoodMask, CmpLtSIMD( fl4LifeTime, fl4EndTime ) );
+ if ( IsAnyNegative( fl4GoodMask ) )
+ {
+ fltx4 fl4FadeWindow = MulSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), fl4OOTimeWidth );
+#ifdef FP_EXCEPTIONS_ENABLED
+ // Wherever fl4GoodMask is zero we need to ensure that fl4FadeWindow is not zero
+ // to avoid 0/0 divides in BiasSIMD. Setting those elements to fl4EndTime
+ // should do the trick...
+ fl4FadeWindow = OrSIMD( AndSIMD( fl4GoodMask, fl4EndTime ), AndNotSIMD( fl4GoodMask, fl4EndTime ) );
+#endif
+ fl4FadeWindow = AddSIMD( fl4StartScale, MulSIMD( BiasSIMD( fl4FadeWindow, m_fl4BiasParam ), fl4ScaleWidth ) );
+ *pRadius = MaskedAssign(
+ fl4GoodMask,
+ MulSIMD( *pInitialRadius, fl4FadeWindow ), *pRadius );
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pRadius;
+ ++pInitialRadius;
+ } while (--nCtr );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Color Fade
+//-----------------------------------------------------------------------------
+class C_OP_ColorInterpolate : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ColorInterpolate );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_flColorFade[0] = m_ColorFade[0] / 255.0f;
+ m_flColorFade[1] = m_ColorFade[1] / 255.0f;
+ m_flColorFade[2] = m_ColorFade[2] / 255.0f;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ Color m_ColorFade;
+ float m_flColorFade[3];
+ float m_flFadeStartTime;
+ float m_flFadeEndTime;
+ bool m_bEaseInOut;
+};
+
+
+
+void C_OP_ColorInterpolate::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeWriteIterator pColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ C4VInitialAttributeIterator pInitialColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ if ( m_flFadeEndTime == m_flFadeStartTime )
+ return;
+
+ fltx4 ooInRange = ReplicateX4( 1.0 / ( m_flFadeEndTime - m_flFadeStartTime ) );
+
+ fltx4 curTime = pParticles->m_fl4CurTime;
+ fltx4 lowRange = ReplicateX4( m_flFadeStartTime );
+
+ fltx4 targetR = ReplicateX4( m_flColorFade[0] );
+ fltx4 targetG = ReplicateX4( m_flColorFade[1] );
+ fltx4 targetB = ReplicateX4( m_flColorFade[2] );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+
+ if ( m_bEaseInOut )
+ {
+ do
+ {
+ fltx4 goodMask = CmpGtSIMD( *pLifeDuration, Four_Zeros );
+ if ( IsAnyNegative( goodMask ) )
+ {
+ fltx4 flLifeTime = DivSIMD( SubSIMD( curTime, *pCreationTime ), *pLifeDuration );
+
+ fltx4 T = MulSIMD( SubSIMD( flLifeTime, lowRange ), ooInRange );
+ T = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, T ) );
+ T = SimpleSpline( T );
+ pColor->x = MaskedAssign( goodMask, AddSIMD( pInitialColor->x, MulSIMD( T, SubSIMD( targetR, pInitialColor->x ) ) ), pColor->x );
+ pColor->y = MaskedAssign( goodMask, AddSIMD( pInitialColor->y, MulSIMD( T, SubSIMD( targetG, pInitialColor->y ) ) ), pColor->y );
+ pColor->z = MaskedAssign( goodMask, AddSIMD( pInitialColor->z, MulSIMD( T, SubSIMD( targetB, pInitialColor->z ) ) ), pColor->z );
+ }
+ ++pColor;
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialColor;
+
+ } while( --nCtr );
+ }
+ else
+ {
+ do
+ {
+ fltx4 goodMask = CmpGtSIMD( *pLifeDuration, Four_Zeros );
+ if ( IsAnyNegative( goodMask ) )
+ {
+ fltx4 flLifeTime = DivSIMD( SubSIMD( curTime, *pCreationTime ), *pLifeDuration );
+
+ fltx4 T = MulSIMD( SubSIMD( flLifeTime, lowRange ), ooInRange );
+ T = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, T ) );
+
+ pColor->x = MaskedAssign( goodMask, AddSIMD( pInitialColor->x, MulSIMD( T, SubSIMD( targetR, pInitialColor->x ) ) ), pColor->x );
+ pColor->y = MaskedAssign( goodMask, AddSIMD( pInitialColor->y, MulSIMD( T, SubSIMD( targetG, pInitialColor->y ) ) ), pColor->y );
+ pColor->z = MaskedAssign( goodMask, AddSIMD( pInitialColor->z, MulSIMD( T, SubSIMD( targetB, pInitialColor->z ) ) ), pColor->z );
+ }
+ ++pColor;
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pInitialColor;
+
+ } while( --nCtr );
+ }
+}
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ColorInterpolate, "Color Fade", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ColorInterpolate )
+ DMXELEMENT_UNPACK_FIELD( "color_fade", "255 255 255 255", Color, m_ColorFade )
+ DMXELEMENT_UNPACK_FIELD( "fade_start_time", "0", float, m_flFadeStartTime )
+ DMXELEMENT_UNPACK_FIELD( "fade_end_time", "1", float, m_flFadeEndTime )
+ DMXELEMENT_UNPACK_FIELD( "ease_in_and_out", "1", bool, m_bEaseInOut )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ColorInterpolate )
+
+
+//-----------------------------------------------------------------------------
+// Position Lock to Control Point
+// Locks all particles to the specified control point
+// Useful for making particles move with their emitter and so forth
+//-----------------------------------------------------------------------------
+class C_OP_PositionLock : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_PositionLock );
+
+ struct C_OP_PositionLockContext_t
+ {
+ Vector m_vPrevPosition;
+ matrix3x4_t m_matPrevTransform;
+ };
+
+ int m_nControlPointNumber;
+ Vector m_vPrevPosition;
+ float m_flStartTime_min;
+ float m_flStartTime_max;
+ float m_flStartTime_exp;
+ float m_flEndTime_min;
+ float m_flEndTime_max;
+ float m_flEndTime_exp;
+ float m_flRange;
+ bool m_bLockRot;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nControlPointNumber;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_PositionLockContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+ pCtx->m_vPrevPosition = vec3_origin;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &pCtx->m_matPrevTransform );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PositionLock , "Movement Lock to Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PositionLock )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_min", "1", float, m_flStartTime_min )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_max", "1", float, m_flStartTime_max )
+ DMXELEMENT_UNPACK_FIELD( "start_fadeout_exponent", "1", float, m_flStartTime_exp )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_min", "1", float, m_flEndTime_min )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_max", "1", float, m_flEndTime_max )
+ DMXELEMENT_UNPACK_FIELD( "end_fadeout_exponent", "1", float, m_flEndTime_exp )
+ DMXELEMENT_UNPACK_FIELD( "distance fade range", "0", float, m_flRange )
+ DMXELEMENT_UNPACK_FIELD( "lock rotation", "0", bool, m_bLockRot )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PositionLock )
+
+#ifdef OLD_NON_SSE_POSLOCK_FOR_TESTING
+void C_OP_PositionLock::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // At initialization, set prevposition to the control point to prevent random placements/velocities
+
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+
+ if ( pCtx->m_vPrevPosition == Vector (0, 0, 0) )
+
+ {
+ pCtx->m_vPrevPosition = vecControlPoint;
+ }
+
+ // Control point movement delta
+
+ int nRandomOffset = pParticles->OperatorRandomSampleOffset();
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ Vector vecPrevCPPos = pCtx->m_vPrevPosition;
+
+ const float *pCreationTime;
+ const float *pLifeDuration;
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ pLifeDuration = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, i );
+ float flLifeTime = *pLifeDuration != 0.0f ? clamp( ( pParticles->m_flCurTime - *pCreationTime ) / ( *pLifeDuration ), 0.0f, 1.0f ) : 0.0f;
+ if ( *pCreationTime >= ( pParticles->m_flCurTime - pParticles->m_flDt ) )
+ {
+ pParticles->GetControlPointAtTime( m_nControlPointNumber, *pCreationTime, &vecPrevCPPos );
+ }
+
+ Vector vDelta = vecControlPoint - vecPrevCPPos;
+ vDelta *= flStrength;
+
+ // clamp activity to start/end time
+ int nParticleId = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_PARTICLE_ID, i );
+ float flStartTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 9, m_flStartTime_min, m_flStartTime_max, m_flStartTime_exp );
+ float flEndTime = pParticles->RandomFloatExp( nParticleId + nRandomOffset + 10, m_flEndTime_min, m_flEndTime_max, m_flEndTime_exp );
+
+ // bias attachedness by fadeout
+ float flLockScale = SimpleSplineRemapValClamped( flLifeTime, flStartTime, flEndTime, 1.0f, 0.0f );
+
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecParticlePosition, vecParticlePosition_prev ;
+ SetVectorFromAttribute( vecParticlePosition, xyz );
+ SetVectorFromAttribute( vecParticlePosition_prev, xyz_prev );
+ float flDampenAmount = 1;
+ if ( m_flRange != 0 )
+ {
+ Vector ofs;
+ ofs = (vecParticlePosition + ( vDelta * flLockScale ) ) - vecControlPoint;
+ float flDistance = ofs.Length();
+ flDampenAmount = SimpleSplineRemapValClamped( flDistance, 0, m_flRange, 1.0f, 0.0f );
+ flDampenAmount = Bias( flDampenAmount, .2 );
+ }
+ Vector vParticleDelta = vDelta * flLockScale * flDampenAmount;
+
+
+ vecParticlePosition += vParticleDelta;
+ vecParticlePosition_prev += vParticleDelta;
+ SetVectorAttribute( xyz, vecParticlePosition );
+ SetVectorAttribute( xyz_prev, vecParticlePosition_prev );
+ }
+
+ // Store off the control point position for the next delta computation
+ pCtx->m_vPrevPosition = vecControlPoint;
+
+
+};
+
+#else
+void C_OP_PositionLock::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // At initialization, set prevposition to the control point to prevent random placements/velocities
+
+ C_OP_PositionLockContext_t *pCtx=reinterpret_cast<C_OP_PositionLockContext_t *>( pContext );
+
+ if ( pCtx->m_vPrevPosition == Vector (0, 0, 0) )
+
+ {
+ pCtx->m_vPrevPosition = vecControlPoint;
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &pCtx->m_matPrevTransform );
+ }
+
+ Vector vDelta;
+ matrix3x4_t matCurrentTransform;
+ matrix3x4_t matTransformLock;
+
+ if ( m_bLockRot )
+ {
+ pParticles->GetControlPointTransformAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &matCurrentTransform );
+ matrix3x4_t matPrev;
+ //if ( MatricesAreEqual ( matCurrentTransform, pCtx->m_matPrevTransform ) )
+ // return;
+ MatrixInvert( pCtx->m_matPrevTransform, matPrev );
+ MatrixMultiply( matCurrentTransform, matPrev, matTransformLock);
+ }
+
+ int nContext = GetSIMDRandContext();
+
+ // Control point movement delta - not full transform
+ vDelta = vecControlPoint - pCtx->m_vPrevPosition;
+ //if ( vDelta == vec3_origin && !m_bLockRot )
+ // return;
+
+ vDelta *= flStrength;
+ FourVectors v4Delta;
+ v4Delta.DuplicateVector( vDelta );
+
+ FourVectors v4ControlPoint;
+ v4ControlPoint.DuplicateVector( vecControlPoint );
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+ fltx4 fl4_Dt = ReplicateX4( pParticles->m_flDt );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ bool bUseRange = ( m_flRange != 0.0 );
+ fltx4 fl4OORange;
+ if ( bUseRange )
+ fl4OORange = ReplicateX4( 1.0 / m_flRange );
+
+ fltx4 fl4BiasParm = PreCalcBiasParameter( ReplicateX4( 0.2 ) );
+ if ( m_flStartTime_min >= 1.0 ) // always locked on
+ {
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ do
+ {
+ fltx4 fl4ParticleAge = SubSIMD( pParticles->m_fl4CurTime, *pCreationTime);
+ fltx4 fl4CreationFrameBias = MinSIMD( fl4ParticleAge, fl4_Dt );
+ fl4CreationFrameBias = MulSIMD( DivSIMD( Four_Ones, fl4_Dt ), fl4CreationFrameBias );
+ FourVectors v4ScaledDelta = v4Delta;
+ v4ScaledDelta *= fl4CreationFrameBias;
+
+ fltx4 fl4LockStrength = ReplicateX4( flStrength );
+ // ok, some of these particles should be moved
+ if ( bUseRange )
+ {
+ FourVectors ofs = *pXYZ;
+ ofs += v4ScaledDelta;
+ ofs -= v4ControlPoint;
+ fltx4 fl4Dist = ofs.length();
+ fl4Dist = BiasSIMD( MinSIMD( Four_Ones, MulSIMD( fl4Dist, fl4OORange ) ), fl4BiasParm );
+ v4ScaledDelta *= SubSIMD( Four_Ones, fl4Dist );
+ fl4LockStrength = SubSIMD( Four_Ones, MulSIMD ( fl4Dist, fl4LockStrength ) );
+ }
+ if ( m_bLockRot )
+ {
+ fl4LockStrength = MulSIMD( fl4LockStrength, fl4CreationFrameBias );
+ FourVectors fvCurPos = *pXYZ;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvCurPos.TransformBy( matTransformLock );
+ fvPrevPos.TransformBy( matTransformLock );
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4LockStrength;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4LockStrength;
+ *(pXYZ) += fvCurPos;
+ *(pPrevXYZ) += fvPrevPos;
+ }
+ else
+ {
+ *(pXYZ) += v4ScaledDelta;
+ *(pPrevXYZ) += v4ScaledDelta;
+ }
+ ++pCreationTime;
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+ }
+ else
+ {
+ CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pLifeDuration( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
+ fltx4 fl4CurTime = pParticles->m_fl4CurTime;
+ fltx4 fl4StartRange = ReplicateX4( m_flStartTime_max - m_flStartTime_min );
+ fltx4 fl4StartBias = ReplicateX4( m_flStartTime_min );
+ fltx4 fl4EndRange = ReplicateX4( m_flEndTime_max - m_flEndTime_min );
+ fltx4 fl4EndBias = ReplicateX4( m_flEndTime_min );
+ int nSSEStartExponent = m_flStartTime_exp * 4.0;
+ int nSSEEndExponent = m_flEndTime_exp * 4.0;
+ do
+ {
+
+ fltx4 fl4LifeTime = SubSIMD( fl4CurTime, *pCreationTime );
+ fltx4 fl4CreationFrameBias = MinSIMD( fl4LifeTime, fl4_Dt );
+ fl4CreationFrameBias = MulSIMD( DivSIMD( Four_Ones, fl4_Dt ), fl4CreationFrameBias );
+
+ FourVectors v4ScaledDelta = v4Delta;
+ v4ScaledDelta *= fl4CreationFrameBias;
+
+ fl4LifeTime = MaxSIMD( Four_Zeros, MinSIMD( Four_Ones,
+ MulSIMD( fl4LifeTime, ReciprocalEstSIMD( *pLifeDuration ) ) ) );
+ fltx4 fl4StartTime = Pow_FixedPoint_Exponent_SIMD( RandSIMD( nContext ), nSSEStartExponent );
+ fl4StartTime = AddSIMD( fl4StartBias, MulSIMD( fl4StartTime, fl4StartRange ) );
+
+ fltx4 fl4EndTime = Pow_FixedPoint_Exponent_SIMD( RandSIMD( nContext ), nSSEEndExponent );
+ fl4EndTime = AddSIMD( fl4EndBias, MulSIMD( fl4EndTime, fl4EndRange ) );
+
+ // now, determine "lockedness"
+ fltx4 fl4LockScale = DivSIMD( SubSIMD( fl4LifeTime, fl4StartTime ), SubSIMD( fl4EndTime, fl4StartTime ) );
+ fl4LockScale = SubSIMD( Four_Ones, MaxSIMD( Four_Zeros, MinSIMD( Four_Ones, fl4LockScale ) ) );
+ if ( IsAnyNegative( CmpGtSIMD( fl4LockScale, Four_Zeros ) ) )
+ {
+ //fl4LockScale = MulSIMD( fl4LockScale, fl4CreationFrameBias );
+ v4ScaledDelta *= fl4LockScale;
+ fltx4 fl4LockStrength = fl4LockScale ;
+ // ok, some of these particles should be moved
+ if ( bUseRange )
+ {
+ FourVectors ofs = *pXYZ;
+ ofs += v4ScaledDelta;
+ ofs -= v4ControlPoint;
+ fltx4 fl4Dist = ofs.length();
+ fl4Dist = BiasSIMD( MinSIMD( Four_Ones, MulSIMD( fl4Dist, fl4OORange ) ), fl4BiasParm );
+ v4ScaledDelta *= SubSIMD( Four_Ones, fl4Dist );
+ fl4LockStrength = SubSIMD( Four_Ones, MulSIMD ( fl4Dist, fl4LockStrength ) );
+ }
+ if ( m_bLockRot )
+ {
+ fl4LockStrength = MulSIMD( fl4LockStrength, fl4CreationFrameBias );
+ FourVectors fvCurPos = *pXYZ;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvCurPos.TransformBy( matTransformLock );
+ fvPrevPos.TransformBy( matTransformLock );
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4LockStrength;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4LockStrength;
+ *(pXYZ) += fvCurPos;
+ *(pPrevXYZ) += fvPrevPos;
+ }
+ else
+ {
+ *(pXYZ) += v4ScaledDelta;
+ *(pPrevXYZ) += v4ScaledDelta;
+ }
+ }
+ ++pCreationTime;
+ ++pLifeDuration;
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+ }
+ // Store off the control point position for the next delta computation
+ pCtx->m_vPrevPosition = vecControlPoint;
+ pCtx->m_matPrevTransform = matCurrentTransform;
+ ReleaseSIMDRandContext( nContext );
+};
+#endif
+
+
+
+
+//-----------------------------------------------------------------------------
+// Controlpoint Light
+// Determines particle color/fakes lighting using the influence of control
+// points
+//-----------------------------------------------------------------------------
+class C_OP_ControlpointLight : public CParticleOperatorInstance
+{
+ float m_flScale;
+ LightDesc_t m_LightNode1, m_LightNode2, m_LightNode3, m_LightNode4;
+ int m_nControlPoint1, m_nControlPoint2, m_nControlPoint3, m_nControlPoint4;
+ Vector m_vecCPOffset1, m_vecCPOffset2, m_vecCPOffset3, m_vecCPOffset4;
+ float m_LightFiftyDist1, m_LightZeroDist1, m_LightFiftyDist2, m_LightZeroDist2,
+ m_LightFiftyDist3, m_LightZeroDist3, m_LightFiftyDist4, m_LightZeroDist4;
+ Color m_LightColor1, m_LightColor2, m_LightColor3, m_LightColor4;
+ bool m_bLightType1, m_bLightType2, m_bLightType3, m_bLightType4, m_bLightDynamic1,
+ m_bLightDynamic2, m_bLightDynamic3, m_bLightDynamic4, m_bUseNormal, m_bUseHLambert,
+ m_bLightActive1, m_bLightActive2, m_bLightActive3, m_bLightActive4,
+ m_bClampLowerRange, m_bClampUpperRange;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_ControlpointLight );
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_TINT_RGB_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPoint1 ) | ( 1ULL << m_nControlPoint2 ) |
+ ( 1ULL << m_nControlPoint3 ) | ( 1ULL << m_nControlPoint4 );
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_LightNode1.m_Color[0] = m_LightColor1[0] / 255.0f;
+ m_LightNode1.m_Color[1] = m_LightColor1[1] / 255.0f;
+ m_LightNode1.m_Color[2] = m_LightColor1[2] / 255.0f;
+ m_LightNode2.m_Color[0] = m_LightColor2[0] / 255.0f;
+ m_LightNode2.m_Color[1] = m_LightColor2[1] / 255.0f;
+ m_LightNode2.m_Color[2] = m_LightColor2[2] / 255.0f;
+ m_LightNode3.m_Color[0] = m_LightColor3[0] / 255.0f;
+ m_LightNode3.m_Color[1] = m_LightColor3[1] / 255.0f;
+ m_LightNode3.m_Color[2] = m_LightColor3[2] / 255.0f;
+ m_LightNode4.m_Color[0] = m_LightColor4[0] / 255.0f;
+ m_LightNode4.m_Color[1] = m_LightColor4[1] / 255.0f;
+ m_LightNode4.m_Color[2] = m_LightColor4[2] / 255.0f;
+ m_LightNode1.m_Range = 0;
+ m_LightNode2.m_Range = 0;
+ m_LightNode3.m_Range = 0;
+ m_LightNode4.m_Range = 0;
+ m_LightNode1.m_Falloff=5.0;
+ m_LightNode2.m_Falloff=5.0;
+ m_LightNode3.m_Falloff=5.0;
+ m_LightNode4.m_Falloff=5.0;
+ m_LightNode1.m_Attenuation0 = 0;
+ m_LightNode1.m_Attenuation1 = 0;
+ m_LightNode1.m_Attenuation2 = 1;
+ m_LightNode2.m_Attenuation0 = 0;
+ m_LightNode2.m_Attenuation1 = 0;
+ m_LightNode2.m_Attenuation2 = 1;
+ m_LightNode3.m_Attenuation0 = 0;
+ m_LightNode3.m_Attenuation1 = 0;
+ m_LightNode3.m_Attenuation2 = 1;
+ m_LightNode4.m_Attenuation0 = 0;
+ m_LightNode4.m_Attenuation1 = 0;
+ m_LightNode4.m_Attenuation2 = 1;
+
+ if ( !m_bLightType1 )
+ {
+ m_LightNode1.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode1.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType2 )
+ {
+ m_LightNode2.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode2.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType3 )
+ {
+ m_LightNode3.m_Type = MATERIAL_LIGHT_POINT;
+ }
+
+ else
+ {
+ m_LightNode3.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightType4 )
+ {
+ m_LightNode4.m_Type = MATERIAL_LIGHT_POINT;
+ }
+ else
+ {
+ m_LightNode4.m_Type = MATERIAL_LIGHT_SPOT;
+ }
+
+ if ( !m_bLightDynamic1 && ( m_LightColor1 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive1 = true;
+ }
+ else
+ {
+ m_bLightActive1 = false;
+ }
+ if ( !m_bLightDynamic2 && ( m_LightColor2 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive2 = true;
+ }
+ else
+ {
+ m_bLightActive2 = false;
+ }
+ if ( !m_bLightDynamic3 && ( m_LightColor3 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive3 = true;
+ }
+ else
+ {
+ m_bLightActive3 = false;
+ }
+ if ( !m_bLightDynamic4 && ( m_LightColor4 != Color( 0, 0, 0, 255 ) ) )
+ {
+ m_bLightActive4 = true;
+ }
+ else
+ {
+ m_bLightActive4 = false;
+ }
+ m_LightNode1.SetupNewStyleAttenuation ( m_LightFiftyDist1, m_LightZeroDist1 );
+ m_LightNode2.SetupNewStyleAttenuation ( m_LightFiftyDist2, m_LightZeroDist2 );
+ m_LightNode3.SetupNewStyleAttenuation ( m_LightFiftyDist3, m_LightZeroDist3 );
+ m_LightNode4.SetupNewStyleAttenuation ( m_LightFiftyDist4, m_LightZeroDist4 );
+
+ }
+
+ void Render( CParticleCollection *pParticles ) const;
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ControlpointLight, "Color Light from Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ControlpointLight )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Control Point", "0", int, m_nControlPoint1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Control Point Offset", "0 0 0", Vector, m_vecCPOffset1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Type 0=Point 1=Spot", "0", bool, m_bLightType1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Color", "0 0 0 255", Color, m_LightColor1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Dynamic Light", "0", bool, m_bLightDynamic1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Direction", "0 0 0", Vector, m_LightNode1.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 50% Distance", "100", float, m_LightFiftyDist1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 0% Distance", "200", float, m_LightZeroDist1 )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Spot Inner Cone", "30.0", float, m_LightNode1.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 1 Spot Outer Cone", "45.0", float, m_LightNode1.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Control Point", "0", int, m_nControlPoint2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Control Point Offset", "0 0 0", Vector, m_vecCPOffset2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Type 0=Point 1=Spot", "0", bool, m_bLightType2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Color", "0 0 0 255", Color, m_LightColor2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Dynamic Light", "0", bool, m_bLightDynamic2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Direction", "0 0 0", Vector, m_LightNode2.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 50% Distance", "100", float, m_LightFiftyDist2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 0% Distance", "200", float, m_LightZeroDist2 )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Spot Inner Cone", "30.0", float, m_LightNode2.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 2 Spot Outer Cone", "45.0", float, m_LightNode2.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Control Point", "0", int, m_nControlPoint3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Control Point Offset", "0 0 0", Vector, m_vecCPOffset3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Type 0=Point 1=Spot", "0", bool, m_bLightType3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Color", "0 0 0 255", Color, m_LightColor3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Dynamic Light", "0", bool, m_bLightDynamic3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Direction", "0 0 0", Vector, m_LightNode3.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 50% Distance", "100", float, m_LightFiftyDist3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 0% Distance", "200", float, m_LightZeroDist3 )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Spot Inner Cone", "30.0", float, m_LightNode3.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 3 Spot Outer Cone", "45.0", float, m_LightNode3.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Control Point", "0", int, m_nControlPoint4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Control Point Offset", "0 0 0", Vector, m_vecCPOffset4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Type 0=Point 1=Spot", "0", bool, m_bLightType4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Color", "0 0 0 255", Color, m_LightColor4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Dynamic Light", "0", bool, m_bLightDynamic4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Direction", "0 0 0", Vector, m_LightNode4.m_Direction )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 50% Distance", "100", float, m_LightFiftyDist4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 0% Distance", "200", float, m_LightZeroDist4 )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Spot Inner Cone", "30.0", float, m_LightNode4.m_Theta )
+ DMXELEMENT_UNPACK_FIELD( "Light 4 Spot Outer Cone", "45.0", float, m_LightNode4.m_Phi )
+ DMXELEMENT_UNPACK_FIELD( "Initial Color Bias", "0.0", float, m_flScale )
+ DMXELEMENT_UNPACK_FIELD( "Clamp Minimum Light Value to Initial Color", "0", bool, m_bClampLowerRange )
+ DMXELEMENT_UNPACK_FIELD( "Clamp Maximum Light Value to Initial Color", "0", bool, m_bClampUpperRange )
+ DMXELEMENT_UNPACK_FIELD( "Compute Normals From Control Points", "0", bool, m_bUseNormal )
+ DMXELEMENT_UNPACK_FIELD( "Half-Lambert Normals", "1", bool, m_bUseHLambert )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ControlpointLight )
+
+void C_OP_ControlpointLight::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ //Set up location of each light - this needs to be done every time as the CP's can move
+ Vector vecLocation1, vecLocation2, vecLocation3, vecLocation4;
+ vecLocation1 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint1 );
+ vecLocation2 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint2 );
+ vecLocation3 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint3 );
+ vecLocation4 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint4 );
+
+
+ LightDesc_t LightNode1 = m_LightNode1;
+ LightDesc_t LightNode2 = m_LightNode2;
+ LightDesc_t LightNode3 = m_LightNode3;
+ LightDesc_t LightNode4 = m_LightNode3;
+
+ // Apply any offsets
+ LightNode1.m_Position = vecLocation1 + m_vecCPOffset1;
+ LightNode2.m_Position = vecLocation2 + m_vecCPOffset2;
+ LightNode3.m_Position = vecLocation3 + m_vecCPOffset3;
+ LightNode4.m_Position = vecLocation4 + m_vecCPOffset4;
+
+
+ C4VAttributeIterator pInitialColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ C4VAttributeWriteIterator pColor( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles );
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+
+
+ // Set up lighting conditions and attenuation
+ if ( m_bLightDynamic1 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode1.m_Position, lc );
+ LightNode1.m_Color[0] = lc[0] / 255.0f;
+ LightNode1.m_Color[1] = lc[1] / 255.0f;
+ LightNode1.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic2 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode2.m_Position, lc );
+ LightNode2.m_Color[0] = lc[0] / 255.0f;
+ LightNode2.m_Color[1] = lc[1] / 255.0f;
+ LightNode2.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic3 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode3.m_Position, lc );
+ LightNode3.m_Color[0] = lc[0] / 255.0f;
+ LightNode3.m_Color[1] = lc[1] / 255.0f;
+ LightNode3.m_Color[2] = lc[2] / 255.0f;
+ }
+ if ( m_bLightDynamic4 )
+ {
+ // Get the color and luminosity at this position
+ Color lc;
+ g_pParticleSystemMgr->Query()->GetLightingAtPoint( LightNode4.m_Position, lc );
+ LightNode4.m_Color[0] = lc[0] / 255.0f;
+ LightNode4.m_Color[1] = lc[1] / 255.0f;
+ LightNode4.m_Color[2] = lc[2] / 255.0f;
+ }
+ LightNode1.RecalculateDerivedValues();
+ LightNode2.RecalculateDerivedValues();
+ LightNode3.RecalculateDerivedValues();
+ LightNode4.RecalculateDerivedValues();
+
+ FourVectors vScale;
+ vScale.DuplicateVector( Vector(m_flScale, m_flScale, m_flScale) );
+
+ if ( m_bUseNormal )
+ {
+ FourVectors vCPPosition1, vCPPosition2, vCPPosition3, vCPPosition4;
+ //vCPPosition1.DuplicateVector( LightNode1.m_Position );
+ vCPPosition1.DuplicateVector( vecLocation1 );
+ vCPPosition2.DuplicateVector( vecLocation2 );
+ vCPPosition3.DuplicateVector( vecLocation3 );
+ vCPPosition4.DuplicateVector( vecLocation4 );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors vLighting = vScale;
+ vLighting *= *pInitialColor;
+ FourVectors vNormal = *pXYZ;
+ vNormal -= vCPPosition1;
+ vNormal.VectorNormalizeFast();
+ LightNode1.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition2;
+ vNormal.VectorNormalizeFast();
+ LightNode2.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition3;
+ vNormal.VectorNormalizeFast();
+ LightNode3.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+ vNormal = *pXYZ;
+ vNormal -= vCPPosition4;
+ vNormal.VectorNormalizeFast();
+ LightNode4.ComputeLightAtPoints( *pXYZ, vNormal, vLighting, m_bUseHLambert );
+
+ if ( m_bClampLowerRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MaxSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MaxSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MaxSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MaxSIMD( vLighting.x, Four_Zeros );
+ vLighting.y = MaxSIMD( vLighting.y, Four_Zeros );
+ vLighting.z = MaxSIMD( vLighting.z, Four_Zeros );
+ }
+ if ( m_bClampUpperRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MinSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MinSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MinSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MinSIMD( vLighting.x, Four_Ones );
+ vLighting.y = MinSIMD( vLighting.y, Four_Ones );
+ vLighting.z = MinSIMD( vLighting.z, Four_Ones );
+ }
+
+ *pColor = vLighting;
+
+ ++pColor;
+ ++pXYZ;
+ ++pInitialColor;
+ } while (--nCtr);
+ }
+ else
+ {
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors vLighting = vScale;
+ vLighting *= *pInitialColor;
+
+ LightNode1.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode2.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode3.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+ LightNode4.ComputeNonincidenceLightAtPoints( *pXYZ, vLighting );
+
+
+ if ( m_bClampLowerRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MaxSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MaxSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MaxSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MaxSIMD( vLighting.x, Four_Zeros );
+ vLighting.y = MaxSIMD( vLighting.y, Four_Zeros );
+ vLighting.z = MaxSIMD( vLighting.z, Four_Zeros );
+ }
+ if ( m_bClampUpperRange )
+ {
+ FourVectors vInitialClamp = *pInitialColor;
+ vLighting.x = MinSIMD( vLighting.x, vInitialClamp.x );
+ vLighting.y = MinSIMD( vLighting.y, vInitialClamp.y );
+ vLighting.z = MinSIMD( vLighting.z, vInitialClamp.z );
+ }
+ else
+ {
+ vLighting.x = MinSIMD( vLighting.x, Four_Ones );
+ vLighting.y = MinSIMD( vLighting.y, Four_Ones );
+ vLighting.z = MinSIMD( vLighting.z, Four_Ones );
+ }
+
+
+ *pColor = vLighting;
+
+ ++pColor;
+ ++pXYZ;
+ ++pInitialColor;
+ } while (--nCtr);
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Render visualization
+//-----------------------------------------------------------------------------
+void C_OP_ControlpointLight::Render( CParticleCollection *pParticles ) const
+{
+ Vector vecOrigin1 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint1 );
+ vecOrigin1 += m_vecCPOffset1;
+ Vector vecOrigin2 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint2 );
+ vecOrigin2 += m_vecCPOffset2;
+ Vector vecOrigin3 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint3 );
+ vecOrigin3 += m_vecCPOffset3;
+ Vector vecOrigin4 = pParticles->GetControlPointAtCurrentTime( m_nControlPoint4 );
+ vecOrigin4 += m_vecCPOffset4;
+
+ Color LightColor1Outer;
+ LightColor1Outer[0] = m_LightColor1[0] / 2.0f;
+ LightColor1Outer[1] = m_LightColor1[1] / 2.0f;
+ LightColor1Outer[2] = m_LightColor1[2] / 2.0f;
+ LightColor1Outer[3] = 255;
+ Color LightColor2Outer;
+ LightColor2Outer[0] = m_LightColor2[0] / 2.0f;
+ LightColor2Outer[1] = m_LightColor2[1] / 2.0f;
+ LightColor2Outer[2] = m_LightColor2[2] / 2.0f;
+ LightColor2Outer[3] = 255;
+ Color LightColor3Outer;
+ LightColor3Outer[0] = m_LightColor3[0] / 2.0f;
+ LightColor3Outer[1] = m_LightColor3[1] / 2.0f;
+ LightColor3Outer[2] = m_LightColor3[2] / 2.0f;
+ LightColor3Outer[3] = 255;
+ Color LightColor4Outer;
+ LightColor4Outer[0] = m_LightColor4[0] / 2.0f;
+ LightColor4Outer[1] = m_LightColor4[1] / 2.0f;
+ LightColor4Outer[2] = m_LightColor4[2] / 2.0f;
+ LightColor4Outer[3] = 255;
+ if ( m_bLightActive1 )
+ {
+ RenderWireframeSphere( vecOrigin1, m_LightFiftyDist1, 16, 8, m_LightColor1, false );
+ RenderWireframeSphere( vecOrigin1, m_LightZeroDist1, 16, 8, LightColor1Outer, false );
+ }
+ if ( m_bLightActive2 )
+ {
+ RenderWireframeSphere( vecOrigin2, m_LightFiftyDist2, 16, 8, m_LightColor2, false );
+ RenderWireframeSphere( vecOrigin2, m_LightZeroDist2, 16, 8, LightColor2Outer, false );
+ }
+ if ( m_bLightActive3 )
+ {
+ RenderWireframeSphere( vecOrigin3, m_LightFiftyDist3, 16, 8, m_LightColor3, false );
+ RenderWireframeSphere( vecOrigin3, m_LightZeroDist3, 16, 8, LightColor3Outer, false );
+ }
+ if ( m_bLightActive4 )
+ {
+ RenderWireframeSphere( vecOrigin4, m_LightFiftyDist4, 16, 8, m_LightColor4, false );
+ RenderWireframeSphere( vecOrigin4, m_LightZeroDist4, 16, 8, LightColor4Outer, false );
+ }
+
+}
+
+
+
+// set child controlpoints - copy the positions of our particles to the control points of a child
+class C_OP_SetChildControlPoints : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetChildControlPoints );
+
+ int m_nChildGroupID;
+ int m_nFirstControlPoint;
+ int m_nNumControlPoints;
+ int m_nFirstSourcePoint;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetChildControlPoints, "Set child control points from particle positions", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetChildControlPoints )
+ DMXELEMENT_UNPACK_FIELD( "Group ID to affect", "0", int, m_nChildGroupID )
+ DMXELEMENT_UNPACK_FIELD( "First control point to set", "0", int, m_nFirstControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "# of control points to set", "1", int, m_nNumControlPoints )
+ DMXELEMENT_UNPACK_FIELD( "first particle to copy", "0", int, m_nFirstSourcePoint )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetChildControlPoints )
+
+
+void C_OP_SetChildControlPoints::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ int nFirst=max(0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nFirstControlPoint ) );
+ int nToSet=min( pParticles->m_nActiveParticles-m_nFirstSourcePoint, m_nNumControlPoints );
+ nToSet=min( nToSet, MAX_PARTICLE_CONTROL_POINTS-nFirst );
+ if ( nToSet )
+ {
+ for( CParticleCollection *pChild = pParticles->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
+ {
+ if ( pChild->GetGroupID() == m_nChildGroupID )
+ {
+ for( int p=0; p < nToSet; p++ )
+ {
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, p + m_nFirstSourcePoint );
+ Vector cPnt( pXYZ[0], pXYZ[4], pXYZ[8] );
+ pChild->SetControlPoint( p+nFirst, cPnt );
+ }
+ }
+ }
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Set Control Point Positions
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointPositions : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointPositions );
+
+ bool m_bUseWorldLocation;
+ int m_nCP1, m_nCP1Parent;
+ int m_nCP2, m_nCP2Parent;
+ int m_nCP3, m_nCP3Parent;
+ int m_nCP4, m_nCP4Parent;
+ Vector m_vecCP1Pos, m_vecCP2Pos, m_vecCP3Pos, m_vecCP4Pos;
+ int m_nHeadLocation;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointPositions, "Set Control Point Positions", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointPositions )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Number", "1", int, m_nCP1 )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Parent", "0", int, m_nCP1Parent )
+ DMXELEMENT_UNPACK_FIELD( "First Control Point Location", "128 0 0", Vector, m_vecCP1Pos )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Number", "2", int, m_nCP2 )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Parent", "0", int, m_nCP2Parent )
+ DMXELEMENT_UNPACK_FIELD( "Second Control Point Location", "0 128 0", Vector, m_vecCP2Pos )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Number", "3", int, m_nCP3 )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Parent", "0", int, m_nCP3Parent )
+ DMXELEMENT_UNPACK_FIELD( "Third Control Point Location", "-128 0 0", Vector, m_vecCP3Pos )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Number", "4", int, m_nCP4 )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Parent", "0", int, m_nCP4Parent )
+ DMXELEMENT_UNPACK_FIELD( "Fourth Control Point Location", "0 -128 0", Vector, m_vecCP4Pos )
+ DMXELEMENT_UNPACK_FIELD( "Set positions in world space", "0", bool, m_bUseWorldLocation )
+ DMXELEMENT_UNPACK_FIELD( "Control Point to offset positions from", "0", int, m_nHeadLocation )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointPositions )
+
+void C_OP_SetControlPointPositions::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( !m_bUseWorldLocation )
+ {
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nHeadLocation );
+ matrix3x4_t mat;
+ pParticles->GetControlPointTransformAtTime( m_nHeadLocation, pParticles->m_flCurTime, &mat );
+ Vector vecTransformLocal = vec3_origin;
+
+ VectorTransform( m_vecCP1Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP1, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP1, m_nCP1Parent );
+
+ VectorTransform( m_vecCP2Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP2, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP2, m_nCP2Parent );
+
+ VectorTransform( m_vecCP3Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP3, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP3, m_nCP3Parent );
+
+ VectorTransform( m_vecCP4Pos, mat, vecTransformLocal );
+ pParticles->SetControlPoint( m_nCP4, vecTransformLocal );
+ pParticles->SetControlPointParent( m_nCP4, m_nCP4Parent );
+ }
+ else
+ {
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos );
+ pParticles->SetControlPointParent( m_nCP1, m_nCP1Parent );
+ pParticles->SetControlPoint( m_nCP2, m_vecCP2Pos );
+ pParticles->SetControlPointParent( m_nCP2, m_nCP2Parent );
+ pParticles->SetControlPoint( m_nCP3, m_vecCP3Pos );
+ pParticles->SetControlPointParent( m_nCP3, m_nCP3Parent );
+ pParticles->SetControlPoint( m_nCP4, m_vecCP4Pos );
+ pParticles->SetControlPointParent( m_nCP4, m_nCP4Parent );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Dampen Movement Relative to Control Point
+// The closer a particle is the the assigned control point, the less
+// it can move
+//-----------------------------------------------------------------------------
+class C_OP_DampenToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DampenToCP );
+
+ int m_nControlPointNumber;
+ float m_flRange, m_flScale;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK |
+ PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPointNumber );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DampenToCP , "Movement Dampen Relative to Control Point", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DampenToCP )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "falloff range", "100", float, m_flRange )
+DMXELEMENT_UNPACK_FIELD( "dampen scale", "1", float, m_flScale )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DampenToCP )
+
+void C_OP_DampenToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_flRange <= 0.0f )
+ return;
+
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nControlPointNumber );
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecParticlePosition, vecParticlePosition_prev, vParticleDelta ;
+
+ SetVectorFromAttribute( vecParticlePosition, xyz );
+ SetVectorFromAttribute( vecParticlePosition_prev, xyz_prev );
+ Vector ofs;
+ ofs = vecParticlePosition - vecControlPoint;
+ float flDistance = ofs.Length();
+ float flDampenAmount;
+ if ( flDistance > m_flRange )
+ {
+ continue;
+ }
+ else
+ {
+ flDampenAmount = flDistance / m_flRange;
+ flDampenAmount = pow( flDampenAmount, m_flScale);
+ }
+
+ vParticleDelta = vecParticlePosition - vecParticlePosition_prev;
+ Vector vParticleDampened = vParticleDelta * flDampenAmount;
+ vecParticlePosition = vecParticlePosition_prev + vParticleDampened;
+ Vector vecParticlePositionOrg;
+ SetVectorFromAttribute( vecParticlePositionOrg, xyz );
+ VectorLerp (vecParticlePositionOrg, vecParticlePosition, flStrength, vecParticlePosition );
+ SetVectorAttribute( xyz, vecParticlePosition );
+ }
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Distance Between CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_DistanceBetweenCPs : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DistanceBetweenCPs );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nStartCP ) | ( 1ULL << m_nEndCP );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nStartCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartCP ) );
+ m_nEndCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndCP ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ int m_nStartCP;
+ int m_nEndCP;
+ bool m_bLOS;
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ float m_flMaxTraceLength;
+ float m_flLOSScale;
+ bool m_bScaleInitialRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DistanceBetweenCPs, "Remap Distance Between Two Control Points to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceBetweenCPs )
+DMXELEMENT_UNPACK_FIELD( "distance minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "distance maximum","128", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "starting control point","0", int, m_nStartCP )
+DMXELEMENT_UNPACK_FIELD( "ending control point","1", int, m_nEndCP )
+DMXELEMENT_UNPACK_FIELD( "ensure line of sight","0", bool, m_bLOS )
+DMXELEMENT_UNPACK_FIELD_STRING( "LOS collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "Maximum Trace Length", "-1", float, m_flMaxTraceLength )
+DMXELEMENT_UNPACK_FIELD( "LOS Failure Scalar", "0", float, m_flLOSScale )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceBetweenCPs )
+
+void C_OP_DistanceBetweenCPs::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint1 = pParticles->GetControlPointAtCurrentTime( m_nStartCP );
+ Vector vecControlPoint2 = pParticles->GetControlPointAtCurrentTime( m_nEndCP );
+ Vector vecDelta = vecControlPoint1 - vecControlPoint2;
+ float flDistance = vecDelta.Length();
+
+
+ if ( m_bLOS )
+ {
+ Vector vecEndPoint = vecControlPoint2;
+ if ( m_flMaxTraceLength != -1.0f && m_flMaxTraceLength < flDistance )
+ {
+ VectorNormalize(vecEndPoint);
+ vecEndPoint *= m_flMaxTraceLength;
+ vecEndPoint += vecControlPoint1;
+ }
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecControlPoint1, vecEndPoint, MASK_OPAQUE_AND_NPCS, NULL, m_nCollisionGroupNumber, &tr );
+ if (tr.fraction != 1.0f)
+ {
+ flDistance *= tr.fraction * m_flLOSScale;
+ }
+
+ }
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ const float *pInitialOutput = pParticles->GetInitialFloatAttributePtr( m_nFieldOutput, i );
+ flOutput = *pInitialOutput * flOutput;
+ }
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Distance to CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_DistanceToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_DistanceToCP );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ uint32 GetReadInitialAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nStartCP ) | ( 1ULL << m_nEndCP );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
+ m_nStartCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartCP ) );
+ m_nEndCP = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndCP ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ int m_nStartCP;
+ int m_nEndCP;
+ bool m_bLOS;
+ char m_CollisionGroupName[128];
+ int m_nCollisionGroupNumber;
+ float m_flMaxTraceLength;
+ float m_flLOSScale;
+ bool m_bScaleInitialRange;
+ bool m_bActiveRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_DistanceToCP, "Remap Distance to Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceToCP )
+DMXELEMENT_UNPACK_FIELD( "distance minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "distance maximum","128", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "control point","0", int, m_nStartCP )
+DMXELEMENT_UNPACK_FIELD( "ensure line of sight","0", bool, m_bLOS )
+DMXELEMENT_UNPACK_FIELD_STRING( "LOS collision group", "NONE", m_CollisionGroupName )
+DMXELEMENT_UNPACK_FIELD( "Maximum Trace Length", "-1", float, m_flMaxTraceLength )
+DMXELEMENT_UNPACK_FIELD( "LOS Failure Scalar", "0", float, m_flLOSScale )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+DMXELEMENT_UNPACK_FIELD( "only active within specified distance","0", bool, m_bActiveRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_DistanceToCP )
+
+void C_OP_DistanceToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint1 = pParticles->GetControlPointAtCurrentTime( m_nStartCP );
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ Vector vecPosition2;
+ const float *pXYZ = pParticles->GetFloatAttributePtr(PARTICLE_ATTRIBUTE_XYZ, i );
+ vecPosition2 = Vector(pXYZ[0], pXYZ[4], pXYZ[8]);
+ Vector vecDelta = vecControlPoint1 - vecPosition2;
+ float flDistance = vecDelta.Length();
+ if ( m_bActiveRange && ( flDistance < m_flInputMin || flDistance > m_flInputMax ) )
+ {
+ continue;
+ }
+ if ( m_bLOS )
+ {
+ Vector vecEndPoint = vecPosition2;
+ if ( m_flMaxTraceLength != -1.0f && m_flMaxTraceLength < flDistance )
+ {
+ VectorNormalize(vecEndPoint);
+ vecEndPoint *= m_flMaxTraceLength;
+ vecEndPoint += vecControlPoint1;
+ }
+ CBaseTrace tr;
+ g_pParticleSystemMgr->Query()->TraceLine( vecControlPoint1, vecEndPoint, MASK_OPAQUE_AND_NPCS, NULL , m_nCollisionGroupNumber, &tr );
+ if (tr.fraction != 1.0f)
+ {
+ flDistance *= tr.fraction * m_flLOSScale;
+ }
+
+ }
+
+ float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ const float *pInitialOutput = pParticles->GetInitialFloatAttributePtr( m_nFieldOutput, i );
+ flOutput = *pInitialOutput * flOutput;
+ }
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+
+ *pOutput = Lerp (flStrength, *pOutput, flOutput);
+ //float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ //float flOutput = RemapValClamped( flDistance, m_flInputMin, m_flInputMax, flMin, flMax );
+ //*pOutput = Lerp (flStrength, *pOutput, flOutput);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Assign CP to Player
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointToPlayer : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointToPlayer );
+
+ int m_nCP1;
+
+ Vector m_vecCP1Pos;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCP1 = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nCP1 ) );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointToPlayer, "Set Control Point To Player", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToPlayer )
+DMXELEMENT_UNPACK_FIELD( "Control Point Number", "1", int, m_nCP1 )
+DMXELEMENT_UNPACK_FIELD( "Control Point Offset", "0 0 0", Vector, m_vecCP1Pos )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToPlayer )
+
+void C_OP_SetControlPointToPlayer::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ Vector vecClientPos =g_pParticleSystemMgr->Query()->GetLocalPlayerPos();
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos + vecClientPos );
+ Vector vecForward;
+ Vector vecRight;
+ Vector vecUp;
+ g_pParticleSystemMgr->Query()->GetLocalPlayerEyeVectors( &vecForward, &vecRight, &vecUp);
+ pParticles->SetControlPointOrientation( m_nCP1, vecForward, vecRight, vecUp );
+}
+
+
+
+
+
+//-------------------------
+// Emits particles from particles
+//NOT FINISHED
+//-------------------------
+class C_OP_PerParticleEmitter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_PerParticleEmitter );
+
+ struct C_OP_PerParticleEmitterContext_t
+ {
+ float m_flTotalActualParticlesSoFar;
+ int m_nTotalEmittedSoFar;
+ bool m_bStoppedEmission;
+ };
+
+ int m_nChildGroupID;
+ bool m_bInheritVelocity;
+ float m_flEmitRate;
+ float m_flVelocityScale;
+ float m_flStartTime;
+ float m_flEmissionDuration;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_CREATION_TIME;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ | PARTICLE_ATTRIBUTE_PREV_XYZ;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_flEmitRate < 0.0f )
+ {
+ m_flEmitRate = 0.0f;
+ }
+ if ( m_flEmissionDuration < 0.0f )
+ {
+ m_flEmissionDuration = 0.0f;
+ }
+ }
+
+ inline bool IsInfinitelyEmitting() const
+ {
+ return ( m_flEmissionDuration == 0.0f );
+ }
+
+ virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx = reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ if ( pCtx->m_bStoppedEmission )
+ return false;
+
+ if ( m_flEmitRate <= 0.0f )
+ return false;
+
+ if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( m_flStartTime + m_flEmissionDuration ) )
+ return false;
+
+ return true;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx=reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ pCtx->m_flTotalActualParticlesSoFar = 0.0f;
+ pCtx->m_nTotalEmittedSoFar = 0;
+ pCtx->m_bStoppedEmission = false;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PerParticleEmitter, "Per Particle Emitter", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PerParticleEmitter )
+DMXELEMENT_UNPACK_FIELD( "Group ID to affect", "1", int, m_nChildGroupID )
+DMXELEMENT_UNPACK_FIELD( "Inherit Velocity", "0", int, m_bInheritVelocity )
+DMXELEMENT_UNPACK_FIELD( "Emission Rate", "100", float, m_flEmitRate )
+DMXELEMENT_UNPACK_FIELD( "Velocity Scale", "0", int, m_flVelocityScale )
+DMXELEMENT_UNPACK_FIELD( "Emission Start Time", "0", float, m_flStartTime )
+DMXELEMENT_UNPACK_FIELD( "Emission Duration", "0", float, m_flEmissionDuration )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PerParticleEmitter )
+
+
+void C_OP_PerParticleEmitter::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ for( CParticleCollection *pChild = pParticles->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
+ {
+ if ( pChild->GetGroupID() == m_nChildGroupID )
+ {
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ C_OP_PerParticleEmitterContext_t *pCtx=reinterpret_cast<C_OP_PerParticleEmitterContext_t *>( pContext );
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *pXYZ_Prev = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Vector vecParticlePosition, vecParticlePosition_prev, vParticleDelta ;
+
+ vecParticlePosition = Vector ( pXYZ[0], pXYZ[4], pXYZ[8] );
+ vecParticlePosition_prev = Vector ( pXYZ_Prev[0], pXYZ_Prev[4], pXYZ_Prev[8] );
+ vParticleDelta = vecParticlePosition - vecParticlePosition_prev;
+
+ float flEmissionRate = m_flEmitRate * flStrength;
+
+ if ( m_flVelocityScale != 0.0f )
+ {
+ float flVelocity = vParticleDelta.Length();
+ flEmissionRate *= flVelocity * m_flVelocityScale * pParticles->m_flDt;
+ }
+
+ if ( flEmissionRate == 0.0f )
+ continue;
+
+ if ( !C_OP_PerParticleEmitter::MayCreateMoreParticles( pChild, pContext ) )
+ continue;
+
+ Assert( flEmissionRate != 0.0f );
+
+ // determine our previous and current draw times and clamp them to start time and emission duration
+ float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
+ float flCurrDrawTime = pParticles->m_flCurTime;
+
+ if ( !IsInfinitelyEmitting() )
+ {
+ if ( flPrevDrawTime < m_flStartTime )
+ {
+ flPrevDrawTime = m_flStartTime;
+ }
+ if ( flCurrDrawTime > m_flStartTime + m_flEmissionDuration )
+ {
+ flCurrDrawTime = m_flStartTime + m_flEmissionDuration;
+ }
+ }
+
+ float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
+
+ //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
+ float flActualParticlesToEmit = flEmissionRate * flDeltaTime;
+ int nParticlesEmitted = pCtx->m_nTotalEmittedSoFar;
+ //Add emitted particle to float counter to allow for fractional emission
+ pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;
+
+ //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
+ int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;
+
+ //Add emitted particles to running int total.
+ pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;
+
+
+ if ( nParticlesToEmit == 0 )
+ continue;
+
+ // We're only allowed to emit so many particles, though..
+ // If we run out of room, only emit the last N particles
+ int nActualParticlesToEmit = nParticlesToEmit;
+ int nAllowedParticlesToEmit = pChild->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
+ if ( nAllowedParticlesToEmit < nParticlesToEmit )
+ {
+ nActualParticlesToEmit = nAllowedParticlesToEmit;
+ }
+ if ( nActualParticlesToEmit == 0 )
+ continue;
+
+ int nStartParticle = pChild->m_nActiveParticles;
+ pChild->SetNActiveParticles( nActualParticlesToEmit + pChild->m_nActiveParticles );
+
+
+ float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
+ float flTimeStep = flPrevDrawTime + flTimeStampStep;
+ Vector vecMoveStampStep = vParticleDelta / nActualParticlesToEmit ;
+ Vector vecMoveStep = vecParticlePosition_prev + vecMoveStampStep ;
+
+ if ( nParticlesEmitted != pChild->m_nActiveParticles )
+ {
+
+ uint32 nInittedMask = ( PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK );
+ // init newly emitted particles
+ pChild->InitializeNewParticles( nParticlesEmitted, pChild->m_nActiveParticles - nParticlesEmitted, nInittedMask );
+ //CHECKSYSTEM( this );
+ }
+
+ // Set the particle creation time to the exact sub-frame particle emission time
+ // !! speed!! do sse init here
+ for( int j = nStartParticle; j < nStartParticle + nActualParticlesToEmit; j++ )
+ {
+ float *pTimeStamp = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, j );
+ flTimeStep = min( flTimeStep, flCurrDrawTime );
+ *pTimeStamp = flTimeStep;
+ flTimeStep += flTimeStampStep;
+ float *pXYZ_Child = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, j );
+ float *pXYZ_Prev_Child = pChild->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, j );
+ Vector vecChildXYZ;
+ SetVectorFromAttribute ( vecChildXYZ, pXYZ_Child);
+ vecChildXYZ = vecMoveStep;
+ SetVectorAttribute ( pXYZ_Child, vecChildXYZ);
+ vecMoveStep += vecMoveStampStep;
+ if ( m_bInheritVelocity )
+ {
+ *pXYZ_Prev_Child = *pXYZ_Prev;
+ }
+ else
+ {
+ *pXYZ_Prev_Child = *pXYZ_Child;
+ }
+
+ }
+
+ }
+ }
+ }
+}
+
+
+class C_OP_LockToBone : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_LockToBone );
+
+ int m_nControlPointNumber;
+ float m_flLifeTimeFadeStart;
+ float m_flLifeTimeFadeEnd;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ int ret= PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK |
+ PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK | PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK;
+ ret |= PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
+ return ret;
+
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nControlPointNumber );
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_LockToBone , "Movement Lock to Bone", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_LockToBone )
+ DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "lifetime fade start", "0", float, m_flLifeTimeFadeStart )
+ DMXELEMENT_UNPACK_FIELD( "lifetime fade end", "0", float, m_flLifeTimeFadeEnd )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_LockToBone )
+
+void C_OP_LockToBone::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ pParticles->UpdateHitBoxInfo( m_nControlPointNumber );
+ if ( pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].CurAndPrevValid() )
+ {
+ float flAgeThreshold = m_flLifeTimeFadeEnd;
+ if ( flAgeThreshold <= 0.0 )
+ flAgeThreshold = 1.0e20;
+ float flIScale = 0.0;
+ if ( m_flLifeTimeFadeEnd > m_flLifeTimeFadeStart )
+ flIScale = 1.0/( m_flLifeTimeFadeEnd - m_flLifeTimeFadeStart );
+
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *pXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *pPrevXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ const float *pUVW = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ, i );
+ const int nBoxIndex = *pParticles->GetIntAttributePtr( PARTICLE_ATTRIBUTE_HITBOX_INDEX, i );
+ float const *pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+
+ float flAge = pParticles->m_flCurTime -*pCreationTime;
+
+ if ( flAge < flAgeThreshold )
+ {
+ if (
+ ( nBoxIndex < pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_nNumHitBoxes ) &&
+ ( nBoxIndex < pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_nNumPrevHitBoxes ) &&
+ ( nBoxIndex >= 0 )
+ )
+ {
+ Vector vecParticlePosition;
+ ModelHitBoxInfo_t const &hb = pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_pHitBoxes[ nBoxIndex ];
+ vecParticlePosition.x = Lerp( pUVW[0], hb.m_vecBoxMins.x, hb.m_vecBoxMaxes.x );
+ vecParticlePosition.y = Lerp( pUVW[4], hb.m_vecBoxMins.y, hb.m_vecBoxMaxes.y );
+ vecParticlePosition.z = Lerp( pUVW[8], hb.m_vecBoxMins.z, hb.m_vecBoxMaxes.z );
+ Vector vecWorldPosition;
+ VectorTransform( vecParticlePosition, hb.m_Transform, vecWorldPosition );
+
+ Vector vecPrevParticlePosition;
+ ModelHitBoxInfo_t phb = pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].m_pPrevBoxes[ nBoxIndex ];
+ vecPrevParticlePosition.x = Lerp( pUVW[0], phb.m_vecBoxMins.x, phb.m_vecBoxMaxes.x );
+ vecPrevParticlePosition.y = Lerp( pUVW[4], phb.m_vecBoxMins.y, phb.m_vecBoxMaxes.y );
+ vecPrevParticlePosition.z = Lerp( pUVW[8], phb.m_vecBoxMins.z, phb.m_vecBoxMaxes.z );
+ Vector vecPrevWorldPosition;
+ VectorTransform( vecPrevParticlePosition, phb.m_Transform, vecPrevWorldPosition );
+
+ Vector Delta = vecWorldPosition-vecPrevWorldPosition;
+
+ if ( flAge > m_flLifeTimeFadeStart )
+ Delta *= flStrength * ( 1.0- ( ( flAge - m_flLifeTimeFadeStart ) * flIScale ) );
+
+ Vector xyz;
+ SetVectorFromAttribute( xyz, pXYZ );
+ xyz += Delta;
+ SetVectorAttribute( pXYZ, xyz );
+
+ Vector prevxyz;
+ SetVectorFromAttribute( prevxyz, pPrevXYZ );
+ prevxyz += Delta;
+ SetVectorAttribute( pPrevXYZ, prevxyz );
+ }
+ }
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Plane Cull Operator - cull particles on the "wrong" side of a plane
+//-----------------------------------------------------------------------------
+class C_OP_PlaneCull : public CParticleOperatorInstance
+{
+ int m_nPlaneControlPoint;
+ Vector m_vecPlaneDirection;
+ float m_flPlaneOffset;
+
+ DECLARE_PARTICLE_OPERATOR( C_OP_PlaneCull );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nPlaneControlPoint );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_PlaneCull, "Cull when crossing plane", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PlaneCull )
+ DMXELEMENT_UNPACK_FIELD( "Control Point for point on plane", "0", int, m_nPlaneControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "Cull plane offset", "0", float, m_flPlaneOffset )
+ DMXELEMENT_UNPACK_FIELD( "Plane Normal", "0 0 1", Vector, m_vecPlaneDirection )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_PlaneCull )
+
+void C_OP_PlaneCull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ int nLimit = pParticles->m_nPaddedActiveParticles << 2;
+
+ // setup vars
+ FourVectors v4N ;
+ v4N.DuplicateVector( m_vecPlaneDirection );
+ v4N.VectorNormalize();
+ FourVectors v4Pnt;
+ v4Pnt.DuplicateVector( pParticles->GetControlPointAtCurrentTime( m_nPlaneControlPoint ) );
+ FourVectors ofs = v4N;
+ ofs *= ReplicateX4( m_flPlaneOffset );
+ v4Pnt -= ofs;
+
+ for ( int i = 0; i < nLimit; i+= 4 )
+ {
+ FourVectors f4PlaneRel = (*pXYZ );
+ f4PlaneRel -= v4Pnt;
+ fltx4 fl4PlaneEq = ( f4PlaneRel * v4N );
+ if ( IsAnyNegative( fl4PlaneEq ) )
+ {
+ // not especially pretty - we need to kill some particles.
+ int nMask = TestSignSIMD( fl4PlaneEq );
+ if ( nMask & 1 )
+ pParticles->KillParticle( i );
+ if ( nMask & 2 )
+ pParticles->KillParticle( i + 1 );
+ if ( nMask & 4 )
+ pParticles->KillParticle( i + 2 );
+ if ( nMask & 8 )
+ pParticles->KillParticle( i + 3 );
+
+ }
+ ++pXYZ;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Model Cull Operator - cull particles inside or outside of a brush/animated model
+//-----------------------------------------------------------------------------
+class C_OP_ModelCull : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_ModelCull );
+
+ int m_nControlPointNumber;
+ bool m_bBoundBox;
+ bool m_bCullOutside;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nControlPointNumber = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nControlPointNumber ) );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_ModelCull , "Cull relative to model", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ModelCull )
+DMXELEMENT_UNPACK_FIELD( "control_point_number", "0", int, m_nControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "use only bounding box", "0", bool, m_bBoundBox )
+DMXELEMENT_UNPACK_FIELD( "cull outside instead of inside", "0", bool, m_bCullOutside )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_ModelCull )
+
+void C_OP_ModelCull::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ pParticles->UpdateHitBoxInfo( m_nControlPointNumber );
+ if ( pParticles->m_ControlPointHitBoxes[m_nControlPointNumber].CurAndPrevValid() )
+ {
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *pXYZ = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ Vector vecParticlePosition;
+
+ SetVectorFromAttribute( vecParticlePosition, pXYZ );
+
+ bool bInside = g_pParticleSystemMgr->Query()->IsPointInControllingObjectHitBox( pParticles, m_nControlPointNumber, vecParticlePosition, m_bBoundBox );
+ if ( ( bInside && m_bCullOutside ) || ( !bInside && !m_bCullOutside ))
+ continue;
+
+ pParticles->KillParticle(i);
+ }
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Assign CP to Center
+//-----------------------------------------------------------------------------
+class C_OP_SetControlPointToCenter : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_SetControlPointToCenter );
+
+ int m_nCP1;
+
+ Vector m_vecCP1Pos;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nCP1 = max( 0, min( MAX_PARTICLE_CONTROL_POINTS-1, m_nCP1 ) );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_SetControlPointToCenter, "Set Control Point To Particles' Center", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToCenter )
+DMXELEMENT_UNPACK_FIELD( "Control Point Number to Set", "1", int, m_nCP1 )
+DMXELEMENT_UNPACK_FIELD( "Center Offset", "0 0 0", Vector, m_vecCP1Pos )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_SetControlPointToCenter )
+
+void C_OP_SetControlPointToCenter::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ Vector vecMinBounds;
+ Vector vecMaxBounds;
+
+ pParticles->GetBounds( &vecMinBounds, &vecMaxBounds );
+
+ Vector vecCenter = ( ( vecMinBounds + vecMaxBounds ) / 2 );
+
+ pParticles->SetControlPoint( m_nCP1, m_vecCP1Pos + vecCenter );
+}
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Velocity Match a group of particles
+//-----------------------------------------------------------------------------
+class C_OP_VelocityMatchingForce : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_VelocityMatchingForce );
+
+ float m_flDirScale;
+ float m_flSpdScale;
+ int m_nCPBroadcast;
+
+ struct VelocityMatchingForceContext_t
+ {
+ Vector m_vecAvgVelocity;
+ float m_flAvgSpeed;
+ };
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ VelocityMatchingForceContext_t *pCtx = reinterpret_cast<VelocityMatchingForceContext_t *>( pContext );
+ pCtx->m_vecAvgVelocity = vec3_origin;
+ pCtx->m_flAvgSpeed = 0;
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( VelocityMatchingForceContext_t );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_VelocityMatchingForce , "Movement Match Particle Velocities", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityMatchingForce )
+DMXELEMENT_UNPACK_FIELD( "Direction Matching Strength", "0.25", float, m_flDirScale )
+DMXELEMENT_UNPACK_FIELD( "Speed Matching Strength", "0.25", float, m_flSpdScale )
+DMXELEMENT_UNPACK_FIELD( "Control Point to Broadcast Speed and Direction To", "-1", int, m_nCPBroadcast )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_VelocityMatchingForce )
+
+void C_OP_VelocityMatchingForce::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ VelocityMatchingForceContext_t *pCtx = reinterpret_cast<VelocityMatchingForceContext_t *>( pContext );
+
+ Vector vecVelocityAvg = vec3_origin;
+ float flAvgSpeed = 0;
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, xyz_prev );
+ Vector vecVelocityCur = ( ( vecXYZ - vecPXYZ ) / pParticles->m_flDt );
+ vecVelocityAvg += vecVelocityCur;
+ float flSpeed = vecVelocityCur.Length();
+ flAvgSpeed += flSpeed;
+
+ if ( pCtx->m_vecAvgVelocity != vec3_origin )
+ {
+ Vector vecScaledXYZ;
+ VectorNormalizeFast(vecVelocityCur);
+ VectorLerp( vecVelocityCur, pCtx->m_vecAvgVelocity, m_flDirScale, vecScaledXYZ );
+ VectorNormalizeFast(vecScaledXYZ);
+ flSpeed = Lerp ( m_flSpdScale, flSpeed, pCtx->m_flAvgSpeed );
+ vecScaledXYZ *= flSpeed;
+ vecScaledXYZ = ( ( vecScaledXYZ * pParticles->m_flDt ) + vecPXYZ );
+ SetVectorAttribute( xyz, vecScaledXYZ );
+ }
+ }
+
+ VectorNormalizeFast( vecVelocityAvg );
+ pCtx->m_vecAvgVelocity = vecVelocityAvg;
+ pCtx->m_flAvgSpeed = ( flAvgSpeed / pParticles->m_nActiveParticles );
+ if ( m_nCPBroadcast != -1 )
+ {
+ pParticles->SetControlPoint( m_nCPBroadcast, Vector ( pCtx->m_flAvgSpeed, pCtx->m_flAvgSpeed, pCtx->m_flAvgSpeed ) );
+ pParticles->SetControlPointForwardVector( m_nCPBroadcast, pCtx->m_vecAvgVelocity );
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Orient to heading
+//-----------------------------------------------------------------------------
+class C_OP_OrientTo2dDirection : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_OrientTo2dDirection );
+
+ float m_flRotOffset;
+ float m_flSpinStrength;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_OrientTo2dDirection , "Rotation Orient to 2D Direction", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_OrientTo2dDirection )
+DMXELEMENT_UNPACK_FIELD( "Rotation Offset", "0", float, m_flRotOffset )
+DMXELEMENT_UNPACK_FIELD( "Spin Strength", "1", float, m_flSpinStrength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_OrientTo2dDirection )
+
+void C_OP_OrientTo2dDirection::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ float flRotOffset = m_flRotOffset * ( M_PI / 180.0f );
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ float *roll = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+ vecPXYZ.x = xyz_prev[0];
+ vecPXYZ.y = xyz_prev[4];
+ vecPXYZ.z = xyz_prev[8];
+ Vector vecVelocityCur = ( vecXYZ - vecPXYZ );
+
+ vecVelocityCur.z = 0.0f;
+ VectorNormalizeFast ( vecVelocityCur );
+
+ float flCurRot = *roll;
+
+ float flVelRot = atan2(vecVelocityCur.y, vecVelocityCur.x ) + M_PI;
+
+ flVelRot += flRotOffset;
+
+ float flRotation = Lerp ( m_flSpinStrength, flCurRot, flVelRot );
+ *roll = flRotation;
+ }
+
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Orient relative to CP
+//-----------------------------------------------------------------------------
+class C_OP_Orient2DRelToCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_Orient2DRelToCP );
+
+ float m_flRotOffset;
+ float m_flSpinStrength;
+ int m_nCP;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK ;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nCP );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_Orient2DRelToCP , "Rotation Orient Relative to CP", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_Orient2DRelToCP )
+DMXELEMENT_UNPACK_FIELD( "Rotation Offset", "0", float, m_flRotOffset )
+DMXELEMENT_UNPACK_FIELD( "Spin Strength", "1", float, m_flSpinStrength )
+DMXELEMENT_UNPACK_FIELD( "Control Point", "0", int, m_nCP )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_Orient2DRelToCP )
+
+void C_OP_Orient2DRelToCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+ float flRotOffset = m_flRotOffset * ( M_PI / 180.0f );
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *roll = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, i );
+
+ Vector vecXYZ;
+ Vector vecCP;
+ vecCP = pParticles->GetControlPointAtCurrentTime( m_nCP );
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+
+ Vector vecVelocityCur = ( vecXYZ - vecCP );
+
+ vecVelocityCur.z = 0.0f;
+ VectorNormalizeFast ( vecVelocityCur );
+
+ float flCurRot = *roll;
+
+ float flVelRot = atan2(vecVelocityCur.y, vecVelocityCur.x ) + M_PI;
+
+ flVelRot += flRotOffset;
+
+ float flRotation = Lerp ( m_flSpinStrength, flCurRot, flVelRot );
+ *roll = flRotation;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Max Velocity - clamps the maximum velocity of a particle
+//-----------------------------------------------------------------------------
+class C_OP_MaxVelocity : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MaxVelocity );
+
+ float m_flMaxVelocity;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MaxVelocity , "Movement Max Velocity", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MaxVelocity )
+DMXELEMENT_UNPACK_FIELD( "Maximum Velocity", "0", float, m_flMaxVelocity )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MaxVelocity )
+
+void C_OP_MaxVelocity::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *xyz_prev = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector vecXYZ;
+ Vector vecPXYZ;
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, xyz_prev );
+ Vector vecVelocityCur = ( ( vecXYZ - vecPXYZ ) );
+ float flSpeed = vecVelocityCur.Length();
+ VectorNormalizeFast( vecVelocityCur );
+ float flMaxVelocityNormalized = m_flMaxVelocity * pParticles->m_flDt;
+ vecVelocityCur *= min( flSpeed, flMaxVelocityNormalized);
+ vecXYZ = vecPXYZ + vecVelocityCur;
+ SetVectorAttribute( xyz, vecXYZ );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Maintain position along a path
+//-----------------------------------------------------------------------------
+struct SequentialPositionContext_t
+{
+ int m_nParticleCount;
+ float m_flStep;
+ int m_nCountAmount;
+};
+
+class C_OP_MaintainSequentialPath : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MaintainSequentialPath );
+
+ float m_fMaxDistance;
+ float m_flNumToAssign;
+ bool m_bLoop;
+ float m_flCohesionStrength;
+ struct CPathParameters m_PathParams;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ uint64 nStartMask = ( 1ULL << m_PathParams.m_nStartControlPointNumber ) - 1;
+ uint64 nEndMask = ( 1ULL << ( m_PathParams.m_nEndControlPointNumber + 1 ) ) - 1;
+ return nEndMask & (~nStartMask);
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ SequentialPositionContext_t *pCtx = reinterpret_cast<SequentialPositionContext_t *>( pContext );
+ pCtx->m_nParticleCount = 0;
+ if ( m_flNumToAssign > 1.0f )
+ {
+ pCtx->m_flStep = 1.0f / ( m_flNumToAssign - 1 );
+ }
+ else
+ {
+ pCtx->m_flStep = 0.0f;
+ }
+ pCtx->m_nCountAmount = 1;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_PathParams.ClampControlPointIndices();
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( SequentialPositionContext_t );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MaintainSequentialPath, "Movement Maintain Position Along Path", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainSequentialPath )
+ DMXELEMENT_UNPACK_FIELD( "maximum distance", "0", float, m_fMaxDistance )
+ DMXELEMENT_UNPACK_FIELD( "bulge", "0", float, m_PathParams.m_flBulge )
+ DMXELEMENT_UNPACK_FIELD( "start control point number", "0", int, m_PathParams.m_nStartControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "end control point number", "0", int, m_PathParams.m_nEndControlPointNumber )
+ DMXELEMENT_UNPACK_FIELD( "bulge control 0=random 1=orientation of start pnt 2=orientation of end point", "0", int, m_PathParams.m_nBulgeControl )
+ DMXELEMENT_UNPACK_FIELD( "mid point position", "0.5", float, m_PathParams.m_flMidPoint )
+ DMXELEMENT_UNPACK_FIELD( "particles to map from start to end", "100", float, m_flNumToAssign )
+ DMXELEMENT_UNPACK_FIELD( "restart behavior (0 = bounce, 1 = loop )", "1", bool, m_bLoop )
+ DMXELEMENT_UNPACK_FIELD( "cohesion strength", "1", float, m_flCohesionStrength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainSequentialPath )
+
+
+void C_OP_MaintainSequentialPath::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
+ SequentialPositionContext_t *pCtx = reinterpret_cast<SequentialPositionContext_t *>( pContext );
+
+ float fl_Cohesion = ( 1 - m_flCohesionStrength );
+
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+
+ Vector StartPnt, MidP, EndPnt;
+ pParticles->CalculatePathValues( m_PathParams, pParticles->m_flCurTime, &StartPnt, &MidP, &EndPnt);
+ if ( pCtx->m_nParticleCount >= m_flNumToAssign || pCtx->m_nParticleCount < 0 )
+ {
+ if ( m_bLoop )
+ {
+ pCtx->m_nParticleCount = 0;
+ }
+ else
+ {
+ pCtx->m_nCountAmount *= -1;
+ pCtx->m_nParticleCount = min ( pCtx->m_nParticleCount, (int)( m_flNumToAssign - 1) );
+ pCtx->m_nParticleCount = max ( pCtx->m_nParticleCount, 1 );
+ }
+ }
+
+ float t= pCtx->m_nParticleCount * pCtx->m_flStep;
+
+
+ // form delta terms needed for quadratic bezier
+ Vector Delta0=MidP-StartPnt;
+ Vector Delta1 = EndPnt-MidP;
+
+ Vector L0 = StartPnt+t*Delta0;
+ Vector L1 = MidP+t*Delta1;
+
+ Vector Pnt = L0+(L1-L0)*t;
+
+ // Allow an offset distance and position lerp
+ Vector vecXYZ;
+ Vector vecPXYZ;
+
+ SetVectorFromAttribute( vecXYZ, xyz );
+ SetVectorFromAttribute( vecPXYZ, pxyz );
+
+ vecXYZ -= Pnt;
+ vecPXYZ -= Pnt;
+
+ float flXYZOffset = min (vecXYZ.Length(), m_fMaxDistance );
+ float flPXYZOffset = min (vecPXYZ.Length(), m_fMaxDistance );
+
+ VectorNormalizeFast( vecXYZ );
+ vecXYZ *= flXYZOffset * fl_Cohesion;
+ VectorNormalizeFast( vecPXYZ );
+ vecPXYZ *= flPXYZOffset * fl_Cohesion;
+
+ vecXYZ += Pnt;
+ vecPXYZ += Pnt;
+
+ xyz[0] = vecXYZ.x;
+ xyz[4] = vecXYZ.y;
+ xyz[8] = vecXYZ.z;
+ pxyz[0] = vecPXYZ.x;
+ pxyz[4] = vecPXYZ.y;
+ pxyz[8] = vecPXYZ.z;
+
+ pCtx->m_nParticleCount += pCtx->m_nCountAmount;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Remap Dot Product to Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapDotProductToScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapDotProductToScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nInputCP1 ) | ( 1ULL << m_nInputCP2 );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nInputCP1;
+ int m_nInputCP2;
+ int m_nFieldOutput;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ bool m_bUseParticleVelocity;
+ bool m_bScaleInitialRange;
+ bool m_bActiveRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapDotProductToScalar, "Remap Dot Product to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapDotProductToScalar )
+ DMXELEMENT_UNPACK_FIELD( "use particle velocity for first input", "0", bool, m_bUseParticleVelocity )
+ DMXELEMENT_UNPACK_FIELD( "first input control point", "0", int, m_nInputCP1 )
+ DMXELEMENT_UNPACK_FIELD( "second input control point", "0", int, m_nInputCP2 )
+ DMXELEMENT_UNPACK_FIELD( "input minimum (-1 to 1)","0", float, m_flInputMin )
+ DMXELEMENT_UNPACK_FIELD( "input maximum (-1 to 1)","1", float, m_flInputMax )
+ DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+ DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+ DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+ DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+ DMXELEMENT_UNPACK_FIELD( "only active within specified input range","0", bool, m_bActiveRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapDotProductToScalar )
+
+void C_OP_RemapDotProductToScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+
+ Vector vecInput1;
+ Vector vecInput2;
+
+ CParticleSIMDTransformation pXForm1;
+ CParticleSIMDTransformation pXForm2;
+ pParticles->GetControlPointTransformAtTime( m_nInputCP1, pParticles->m_flCurTime, &pXForm1 );
+ pParticles->GetControlPointTransformAtTime( m_nInputCP2, pParticles->m_flCurTime, &pXForm2 );
+
+ vecInput1 = pXForm1.m_v4Fwd.Vec( 0 );
+ vecInput2 = pXForm2.m_v4Fwd.Vec( 0 );
+
+ float flInput = DotProduct( vecInput1, vecInput2 );
+
+ // only use within start/end time frame and, if set, active input range
+ if ( ( m_bActiveRange && !m_bUseParticleVelocity && ( flInput < m_flInputMin || flInput > m_flInputMax ) ) )
+ return;
+
+ // FIXME: SSE-ize
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ if ( m_bUseParticleVelocity )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *pxyz = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+ Vector vecXYZ;
+ Vector vecPXYZ;
+
+ vecXYZ.x = xyz[0];
+ vecXYZ.y = xyz[4];
+ vecXYZ.z = xyz[8];
+ vecPXYZ.x = pxyz[0];
+ vecPXYZ.y = pxyz[4];
+ vecPXYZ.z = pxyz[8];
+
+ vecInput1 = vecXYZ - vecPXYZ;
+ VectorNormalizeFast( vecInput1 );
+
+ float flInputDot = DotProduct( vecInput1, vecInput2 );
+
+ // only use within start/end time frame and, if set, active input range
+ if ( ( m_bActiveRange && (flInputDot < m_flInputMin || flInputDot > m_flInputMax ) ) )
+ continue;
+ }
+
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( flInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ flOutput *= *pOutput;
+ }
+ if ( ATTRIBUTES_WHICH_ARE_INTS & ( 1 << m_nFieldOutput ) )
+ {
+ *pOutput = int ( flOutput );
+ }
+ else
+ {
+ *pOutput = flOutput;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Remap CP to Scalar Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapCPtoScalar : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapCPtoScalar );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 1 << m_nFieldOutput;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nCPInput;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ m_nField = int (clamp (m_nField, 0, 2));
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nCPInput;
+ int m_nFieldOutput;
+ int m_nField;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+ float m_flStartTime;
+ float m_flEndTime;
+ bool m_bScaleInitialRange;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapCPtoScalar, "Remap Control Point to Scalar", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapCPtoScalar )
+DMXELEMENT_UNPACK_FIELD( "emitter lifetime start time (seconds)", "-1", float, m_flStartTime )
+DMXELEMENT_UNPACK_FIELD( "emitter lifetime end time (seconds)", "-1", float, m_flEndTime )
+DMXELEMENT_UNPACK_FIELD( "input control point number", "0", int, m_nCPInput )
+DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD( "input field 0-2 X/Y/Z","0", int, m_nField )
+DMXELEMENT_UNPACK_FIELD_USERDATA( "output field", "3", int, m_nFieldOutput, "intchoice particlefield_scalar" )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+DMXELEMENT_UNPACK_FIELD( "output is scalar of initial random range","0", bool, m_bScaleInitialRange )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapCPtoScalar )
+
+void C_OP_RemapCPtoScalar::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ const float *pCreationTime;
+ // clamp the result to 0 and 1 if it's alpha
+ float flMin=m_flOutputMin;
+ float flMax=m_flOutputMax;
+ if ( ATTRIBUTES_WHICH_ARE_0_TO_1 & ( 1 << m_nFieldOutput ) )
+ {
+ flMin = clamp(m_flOutputMin, 0.0f, 1.0f );
+ flMax = clamp(m_flOutputMax, 0.0f, 1.0f );
+ }
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nCPInput );
+
+ float flInput = vecControlPoint[m_nField];
+
+ // FIXME: SSE-ize
+ for( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ pCreationTime = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
+ // using raw creation time to map to emitter lifespan
+ float flLifeTime = *pCreationTime;
+
+ // only use within start/end time frame
+ if ( ( ( flLifeTime < m_flStartTime ) || ( flLifeTime >= m_flEndTime ) ) && ( ( m_flStartTime != -1.0f) && ( m_flEndTime != -1.0f) ) )
+ continue;
+
+
+ float *pOutput = pParticles->GetFloatAttributePtrForWrite( m_nFieldOutput, i );
+ float flOutput = RemapValClamped( flInput, m_flInputMin, m_flInputMax, flMin, flMax );
+ if ( m_bScaleInitialRange )
+ {
+ flOutput = *pOutput * flOutput;
+ }
+ if ( ATTRIBUTES_WHICH_ARE_INTS & ( 1 << m_nFieldOutput ) )
+ {
+ *pOutput = int ( flOutput );
+ }
+ else
+ {
+ *pOutput = flOutput;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Rotate Particle around axis
+//-----------------------------------------------------------------------------
+class C_OP_MovementRotateParticleAroundAxis : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_MovementRotateParticleAroundAxis );
+
+ Vector m_vecRotAxis;
+ float m_flRotRate;
+ int m_nCP;
+ bool m_bLocalSpace;
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK ;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return 1ULL << m_nCP;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef )
+ {
+ VectorNormalize( m_vecRotAxis );
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_MovementRotateParticleAroundAxis , "Movement Rotate Particle Around Axis", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MovementRotateParticleAroundAxis )
+DMXELEMENT_UNPACK_FIELD( "Rotation Axis", "0 0 1", Vector, m_vecRotAxis )
+DMXELEMENT_UNPACK_FIELD( "Rotation Rate", "180", float, m_flRotRate )
+DMXELEMENT_UNPACK_FIELD( "Control Point", "0", int, m_nCP )
+DMXELEMENT_UNPACK_FIELD( "Use Local Space", "0", bool, m_bLocalSpace )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_MovementRotateParticleAroundAxis )
+
+void C_OP_MovementRotateParticleAroundAxis::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ float flRotRate = m_flRotRate * pParticles->m_flDt;
+
+ matrix3x4_t matRot;
+
+ Vector vecRotAxis = m_vecRotAxis;
+
+ if ( m_bLocalSpace )
+ {
+ matrix3x4_t matLocalCP;
+ pParticles->GetControlPointTransformAtCurrentTime( m_nCP, &matLocalCP );
+ VectorRotate( m_vecRotAxis, matLocalCP, vecRotAxis );
+ }
+
+ MatrixBuildRotationAboutAxis ( vecRotAxis, flRotRate, matRot );
+
+ Vector vecCPPos = pParticles->GetControlPointAtCurrentTime( m_nCP );
+
+ FourVectors fvCPPos;
+ fvCPPos.DuplicateVector( vecCPPos );
+
+ fltx4 fl4Strength = ReplicateX4( flStrength );
+
+ C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
+
+ int nCtr = pParticles->m_nPaddedActiveParticles;
+ do
+ {
+ FourVectors fvCurPos = *pXYZ;
+ fvCurPos -= fvCPPos;
+ FourVectors fvPrevPos = *pPrevXYZ;
+ fvPrevPos -= fvCPPos;
+
+ fvCurPos.RotateBy( matRot );
+ fvPrevPos.RotateBy( matRot );
+
+ fvCurPos += fvCPPos;
+ fvCurPos -= *pXYZ;
+ fvCurPos *= fl4Strength;
+ *pXYZ += fvCurPos;
+ fvPrevPos += fvCPPos;
+ fvPrevPos -= *pPrevXYZ;
+ fvPrevPos *= fl4Strength;
+ *pPrevXYZ += fvPrevPos;
+
+ ++pXYZ;
+ ++pPrevXYZ;
+ } while ( --nCtr );
+
+};
+
+//-----------------------------------------------------------------------------
+// Remap Speed to CP Operator
+//-----------------------------------------------------------------------------
+class C_OP_RemapSpeedtoCP : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RemapSpeedtoCP );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return 0;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ return ( 1ULL << m_nInControlPointNumber ) | ( 1ULL << m_nOutControlPointNumber );
+ }
+
+ bool ShouldRunBeforeEmitters( void ) const
+ {
+ return true;
+ }
+ virtual void InitParams(CParticleSystemDefinition *pDef )
+ {
+ // Safety for bogus input->output feedback loop
+ if ( m_nInControlPointNumber == m_nOutControlPointNumber )
+ m_nOutControlPointNumber = -1;
+ }
+
+ virtual void Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const;
+
+ int m_nInControlPointNumber;
+ int m_nOutControlPointNumber;
+ int m_nField;
+ float m_flInputMin;
+ float m_flInputMax;
+ float m_flOutputMin;
+ float m_flOutputMax;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RemapSpeedtoCP, "Remap CP Speed to CP", OPERATOR_GENERIC );
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RemapSpeedtoCP )
+DMXELEMENT_UNPACK_FIELD( "input control point", "0", int, m_nInControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "input minimum","0", float, m_flInputMin )
+DMXELEMENT_UNPACK_FIELD( "input maximum","1", float, m_flInputMax )
+DMXELEMENT_UNPACK_FIELD( "output control point", "-1", int, m_nOutControlPointNumber )
+DMXELEMENT_UNPACK_FIELD( "Output field 0-2 X/Y/Z","0", int, m_nField )
+DMXELEMENT_UNPACK_FIELD( "output minimum","0", float, m_flOutputMin )
+DMXELEMENT_UNPACK_FIELD( "output maximum","1", float, m_flOutputMax )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RemapSpeedtoCP );
+
+void C_OP_RemapSpeedtoCP::Operate( CParticleCollection *pParticles, float flStrength, void *pContext ) const
+{
+ if ( m_nOutControlPointNumber >= 0 )
+ {
+ Vector vecPrevPos;
+ pParticles->GetControlPointAtPrevTime( m_nInControlPointNumber, &vecPrevPos );
+ Vector vecDelta;
+ vecDelta = pParticles->GetControlPointAtCurrentTime( m_nInControlPointNumber ) - vecPrevPos;
+ float flSpeed = vecDelta.Length() / pParticles->m_flPreviousDt;
+ float flOutput = RemapValClamped( flSpeed, m_flInputMin, m_flInputMax, m_flOutputMin, m_flOutputMax );
+
+ Vector vecControlPoint = pParticles->GetControlPointAtCurrentTime( m_nOutControlPointNumber );
+ vecControlPoint[m_nField] = flOutput;
+ pParticles->SetControlPoint( m_nOutControlPointNumber, vecControlPoint );
+ }
+}
+
+
+void AddBuiltInParticleOperators( void )
+{
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_BasicMovement );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Decay );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VelocityDecay );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeAndKill );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeIn );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_FadeOut );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Spin );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SpinUpdate );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SpinYaw );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OrientTo2dDirection );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Orient2DRelToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_InterpolateRadius );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ColorInterpolate );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OscillateScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_OscillateVector );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DampenToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_PositionLock );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_LockToBone );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DistanceBetweenCPs );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_DistanceToCP );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointToPlayer );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointToCenter );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetChildControlPoints );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_SetControlPointPositions );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_PlaneCull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ModelCull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Cull );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_ControlpointLight );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_Noise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VectorNoise );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_VelocityMatchingForce );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MaxVelocity );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MaintainSequentialPath );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapDotProductToScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapCPtoScalar );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_MovementRotateParticleAroundAxis );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_OPERATOR, C_OP_RemapSpeedtoCP );
+}
+
diff --git a/particles/builtin_particle_render_ops.cpp b/particles/builtin_particle_render_ops.cpp
new file mode 100644
index 0000000..966b155
--- /dev/null
+++ b/particles/builtin_particle_render_ops.cpp
@@ -0,0 +1,2416 @@
+//========= 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 "tier2/beamsegdraw.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "psheet.h"
+#include "tier0/vprof.h"
+
+#ifdef USE_BLOBULATOR
+// TODO: These should be in public by the time the SDK ships
+ #include "../common/blobulator/Implicit/ImpDefines.h"
+ #include "../common/blobulator/Implicit/ImpRenderer.h"
+ #include "../common/blobulator/Implicit/ImpTiler.h"
+ #include "../common/blobulator/Implicit/UserFunctions.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Vertex instancing (1 vert submitted per particle, duplicated to 4 (a quad) on the GPU) is supported only on 360
+const bool bUseInstancing = IsX360();
+
+
+//-----------------------------------------------------------------------------
+// Utility method to compute the max # of particles per batch
+//-----------------------------------------------------------------------------
+static inline int GetMaxParticlesPerBatch( IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bWithInstancing )
+{
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+
+ if ( bWithInstancing )
+ return nMaxVertices;
+ else
+ return min( (nMaxVertices / 4), (nMaxIndices / 6) );
+}
+
+void SetupParticleVisibility( CParticleCollection *pParticles, CParticleVisibilityData *pVisibilityData, const CParticleVisibilityInputs *pVisibilityInputs, int *nQueryHandle )
+{
+ float flScale = pVisibilityInputs->m_flProxyRadius;
+ Vector vecOrigin;
+ /*
+ if ( pVisibilityInputs->m_bUseBBox )
+ {
+ Vector vecMinBounds;
+ Vector vecMaxBounds;
+ Vector mins;
+ Vector maxs;
+
+ pParticles->GetBounds( &vecMinBounds, &vecMaxBounds );
+
+ vecOrigin = ( ( vecMinBounds + vecMaxBounds ) / 2 );
+
+ Vector vecBounds = ( vecMaxBounds - vecMinBounds );
+
+ flScale = ( max(vecBounds.x, max (vecBounds.y, vecBounds.z) ) * pVisibilityInputs->m_flBBoxScale );
+ }
+ if ( pVisibilityInputs->m_nCPin >= 0 )
+ {
+ vecOrigin = pParticles->GetControlPointAtCurrentTime( pVisibilityInputs->m_nCPin );
+ }
+ */
+ vecOrigin = pParticles->GetControlPointAtCurrentTime( pVisibilityInputs->m_nCPin );
+ float flVisibility = g_pParticleSystemMgr->Query()->GetPixelVisibility( nQueryHandle, vecOrigin, flScale );
+
+ pVisibilityData->m_flAlphaVisibility = RemapValClamped( flVisibility, pVisibilityInputs->m_flInputMin,
+ pVisibilityInputs->m_flInputMax, pVisibilityInputs->m_flAlphaScaleMin, pVisibilityInputs->m_flAlphaScaleMax );
+ pVisibilityData->m_flRadiusVisibility = RemapValClamped( flVisibility, pVisibilityInputs->m_flInputMin,
+ pVisibilityInputs->m_flInputMax, pVisibilityInputs->m_flRadiusScaleMin, pVisibilityInputs->m_flRadiusScaleMax );
+
+ pVisibilityData->m_flCameraBias = pVisibilityInputs->m_flCameraBias;
+}
+
+static SheetSequenceSample_t s_DefaultSheetSequence =
+{
+ {
+ { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }, // SequenceSampleTextureCoords_t image 0
+ { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f } // SequenceSampleTextureCoords_t image 1
+ },
+ 1.0f // m_fBlendFactor
+};
+
+
+class C_OP_RenderPoints : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderPoints );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+
+ struct C_OP_RenderPointsContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderPointsContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderPointsContext_t *pCtx = reinterpret_cast<C_OP_RenderPointsContext_t *>( pContext );
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderPoints, "render_points", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderPoints )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderPoints )
+
+void C_OP_RenderPoints::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderPointsContext_t *pCtx = reinterpret_cast<C_OP_RenderPointsContext_t *>( pContext );
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ int nParticles;
+ const ParticleRenderData_t *pRenderList =
+ pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ pRenderContext->Bind( pMaterial );
+
+ CMeshBuilder meshBuilder;
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ while ( nParticles )
+ {
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+
+ int nParticlesInBatch = min( nMaxVertices, nParticles );
+ nParticles -= nParticlesInBatch;
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch );
+
+ meshBuilder.Begin( pMesh, MATERIAL_POINTS, nParticlesInBatch );
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pRenderList)->m_nIndex;
+ int nIndex = ( hParticle / 4 ) * xyz_stride;
+ int nOffset = hParticle & 0x3;
+ meshBuilder.Position3f( SubFloat( xyz[nIndex], nOffset ), SubFloat( xyz[nIndex+1], nOffset ), SubFloat( xyz[nIndex+2], nOffset ) );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sprite Rendering
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Utility struct to help with sprite rendering
+//-----------------------------------------------------------------------------
+struct SpriteRenderInfo_t
+{
+ size_t m_nXYZStride;
+ const fltx4 *m_pXYZ;
+ size_t m_nRotStride;
+ const fltx4 *m_pRot;
+ size_t m_nYawStride;
+ const fltx4 *m_pYaw;
+ size_t m_nRGBStride;
+ const fltx4 *m_pRGB;
+ size_t m_nCreationTimeStride;
+ const fltx4 *m_pCreationTimeStamp;
+ size_t m_nSequenceStride;
+ const fltx4 *m_pSequenceNumber;
+ size_t m_nSequence1Stride;
+ const fltx4 *m_pSequence1Number;
+ float m_flAgeScale;
+ float m_flAgeScale2;
+
+ CSheet *m_pSheet;
+ int m_nVertexOffset;
+ CParticleCollection *m_pParticles;
+
+ void Init( CParticleCollection *pParticles, int nVertexOffset, float flAgeScale, float flAgeScale2, CSheet *pSheet )
+ {
+ m_pParticles = pParticles;
+ m_pXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &m_nXYZStride );
+ m_pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &m_nRotStride );
+ m_pYaw = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_YAW, &m_nYawStride );
+ m_pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &m_nRGBStride );
+ m_pCreationTimeStamp = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &m_nCreationTimeStride );
+ m_pSequenceNumber = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, &m_nSequenceStride );
+ m_pSequence1Number = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, &m_nSequence1Stride );
+ m_flAgeScale = flAgeScale;
+ m_flAgeScale2 = flAgeScale2;
+ m_pSheet = pSheet;
+ m_nVertexOffset = nVertexOffset;
+ }
+};
+
+class C_OP_RenderSprites : public C_OP_RenderPoints
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderSprites );
+
+ struct C_OP_RenderSpritesContext_t
+ {
+ unsigned int m_nOrientationVarToken;
+ unsigned int m_nOrientationMatrixVarToken;
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderSpritesContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ pCtx->m_nOrientationVarToken = 0;
+ pCtx->m_nOrientationMatrixVarToken = 0;
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ if ( m_nOrientationControlPoint >= 0 )
+ return 1ULL << m_nOrientationControlPoint;
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_ROTATION_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+ void RenderSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const;
+ void RenderTwoSequenceSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const;
+
+ void RenderNonSpriteCardCameraFacing( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const;
+
+ void RenderNonSpriteCardZRotating( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList ) const;
+ void RenderNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const;
+ void RenderUnsortedNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ void RenderNonSpriteCardOriented( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList, bool bUseYaw ) const;
+ void RenderNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bUseYaw ) const;
+ void RenderUnsortedNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ // cycles per second
+ float m_flAnimationRate;
+ float m_flAnimationRate2;
+ bool m_bFitCycleToLifetime;
+ bool m_bAnimateInFPS;
+ int m_nOrientationType;
+
+
+ int m_nOrientationControlPoint;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderSprites, "render_animated_sprites", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderSprites )
+ DMXELEMENT_UNPACK_FIELD( "animation rate", ".1", float, m_flAnimationRate )
+ DMXELEMENT_UNPACK_FIELD( "animation_fit_lifetime", "0", bool, m_bFitCycleToLifetime )
+ DMXELEMENT_UNPACK_FIELD( "orientation_type", "0", int, m_nOrientationType )
+ DMXELEMENT_UNPACK_FIELD( "orientation control point", "-1", int, m_nOrientationControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "second sequence animation rate", "0", float, m_flAnimationRate2 )
+ DMXELEMENT_UNPACK_FIELD( "use animation rate as FPS", "0", bool, m_bAnimateInFPS )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderSprites )
+
+void C_OP_RenderSprites::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+{
+}
+
+const SheetSequenceSample_t *GetSampleForSequence( CSheet *pSheet, float flCreationTime, float flCurTime, float flAgeScale, int nSequence )
+{
+ if ( pSheet == NULL )
+ return NULL;
+
+ if ( pSheet->m_nNumFrames[nSequence] == 1 )
+ return (const SheetSequenceSample_t *) &pSheet->m_pSamples[nSequence][0];
+
+ float flAge = flCurTime - flCreationTime;
+
+ flAge *= flAgeScale;
+ unsigned int nFrame = flAge;
+ if ( pSheet->m_bClamp[nSequence] )
+ {
+ nFrame = min( nFrame, (unsigned int)SEQUENCE_SAMPLE_COUNT-1 );
+ }
+ else
+ {
+ nFrame &= SEQUENCE_SAMPLE_COUNT-1;
+ }
+
+ return (const SheetSequenceSample_t *) &pSheet->m_pSamples[nSequence][nFrame];
+}
+
+int C_OP_RenderSprites::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle,
+ int nRemainingVertices, int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ int nMaxParticles = ( (nRemainingVertices / 4) > (nRemainingIndices / 6) ) ? nRemainingIndices / 6 : nRemainingVertices / 4;
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+ if ( nParticleCount > nMaxParticles )
+ {
+ nParticleCount = nMaxParticles;
+ }
+ *pVertsUsed = nParticleCount * 4;
+ *pIndicesUsed = nParticleCount * 6;
+ return nParticleCount;
+}
+
+void C_OP_RenderSprites::RenderNonSpriteCardCameraFacing( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const
+{
+
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // generate the sort list before this code starts messing with the matrices
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = (&pCtx->m_VisibilityData )->m_flCameraBias;
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ VMatrix tempView;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->GetMatrix(MATERIAL_VIEW, &tempView);
+
+ // Force the user clip planes to use the old view matrix
+ pRenderContext->EnableUserClipTransformOverride( true );
+ pRenderContext->UserClipTransform( tempView );
+
+ // The particle renderers want to do things in camera space
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ size_t rot_stride;
+ const fltx4 *pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+
+ size_t rgb_stride;
+ const fltx4 *pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &rgb_stride );
+
+ size_t ct_stride;
+ const fltx4 *pCreationTimeStamp = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &ct_stride );
+
+ size_t seq_stride;
+ const fltx4 *pSequenceNumber = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, &seq_stride );
+
+ size_t ld_stride;
+ const fltx4 *pLifeDuration = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, &ld_stride );
+
+
+ float flAgeScale;
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+
+ CSheet *pSheet = pParticles->m_Sheet();
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nParticlesInBatch );
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ continue;
+
+ int nColorIndex = nGroup * rgb_stride;
+ float r = SubFloat( pRGB[nColorIndex], nOffset );
+ float g = SubFloat( pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+
+ int nXYZIndex = nGroup * xyz_stride;
+ Vector vecWorldPos( SubFloat( xyz[ nXYZIndex ], nOffset ), SubFloat( xyz[ nXYZIndex+1 ], nOffset ), SubFloat( xyz[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewPos;
+ Vector3DMultiplyPosition( tempView, vecWorldPos, vecViewPos );
+
+ if ( !IsFinite( vecViewPos.x ) )
+ continue;
+
+ float rot = SubFloat( pRot[ nGroup * rot_stride ], nOffset );
+ float ca = (float)cos(rot);
+ float sa = (float)sin(rot);
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( pSheet )
+ {
+ if ( m_bFitCycleToLifetime )
+ {
+ float flLifetime = SubFloat( pLifeDuration[ nGroup * ld_stride ], nOffset );
+ flAgeScale = ( flLifetime > 0.0f ) ? ( 1.0f / flLifetime ) * SEQUENCE_SAMPLE_COUNT : 0.0f;
+ }
+ else
+ {
+ flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ if ( m_bAnimateInFPS )
+ {
+ int nSequence = SubFloat( pSequenceNumber[ nGroup * seq_stride ], nOffset );
+ flAgeScale = flAgeScale / pSheet->m_flFrameSpan[nSequence];
+ }
+ }
+ pSample = GetSampleForSequence( pSheet,
+ SubFloat( pCreationTimeStamp[ nGroup * ct_stride ], nOffset ),
+ pParticles->m_flCurTime,
+ flAgeScale,
+ SubFloat( pSequenceNumber[ nGroup * seq_stride ], nOffset ) );
+ }
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+
+ meshBuilder.Position3f( vecViewPos.x + (-ca + sa) * rad, vecViewPos.y + (-sa - ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (-ca - sa) * rad, vecViewPos.y + (-sa + ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (ca - sa) * rad, vecViewPos.y + (sa + ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (ca + sa) * rad, vecViewPos.y + (sa - ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+
+ pRenderContext->EnableUserClipTransformOverride( false );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardZRotating( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+
+ float ca = (float)cos(-rot);
+ float sa = (float)sin(-rot);
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCameraPos - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewToPos;
+ VectorSubtract( vecWorldPos, vecCameraPos, vecViewToPos );
+ float flLength = vecViewToPos.Length();
+ if ( flLength < rad / 2 )
+ return;
+
+ Vector vecUp( 0, 0, 1 );
+ Vector vecRight;
+ CrossProduct( vecUp, vecCameraPos, vecRight );
+ VectorNormalize( vecRight );
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ vecRight *= rad;
+
+ float x, y;
+ Vector vecCorner;
+
+ x = - ca - sa; y = - ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca + sa; y = + ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca + sa; y = + ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca - sa; y = - ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderNonSpriteCardZRotating( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList );
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSprites::RenderUnsortedNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderNonSpriteCardZRotating( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardOriented(
+ CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList, bool bUseYaw ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+
+ float ca = (float)cos(-rot);
+ float sa = (float)sin(-rot);
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCameraPos - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewToPos;
+ VectorSubtract( vecWorldPos, vecCameraPos, vecViewToPos );
+ float flLength = vecViewToPos.Length();
+ if ( flLength < rad / 2 )
+ return;
+
+ Vector vecNormal, vecRight, vecUp;
+ if ( m_nOrientationControlPoint < 0 )
+ {
+ vecNormal.Init( 0, 0, 1 );
+ vecRight.Init( 1, 0, 0 );
+ vecUp.Init( 0, -1, 0 );
+ }
+ else
+ {
+ info.m_pParticles->GetControlPointOrientationAtCurrentTime(
+ m_nOrientationControlPoint, &vecRight, &vecUp, &vecNormal );
+ }
+
+ if ( bUseYaw )
+ {
+ float yaw = SubFloat( info.m_pYaw[nGroup * info.m_nYawStride], nOffset );
+
+ if ( yaw != 0.0f )
+ {
+ Vector particleRight = Vector( 1, 0, 0 );
+ yaw = RAD2DEG( yaw ); // I hate you source (VectorYawRotate will undo this)
+ matrix3x4_t matRot;
+ MatrixBuildRotationAboutAxis( vecUp, yaw, matRot );
+ VectorRotate( vecRight, matRot, particleRight );
+ vecRight = particleRight;
+ }
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ vecRight *= rad;
+ vecUp *= rad;
+
+ float x, y;
+ Vector vecCorner;
+
+ x = + ca - sa; y = - ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca + sa; y = + ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca + sa; y = + ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca - sa; y = - ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+void C_OP_RenderSprites::RenderNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bUseYaw ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+
+ for( int i = 0; i < nParticlesInBatch; i++)
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderNonSpriteCardOriented( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList, bUseYaw );
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSprites::RenderUnsortedNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderNonSpriteCardOriented( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList, false );
+ }
+}
+
+void C_OP_RenderSprites::RenderSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= -1e-6f) && (g >= -1e-6f) && (b >= -1e-6f) );
+ if ( !HushAsserts() )
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+ unsigned char ac = pSortList->m_nAlpha;
+
+ float rad = pSortList->m_flRadius;
+ if ( !IsFinite( rad ) )
+ {
+ return;
+ }
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+ float yaw = SubFloat( info.m_pYaw[ nGroup * info.m_nYawStride ], nOffset );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos;
+ vecWorldPos.x = SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset );
+ vecWorldPos.y = SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset );
+ vecWorldPos.z = SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset );
+
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = *pCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ float flAgeScale = info.m_flAgeScale;
+// if ( m_bFitCycleToLifetime )
+// {
+// float flLifetime = SubFloat( pLifeDuration[ nGroup * ld_stride ], nOffset );
+// flAgeScale = ( flLifetime > 0.0f ) ? ( 1.0f / flLifetime ) * SEQUENCE_SAMPLE_COUNT : 0.0f;
+// }
+ if ( m_bAnimateInFPS )
+ {
+ int nSequence = SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset );
+ flAgeScale = flAgeScale / info.m_pParticles->m_Sheet()->m_flFrameSpan[nSequence];
+ }
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
+
+ // Submit 1 (instanced) or 4 (non-instanced) verts (if we're instancing, we don't produce indices either)
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ // FIXME: change the vertex decl (remove texcoord3/cornerid) if instancing - need to adjust elements beyond texcoord3 down, though
+ if ( !bUseInstancing )
+ meshBuilder.TexCoord2f( 3, 0, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ if ( !bUseInstancing )
+ {
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 0, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+ }
+}
+
+void C_OP_RenderSprites::RenderTwoSequenceSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+ unsigned char ac = pSortList->m_nAlpha;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+ float yaw = SubFloat( info.m_pYaw[ nGroup * info.m_nYawStride ], nOffset );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos;
+ vecWorldPos.x = SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset );
+ vecWorldPos.y = SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset );
+ vecWorldPos.z = SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset );
+
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = *pCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ const SheetSequenceSample_t *pSample1 = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ pSample1 = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale2,
+ SubFloat( info.m_pSequence1Number[ nGroup * info.m_nSequence1Stride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
+ const SequenceSampleTextureCoords_t *pSample1Frame = &(pSample1->m_TextureCoordData[0]);
+
+ // Submit 1 (instanced) or 4 (non-instanced) verts (if we're instancing, we don't produce indices either)
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ // FIXME: change the vertex decl (remove texcoord3/cornerid) if instancing - need to adjust elements beyond texcoord3 down, though
+ if ( ! bUseInstancing )
+ meshBuilder.TexCoord2f( 3, 0, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ if ( !bUseInstancing )
+ {
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 0, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+ IMaterialVar* pVar = pMaterial->FindVarFast( "$orientation", &pCtx->m_nOrientationVarToken );
+ if ( pVar )
+ {
+ pVar->SetIntValue( m_nOrientationType );
+ }
+
+ pRenderContext->Bind( pMaterial );
+
+ if ( !pMaterial->IsSpriteCard() )
+ {
+ switch( m_nOrientationType )
+ {
+ case 0:
+ RenderNonSpriteCardCameraFacing( pParticles, pContext, pRenderContext, pMaterial );
+ break;
+
+ case 1:
+ RenderNonSpriteCardZRotating( pParticles, pContext, pRenderContext, pMaterial );
+ break;
+
+ case 2:
+ RenderNonSpriteCardOriented( pParticles, pContext, pRenderContext, pMaterial, false );
+ break;
+
+ case 3:
+ RenderNonSpriteCardOriented( pParticles, pContext, pRenderContext, pMaterial, true );
+ break;
+ }
+
+ return;
+ }
+
+ if ( m_nOrientationType == 2 )
+ {
+ pVar = pMaterial->FindVarFast( "$orientationMatrix", &pCtx->m_nOrientationMatrixVarToken );
+ if ( pVar )
+ {
+ VMatrix mat;
+ if ( m_nOrientationControlPoint < 0 )
+ {
+ MatrixSetIdentity( mat );
+ }
+ else
+ {
+ pParticles->GetControlPointTransformAtCurrentTime( m_nOrientationControlPoint, &mat );
+ }
+ pVar->SetMatrixValue( mat );
+ }
+ }
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ float flAgeScale2 = m_flAnimationRate2 * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, flAgeScale2, pParticles->m_Sheet() );
+
+ MaterialPrimitiveType_t primType = bUseInstancing ? MATERIAL_INSTANCED_QUADS : MATERIAL_TRIANGLES;
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, bUseInstancing );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ int vertexCount = bUseInstancing ? nParticlesInBatch : nParticlesInBatch * 4;
+ int indexCount = bUseInstancing ? 0 : nParticlesInBatch * 6;
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+
+ if ( bUseInstancing )
+ {
+ g_pParticleSystemMgr->TallyParticlesRendered( vertexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ) );
+ meshBuilder.Begin( pMesh, primType, vertexCount );
+ }
+ else
+ {
+ g_pParticleSystemMgr->TallyParticlesRendered( vertexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ), indexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ) );
+ meshBuilder.Begin( pMesh, primType, vertexCount, indexCount );
+ }
+ info.m_nVertexOffset = 0;
+ if ( meshBuilder.TextureCoordinateSize( 5 ) ) // second sequence?
+ {
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderTwoSequenceSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+ }
+ else
+ {
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+
+void C_OP_RenderSprites::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ if ( !pParticles->m_pDef->GetMaterial()->IsSpriteCard() )
+ {
+ switch( m_nOrientationType )
+ {
+ case 0:
+ // FIXME: Implement! Requires removing MATERIAL_VIEW modification from sorted version
+ Warning( "C_OP_RenderSprites::RenderUnsorted: Attempting to use an unimplemented sprite renderer for system \"%s\"!\n",
+ pParticles->m_pDef->GetName() );
+// RenderUnsortedNonSpriteCardCameraFacing( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+
+ case 1:
+ RenderUnsortedNonSpriteCardZRotating( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+
+ case 2:
+ RenderUnsortedNonSpriteCardOriented( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+ }
+ return;
+ }
+
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ float flAgeScale2 = m_flAnimationRate2 * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, flAgeScale2, pParticles->m_Sheet() );
+
+ int hParticle = nFirstParticle;
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+}
+
+//
+//
+//
+//
+
+struct SpriteTrailRenderInfo_t : public SpriteRenderInfo_t
+{
+ size_t m_nPrevXYZStride;
+ const fltx4 *m_pPrevXYZ;
+ size_t length_stride;
+ const fltx4 *m_pLength;
+
+ const fltx4 *m_pCreationTime;
+ size_t m_nCreationTimeStride;
+
+
+ void Init( CParticleCollection *pParticles, int nVertexOffset, float flAgeScale, CSheet *pSheet )
+ {
+ SpriteRenderInfo_t::Init( pParticles, nVertexOffset, flAgeScale, 0, pSheet );
+ m_pParticles = pParticles;
+ m_pPrevXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &m_nPrevXYZStride );
+ m_pLength = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, &length_stride );
+ m_pCreationTime = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &m_nCreationTimeStride );
+ }
+};
+
+class C_OP_RenderSpritesTrail : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderSpritesTrail );
+
+ struct C_OP_RenderSpriteTrailContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderSpriteTrailContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK | PARTICLE_ATTRIBUTE_TRAIL_LENGTH_MASK;
+ }
+
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const ;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ void RenderSpriteTrail( CMeshBuilder &meshBuilder, SpriteTrailRenderInfo_t& info, int hParticle, const Vector &vecCameraPos, float flOODt, ParticleRenderData_t const *pSortlist ) const;
+
+ float m_flAnimationRate;
+ float m_flLengthFadeInTime;
+ float m_flMaxLength;
+ float m_flMinLength;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderSpritesTrail, "render_sprite_trail", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderSpritesTrail )
+ DMXELEMENT_UNPACK_FIELD( "animation rate", ".1", float, m_flAnimationRate )
+ DMXELEMENT_UNPACK_FIELD( "length fade in time", "0", float, m_flLengthFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "max length", "2000", float, m_flMaxLength )
+ DMXELEMENT_UNPACK_FIELD( "min length", "0", float, m_flMinLength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderSpritesTrail )
+
+int C_OP_RenderSpritesTrail::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle, int nRemainingVertices,
+ int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ int nMaxParticles = ( (nRemainingVertices / 4) > (nRemainingIndices / 6) ) ? nRemainingIndices / 6 : nRemainingVertices / 4;
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+ if ( nParticleCount > nMaxParticles )
+ {
+ nParticleCount = nMaxParticles;
+ }
+ *pVertsUsed = nParticleCount * 4;
+ *pIndicesUsed = nParticleCount * 6;
+ return nParticleCount;
+}
+
+void C_OP_RenderSpritesTrail::RenderSpriteTrail( CMeshBuilder &meshBuilder,
+ SpriteTrailRenderInfo_t& info, int hParticle,
+ const Vector &vecCameraPos, float flOODt, ParticleRenderData_t const *pSortList ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ // Setup our alpha
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ // Setup our colors
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= -1e-6f) && (g >= -1e-6f) && (b >= -1e-6f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ // Setup the scale and rotation
+ float rad = pSortList->m_flRadius;
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+
+ int nCreationTimeIndex = nGroup * info.m_nCreationTimeStride;
+ float flAge = info.m_pParticles->m_flCurTime - SubFloat( info.m_pCreationTimeStamp[ nCreationTimeIndex ], nOffset );
+
+ float flLengthScale = ( flAge >= m_flLengthFadeInTime ) ? 1.0 : ( flAge / m_flLengthFadeInTime );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ Vector vecViewPos = vecWorldPos;
+
+ // Get our screenspace last position
+ int nPrevXYZIndex = nGroup * info.m_nPrevXYZStride;
+ Vector vecPrevWorldPos( SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex ], nOffset ), SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex+1 ], nOffset ), SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex+2 ], nOffset ) );
+ Vector vecPrevViewPos = vecPrevWorldPos;
+
+ // Get the delta direction and find the magnitude, then scale the length by the desired length amount
+ Vector vecDelta;
+ VectorSubtract( vecPrevViewPos, vecViewPos, vecDelta );
+ float flMag = VectorNormalize( vecDelta );
+ float flLength = flLengthScale * flMag * flOODt * SubFloat( info.m_pLength[ nGroup * info.length_stride ], nOffset );
+ if ( flLength <= 0.0f )
+ return;
+
+ flLength = max( m_flMinLength, min( m_flMaxLength, flLength ) );
+
+ vecDelta *= flLength;
+
+ // Fade the width as the length fades to keep it at a square aspect ratio
+ if ( flLength < rad )
+ {
+ rad = flLength;
+ }
+
+ // Find our tangent direction which "fattens" the line
+ Vector vDirToBeam, vTangentY;
+ VectorSubtract( vecWorldPos, vecCameraPos, vDirToBeam );
+ CrossProduct( vDirToBeam, vecDelta, vTangentY );
+ VectorNormalizeFast( vTangentY );
+
+ // Calculate the verts we'll use as our points
+ Vector verts[4];
+ VectorMA( vecWorldPos, rad*0.5f, vTangentY, verts[0] );
+ VectorMA( vecWorldPos, -rad*0.5f, vTangentY, verts[1] );
+ VectorAdd( verts[0], vecDelta, verts[3] );
+ VectorAdd( verts[1], vecDelta, verts[2] );
+ Assert( verts[0].IsValid() && verts[1].IsValid() && verts[2].IsValid() && verts[3].IsValid() );
+
+ meshBuilder.Position3fv( verts[0].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[1].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[2].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[3].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+
+void C_OP_RenderSpritesTrail::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+ // Right now we only have a meshbuilder version!
+ if ( !HushAsserts() )
+ Assert( pMaterial->IsSpriteCard() == false );
+ if ( pMaterial->IsSpriteCard() )
+ return;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->Bind( pMaterial );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+
+ // Get the camera's worldspace position
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ SpriteTrailRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ float flOODt = ( pParticles->m_flDt != 0.0f ) ? ( 1.0f / pParticles->m_flDt ) : 1.0f;
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderSpriteTrail( meshBuilder, info, hParticle, vecCameraPos, flOODt, pSortList );
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSpritesTrail::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteTrailRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ float flOODt = ( pParticles->m_flDt != 0.0f ) ? ( 1.0f / pParticles->m_flDt ) : 1.0f;
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderSpriteTrail( meshBuilder, info, hParticle, vecCameraPos, flOODt, pSortList );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Rope renderer
+//
+//-----------------------------------------------------------------------------
+struct RopeRenderInfo_t
+{
+ size_t m_nXYZStride;
+ const fltx4 *m_pXYZ;
+ size_t m_nRadStride;
+ const fltx4 *m_pRadius;
+ size_t m_nRGBStride;
+ const fltx4 *m_pRGB;
+ size_t m_nAlphaStride;
+ const fltx4 *m_pAlpha;
+ CParticleCollection *m_pParticles;
+
+ void Init( CParticleCollection *pParticles )
+ {
+ m_pParticles = pParticles;
+ m_pXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &m_nXYZStride );
+ m_pRadius = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_RADIUS, &m_nRadStride );
+ m_pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &m_nRGBStride );
+ m_pAlpha = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ALPHA, &m_nAlphaStride );
+ }
+
+ void GenerateSeg( int hParticle, BeamSeg_t& seg )
+ {
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nXYZIndex = nGroup * m_nXYZStride;
+ int nColorIndex = nGroup * m_nRGBStride;
+ seg.m_vPos.Init( SubFloat( m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ seg.m_vColor.Init( SubFloat( m_pRGB[ nColorIndex ], nOffset ), SubFloat( m_pRGB[ nColorIndex+1 ], nOffset ), SubFloat( m_pRGB[nColorIndex+2], nOffset ) );
+ seg.m_flAlpha = SubFloat( m_pAlpha[ nGroup * m_nAlphaStride ], nOffset );
+ seg.m_flWidth = SubFloat( m_pRadius[ nGroup * m_nRadStride ], nOffset );
+ }
+};
+
+
+struct RenderRopeContext_t
+{
+ float m_flRenderedRopeLength;
+};
+
+class C_OP_RenderRope : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderRope );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ RenderRopeContext_t *pCtx = reinterpret_cast<RenderRopeContext_t *>( pContext );
+ pCtx->m_flRenderedRopeLength = false;
+ float *pSubdivList = (float*)( pCtx + 1 );
+ for ( int iSubdiv = 0; iSubdiv < m_nSubdivCount; iSubdiv++ )
+ {
+ pSubdivList[iSubdiv] = (float)iSubdiv / (float)m_nSubdivCount;
+ }
+
+ // NOTE: Has to happen here, and not in InitParams, since the material isn't set up yet
+ const_cast<C_OP_RenderRope*>( this )->m_flTextureScale = 1.0f / ( pParticles->m_pDef->GetMaterial()->GetMappingHeight() * m_flTexelSizeInUnits );
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( RenderRopeContext_t ) + m_nSubdivCount * sizeof(float);
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_nSubdivCount <= 0 )
+ {
+ m_nSubdivCount = 1;
+ }
+ if ( m_flTexelSizeInUnits <= 0 )
+ {
+ m_flTexelSizeInUnits = 1.0f;
+ }
+ m_flTStep = 1.0 / m_nSubdivCount;
+ }
+
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderSpriteCard( CParticleCollection *pParticles, void *pContext, IMaterial *pMaterial ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ // We connect neighboring particle instances to each other, so if the order isn't maintained we will have a particle that jumps
+ // back to the wrong place and look terrible.
+ virtual bool RequiresOrderInvariance( void ) const OVERRIDE
+ {
+ return true;
+ }
+
+ int m_nSubdivCount;
+
+ float m_flTexelSizeInUnits;
+ float m_flTextureScale;
+ float m_flTextureScrollRate;
+ float m_flTStep;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderRope, "render_rope", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RenderRope )
+ DMXELEMENT_UNPACK_FIELD( "subdivision_count", "3", int, m_nSubdivCount )
+ DMXELEMENT_UNPACK_FIELD( "texel_size", "4.0f", float, m_flTexelSizeInUnits )
+ DMXELEMENT_UNPACK_FIELD( "texture_scroll_rate", "0.0f", float, m_flTextureScrollRate )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderRope )
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of particles to render
+//-----------------------------------------------------------------------------
+int C_OP_RenderRope::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ if ( ( nFirstParticle >= pParticles->m_nActiveParticles - 1 ) || ( pParticles->m_nActiveParticles <= 1 ) )
+ {
+ *pVertsUsed = 0;
+ *pIndicesUsed = 0;
+ return 0;
+ }
+
+ // NOTE: This is only true for particles *after* the first particle.
+ // First particle takes 2 verts, no indices.
+ int nVertsPerParticle = 2 * m_nSubdivCount;
+ int nIndicesPerParticle = 6 * m_nSubdivCount;
+
+ // Subtract 2 is because the first particle uses an extra pair of vertices
+ int nMaxParticleCount = 1 + ( nRemainingVertices - 2 ) / nVertsPerParticle;
+ int nMaxParticleCount2 = nRemainingIndices / nIndicesPerParticle;
+ if ( nMaxParticleCount > nMaxParticleCount2 )
+ {
+ nMaxParticleCount = nMaxParticleCount2;
+ }
+
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+
+ // We can't choose a max particle count so that we only have 1 particle to render next time
+ if ( nMaxParticleCount == nParticleCount - 1 )
+ {
+ --nMaxParticleCount;
+ Assert( nMaxParticleCount > 0 );
+ }
+
+ if ( nParticleCount > nMaxParticleCount )
+ {
+ nParticleCount = nMaxParticleCount;
+ }
+
+ *pVertsUsed = ( nParticleCount - 1 ) * m_nSubdivCount * 2 + 2;
+ *pIndicesUsed = nParticleCount * m_nSubdivCount * 6;
+ return nParticleCount;
+}
+
+
+#define OUTPUT_2SPLINE_VERTS( t ) \
+ meshBuilder.Color4ub( FastFToC( vecColor.x ), FastFToC( vecColor.y), FastFToC( vecColor.z), FastFToC( vecColor.w ) ); \
+ meshBuilder.Position3f( (t), flU, 0 ); \
+ meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \
+ meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \
+ meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \
+ meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \
+ meshBuilder.AdvanceVertex(); \
+ meshBuilder.Color4ub( FastFToC( vecColor.x ), FastFToC( vecColor.y), FastFToC( vecColor.z), FastFToC( vecColor.w ) ); \
+ meshBuilder.Position3f( (t), flU, 1 ); \
+ meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \
+ meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \
+ meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \
+ meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \
+ meshBuilder.AdvanceVertex();
+
+void C_OP_RenderRope::RenderSpriteCard( CParticleCollection *pParticles, void *pContext, IMaterial *pMaterial ) const
+{
+ int nParticles = pParticles->m_nActiveParticles;
+
+ int nSegmentsToRender = nParticles - 1;
+ if ( ! nSegmentsToRender )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+
+ int nNumIndicesPerSegment = 6 * m_nSubdivCount;
+ int nNumVerticesPerSegment = 2 * m_nSubdivCount;
+
+ int nNumSegmentsPerBatch = min( ( nMaxVertices - 2 )/nNumVerticesPerSegment,
+ ( nMaxIndices ) / nNumIndicesPerSegment );
+
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, 0 );
+ const float *pColor = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_TINT_RGB, 0 );
+
+ const float *pRadius = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_RADIUS, 0 );
+ const float *pAlpha = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_ALPHA, 0 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+
+ int nNumSegmentsIWillRenderPerBatch = min( nNumSegmentsPerBatch, nSegmentsToRender );
+
+ bool bFirstPoint = true;
+
+ float flTexOffset = m_flTextureScrollRate * pParticles->m_flCurTime;
+ float flU = flTexOffset;
+
+ // initialize first spline segment
+ Vector4D vecP1( pXYZ[0], pXYZ[4], pXYZ[8], pRadius[0] );
+ Vector4D vecP2( pXYZ[1], pXYZ[5], pXYZ[9], pRadius[1] );
+ Vector4D vecP0 = vecP1;
+ Vector4D vecColor( pColor[0], pColor[4], pColor[8], pAlpha[0] );
+ Vector4D vecDelta = vecP2;
+ vecDelta -= vecP1;
+ vecP0 -= vecDelta;
+
+ Vector4D vecP3;
+
+ if ( nParticles < 3 )
+ {
+ vecP3 = vecP2;
+ vecP3 += vecDelta;
+ }
+ else
+ {
+ vecP3.Init( pXYZ[2], pXYZ[6], pXYZ[10], pRadius[2] );
+ }
+ int nPnt = 3;
+ int nCurIDX = 0;
+
+ int nSegmentsAvailableInBuffer = nNumSegmentsIWillRenderPerBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment * 3, nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch * 3 );
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES,
+ 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment,
+ nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch );
+
+
+
+ float flDUScale = ( m_flTStep * m_flTexelSizeInUnits );
+ float flT = 0;
+
+ do
+ {
+ if ( ! nSegmentsAvailableInBuffer )
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+
+ g_pParticleSystemMgr->TallyParticlesRendered( 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment * 3, nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch * 3 );
+
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment,
+ nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch );
+
+ // copy the last emitted points
+ OUTPUT_2SPLINE_VERTS( flT );
+ nSegmentsAvailableInBuffer = nNumSegmentsIWillRenderPerBatch;
+ nCurIDX = 0;
+ }
+ nSegmentsAvailableInBuffer--;
+ flT = 0.;
+ float flDu = flDUScale * ( vecP2.AsVector3D() - vecP1.AsVector3D() ).Length();
+ for( int nSlice = 0 ; nSlice < m_nSubdivCount; nSlice++ )
+ {
+ OUTPUT_2SPLINE_VERTS( flT );
+ flT += m_flTStep;
+ flU += flDu;
+ if ( ! bFirstPoint )
+ {
+ meshBuilder.FastIndex( nCurIDX );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+3 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ nCurIDX += 2;
+ }
+ bFirstPoint = false;
+ }
+ // next segment
+ if ( nSegmentsToRender > 1 )
+ {
+ vecP0 = vecP1;
+ vecP1 = vecP2;
+ vecP2 = vecP3;
+ pRadius = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_RADIUS, nPnt );
+ pAlpha = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_ALPHA, nPnt -2 );
+ vecColor.Init( pColor[0], pColor[4], pColor[8], pAlpha[0] );
+
+ if ( nPnt < nParticles )
+ {
+ pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, nPnt );
+ vecP3.Init( pXYZ[0], pXYZ[4], pXYZ[8], pRadius[0] );
+ nPnt++;
+ }
+ else
+ {
+ // fake last point by extrapolating
+ vecP3 += vecP2;
+ vecP3 -= vecP1;
+ }
+ }
+ } while( --nSegmentsToRender );
+
+ // output last piece
+ OUTPUT_2SPLINE_VERTS( 1.0 );
+ meshBuilder.FastIndex( nCurIDX );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+3 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Renders particles, sorts them (?)
+//-----------------------------------------------------------------------------
+void C_OP_RenderRope::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ // FIXME: What does this even mean? Ropes can't really be sorted.
+
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+ if ( pMaterial->IsSpriteCard() )
+ {
+ RenderSpriteCard( pParticles, pContext, pMaterial );
+ return;
+ }
+
+ pRenderContext->Bind( pMaterial );
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+ int nParticles = pParticles->m_nActiveParticles;
+
+ int nFirstParticle = 0;
+ while ( nParticles )
+ {
+ int nVertCount, nIndexCount;
+ int nParticlesInBatch = GetParticlesToRender( pParticles, pContext, nFirstParticle, nMaxVertices, nMaxIndices, &nVertCount, &nIndexCount );
+ if ( nParticlesInBatch == 0 )
+ break;
+
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nVertCount * 3, nIndexCount * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
+
+ RenderUnsorted( pParticles, pContext, pRenderContext, meshBuilder, 0, nFirstParticle, nParticlesInBatch );
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nFirstParticle += nParticlesInBatch;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderRope::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ // Right now we only have a meshbuilder version!
+ Assert( pMaterial->IsSpriteCard() == false );
+ if ( pMaterial->IsSpriteCard() )
+ return;
+
+ RenderRopeContext_t *pCtx = reinterpret_cast<RenderRopeContext_t *>( pContext );
+ float *pSubdivList = (float*)( pCtx + 1 );
+ if ( nFirstParticle == 0 )
+ {
+ pCtx->m_flRenderedRopeLength = 0.0f;
+ }
+
+ float flTexOffset = m_flTextureScrollRate * pParticles->m_flCurTime;
+
+ RopeRenderInfo_t info;
+ info.Init( pParticles );
+
+ CBeamSegDraw beamSegment;
+ beamSegment.Start( pRenderContext, ( nParticleCount - 1 ) * m_nSubdivCount + 1, pMaterial, &meshBuilder, nVertexOffset );
+
+ Vector vecCatmullRom[4];
+ BeamSeg_t seg[2];
+ info.GenerateSeg( nFirstParticle, seg[0] );
+ seg[0].m_flTexCoord = ( pCtx->m_flRenderedRopeLength + flTexOffset ) * m_flTextureScale;
+
+ beamSegment.NextSeg( &seg[0] );
+ vecCatmullRom[1] = seg[0].m_vPos;
+ if ( nFirstParticle == 0 )
+ {
+ vecCatmullRom[0] = vecCatmullRom[1];
+ }
+ else
+ {
+ int nGroup = ( nFirstParticle-1 ) / 4;
+ int nOffset = ( nFirstParticle-1 ) & 0x3;
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ vecCatmullRom[0].Init( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ }
+
+ float flOOSubDivCount = 1.0f / m_nSubdivCount;
+ int hParticle = nFirstParticle + 1;
+ for ( int i = 1; i < nParticleCount; ++i, ++hParticle )
+ {
+ int nCurr = i & 1;
+ int nPrev = 1 - nCurr;
+ info.GenerateSeg( hParticle, seg[nCurr] );
+ pCtx->m_flRenderedRopeLength += seg[nCurr].m_vPos.DistTo( seg[nPrev].m_vPos );
+ seg[nCurr].m_flTexCoord = ( pCtx->m_flRenderedRopeLength + flTexOffset ) * m_flTextureScale;
+
+ if ( m_nSubdivCount > 1 )
+ {
+ vecCatmullRom[ (i+1) & 0x3 ] = seg[nCurr].m_vPos;
+ if ( hParticle != info.m_pParticles->m_nActiveParticles - 1 )
+ {
+ int nGroup = ( hParticle+1 ) / 4;
+ int nOffset = ( hParticle+1 ) & 0x3;
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ vecCatmullRom[ (i+2) & 0x3 ].Init( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ }
+ else
+ {
+ vecCatmullRom[ (i+2) & 0x3 ] = vecCatmullRom[ (i+1) & 0x3 ];
+ }
+
+ BeamSeg_t &subDivSeg = seg[nPrev];
+ Vector vecColorInc = ( seg[nCurr].m_vColor - seg[nPrev].m_vColor ) * flOOSubDivCount;
+ float flAlphaInc = ( seg[nCurr].m_flAlpha - seg[nPrev].m_flAlpha ) * flOOSubDivCount;
+ float flTexcoordInc = ( seg[nCurr].m_flTexCoord - seg[nPrev].m_flTexCoord ) * flOOSubDivCount;
+ float flWidthInc = ( seg[nCurr].m_flWidth - seg[nPrev].m_flWidth ) * flOOSubDivCount;
+ for( int iSubdiv = 1; iSubdiv < m_nSubdivCount; ++iSubdiv )
+ {
+ subDivSeg.m_vColor += vecColorInc;
+ subDivSeg.m_vColor.x = clamp( subDivSeg.m_vColor.x, 0.0f, 1.0f );
+ subDivSeg.m_vColor.y = clamp( subDivSeg.m_vColor.y, 0.0f, 1.0f );
+ subDivSeg.m_vColor.z = clamp( subDivSeg.m_vColor.z, 0.0f, 1.0f );
+ subDivSeg.m_flAlpha += flAlphaInc;
+ subDivSeg.m_flAlpha = clamp( subDivSeg.m_flAlpha, 0.0f, 1.0f );
+ subDivSeg.m_flTexCoord += flTexcoordInc;
+ subDivSeg.m_flWidth += flWidthInc;
+
+ Catmull_Rom_Spline( vecCatmullRom[ (i+3) & 0x3 ], vecCatmullRom[ i & 0x3 ],
+ vecCatmullRom[ (i+1) & 0x3 ], vecCatmullRom[ (i+2) & 0x3 ],
+ pSubdivList[iSubdiv], subDivSeg.m_vPos );
+
+ beamSegment.NextSeg( &subDivSeg );
+ }
+ }
+
+ beamSegment.NextSeg( &seg[nCurr] );
+ }
+
+ beamSegment.End();
+}
+
+#ifdef USE_BLOBULATOR // Enable blobulator for EP3
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+class C_OP_RenderBlobs : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderBlobs );
+
+ float m_cubeWidth;
+ float m_cutoffRadius;
+ float m_renderRadius;
+
+
+ struct C_OP_RenderBlobsContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderBlobsContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderBlobsContext_t *pCtx = reinterpret_cast<C_OP_RenderBlobsContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+
+ virtual bool IsBatchable() const
+ {
+ return false;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderBlobs, "render_blobs", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderBlobs )
+ DMXELEMENT_UNPACK_FIELD( "cube_width", "1.0f", float, m_cubeWidth )
+ DMXELEMENT_UNPACK_FIELD( "cutoff_radius", "3.3f", float, m_cutoffRadius )
+ DMXELEMENT_UNPACK_FIELD( "render_radius", "1.3f", float, m_renderRadius )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderBlobs )
+
+
+void C_OP_RenderBlobs::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ ImpTiler* tiler = ImpTilerFactory::factory->getTiler();
+ //RENDERER_CLASS* sweepRenderer = tiler->getRenderer();
+
+ C_OP_RenderBlobsContext_t *pCtx = reinterpret_cast<C_OP_RenderBlobsContext_t *>( pContext );
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+
+ #if 0
+ // Note: it is not good to have these static variables here.
+ static RENDERER_CLASS* sweepRenderer = NULL;
+ static ImpTiler* tiler = NULL;
+ if(!sweepRenderer)
+ {
+ sweepRenderer = new RENDERER_CLASS();
+ tiler = new ImpTiler(sweepRenderer);
+ }
+ #endif
+
+ // TODO: I should get rid of this static array and static calls
+ // to setCubeWidth, etc...
+ static SmartArray<ImpParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
+
+ RENDERER_CLASS::setCubeWidth(m_cubeWidth);
+ RENDERER_CLASS::setRenderR(m_renderRadius);
+ RENDERER_CLASS::setCutoffR(m_cutoffRadius);
+
+ RENDERER_CLASS::setCalcSignFunc(calcSign);
+ RENDERER_CLASS::setCalcSign2Func(calcSign2);
+ #if 0
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_COLOR_CI_SIZE, calcCornerNormalColor);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalNColor);
+ #endif
+ #if 1
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_CI_SIZE, calcCornerNormal);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalDebugColor);
+ #endif
+ #if 0
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_COLOR_CI_SIZE, calcCornerNormalHiFreqColor);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalNColor);
+ #endif
+
+
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ // TODO: I don't need to load this as a sorted list. See Lennard Jones forces for better way!
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ Vector bbMin;
+ Vector bbMax;
+ pParticles->GetBounds( &bbMin, &bbMax );
+ Vector bbCenter = 0.5f * ( bbMin + bbMax );
+
+ // FIXME: Make this configurable. Not all shaders perform lighting. Although it's pretty likely for isosurface shaders.
+ g_pParticleSystemMgr->Query()->SetUpLightingEnvironment( bbCenter );
+
+ // FIXME: Ugly hack to get particle system location to a special blob shader lighting proxy
+ pRenderContext->Bind( pMaterial, &bbCenter );
+
+ //CMeshBuilder meshBuilder;
+ //int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+
+ tiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f), (void*)&pRenderContext);
+
+ while(imp_particles_sa.size < nParticles)
+ {
+ imp_particles_sa.pushAutoSize(ImpParticle());
+ }
+
+ for( int i = 0; i < nParticles; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nIndex = ( hParticle / 4 ) * xyz_stride;
+ int nOffset = hParticle & 0x3;
+ float x = SubFloat( xyz[nIndex], nOffset );
+ float y = SubFloat( xyz[nIndex+1], nOffset );
+ float z = SubFloat( xyz[nIndex+2], nOffset );
+
+ ImpParticle* imp_particle = &imp_particles_sa[i];
+ imp_particle->center[0]=x;
+ imp_particle->center[1]=y;
+ imp_particle->center[2]=z;
+ imp_particle->setFieldScale(1.0f);
+ //imp_particle->interpolants1.set(1.0f, 1.0f, 1.0f);
+ //imp_particle->interpolants1[3] = 0.0f; //m_flSurfaceV[i];
+ tiler->insertParticle(imp_particle);
+ }
+
+
+ tiler->drawSurface(); // NOTE: need to call drawSurfaceSorted for transparency
+ tiler->endFrame();
+
+ ImpTilerFactory::factory->returnTiler(tiler);
+
+}
+
+#endif //blobs
+
+
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+class C_OP_RenderScreenVelocityRotate : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderScreenVelocityRotate );
+
+ float m_flRotateRateDegrees;
+ float m_flForwardDegrees;
+
+ struct C_OP_RenderScreenVelocityRotateContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderScreenVelocityRotateContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderScreenVelocityRotateContext_t *pCtx = reinterpret_cast<C_OP_RenderScreenVelocityRotateContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_ROTATION_MASK ;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderScreenVelocityRotate, "render_screen_velocity_rotate", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderScreenVelocityRotate )
+ DMXELEMENT_UNPACK_FIELD( "rotate_rate(dps)", "0.0f", float, m_flRotateRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "forward_angle", "-90.0f", float, m_flForwardDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderScreenVelocityRotate )
+
+
+void C_OP_RenderScreenVelocityRotate::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderScreenVelocityRotateContext_t *pCtx = reinterpret_cast<C_OP_RenderScreenVelocityRotateContext_t *>( pContext );
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ VMatrix tempView;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->GetMatrix(MATERIAL_VIEW, &tempView);
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ size_t prev_xyz_stride;
+ const fltx4 *prev_xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &prev_xyz_stride );
+
+ size_t rot_stride;
+ // const fltx4 *pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+ fltx4 *pRot = pParticles->GetM128AttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+
+ float flForwardRadians = m_flForwardDegrees * ( M_PI / 180.0f );
+ //float flRotateRateRadians = m_flRotateRateDegrees * ( M_PI / 180.0f );
+
+ for( int i = 0; i < nParticles; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nGroup = ( hParticle / 4 );
+ int nOffset = hParticle & 0x3;
+
+ int nXYZIndex = nGroup * xyz_stride;
+ Vector vecWorldPos( SubFloat( xyz[ nXYZIndex ], nOffset ), SubFloat( xyz[ nXYZIndex+1 ], nOffset ), SubFloat( xyz[ nXYZIndex+2 ], nOffset ) );
+ Vector vecViewPos;
+ Vector3DMultiplyPosition( tempView, vecWorldPos, vecViewPos );
+
+ if (!IsFinite(vecViewPos.x))
+ continue;
+
+ int nPrevXYZIndex = nGroup * prev_xyz_stride;
+ Vector vecPrevWorldPos( SubFloat( prev_xyz[ nPrevXYZIndex ], nOffset ), SubFloat( prev_xyz[ nPrevXYZIndex+1 ], nOffset ), SubFloat( prev_xyz[ nPrevXYZIndex+2 ], nOffset ) );
+ Vector vecPrevViewPos;
+ Vector3DMultiplyPosition( tempView, vecPrevWorldPos, vecPrevViewPos );
+
+ float rot = atan2( vecViewPos.y - vecPrevViewPos.y, vecViewPos.x - vecPrevViewPos.x ) + flForwardRadians;
+ SubFloat( pRot[ nGroup * rot_stride ], nOffset ) = rot;
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+void AddBuiltInParticleRenderers( void )
+{
+#ifdef _DEBUG
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderPoints );
+#endif
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderSprites );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderSpritesTrail );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderRope );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderScreenVelocityRotate );
+#ifdef USE_BLOBULATOR
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderBlobs );
+#endif // blobs
+}
+
+
+
+
diff --git a/particles/particle_sort.cpp b/particles/particle_sort.cpp
new file mode 100644
index 0000000..ecc5bb8
--- /dev/null
+++ b/particles/particle_sort.cpp
@@ -0,0 +1,194 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include <algorithm>
+#include "tier0/platform.h"
+#include "tier0/vprof.h"
+#include "particles/particles.h"
+#include "psheet.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+static ALIGN16 ParticleRenderData_t s_SortedIndexList[MAX_PARTICLES_IN_A_SYSTEM] ALIGN16_POST;
+
+
+enum EParticleSortKeyType
+{
+ SORT_KEY_NONE,
+ SORT_KEY_DISTANCE,
+ SORT_KEY_CREATION_TIME,
+};
+
+
+template<EParticleSortKeyType eSortKeyMode> void s_GenerateData( Vector CameraPos, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles )
+{
+ fltx4 *pOutUnSorted = reinterpret_cast<fltx4 *>( s_SortedIndexList );
+
+ C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
+ CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
+ CM128AttributeIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles );
+ CM128AttributeIterator pAlpha2( PARTICLE_ATTRIBUTE_ALPHA2, pParticles );
+ CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
+
+ int nParticles = pParticles->m_nActiveParticles;
+
+ FourVectors EyePos;
+ EyePos.DuplicateVector( CameraPos );
+
+
+ fltx4 fl4AlphaVis = ReplicateX4( pVisibilityData->m_flAlphaVisibility );
+ fltx4 fl4RadVis = ReplicateX4( pVisibilityData->m_flRadiusVisibility );
+
+ // indexing. We will generate the index as float and use magicf2i to convert to integer
+ fltx4 fl4OutIdx = g_SIMD_0123; // 0 1 2 3
+
+ fl4OutIdx = AddSIMD( fl4OutIdx, Four_2ToThe23s); // fix as int
+
+ bool bUseVis = pVisibilityData->m_bUseVisibility;
+ bool bCameraBias = pVisibilityData->m_flCameraBias != 0.0f;
+ fltx4 fl4Bias = ReplicateX4( pVisibilityData->m_flCameraBias );
+
+ fltx4 fl4AlphaScale = ReplicateX4( 255.0 );
+
+ do
+ {
+ fltx4 fl4X = pXYZ->x;
+ fltx4 fl4Y = pXYZ->y;
+ fltx4 fl4Z = pXYZ->z;
+
+ fltx4 fl4SortKey;
+ if ( eSortKeyMode == SORT_KEY_DISTANCE )
+ {
+ fltx4 Xdiff = SubSIMD( EyePos.x, fl4X );
+ fltx4 Ydiff = SubSIMD( EyePos.y, fl4Y );
+ fltx4 Zdiff = SubSIMD( EyePos.z, fl4Z );
+
+ if ( bCameraBias )
+ {
+ FourVectors v4CameraBias;
+ v4CameraBias.x = Xdiff;
+ v4CameraBias.y = Ydiff;
+ v4CameraBias.z = Zdiff;
+ //v4CameraBias = VectorNormalizeFast( v4CameraBias );
+ v4CameraBias.VectorNormalizeFast();
+ v4CameraBias *= fl4Bias;
+ fl4X = SubSIMD( fl4X, v4CameraBias.x );
+ fl4Y = SubSIMD( fl4Y, v4CameraBias.y );
+ fl4Z = SubSIMD( fl4Z, v4CameraBias.z );
+
+ Xdiff = SubSIMD( EyePos.x, fl4X );
+ Ydiff = SubSIMD( EyePos.y, fl4Y );
+ Zdiff = SubSIMD( EyePos.z, fl4Z );
+ }
+
+ fl4SortKey = AddSIMD( MulSIMD( Xdiff, Xdiff ),
+ AddSIMD( MulSIMD( Ydiff, Ydiff ),
+ MulSIMD( Zdiff, Zdiff ) ) );
+ }
+ else
+ {
+ Assert ( eSortKeyMode == SORT_KEY_CREATION_TIME || eSortKeyMode == SORT_KEY_NONE );
+ fl4SortKey = *pCreationTimeStamp;
+ }
+
+ fltx4 fl4FinalAlpha = MulSIMD( *pAlpha, *pAlpha2 );
+ fltx4 fl4FinalRadius = *pRadius;
+
+ if ( bUseVis )
+ {
+ fl4FinalAlpha = MaxSIMD ( Four_Zeros, MinSIMD( Four_Ones, MulSIMD( fl4FinalAlpha, fl4AlphaVis) ) );
+ fl4FinalRadius = MulSIMD( fl4FinalRadius, fl4RadVis );
+ }
+
+ // convert float 0..1 to int 0..255
+ fl4FinalAlpha = AddSIMD( MulSIMD( fl4FinalAlpha, fl4AlphaScale ), Four_2ToThe23s );
+
+ // now, we will use simd transpose to write the output
+ fltx4 i4Indices = AndSIMD( fl4OutIdx, LoadAlignedSIMD( (float *) g_SIMD_Low16BitsMask ) );
+ TransposeSIMD( fl4SortKey, i4Indices, fl4FinalRadius, fl4FinalAlpha );
+ pOutUnSorted[0] = fl4SortKey;
+ pOutUnSorted[1] = i4Indices;
+ pOutUnSorted[2] = fl4FinalRadius;
+ pOutUnSorted[3] = fl4FinalAlpha;
+
+ pOutUnSorted += 4;
+ fl4OutIdx = AddSIMD( fl4OutIdx, Four_Fours );
+
+ nParticles -= 4;
+
+ ++pXYZ;
+ ++pAlpha;
+ ++pAlpha2;
+ ++pRadius;
+ } while( nParticles > 0 ); // we're not called with 0
+
+}
+
+
+
+#define TREATASINT(x) ( *( ( (int32 const *)( &(x) ) ) ) )
+
+static bool SortLessFunc( const ParticleRenderData_t &left, const ParticleRenderData_t &right )
+{
+ return TREATASINT( left.m_flSortKey ) < TREATASINT( right.m_flSortKey );
+
+}
+
+
+void CParticleCollection::GenerateSortedIndexList( Vector vecCamera, CParticleVisibilityData *pVisibilityData, bool bSorted )
+{
+ VPROF_BUDGET( "CParticleCollection::GenerateSortedIndexList", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+
+ if ( bSorted )
+ {
+ s_GenerateData<SORT_KEY_DISTANCE>( vecCamera, pVisibilityData, this );
+ }
+ else
+ s_GenerateData<SORT_KEY_NONE>( vecCamera, pVisibilityData, this );
+
+// check data
+#if 0
+ bool bBad = false;
+ for( int i = 0; i < m_nActiveParticles; i++ )
+ {
+ Assert( s_SortedIndexList[i].m_nIndex == i );
+ if ( s_SortedIndexList[i].m_nIndex != i )
+ bBad = true;
+ }
+ if ( bBad )
+ {
+ s_GenerateData<SORT_KEY_NONE>( vecCamera, pVisibilityData, this );
+ }
+#endif
+
+#ifndef SWDS
+ if ( bSorted )
+ {
+ // sort the output in place
+ std::make_heap( s_SortedIndexList, s_SortedIndexList + m_nActiveParticles, SortLessFunc );
+ std::sort_heap( s_SortedIndexList, s_SortedIndexList + m_nActiveParticles, SortLessFunc );
+ }
+#endif
+}
+
+
+const ParticleRenderData_t *CParticleCollection::GetRenderList( IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData)
+{
+ if ( bSorted )
+ bSorted = m_pDef->m_bShouldSort;
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+ *pNparticles = m_nActiveParticles;
+ GenerateSortedIndexList( vecCamera, pVisibilityData, bSorted );
+ return s_SortedIndexList+m_nActiveParticles;
+}
+
+
+
+
diff --git a/particles/particles.cpp b/particles/particles.cpp
new file mode 100644
index 0000000..a78417a
--- /dev/null
+++ b/particles/particles.cpp
@@ -0,0 +1,3881 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "psheet.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "dmxloader/dmxloader.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imesh.h"
+#include "tier0/vprof.h"
+#include "tier1/KeyValues.h"
+#include "tier1/lzmaDecoder.h"
+#include "random_floats.h"
+#include "vtf/vtf.h"
+#include "studio.h"
+#include "particles_internal.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+
+
+// rename table from the great rename
+static const char *s_RemapOperatorNameTable[]={
+ "alpha_fade", "Alpha Fade and Decay",
+ "alpha_fade_in_random", "Alpha Fade In Random",
+ "alpha_fade_out_random", "Alpha Fade Out Random",
+ "basic_movement", "Movement Basic",
+ "color_fade", "Color Fade",
+ "controlpoint_light", "Color Light From Control Point",
+ "Dampen Movement Relative to Control Point", "Movement Dampen Relative to Control Point",
+ "Distance Between Control Points Scale", "Remap Distance Between Two Control Points to Scalar",
+ "Distance to Control Points Scale", "Remap Distance to Control Point to Scalar",
+ "lifespan_decay", "Lifespan Decay",
+ "lock to bone", "Movement Lock to Bone",
+ "postion_lock_to_controlpoint", "Movement Lock to Control Point",
+ "maintain position along path", "Movement Maintain Position Along Path",
+ "Match Particle Velocities", "Movement Match Particle Velocities",
+ "Max Velocity", "Movement Max Velocity",
+ "noise", "Noise Scalar",
+ "vector noise", "Noise Vector",
+ "oscillate_scalar", "Oscillate Scalar",
+ "oscillate_vector", "Oscillate Vector",
+ "Orient Rotation to 2D Direction", "Rotation Orient to 2D Direction",
+ "radius_scale", "Radius Scale",
+ "Random Cull", "Cull Random",
+ "remap_scalar", "Remap Scalar",
+ "rotation_movement", "Rotation Basic",
+ "rotation_spin", "Rotation Spin Roll",
+ "rotation_spin yaw", "Rotation Spin Yaw",
+ "alpha_random", "Alpha Random",
+ "color_random", "Color Random",
+ "create from parent particles", "Position From Parent Particles",
+ "Create In Hierarchy", "Position In CP Hierarchy",
+ "random position along path", "Position Along Path Random",
+ "random position on model", "Position on Model Random",
+ "sequential position along path", "Position Along Path Sequential",
+ "position_offset_random", "Position Modify Offset Random",
+ "position_warp_random", "Position Modify Warp Random",
+ "position_within_box", "Position Within Box Random",
+ "position_within_sphere", "Position Within Sphere Random",
+ "Inherit Velocity", "Velocity Inherit from Control Point",
+ "Initial Repulsion Velocity", "Velocity Repulse from World",
+ "Initial Velocity Noise", "Velocity Noise",
+ "Initial Scalar Noise", "Remap Noise to Scalar",
+ "Lifespan from distance to world", "Lifetime from Time to Impact",
+ "Pre-Age Noise", "Lifetime Pre-Age Noise",
+ "lifetime_random", "Lifetime Random",
+ "radius_random", "Radius Random",
+ "random yaw", "Rotation Yaw Random",
+ "Randomly Flip Yaw", "Rotation Yaw Flip Random",
+ "rotation_random", "Rotation Random",
+ "rotation_speed_random", "Rotation Speed Random",
+ "sequence_random", "Sequence Random",
+ "second_sequence_random", "Sequence Two Random",
+ "trail_length_random", "Trail Length Random",
+ "velocity_random", "Velocity Random",
+};
+
+static char const *RemapOperatorName( char const *pOpName )
+{
+ for( int i = 0 ; i < ARRAYSIZE( s_RemapOperatorNameTable ) ; i += 2 )
+ {
+ if ( Q_stricmp( pOpName, s_RemapOperatorNameTable[i] ) == 0 )
+ {
+ return s_RemapOperatorNameTable[i + 1 ];
+ }
+ }
+ return pOpName;
+}
+
+
+// This is the soft limit - if we exceed this, we spit out a report of all the particles in the frame
+#define MAX_PARTICLE_VERTS 50000
+// These are some limits that control g_pParticleSystemMgr->ParticleThrottleScaling() and g_pParticleSystemMgr->ParticleThrottleRandomEnable()
+//ConVar cl_particle_scale_lower ( "cl_particle_scale_lower", "20000", FCVAR_CLIENTDLL | FCVAR_CHEAT );
+//ConVar cl_particle_scale_upper ( "cl_particle_scale_upper", "40000", FCVAR_CLIENTDLL | FCVAR_CHEAT );
+#define CL_PARTICLE_SCALE_LOWER 20000
+#define CL_PARTICLE_SCALE_UPPER 40000
+
+
+//-----------------------------------------------------------------------------
+// Default implementation of particle system mgr
+//-----------------------------------------------------------------------------
+static CParticleSystemMgr s_ParticleSystemMgr;
+CParticleSystemMgr *g_pParticleSystemMgr = &s_ParticleSystemMgr;
+
+
+int g_nParticle_Multiplier = 1;
+
+
+//-----------------------------------------------------------------------------
+// Particle dictionary
+//-----------------------------------------------------------------------------
+class CParticleSystemDictionary
+{
+public:
+ ~CParticleSystemDictionary();
+
+ CParticleSystemDefinition* AddParticleSystem( CDmxElement *pParticleSystem );
+ int Count() const;
+ int NameCount() const;
+ CParticleSystemDefinition* GetParticleSystem( int i );
+ ParticleSystemHandle_t FindParticleSystemHandle( const char *pName );
+ CParticleSystemDefinition* FindParticleSystem( ParticleSystemHandle_t h );
+ CParticleSystemDefinition* FindParticleSystem( const char *pName );
+ CParticleSystemDefinition* FindParticleSystem( const DmObjectId_t &id );
+
+ CParticleSystemDefinition* operator[]( int idx )
+ {
+ return m_ParticleNameMap[ idx ];
+ }
+
+private:
+ typedef CUtlStringMap< CParticleSystemDefinition * > ParticleNameMap_t;
+ typedef CUtlVector< CParticleSystemDefinition* > ParticleIdMap_t;
+
+ void DestroyExistingElement( CDmxElement *pElement );
+
+ ParticleNameMap_t m_ParticleNameMap;
+ ParticleIdMap_t m_ParticleIdMap;
+};
+
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+CParticleSystemDictionary::~CParticleSystemDictionary()
+{
+ int nCount = m_ParticleIdMap.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ delete m_ParticleIdMap[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Destroys an existing element, returns if this element should be added to the name list
+//-----------------------------------------------------------------------------
+void CParticleSystemDictionary::DestroyExistingElement( CDmxElement *pElement )
+{
+ const char *pParticleSystemName = pElement->GetName();
+ bool bPreventNameBasedLookup = pElement->GetValue<bool>( "preventNameBasedLookup" );
+ if ( !bPreventNameBasedLookup )
+ {
+ if ( m_ParticleNameMap.Defined( pParticleSystemName ) )
+ {
+ CParticleSystemDefinition *pDef = m_ParticleNameMap[ pParticleSystemName ];
+ delete pDef;
+ m_ParticleNameMap[ pParticleSystemName ] = NULL;
+ }
+ return;
+ }
+
+ // Use id based lookup instead
+ int nCount = m_ParticleIdMap.Count();
+ const DmObjectId_t& id = pElement->GetId();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ // Was already removed by the name lookup
+ if ( !IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
+ continue;
+
+ CParticleSystemDefinition *pDef = m_ParticleIdMap[ i ];
+ m_ParticleIdMap.FastRemove( i );
+ delete pDef;
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a destructor
+//-----------------------------------------------------------------------------
+CParticleSystemDefinition* CParticleSystemDictionary::AddParticleSystem( CDmxElement *pParticleSystem )
+{
+ if ( Q_stricmp( pParticleSystem->GetTypeString(), "DmeParticleSystemDefinition" ) )
+ return NULL;
+
+ DestroyExistingElement( pParticleSystem );
+
+ CParticleSystemDefinition *pDef = new CParticleSystemDefinition;
+
+ // Must add the def to the maps before Read() because Read() may create new child particle systems
+ bool bPreventNameBasedLookup = pParticleSystem->GetValue<bool>( "preventNameBasedLookup" );
+ if ( !bPreventNameBasedLookup )
+ {
+ m_ParticleNameMap[ pParticleSystem->GetName() ] = pDef;
+ }
+ else
+ {
+ m_ParticleIdMap.AddToTail( pDef );
+ }
+
+ pDef->Read( pParticleSystem );
+ return pDef;
+}
+
+int CParticleSystemDictionary::NameCount() const
+{
+ return m_ParticleNameMap.GetNumStrings();
+}
+
+int CParticleSystemDictionary::Count() const
+{
+ return m_ParticleIdMap.Count();
+}
+
+CParticleSystemDefinition* CParticleSystemDictionary::GetParticleSystem( int i )
+{
+ return m_ParticleIdMap[i];
+}
+
+ParticleSystemHandle_t CParticleSystemDictionary::FindParticleSystemHandle( const char *pName )
+{
+ return m_ParticleNameMap.Find( pName );
+}
+
+CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( ParticleSystemHandle_t h )
+{
+ if ( h == UTL_INVAL_SYMBOL || h >= m_ParticleNameMap.GetNumStrings() )
+ return NULL;
+ return m_ParticleNameMap[ h ];
+}
+
+CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const char *pName )
+{
+ if ( m_ParticleNameMap.Defined( pName ) )
+ return m_ParticleNameMap[ pName ];
+ return NULL;
+}
+
+CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const DmObjectId_t &id )
+{
+ int nCount = m_ParticleIdMap.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
+ return m_ParticleIdMap[i];
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// For editing, create a faked particle operator definition for children
+// The only thing used in here is GetUnpackStructure.
+//-----------------------------------------------------------------------------
+BEGIN_DMXELEMENT_UNPACK( ParticleChildrenInfo_t )
+ DMXELEMENT_UNPACK_FIELD( "delay", "0.0", float, m_flDelay )
+END_DMXELEMENT_UNPACK( ParticleChildrenInfo_t, s_ChildrenInfoUnpack )
+
+class CChildOperatorDefinition : public IParticleOperatorDefinition
+{
+public:
+ virtual const char *GetName() const { Assert(0); return NULL; }
+ virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const { Assert(0); return NULL; }
+ // virtual void DestroyInstance( CParticleOperatorInstance *pInstance ) const { Assert(0); }
+ virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const
+ {
+ return s_ChildrenInfoUnpack;
+ }
+ virtual ParticleOperatorId_t GetId() const { return OPERATOR_GENERIC; }
+ virtual bool IsObsolete() const { return false; }
+ virtual size_t GetClassSize() const { return 0; }
+};
+
+static CChildOperatorDefinition s_ChildOperatorDefinition;
+
+
+//-----------------------------------------------------------------------------
+//
+// CParticleSystemDefinition
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Unpack structure for CParticleSystemDefinition
+//-----------------------------------------------------------------------------
+BEGIN_DMXELEMENT_UNPACK( CParticleSystemDefinition )
+ DMXELEMENT_UNPACK_FIELD( "max_particles", "1000", int, m_nMaxParticles )
+ DMXELEMENT_UNPACK_FIELD( "initial_particles", "0", int, m_nInitialParticles )
+ DMXELEMENT_UNPACK_FIELD_STRING_USERDATA( "material", "vgui/white", m_pszMaterialName, "vmtPicker" )
+ DMXELEMENT_UNPACK_FIELD( "bounding_box_min", "-10 -10 -10", Vector, m_BoundingBoxMin )
+ DMXELEMENT_UNPACK_FIELD( "bounding_box_max", "10 10 10", Vector, m_BoundingBoxMax )
+ DMXELEMENT_UNPACK_FIELD( "cull_radius", "0", float, m_flCullRadius )
+ DMXELEMENT_UNPACK_FIELD( "cull_cost", "1", float, m_flCullFillCost )
+ DMXELEMENT_UNPACK_FIELD( "cull_control_point", "0", int, m_nCullControlPoint )
+ DMXELEMENT_UNPACK_FIELD_STRING( "cull_replacement_definition", "", m_pszCullReplacementName )
+ DMXELEMENT_UNPACK_FIELD( "radius", "5", float, m_flConstantRadius )
+ DMXELEMENT_UNPACK_FIELD( "color", "255 255 255 255", Color, m_ConstantColor )
+ DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_flConstantRotation )
+ DMXELEMENT_UNPACK_FIELD( "rotation_speed", "0", float, m_flConstantRotationSpeed )
+ DMXELEMENT_UNPACK_FIELD( "sequence_number", "0", int, m_nConstantSequenceNumber )
+ DMXELEMENT_UNPACK_FIELD( "sequence_number 1", "0", int, m_nConstantSequenceNumber1 )
+ DMXELEMENT_UNPACK_FIELD( "group id", "0", int, m_nGroupID )
+ DMXELEMENT_UNPACK_FIELD( "maximum time step", "0.1", float, m_flMaximumTimeStep )
+ DMXELEMENT_UNPACK_FIELD( "maximum sim tick rate", "0.0", float, m_flMaximumSimTime )
+ DMXELEMENT_UNPACK_FIELD( "minimum sim tick rate", "0.0", float, m_flMinimumSimTime )
+ DMXELEMENT_UNPACK_FIELD( "minimum rendered frames", "0", int, m_nMinimumFrames )
+ DMXELEMENT_UNPACK_FIELD( "control point to disable rendering if it is the camera", "-1", int, m_nSkipRenderControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "maximum draw distance", "100000.0", float, m_flMaxDrawDistance )
+ DMXELEMENT_UNPACK_FIELD( "time to sleep when not drawn", "8", float, m_flNoDrawTimeToGoToSleep )
+ DMXELEMENT_UNPACK_FIELD( "Sort particles", "1", bool, m_bShouldSort )
+ DMXELEMENT_UNPACK_FIELD( "batch particle systems", "0", bool, m_bShouldBatch )
+ DMXELEMENT_UNPACK_FIELD( "view model effect", "0", bool, m_bViewModelEffect )
+END_DMXELEMENT_UNPACK( CParticleSystemDefinition, s_pParticleSystemDefinitionUnpack )
+
+
+
+//-----------------------------------------------------------------------------
+//
+// CParticleOperatorDefinition begins here
+// A template describing how a particle system will function
+//
+//-----------------------------------------------------------------------------
+void CParticleSystemDefinition::UnlinkAllCollections()
+{
+ while ( m_pFirstCollection )
+ {
+ m_pFirstCollection->UnlinkFromDefList();
+ }
+}
+
+const char *CParticleSystemDefinition::GetName() const
+{
+ return m_Name;
+}
+
+
+//-----------------------------------------------------------------------------
+// Should we always precache this?
+//-----------------------------------------------------------------------------
+bool CParticleSystemDefinition::ShouldAlwaysPrecache() const
+{
+ return m_bAlwaysPrecache;
+}
+
+
+//-----------------------------------------------------------------------------
+// Precache/uncache
+//-----------------------------------------------------------------------------
+void CParticleSystemDefinition::Precache()
+{
+ if ( m_bIsPrecached )
+ return;
+
+ m_bIsPrecached = true;
+#ifndef SWDS
+ m_Material.Init( MaterialName(), TEXTURE_GROUP_OTHER, true );
+#endif
+
+ int nChildCount = m_Children.Count();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ CParticleSystemDefinition *pChild;
+ if ( m_Children[i].m_bUseNameBasedLookup )
+ {
+ pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
+ }
+ else
+ {
+ pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
+ }
+
+ if ( pChild )
+ {
+ pChild->Precache();
+ }
+ }
+}
+
+void CParticleSystemDefinition::Uncache()
+{
+ if ( !m_bIsPrecached )
+ return;
+
+ m_bIsPrecached = false;
+ m_Material.Shutdown();
+// m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
+
+ int nChildCount = m_Children.Count();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ CParticleSystemDefinition *pChild;
+ if ( m_Children[i].m_bUseNameBasedLookup )
+ {
+ pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
+ }
+ else
+ {
+ pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
+ }
+
+ if ( pChild )
+ {
+ pChild->Uncache();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Has this been precached?
+//-----------------------------------------------------------------------------
+bool CParticleSystemDefinition::IsPrecached() const
+{
+ return m_bIsPrecached;
+}
+
+//-----------------------------------------------------------------------------
+// Helper methods to help with unserialization
+//-----------------------------------------------------------------------------
+void CParticleSystemDefinition::ParseOperators(
+ const char *pszOpKey, ParticleFunctionType_t nFunctionType,
+ CDmxElement *pElement,
+ CUtlVector<CParticleOperatorInstance *> &outList)
+{
+ const CDmxAttribute* pAttribute = pElement->GetAttribute( pszOpKey );
+ if ( !pAttribute || pAttribute->GetType() != AT_ELEMENT_ARRAY )
+ return;
+
+ const CUtlVector<IParticleOperatorDefinition *> &flist = g_pParticleSystemMgr->GetAvailableParticleOperatorList( nFunctionType );
+
+ const CUtlVector< CDmxElement* >& ops = pAttribute->GetArray<CDmxElement*>( );
+ int nCount = ops.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const char *pOrigName = ops[i]->GetValueString( "functionName" );
+ char const *pOpName = RemapOperatorName( pOrigName );
+ if ( pOpName != pOrigName )
+ {
+ pElement->SetValue( "functionName", pOpName );
+ }
+ bool bFound = false;
+ int nFunctionCount = flist.Count();
+ for( int j = 0; j < nFunctionCount; ++j )
+ {
+ if ( Q_stricmp( pOpName, flist[j]->GetName() ) )
+ continue;
+
+ // found it!
+ bFound = true;
+
+ CParticleOperatorInstance *pNewRef = flist[j]->CreateInstance( ops[i]->GetId() );
+ const DmxElementUnpackStructure_t *pUnpack = flist[j]->GetUnpackStructure();
+ if ( pUnpack )
+ {
+ ops[i]->UnpackIntoStructure( pNewRef, flist[j]->GetClassSize(), pUnpack );
+ }
+ pNewRef->InitParams( this, pElement );
+ m_nAttributeReadMask |= pNewRef->GetReadAttributes();
+ m_nControlPointReadMask |= pNewRef->GetReadControlPointMask();
+
+ switch( nFunctionType )
+ {
+ case FUNCTION_INITIALIZER:
+ case FUNCTION_EMITTER:
+ m_nPerParticleInitializedAttributeMask |= pNewRef->GetWrittenAttributes();
+ Assert( pNewRef->GetReadInitialAttributes() == 0 );
+ break;
+
+ case FUNCTION_OPERATOR:
+ m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
+ m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
+ break;
+
+ case FUNCTION_RENDERER:
+ m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
+ m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
+ break;
+ }
+
+ // Special case: Reading particle ID means we're reading the initial particle id
+ if ( ( pNewRef->GetReadAttributes() | pNewRef->GetReadInitialAttributes() ) & PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK )
+ {
+ m_nInitialAttributeReadMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ m_nPerParticleInitializedAttributeMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
+ }
+
+ outList.AddToTail( pNewRef );
+ break;
+ }
+
+ if ( !bFound )
+ {
+ if ( flist.Count() ) // don't warn if no ops of that type defined (server)
+ Warning( "Didn't find particle function %s\n", pOpName );
+ }
+ }
+}
+
+void CParticleSystemDefinition::ParseChildren( CDmxElement *pElement )
+{
+ const CUtlVector<CDmxElement*>& children = pElement->GetArray<CDmxElement*>( "children" );
+ int nCount = children.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxElement *pChild = children[i]->GetValue<CDmxElement*>( "child" );
+ if ( !pChild || Q_stricmp( pChild->GetTypeString(), "DmeParticleSystemDefinition" ) )
+ continue;
+
+ int j = m_Children.AddToTail();
+ children[i]->UnpackIntoStructure( &m_Children[j], sizeof( m_Children[j] ), s_ChildrenInfoUnpack );
+ m_Children[j].m_bUseNameBasedLookup = !pChild->GetValue<bool>( "preventNameBasedLookup" );
+ if ( m_Children[j].m_bUseNameBasedLookup )
+ {
+ m_Children[j].m_Name = pChild->GetName();
+ }
+ else
+ {
+ CopyUniqueId( pChild->GetId(), &m_Children[j].m_Id );
+ }
+
+ // Check to see if this child has been encountered already, and if not, then
+ // create a new particle definition for this child
+ g_pParticleSystemMgr->AddParticleSystem( pChild );
+ }
+}
+
+void CParticleSystemDefinition::Read( CDmxElement *pElement )
+{
+ m_Name = pElement->GetName();
+ CopyUniqueId( pElement->GetId(), &m_Id );
+ pElement->UnpackIntoStructure( this, sizeof( *this ), s_pParticleSystemDefinitionUnpack );
+
+#ifndef SWDS // avoid material/ texture load
+// NOTE: This makes a X appear for uncached particles.
+// m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
+#endif
+
+ if ( m_nInitialParticles < 0 )
+ {
+ m_nInitialParticles = 0;
+ }
+ if ( m_nMaxParticles < 1 )
+ {
+ m_nMaxParticles = 1;
+ }
+ m_nMaxParticles *= g_nParticle_Multiplier;
+ m_nMaxParticles = min( m_nMaxParticles, MAX_PARTICLES_IN_A_SYSTEM );
+ if ( m_flCullRadius > 0 )
+ {
+ m_nControlPointReadMask |= 1ULL << m_nCullControlPoint;
+ }
+
+ ParseOperators( "renderers", FUNCTION_RENDERER, pElement, m_Renderers );
+ ParseOperators( "operators", FUNCTION_OPERATOR, pElement, m_Operators );
+ ParseOperators( "initializers", FUNCTION_INITIALIZER, pElement, m_Initializers );
+ ParseOperators( "emitters", FUNCTION_EMITTER, pElement, m_Emitters );
+ ParseChildren( pElement );
+ ParseOperators( "forces", FUNCTION_FORCEGENERATOR, pElement, m_ForceGenerators );
+ ParseOperators( "constraints", FUNCTION_CONSTRAINT, pElement, m_Constraints );
+ SetupContextData();
+}
+
+IMaterial *CParticleSystemDefinition::GetMaterial() const
+{
+ // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
+ Assert( IsPrecached() );
+ if ( !IsPrecached() )
+ return NULL;
+ return m_Material;
+}
+
+
+//----------------------------------------------------------------------------------
+// Does the particle system use the power of two frame buffer texture (refraction?)
+//----------------------------------------------------------------------------------
+bool CParticleSystemDefinition::UsesPowerOfTwoFrameBufferTexture()
+{
+ // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
+ Assert( IsPrecached() );
+ return m_Material->NeedsPowerOfTwoFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
+}
+
+//----------------------------------------------------------------------------------
+// Does the particle system use the power of two frame buffer texture (refraction?)
+//----------------------------------------------------------------------------------
+bool CParticleSystemDefinition::UsesFullFrameBufferTexture()
+{
+ // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
+ Assert( IsPrecached() );
+ return m_Material->NeedsFullFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
+}
+
+//-----------------------------------------------------------------------------
+// Helper methods to write particle systems
+//-----------------------------------------------------------------------------
+void CParticleSystemDefinition::WriteOperators( CDmxElement *pElement,
+ const char *pOpKeyName, const CUtlVector<CParticleOperatorInstance *> &inList )
+{
+ CDmxElementModifyScope modify( pElement );
+ CDmxAttribute* pAttribute = pElement->AddAttribute( pOpKeyName );
+ CUtlVector< CDmxElement* >& ops = pAttribute->GetArrayForEdit<CDmxElement*>( );
+
+ int nCount = inList.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxElement *pOperator = CreateDmxElement( "DmeParticleOperator" );
+ ops.AddToTail( pOperator );
+
+ const IParticleOperatorDefinition *pDef = inList[i]->GetDefinition();
+ pOperator->SetValue( "name", pDef->GetName() );
+ pOperator->SetValue( "functionName", pDef->GetName() );
+
+ const DmxElementUnpackStructure_t *pUnpack = pDef->GetUnpackStructure();
+ if ( pUnpack )
+ {
+ pOperator->AddAttributesFromStructure( inList[i], pUnpack );
+ }
+ }
+}
+
+void CParticleSystemDefinition::WriteChildren( CDmxElement *pElement )
+{
+ CDmxElementModifyScope modify( pElement );
+ CDmxAttribute* pAttribute = pElement->AddAttribute( "children" );
+ CUtlVector< CDmxElement* >& children = pAttribute->GetArrayForEdit<CDmxElement*>( );
+ int nCount = m_Children.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxElement *pChildRef = CreateDmxElement( "DmeParticleChild" );
+ children.AddToTail( pChildRef );
+ children[i]->AddAttributesFromStructure( &m_Children[i], s_ChildrenInfoUnpack );
+ CDmxElement *pChildParticleSystem;
+ if ( m_Children[i].m_bUseNameBasedLookup )
+ {
+ pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement( m_Children[i].m_Name );
+ }
+ else
+ {
+ pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement( m_Children[i].m_Id );
+ }
+ pChildRef->SetValue( "name", pChildParticleSystem->GetName() );
+ pChildRef->SetValue( "child", pChildParticleSystem );
+ }
+}
+
+CDmxElement *CParticleSystemDefinition::Write()
+{
+ const char *pName = GetName();
+
+ CDmxElement *pElement = CreateDmxElement( "DmeParticleSystemDefinition" );
+ pElement->SetValue( "name", pName );
+ pElement->AddAttributesFromStructure( this, s_pParticleSystemDefinitionUnpack );
+ WriteOperators( pElement, "renderers",m_Renderers );
+ WriteOperators( pElement, "operators", m_Operators );
+ WriteOperators( pElement, "initializers", m_Initializers );
+ WriteOperators( pElement, "emitters", m_Emitters );
+ WriteChildren( pElement );
+ WriteOperators( pElement, "forces", m_ForceGenerators );
+ WriteOperators( pElement, "constraints", m_Constraints );
+
+ return pElement;
+}
+
+void CParticleSystemDefinition::SetupContextData( void )
+{
+ // calcuate sizes and offsets for context data
+ CUtlVector<CParticleOperatorInstance *> *olists[] = {
+ &m_Operators, &m_Renderers, &m_Initializers, &m_Emitters, &m_ForceGenerators,
+ &m_Constraints
+ };
+ CUtlVector<size_t> *offsetLists[] = {
+ &m_nOperatorsCtxOffsets, &m_nRenderersCtxOffsets,
+ &m_nInitializersCtxOffsets, &m_nEmittersCtxOffsets,
+ &m_nForceGeneratorsCtxOffsets, &m_nConstraintsCtxOffsets,
+ };
+
+ // loop through all operators, fill in offset entries, and calulate total data needed
+ m_nContextDataSize = 0;
+ for( int i = 0; i < NELEMS( olists ); i++ )
+ {
+ int nCount = olists[i]->Count();
+ for( int j = 0; j < nCount; j++ )
+ {
+ offsetLists[i]->AddToTail( m_nContextDataSize );
+ m_nContextDataSize += (*olists[i])[j]->GetRequiredContextBytes();
+ // align context data
+ m_nContextDataSize = (m_nContextDataSize + 15) & (~0xf );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an operator by id
+//-----------------------------------------------------------------------------
+CUtlVector<CParticleOperatorInstance *> *CParticleSystemDefinition::GetOperatorList( ParticleFunctionType_t type )
+{
+ switch( type )
+ {
+ case FUNCTION_EMITTER:
+ return &m_Emitters;
+ case FUNCTION_RENDERER:
+ return &m_Renderers;
+ case FUNCTION_INITIALIZER:
+ return &m_Initializers;
+ case FUNCTION_OPERATOR:
+ return &m_Operators;
+ case FUNCTION_FORCEGENERATOR:
+ return &m_ForceGenerators;
+ case FUNCTION_CONSTRAINT:
+ return &m_Constraints;
+ default:
+ Assert(0);
+ return NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an operator by id
+//-----------------------------------------------------------------------------
+CParticleOperatorInstance *CParticleSystemDefinition::FindOperatorById( ParticleFunctionType_t type, const DmObjectId_t &id )
+{
+ CUtlVector<CParticleOperatorInstance *> *pVec = GetOperatorList( type );
+ if ( !pVec )
+ return NULL;
+
+ int nCount = pVec->Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( IsUniqueIdEqual( id, pVec->Element(i)->GetId() ) )
+ return pVec->Element(i);
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CParticleOperatorInstance
+//
+//-----------------------------------------------------------------------------
+void CParticleOperatorInstance::InitNewParticles( CParticleCollection *pParticles,
+ int nFirstParticle, int nParticleCount,
+ int nAttributeWriteMask, void *pContext ) const
+{
+ if ( !nParticleCount )
+ return;
+
+ if ( nParticleCount < 16 ) // don't bother with vectorizing
+ // unless enough particles to bother
+ {
+ InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
+ return;
+ }
+
+ int nHead = nFirstParticle & 3;
+ if ( nHead )
+ {
+ // need to init up to 3 particles before we are block aligned
+ int nHeadCount = min( nParticleCount, 4 - nHead );
+ InitNewParticlesScalar( pParticles, nFirstParticle, nHeadCount, nAttributeWriteMask, pContext );
+ nParticleCount -= nHeadCount;
+ nFirstParticle += nHeadCount;
+ }
+
+ // now, we are aligned
+ int nBlockCount = nParticleCount / 4;
+ if ( nBlockCount )
+ {
+ InitNewParticlesBlock( pParticles, nFirstParticle / 4, nBlockCount, nAttributeWriteMask, pContext );
+ nParticleCount -= 4 * nBlockCount;
+ nFirstParticle += 4 * nBlockCount;
+ }
+
+ // do tail
+ if ( nParticleCount )
+ {
+ InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CParticleCollection
+//
+//-----------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// need custom new/delete for alignment for simd
+//------------------------------------------------------------------------------
+#include "tier0/memdbgoff.h"
+void *CParticleCollection::operator new( size_t nSize )
+{
+ return MemAlloc_AllocAligned( nSize, 16 );
+}
+
+void* CParticleCollection::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
+}
+
+void CParticleCollection::operator delete(void *pData)
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+void CParticleCollection::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+void *CWorldCollideContextData::operator new( size_t nSize )
+{
+ return MemAlloc_AllocAligned( nSize, 16 );
+}
+
+void* CWorldCollideContextData::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
+}
+
+void CWorldCollideContextData::operator delete(void *pData)
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+void CWorldCollideContextData::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CParticleCollection::CParticleCollection( )
+{
+ COMPILE_TIME_ASSERT( ( MAX_RANDOM_FLOATS & ( MAX_RANDOM_FLOATS - 1 ) ) == 0 );
+ COMPILE_TIME_ASSERT( sizeof( s_pRandomFloats ) / sizeof( float ) >= MAX_RANDOM_FLOATS );
+
+ m_pNextDef = m_pPrevDef = NULL;
+ m_nUniqueParticleId = 0;
+ m_nRandomQueryCount = 0;
+ m_bIsScrubbable = false;
+ m_bIsRunningInitializers = false;
+ m_bIsRunningOperators = false;
+ m_bIsTranslucent = false;
+ m_bIsTwoPass = false;
+ m_bIsBatchable = false;
+ m_bUsesPowerOfTwoFrameBufferTexture = false;
+ m_bUsesFullFrameBufferTexture = false;
+ m_pRenderOp = NULL;
+ m_nControlPointReadMask = 0;
+
+ m_flLastMinDistSqr = m_flLastMaxDistSqr = 0.0f;
+ m_flMinDistSqr = m_flMaxDistSqr = 0.0f;
+ m_flOOMaxDistSqr = 1.0f;
+ m_vecLastCameraPos.Init();
+ m_MinBounds.Init();
+ m_MaxBounds.Init();
+ m_bBoundsValid = false;
+
+ memset( m_ControlPoints, 0, sizeof(m_ControlPoints) );
+
+ // align all control point orientations with the global world
+ for( int i=0; i < MAX_PARTICLE_CONTROL_POINTS; i++ )
+ {
+ m_ControlPoints[i].m_ForwardVector.Init( 0, 1, 0 );
+ m_ControlPoints[i].m_UpVector.Init( 0, 0, 1 );
+ m_ControlPoints[i].m_RightVector.Init( 1, 0, 0 );
+ }
+
+ memset( m_pParticleInitialAttributes, 0, sizeof(m_pParticleInitialAttributes) );
+
+ m_nPerParticleUpdatedAttributeMask = 0;
+ m_nPerParticleInitializedAttributeMask = 0;
+ m_nPerParticleReadInitialAttributeMask = 0;
+ m_pParticleMemory = NULL;
+ m_pParticleInitialMemory = NULL;
+ m_pConstantMemory = NULL;
+ m_nActiveParticles = 0;
+ m_nPaddedActiveParticles = 0;
+ m_flCurTime = 0.0f;
+ m_fl4CurTime = Four_Zeros;
+ m_flDt = 0.0f;
+ m_flPreviousDt = 0.05f;
+ m_nParticleFlags = PCFLAGS_FIRST_FRAME;
+ m_pOperatorContextData = NULL;
+ m_pNext = m_pPrev = NULL;
+ m_nRandomSeed = 0;
+ m_pDef = NULL;
+ m_nAllocatedParticles = 0;
+ m_nMaxAllowedParticles = 0;
+ m_bDormant = false;
+ m_bEmissionStopped = false;
+ m_bRequiresOrderInvariance = false;
+ m_nSimulatedFrames = 0;
+
+ m_nNumParticlesToKill = 0;
+ m_pParticleKillList = NULL;
+ m_nHighestCP = 0;
+ memset( m_pCollisionCacheData, 0, sizeof( m_pCollisionCacheData ) );
+ m_pParent = NULL;
+ m_LocalLighting = Color(255, 255, 255, 255);
+ m_LocalLightingCP = -1;
+
+}
+
+CParticleCollection::~CParticleCollection( void )
+{
+ UnlinkFromDefList();
+
+ m_Children.Purge();
+
+ if ( m_pParticleMemory )
+ {
+ delete[] m_pParticleMemory;
+ }
+ if ( m_pParticleInitialMemory )
+ {
+ delete[] m_pParticleInitialMemory;
+ }
+ if ( m_pConstantMemory )
+ {
+ delete[] m_pConstantMemory;
+ }
+ if ( m_pOperatorContextData )
+ {
+ MemAlloc_FreeAligned( m_pOperatorContextData );
+ }
+
+ for( int i = 0 ; i < ARRAYSIZE( m_pCollisionCacheData ) ; i++ )
+ {
+ if ( m_pCollisionCacheData[i] )
+ {
+ delete m_pCollisionCacheData[i];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+void CParticleCollection::Init( CParticleSystemDefinition *pDef, float flDelay, int nRandomSeed )
+{
+ m_pDef = pDef;
+
+ // Link into def list
+ LinkIntoDefList();
+
+ InitStorage( pDef );
+
+ // Initialize sheet data
+ m_Sheet.Set( g_pParticleSystemMgr->FindOrLoadSheet( pDef->GetMaterial() ) );
+
+ // FIXME: This seed needs to be recorded per instance!
+ m_bIsScrubbable = ( nRandomSeed != 0 );
+ if ( m_bIsScrubbable )
+ {
+ m_nRandomSeed = nRandomSeed;
+ }
+ else
+ {
+ m_nRandomSeed = (int)this;
+#ifndef _DEBUG
+ m_nRandomSeed += Plat_MSTime();
+#endif
+ }
+
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_XYZ, 0.0f, 0.0f, 0.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_PREV_XYZ, 0.0f, 0.0f, 0.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_LIFE_DURATION, 1.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_RADIUS, pDef->m_flConstantRadius );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION, pDef->m_flConstantRotation );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION_SPEED, pDef->m_flConstantRotationSpeed );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_TINT_RGB,
+ pDef->m_ConstantColor.r() / 255.0f, pDef->m_ConstantColor.g() / 255.0f,
+ pDef->m_ConstantColor.g() / 255.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA, pDef->m_ConstantColor.a() / 255.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_CREATION_TIME, 0.0f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, pDef->m_nConstantSequenceNumber );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, pDef->m_nConstantSequenceNumber1 );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, 0.1f );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_PARTICLE_ID, 0 );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_YAW, 0 );
+ SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA2, 1.0f );
+
+ // Offset the child in time
+ m_flCurTime = -flDelay;
+ m_fl4CurTime = ReplicateX4( m_flCurTime );
+ if ( m_pDef->m_nContextDataSize )
+ {
+ m_pOperatorContextData = reinterpret_cast<uint8 *>
+ ( MemAlloc_AllocAligned( m_pDef->m_nContextDataSize, 16 ) );
+ }
+
+ m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + pDef->m_flNoDrawTimeToGoToSleep;
+
+ // now, init context data
+ CUtlVector<CParticleOperatorInstance *> *olists[] =
+ {
+ &(m_pDef->m_Operators), &(m_pDef->m_Renderers),
+ &(m_pDef->m_Initializers), &(m_pDef->m_Emitters),
+ &(m_pDef->m_ForceGenerators),
+ &(m_pDef->m_Constraints),
+ };
+ CUtlVector<size_t> *offsetlists[]=
+ {
+ &(m_pDef->m_nOperatorsCtxOffsets), &(m_pDef->m_nRenderersCtxOffsets),
+ &(m_pDef->m_nInitializersCtxOffsets), &(m_pDef->m_nEmittersCtxOffsets),
+ &(m_pDef->m_nForceGeneratorsCtxOffsets),
+ &(m_pDef->m_nConstraintsCtxOffsets),
+
+ };
+
+ for( int i=0; i<NELEMS( olists ); i++ )
+ {
+ int nOperatorCount = olists[i]->Count();
+ for( int j=0; j < nOperatorCount; j++ )
+ {
+ (*olists[i])[j]->InitializeContextData( this, m_pOperatorContextData+ (*offsetlists)[i][j] );
+ }
+ }
+
+ m_nControlPointReadMask = pDef->m_nControlPointReadMask;
+
+ // Instance child particle systems
+ int nChildCount = pDef->m_Children.Count();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ if ( nRandomSeed != 0 )
+ {
+ nRandomSeed += 129;
+ }
+
+ CParticleCollection *pChild;
+ if ( pDef->m_Children[i].m_bUseNameBasedLookup )
+ {
+ pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Name, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
+ }
+ else
+ {
+ pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Id, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
+ }
+ if ( pChild )
+ {
+ pChild->m_pParent = this;
+ m_Children.AddToTail( pChild );
+ m_nControlPointReadMask |= pChild->m_nControlPointReadMask;
+ }
+ }
+
+ if ( !IsValid() )
+ return;
+
+ m_bIsTranslucent = ComputeIsTranslucent();
+ m_bIsTwoPass = ComputeIsTwoPass();
+ m_bIsBatchable = ComputeIsBatchable();
+ LabelTextureUsage();
+ m_bAnyUsesPowerOfTwoFrameBufferTexture = ComputeUsesPowerOfTwoFrameBufferTexture();
+ m_bAnyUsesFullFrameBufferTexture = ComputeUsesFullFrameBufferTexture();
+ m_bRequiresOrderInvariance = ComputeRequiresOrderInvariance();
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by client code
+//-----------------------------------------------------------------------------
+bool CParticleCollection::Init( CParticleSystemDefinition *pDef )
+{
+ if ( !pDef ) // || !pDef->IsPrecached() )
+ {
+ Warning( "Particlelib: Missing precache for particle system type \"%s\"!\n", pDef ? pDef->GetName() : "unknown" );
+ CParticleSystemDefinition *pErrorDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
+ if ( pErrorDef )
+ {
+ pDef = pErrorDef;
+ }
+ }
+
+ Init( pDef, 0.0f, 0 );
+ return IsValid();
+}
+
+bool CParticleCollection::Init( const char *pParticleSystemName )
+{
+ if ( !pParticleSystemName )
+ return false;
+
+ CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName );
+ if ( !pDef )
+ {
+ Warning( "Attempted to create unknown particle system type \"%s\"!\n", pParticleSystemName );
+ return false;
+ }
+ return Init( pDef );
+}
+
+
+//-----------------------------------------------------------------------------
+// List management for collections sharing the same particle definition
+//-----------------------------------------------------------------------------
+void CParticleCollection::LinkIntoDefList( )
+{
+ Assert( !m_pPrevDef && !m_pNextDef );
+
+ m_pPrevDef = NULL;
+ m_pNextDef = m_pDef->m_pFirstCollection;
+ m_pDef->m_pFirstCollection = this;
+ if ( m_pNextDef )
+ {
+ m_pNextDef->m_pPrevDef = this;
+ }
+
+#ifdef _DEBUG
+ CParticleCollection *pCollection = m_pDef->FirstCollection();
+ while ( pCollection )
+ {
+ Assert( pCollection->m_pDef == m_pDef );
+ pCollection = pCollection->GetNextCollectionUsingSameDef();
+ }
+#endif
+}
+
+void CParticleCollection::UnlinkFromDefList( )
+{
+ if ( !m_pDef )
+ return;
+
+ if ( m_pDef->m_pFirstCollection == this )
+ {
+ m_pDef->m_pFirstCollection = m_pNextDef;
+ Assert( !m_pPrevDef );
+ }
+ else
+ {
+ Assert( m_pPrevDef );
+ m_pPrevDef->m_pNextDef = m_pNextDef;
+ }
+
+ if ( m_pNextDef )
+ {
+ m_pNextDef->m_pPrevDef = m_pPrevDef;
+ }
+
+ m_pNextDef = m_pPrevDef = NULL;
+
+#ifdef _DEBUG
+ CParticleCollection *pCollection = m_pDef->FirstCollection();
+ while ( pCollection )
+ {
+ Assert( pCollection->m_pDef == m_pDef );
+ pCollection = pCollection->GetNextCollectionUsingSameDef();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Determine if this particle has moved since the last time it was simulated,
+// which will let us know if the bbox needs to be updated.
+//-----------------------------------------------------------------------------
+bool CParticleCollection::HasMoved() const
+{
+ // It's weird that this is possible, but it apparently is (see the many other functions that
+ // check).
+ if ( !m_pDef )
+ return false;
+
+ Vector prevCP;
+ for ( int i = 0; i <= m_nHighestCP; ++i )
+ {
+ if ( !m_pDef->ReadsControlPoint( i ) )
+ continue;
+
+ GetControlPointAtPrevTime( i, &prevCP );
+ if ( prevCP != GetControlPointAtCurrentTime( i ) )
+ {
+ return true;
+ }
+ }
+
+ for ( CParticleCollection *child = m_Children.m_pHead; child; child = child->m_pNext )
+ {
+ if ( child->HasMoved() )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Particle memory initialization
+//-----------------------------------------------------------------------------
+void CParticleCollection::InitStorage( CParticleSystemDefinition *pDef )
+{
+ Assert( pDef->m_nMaxParticles < 65536 );
+
+ m_nMaxAllowedParticles = min ( MAX_PARTICLES_IN_A_SYSTEM, pDef->m_nMaxParticles );
+ m_nAllocatedParticles = 4 + 4 * ( ( m_nMaxAllowedParticles + 3 ) / 4 );
+
+ int nConstantMemorySize = 3 * 4 * MAX_PARTICLE_ATTRIBUTES * sizeof(float) + 16;
+
+ // Align allocation for constant attributes to 16 byte boundaries
+ m_pConstantMemory = new unsigned char[nConstantMemorySize];
+ m_pConstantAttributes = (float*)( (size_t)( m_pConstantMemory + 15 ) & ~0xF );
+
+ // We have to zero-init the memory so that any attributes that are not initialized
+ // get predictable and sensible values.
+ memset( m_pConstantMemory, 0, nConstantMemorySize );
+
+ m_nPerParticleInitializedAttributeMask = pDef->m_nPerParticleInitializedAttributeMask;
+ m_nPerParticleUpdatedAttributeMask = pDef->m_nPerParticleUpdatedAttributeMask;
+
+ // Only worry about initial attributes that are per-particle *and* are updated at a later time
+ // If they aren't updated at a later time, then we can just point the initial + current pointers at the same memory
+ m_nPerParticleReadInitialAttributeMask = pDef->m_nInitialAttributeReadMask &
+ ( pDef->m_nPerParticleInitializedAttributeMask & pDef->m_nPerParticleUpdatedAttributeMask );
+
+ // This is the mask of attributes which are initialized per-particle, but never updated
+ // *and* where operators want to read initial particle state
+ int nPerParticleReadConstantAttributeMask = pDef->m_nInitialAttributeReadMask &
+ ( pDef->m_nPerParticleInitializedAttributeMask & ( ~pDef->m_nPerParticleUpdatedAttributeMask ) );
+
+ int sz = 0;
+ int nInitialAttributeSize = 0;
+ int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
+ for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
+ {
+ int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
+ if ( nPerParticleAttributeMask & ( 1 << bit ) )
+ {
+ sz += nAttrSize;
+ }
+ if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
+ {
+ nInitialAttributeSize += nAttrSize;
+ }
+ }
+
+ // Gotta allocate a couple extra floats to account for
+ int nAllocationSize = m_nAllocatedParticles * sz * sizeof(float) + 16;
+ m_pParticleMemory = new unsigned char[ nAllocationSize ];
+ memset( m_pParticleMemory, 0, nAllocationSize );
+
+ // Allocate space for the initial attributes
+ if ( nInitialAttributeSize != 0 )
+ {
+ int nInitialAllocationSize = m_nAllocatedParticles * nInitialAttributeSize * sizeof(float) + 16;
+ m_pParticleInitialMemory = new unsigned char[ nInitialAllocationSize ];
+ memset( m_pParticleInitialMemory, 0, nInitialAllocationSize );
+ }
+
+ // Align allocation to 16-byte boundaries
+ float *pMem = (float*)( (size_t)( m_pParticleMemory + 15 ) & ~0xF );
+ float *pInitialMem = (float*)( (size_t)( m_pParticleInitialMemory + 15 ) & ~0xF );
+
+ // Point each attribute to memory associated with that attribute
+ for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
+ {
+ int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
+
+ if ( nPerParticleAttributeMask & ( 1 << bit ) )
+ {
+ m_pParticleAttributes[ bit ] = pMem;
+ m_nParticleFloatStrides[ bit ] = nAttrSize * 4;
+ pMem += nAttrSize * m_nAllocatedParticles;
+ }
+ else
+ {
+ m_pParticleAttributes[ bit ] = GetConstantAttributeMemory( bit );
+ m_nParticleFloatStrides[ bit ] = 0;
+ }
+
+ // Are we reading
+ if ( pDef->m_nInitialAttributeReadMask & ( 1 << bit ) )
+ {
+ if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
+ {
+ Assert( pInitialMem );
+ m_pParticleInitialAttributes[ bit ] = pInitialMem;
+ m_nParticleInitialFloatStrides[ bit ] = nAttrSize * 4;
+ pInitialMem += nAttrSize * m_nAllocatedParticles;
+ }
+ else if ( nPerParticleReadConstantAttributeMask & ( 1 << bit ) )
+ {
+ m_pParticleInitialAttributes[ bit ] = m_pParticleAttributes[ bit ];
+ m_nParticleInitialFloatStrides[ bit ] = m_nParticleFloatStrides[ bit ];
+ }
+ else
+ {
+ m_pParticleInitialAttributes[ bit ] = GetConstantAttributeMemory( bit );
+ m_nParticleInitialFloatStrides[ bit ] = 0;
+ }
+ }
+ else
+ {
+ // Catch errors where code is reading data it didn't request
+ m_pParticleInitialAttributes[ bit ] = NULL;
+ m_nParticleInitialFloatStrides[ bit ] = 0;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the particle collection name
+//-----------------------------------------------------------------------------
+const char *CParticleCollection::GetName() const
+{
+ return m_pDef ? m_pDef->GetName() : "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Does the particle system use the frame buffer texture (refraction?)
+//-----------------------------------------------------------------------------
+bool CParticleCollection::UsesPowerOfTwoFrameBufferTexture( bool bThisFrame ) const
+{
+ if ( ! m_bAnyUsesPowerOfTwoFrameBufferTexture ) // quick out if neither us or our children ever use
+ {
+ return false;
+ }
+ if ( bThisFrame )
+ {
+ return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesPowerOfTwoFrameBufferTexture );
+ }
+ return true;
+}
+//-----------------------------------------------------------------------------
+// Does the particle system use the full frame buffer texture (soft particles)
+//-----------------------------------------------------------------------------
+bool CParticleCollection::UsesFullFrameBufferTexture( bool bThisFrame ) const
+{
+ if ( ! m_bAnyUsesFullFrameBufferTexture ) // quick out if neither us or our children ever use
+ {
+ return false;
+ }
+ if ( bThisFrame )
+ {
+ return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesFullFrameBufferTexture );
+ }
+ return true;
+}
+
+bool CParticleCollection::SystemContainsParticlesWithBoolSet( bool CParticleCollection::*pField ) const
+{
+ if ( m_nActiveParticles && ( this->*pField ) )
+ return true;
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( p->SystemContainsParticlesWithBoolSet( pField ) )
+ return true;
+ }
+ return false;
+
+}
+
+void CParticleCollection::LabelTextureUsage( void )
+{
+ if ( m_pDef )
+ {
+ m_bUsesPowerOfTwoFrameBufferTexture = m_pDef->UsesPowerOfTwoFrameBufferTexture();
+ m_bUsesFullFrameBufferTexture = m_pDef->UsesFullFrameBufferTexture();
+ }
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ p->LabelTextureUsage();
+ }
+}
+
+bool CParticleCollection::ComputeUsesPowerOfTwoFrameBufferTexture()
+{
+ if ( !m_pDef )
+ return false;
+
+ if ( m_pDef->UsesPowerOfTwoFrameBufferTexture() )
+ return true;
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( p->UsesPowerOfTwoFrameBufferTexture( false ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool CParticleCollection::ComputeUsesFullFrameBufferTexture()
+{
+ if ( !m_pDef )
+ return false;
+
+ if ( m_pDef->UsesFullFrameBufferTexture() )
+ return true;
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( p->UsesFullFrameBufferTexture( false ) )
+ return true;
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+// Is the particle system two-pass?
+//-----------------------------------------------------------------------------
+bool CParticleCollection::ContainsOpaqueCollections()
+{
+ if ( !m_pDef )
+ return false;
+
+ if ( !m_pDef->GetMaterial()->IsTranslucent() )
+ return true;
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( p->ContainsOpaqueCollections( ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the particle system two-pass?
+//-----------------------------------------------------------------------------
+bool CParticleCollection::IsTwoPass() const
+{
+ return m_bIsTwoPass;
+}
+
+bool CParticleCollection::ComputeIsTwoPass()
+{
+ if ( !ComputeIsTranslucent() )
+ return false;
+
+ return ContainsOpaqueCollections();
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the particle system translucent
+//-----------------------------------------------------------------------------
+bool CParticleCollection::IsTranslucent() const
+{
+ return m_bIsTranslucent;
+}
+
+bool CParticleCollection::ComputeIsTranslucent()
+{
+ if ( !m_pDef )
+ return false;
+
+ if ( m_pDef->GetMaterial()->IsTranslucent() )
+ return true;
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( p->IsTranslucent( ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Is the particle system batchable
+//-----------------------------------------------------------------------------
+bool CParticleCollection::IsBatchable() const
+{
+ return m_bIsBatchable;
+}
+
+bool CParticleCollection::ComputeIsBatchable()
+{
+ int nRendererCount = GetRendererCount();
+ for( int i = 0; i < nRendererCount; i++ )
+ {
+ if ( !GetRenderer( i )->IsBatchable() )
+ return false;
+ }
+
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ if ( !p->IsBatchable() )
+ return false;
+ }
+
+ return true;
+}
+//-----------------------------------------------------------------------------
+// Does this system require order invariance of the particles?
+//-----------------------------------------------------------------------------
+bool CParticleCollection::ComputeRequiresOrderInvariance()
+{
+ const int nRendererCount = GetRendererCount();
+ for( int i = 0; i < nRendererCount; i++ )
+ {
+ if ( GetRenderer( i )->RequiresOrderInvariance() )
+ return true;
+ }
+
+ for (CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext)
+ {
+ if ( p->m_bRequiresOrderInvariance )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Renderer iteration
+//-----------------------------------------------------------------------------
+int CParticleCollection::GetRendererCount() const
+{
+ return IsValid() ? m_pDef->m_Renderers.Count() : 0;
+}
+
+CParticleOperatorInstance *CParticleCollection::GetRenderer( int i )
+{
+ return IsValid() ? m_pDef->m_Renderers[i] : NULL;
+}
+
+void *CParticleCollection::GetRendererContext( int i )
+{
+ return IsValid() ? m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] : NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Visualize operators (for editing/debugging)
+//-----------------------------------------------------------------------------
+void CParticleCollection::VisualizeOperator( const DmObjectId_t *pOpId )
+{
+ m_pRenderOp = NULL;
+ if ( !pOpId || !m_pDef )
+ return;
+
+ m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_EMITTER, *pOpId );
+ if ( !m_pRenderOp )
+ {
+ m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_INITIALIZER, *pOpId );
+ if ( !m_pRenderOp )
+ {
+ m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_OPERATOR, *pOpId );
+ }
+ }
+}
+
+
+float FadeInOut( float flFadeInStart, float flFadeInEnd, float flFadeOutStart, float flFadeOutEnd, float flCurTime )
+{
+ if ( flFadeInStart > flCurTime ) // started yet?
+ return 0.0;
+
+ if ( ( flFadeOutEnd > 0. ) && ( flFadeOutEnd < flCurTime ) ) // timed out?
+ return 0.;
+
+ // handle out of order cases
+ flFadeInEnd = max( flFadeInEnd, flFadeInStart );
+ flFadeOutStart = max( flFadeOutStart, flFadeInEnd );
+ flFadeOutEnd = max( flFadeOutEnd, flFadeOutStart );
+
+ float flStrength = 1.0;
+ if (
+ ( flFadeInEnd > flCurTime ) &&
+ ( flFadeInEnd > flFadeInStart ) )
+ flStrength = min( flStrength, FLerp( 0, 1, flFadeInStart, flFadeInEnd, flCurTime ) );
+
+ if ( ( flCurTime > flFadeOutStart) &&
+ ( flFadeOutEnd > flFadeOutStart) )
+ flStrength = min ( flStrength, FLerp( 0, 1, flFadeOutEnd, flFadeOutStart, flCurTime ) );
+
+ return flStrength;
+
+}
+
+bool CParticleCollection::CheckIfOperatorShouldRun(
+ CParticleOperatorInstance const * pOp ,
+ float *pflCurStrength)
+{
+ float flTime=m_flCurTime;
+ if ( pOp->m_flOpFadeOscillatePeriod > 0.0 )
+ {
+ flTime=fmod( m_flCurTime*( 1.0/pOp->m_flOpFadeOscillatePeriod ), 1.0 );
+ }
+
+ float flStrength = FadeInOut( pOp->m_flOpStartFadeInTime, pOp->m_flOpEndFadeInTime,
+ pOp->m_flOpStartFadeOutTime, pOp->m_flOpEndFadeOutTime,
+ flTime );
+ if ( pflCurStrength )
+ *pflCurStrength = flStrength;
+ return ( flStrength > 0.0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Restarts a particle system
+//-----------------------------------------------------------------------------
+void CParticleCollection::Restart()
+{
+ int i;
+ int nEmitterCount = m_pDef->m_Emitters.Count();
+ for( i = 0; i < nEmitterCount; i++ )
+ {
+ m_pDef->m_Emitters[i]->Restart( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
+ }
+
+ // Update all children
+ CParticleCollection *pChild;
+ for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
+ {
+ // Remove any delays from the time (otherwise we're offset by it oddly)
+ pChild->Restart( );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for rendering
+//-----------------------------------------------------------------------------
+void CParticleCollection::Render( IMatRenderContext *pRenderContext, bool bTranslucentOnly, void *pCameraObject )
+{
+ if ( !IsValid() )
+ return;
+
+ m_flNextSleepTime = Max ( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep ));
+
+ if ( m_nActiveParticles != 0 )
+ {
+ if ( !bTranslucentOnly || m_pDef->GetMaterial()->IsTranslucent() )
+ {
+ int nCount = m_pDef->m_Renderers.Count();
+ for( int i = 0; i < nCount; i++ )
+ {
+ if ( CheckIfOperatorShouldRun( m_pDef->m_Renderers[i] ) )
+ {
+// pRenderContext->MatrixMode( MATERIAL_VIEW );
+// pRenderContext->PushMatrix();
+// pRenderContext->LoadIdentity();
+// pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+// pRenderContext->PushMatrix();
+// pRenderContext->LoadIdentity();
+// pRenderContext->Ortho( -100, -100, 100, 100, -100, 100 );
+ m_pDef->m_Renderers[i]->Render(
+ pRenderContext, this, m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] );
+// pRenderContext->MatrixMode( MATERIAL_VIEW );
+// pRenderContext->PopMatrix();
+// pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+// pRenderContext->PopMatrix();
+ }
+ }
+ }
+ }
+
+ // let children render
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ p->Render( pRenderContext, bTranslucentOnly, pCameraObject );
+ }
+
+ // Visualize specific ops for debugging/editing
+ if ( m_pRenderOp )
+ {
+ m_pRenderOp->Render( this );
+ }
+}
+
+void CParticleCollection::UpdatePrevControlPoints( float dt )
+{
+ m_flPreviousDt = dt;
+ for(int i=0; i <= m_nHighestCP; i++ )
+ m_ControlPoints[i].m_PrevPosition = m_ControlPoints[i].m_Position;
+ m_nParticleFlags |= PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED;
+}
+
+#if MEASURE_PARTICLE_PERF
+
+#if VPROF_LEVEL > 0
+#define START_OP float flOpStartTime = Plat_FloatTime(); VPROF_ENTER_SCOPE(pOp->GetDefinition()->GetName())
+#else
+#define START_OP float flOpStartTime = Plat_FloatTime();
+#endif
+
+#if VPROF_LEVEL > 0
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
+ pDef->RecordExecutionTime( flETime ); \
+} \
+ VPROF_EXIT_SCOPE()
+#else
+#define END_OP if ( 1 ) { \
+ float flETime = Plat_FloatTime() - flOpStartTime; \
+ IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
+ pDef->RecordExecutionTime( flETime ); \
+}
+#endif
+#else
+#define START_OP
+#define END_OP
+#endif
+
+void CParticleCollection::InitializeNewParticles( int nFirstParticle, int nParticleCount, uint32 nInittedMask )
+{
+ VPROF_BUDGET( "CParticleCollection::InitializeNewParticles", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
+
+#ifdef _DEBUG
+ m_bIsRunningInitializers = true;
+#endif
+
+ // now, initialize the attributes of all the new particles
+ int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
+ int nAttrsLeftToInit = nPerParticleAttributeMask & ~nInittedMask;
+ int nInitializerCount = m_pDef->m_Initializers.Count();
+ for ( int i = 0; i < nInitializerCount; i++ )
+ {
+ CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
+ int nInitializerAttrMask = pOp->GetWrittenAttributes();
+ if ( ( ( nInitializerAttrMask & nAttrsLeftToInit ) == 0 ) || pOp->InitMultipleOverride() )
+ continue;
+
+ void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
+ START_OP;
+ if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
+ {
+ for ( int j = 0; j < nParticleCount; ++j )
+ {
+ pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
+ }
+ }
+ else
+ {
+ pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
+ }
+ END_OP;
+ nAttrsLeftToInit &= ~nInitializerAttrMask;
+ }
+
+ // always run second tier initializers (modifiers) after first tier - this ensures they don't get stomped.
+ for ( int i = 0; i < nInitializerCount; i++ )
+ {
+ int nInitializerAttrMask = m_pDef->m_Initializers[i]->GetWrittenAttributes();
+ CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
+ if ( !pOp->InitMultipleOverride() )
+ continue;
+
+ void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
+ START_OP;
+ if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
+ {
+ for ( int j = 0; j < nParticleCount; ++j )
+ {
+ pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
+ }
+ }
+ else
+ {
+ pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
+ }
+ END_OP;
+ nAttrsLeftToInit &= ~nInitializerAttrMask;
+ }
+
+#ifdef _DEBUG
+ m_bIsRunningInitializers = false;
+#endif
+
+ InitParticleAttributes( nFirstParticle, nParticleCount, nAttrsLeftToInit );
+
+ CopyInitialAttributeValues( nFirstParticle, nParticleCount );
+}
+
+void CParticleCollection::SkipToTime( float t )
+{
+ if ( t > m_flCurTime )
+ {
+ UpdatePrevControlPoints( t - m_flCurTime );
+ m_flCurTime = t;
+ m_fl4CurTime = ReplicateX4( t );
+ m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
+
+ // FIXME: In future, we may have to tell operators, initializers about this too
+ int nEmitterCount = m_pDef->m_Emitters.Count();
+ int i;
+ for( i = 0; i < nEmitterCount; i++ )
+ {
+ m_pDef->m_Emitters[i]->SkipToTime( t, this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
+ }
+
+ CParticleCollection *pChild;
+
+ // Update all children
+ for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
+ {
+ // Remove any delays from the time (otherwise we're offset by it oddly)
+ pChild->SkipToTime( t - m_pDef->m_Children[i].m_flDelay );
+ }
+ }
+}
+
+#ifdef NDEBUG
+#define CHECKSYSTEM( p ) 0
+#else
+static void CHECKSYSTEM( CParticleCollection *pParticles )
+{
+// Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
+ for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
+ {
+ const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
+ const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
+/*
+ const float *rad = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, i );
+ Assert( IsFinite( rad[0] ) );
+
+ RJ: Disabling this assert. While the proper way is to fix the math which leads to the bad number, the fix would result in more particles being drawn and in a post shipping world, users were not happy.
+ See Changelists #1368648, #1368635, and #1368434 for proper math calculation fixes.
+
+ In a post shipping world, as these particles would not render with infinites, code was added C_OP_RenderSprites::RenderSpriteCard() to check for the infinite and not add the vert to meshbuilder.
+*/
+ Assert( IsFinite( xyz[0] ) );
+ Assert( IsFinite( xyz[4] ) );
+ Assert( IsFinite( xyz[8] ) );
+ Assert( IsFinite( xyz_prev[0] ) );
+ Assert( IsFinite( xyz_prev[4] ) );
+ Assert( IsFinite( xyz_prev[8] ) );
+ }
+}
+#endif
+
+void CParticleCollection::SimulateFirstFrame( )
+{
+ m_flDt = 0.0f;
+ m_nDrawnFrames = 0;
+ m_nSimulatedFrames = 1;
+
+ // For the first frame, copy over the initial control points
+ if ( ( m_nParticleFlags & PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED ) == 0 )
+ {
+ UpdatePrevControlPoints( 0.05f );
+ }
+
+ m_nOperatorRandomSampleOffset = 0;
+ int nCount = m_pDef->m_Operators.Count();
+ for( int i = 0; i < nCount; i++ )
+ {
+ float flStrength;
+ CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
+ if ( pOp->ShouldRunBeforeEmitters() &&
+ CheckIfOperatorShouldRun( pOp, &flStrength ) )
+ {
+ pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
+ CHECKSYSTEM( this );
+ UpdatePrevControlPoints( 0.05f );
+ }
+ m_nOperatorRandomSampleOffset += 17;
+ }
+
+ // first, create initial particles
+ int nNumToCreate = min( m_pDef->m_nInitialParticles, m_nMaxAllowedParticles );
+ if ( nNumToCreate > 0 )
+ {
+ SetNActiveParticles( nNumToCreate );
+ InitializeNewParticles( 0, nNumToCreate, 0 );
+ CHECKSYSTEM( this );
+ }
+}
+
+
+
+void CParticleCollection::Simulate( float dt, bool updateBboxOnly )
+{
+ VPROF_BUDGET( "CParticleCollection::Simulate", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
+ if ( dt < 0.0f )
+ return;
+
+ if ( !m_pDef )
+ return;
+
+ // Don't do anything until we've hit t == 0
+ // This is used for delayed children
+ if ( m_flCurTime < 0.0f )
+ {
+ if ( dt >= 1.0e-22 )
+ {
+ m_flCurTime += dt;
+ m_fl4CurTime = ReplicateX4( m_flCurTime );
+ UpdatePrevControlPoints( dt );
+ }
+ return;
+ }
+
+ // run initializers if necessary (once we hit t == 0)
+ if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
+ {
+ SimulateFirstFrame();
+ m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
+ }
+
+ if ( dt < 1.0e-22 )
+ return;
+
+
+#if MEASURE_PARTICLE_PERF
+ float flStartSimTime = Plat_FloatTime();
+#endif
+
+ bool bAttachedKillList = false;
+
+ if (!HasAttachedKillList())
+ {
+ g_pParticleSystemMgr->AttachKillList(this);
+ bAttachedKillList = true;
+ }
+
+ if (!updateBboxOnly)
+ {
+ ++m_nSimulatedFrames;
+
+ float flRemainingDt = dt;
+ float flMaxDT = 0.1; // default
+ if ( m_pDef->m_flMaximumTimeStep > 0.0 )
+ flMaxDT = m_pDef->m_flMaximumTimeStep;
+
+ // Limit timestep if needed (prevents short lived particles from being created and destroyed before being rendered.
+ //if ( m_pDef->m_flMaximumSimTime != 0.0 && !m_bHasDrawnOnce )
+ if ( m_pDef->m_flMaximumSimTime != 0.0 && ( m_nDrawnFrames <= m_pDef->m_nMinimumFrames ) )
+ {
+ if ( ( flRemainingDt + m_flCurTime ) > m_pDef->m_flMaximumSimTime )
+ {
+ //if delta+current > checkpoint then delta = checkpoint - current
+ flRemainingDt = m_pDef->m_flMaximumSimTime - m_flCurTime;
+ flRemainingDt = max( m_pDef->m_flMinimumSimTime, flRemainingDt );
+ }
+ m_nDrawnFrames += 1;
+ }
+
+ flRemainingDt = min( flRemainingDt, 10 * flMaxDT ); // no more than 10 passes ever
+
+ while( flRemainingDt > 0.0 )
+ {
+ float flDT_ThisStep = min( flRemainingDt, flMaxDT );
+ flRemainingDt -= flDT_ThisStep;
+ m_flDt = flDT_ThisStep;
+ m_flCurTime += flDT_ThisStep;
+ m_fl4CurTime = ReplicateX4( m_flCurTime );
+
+#ifdef _DEBUG
+ m_bIsRunningOperators = true;
+#endif
+
+ m_nOperatorRandomSampleOffset = 0;
+ int nCount = m_pDef->m_Operators.Count();
+ for( int i = 0; i < nCount; i++ )
+ {
+ float flStrength;
+ CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
+ if ( pOp->ShouldRunBeforeEmitters() &&
+ CheckIfOperatorShouldRun( pOp, &flStrength ) )
+ {
+ START_OP;
+ pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
+ END_OP;
+ CHECKSYSTEM( this );
+ if ( m_nNumParticlesToKill )
+ {
+ ApplyKillList();
+ }
+ m_nOperatorRandomSampleOffset += 17;
+ }
+ }
+#ifdef _DEBUG
+ m_bIsRunningOperators = false;
+#endif
+
+
+ int nEmitterCount = m_pDef->m_Emitters.Count();
+ for( int i=0; i < nEmitterCount; i++ )
+ {
+ int nOldParticleCount = m_nActiveParticles;
+ float flEmitStrength;
+ if ( CheckIfOperatorShouldRun( m_pDef->m_Emitters[i], &flEmitStrength ) )
+ {
+ uint32 nInittedMask = m_pDef->m_Emitters[i]->Emit(
+ this, flEmitStrength,
+ m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
+ if ( nOldParticleCount != m_nActiveParticles )
+ {
+ // init newly emitted particles
+ InitializeNewParticles( nOldParticleCount, m_nActiveParticles - nOldParticleCount, nInittedMask );
+ CHECKSYSTEM( this );
+ }
+ }
+ }
+
+ m_nOperatorRandomSampleOffset = 0;
+ nCount = m_pDef->m_Operators.Count();
+ if ( m_nActiveParticles )
+ {
+#ifdef FP_EXCEPTIONS_ENABLED
+ const int processedParticles = m_nPaddedActiveParticles * 4;
+ for ( int unusedParticle = m_nActiveParticles; unusedParticle < processedParticles; ++unusedParticle )
+ {
+ // Set the unused-but-processed particle lifetimes to a value that
+ // won't cause division by zero or other madness. This allows us
+ // to enable floating-point exceptions during particle processing,
+ // which helps us to find bugs.
+ float *dtime = GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, unusedParticle );
+ *dtime = 1.0f;
+ }
+#endif
+#ifdef _DEBUG
+ m_bIsRunningOperators = true;
+#endif
+ for( int i = 0; i < nCount; i++ )
+ {
+ float flStrength;
+ CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
+ if ( (! pOp->ShouldRunBeforeEmitters() ) &&
+ CheckIfOperatorShouldRun( pOp, &flStrength ) )
+ {
+ START_OP;
+ pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
+ END_OP;
+ CHECKSYSTEM( this );
+ if ( m_nNumParticlesToKill )
+ {
+ ApplyKillList();
+ if ( ! m_nActiveParticles )
+ break; // don't run any more operators
+ }
+ m_nOperatorRandomSampleOffset += 17;
+ }
+ }
+#ifdef _DEBUG
+ m_bIsRunningOperators = false;
+#endif
+ }
+ }
+
+#if MEASURE_PARTICLE_PERF
+ m_pDef->m_nMaximumActiveParticles = max( m_pDef->m_nMaximumActiveParticles, m_nActiveParticles );
+ float flETime = Plat_FloatTime() - flStartSimTime;
+ m_pDef->m_flUncomittedTotalSimTime += flETime;
+ m_pDef->m_flMaxMeasuredSimTime = max( m_pDef->m_flMaxMeasuredSimTime, flETime );
+#endif
+ }
+
+ // let children simulate
+ for (CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext)
+ {
+ LoanKillListTo(i); // re-use the allocated kill list for the children
+ i->Simulate(dt, updateBboxOnly);
+ i->m_pParticleKillList = NULL;
+ }
+
+ if (bAttachedKillList)
+ g_pParticleSystemMgr->DetachKillList(this);
+
+ UpdatePrevControlPoints(dt);
+
+ // Bloat the bounding box by bounds around the control point
+ BloatBoundsUsingControlPoint();
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies the constant attributes into the per-particle attributes
+//-----------------------------------------------------------------------------
+void CParticleCollection::InitParticleAttributes( int nStartParticle, int nNumParticles, int nAttrsLeftToInit )
+{
+ if ( nAttrsLeftToInit == 0 )
+ return;
+
+ // !! speed!! do sse init here
+ for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
+ {
+ for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
+ {
+ if ( ( nAttrsLeftToInit & ( 1 << nAttr ) ) == 0 )
+ continue;
+
+ float *pAttrData = GetFloatAttributePtrForWrite( nAttr, i );
+
+ // Special case for particle id
+ if ( nAttr == PARTICLE_ATTRIBUTE_PARTICLE_ID )
+ {
+ *( (int*)pAttrData ) = ( m_nRandomSeed + m_nUniqueParticleId ) & RANDOM_FLOAT_MASK;
+ m_nUniqueParticleId++;
+ continue;
+ }
+
+ // Special case for the creation time mask
+ if ( nAttr == PARTICLE_ATTRIBUTE_CREATION_TIME )
+ {
+ *pAttrData = m_flCurTime;
+ continue;
+ }
+
+ // If this assertion fails, it means we're writing into constant memory, which is a nono
+ Assert( m_nParticleFloatStrides[nAttr] != 0 );
+ float *pConstantAttr = GetConstantAttributeMemory( nAttr );
+ *pAttrData = *pConstantAttr;
+ if ( m_nParticleFloatStrides[nAttr] == 12 )
+ {
+ pAttrData[4] = pConstantAttr[4];
+ pAttrData[8] = pConstantAttr[8];
+ }
+ }
+ }
+}
+
+void CParticleCollection::CopyInitialAttributeValues( int nStartParticle, int nNumParticles )
+{
+ if ( m_nPerParticleReadInitialAttributeMask == 0 )
+ return;
+
+ // FIXME: Do SSE copy here
+ for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
+ {
+ for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
+ {
+ if ( m_nPerParticleReadInitialAttributeMask & (1 << nAttr) )
+ {
+ const float *pSrcAttribute = GetFloatAttributePtr( nAttr, i );
+ float *pDestAttribute = GetInitialFloatAttributePtrForWrite( nAttr, i );
+ Assert( m_nParticleInitialFloatStrides[nAttr] != 0 );
+ Assert( m_nParticleFloatStrides[nAttr] == m_nParticleInitialFloatStrides[nAttr] );
+ *pDestAttribute = *pSrcAttribute;
+ if ( m_nParticleFloatStrides[nAttr] == 12 )
+ {
+ pDestAttribute[4] = pSrcAttribute[4];
+ pDestAttribute[8] = pSrcAttribute[8];
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------e
+// Computes a random vector inside a sphere
+//-----------------------------------------------------------------------------
+float CParticleCollection::RandomVectorInUnitSphere( int nRandomSampleId, Vector *pVector )
+{
+ // Guarantee uniform random distribution within a sphere
+ // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
+ float u = RandomFloat( nRandomSampleId, 0.0001f, 1.0f );
+ float v = RandomFloat( nRandomSampleId+1, 0.0001f, 1.0f );
+ float w = RandomFloat( nRandomSampleId+2, 0.0001f, 1.0f );
+
+ float flPhi = acos( 1 - 2 * u );
+ float flTheta = 2 * M_PI * v;
+ float flRadius = powf( w, 1.0f / 3.0f );
+
+ float flSinPhi, flCosPhi;
+ float flSinTheta, flCosTheta;
+ SinCos( flPhi, &flSinPhi, &flCosPhi );
+ SinCos( flTheta, &flSinTheta, &flCosTheta );
+
+ pVector->x = flRadius * flSinPhi * flCosTheta;
+ pVector->y = flRadius * flSinPhi * flSinTheta;
+ pVector->z = flRadius * flCosPhi;
+ return flRadius;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Used to retrieve the position of a control point
+// somewhere between m_flCurTime and m_flCurTime - m_fPreviousDT
+//-----------------------------------------------------------------------------
+void CParticleCollection::GetControlPointAtTime( int nControlPoint, float flTime, Vector *pControlPoint ) const
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+ if ( nControlPoint > GetHighestControlPoint() )
+ {
+ DevWarning(2, "Warning : Particle system (%s) using unassigned ControlPoint %d!\n", GetName(), nControlPoint );
+ }
+ if ( m_flDt == 0.0f )
+ {
+ VectorCopy( m_ControlPoints[nControlPoint].m_Position, *pControlPoint );
+ return;
+ }
+
+ // The original calculation for 't' was this:
+ // float flPrevTime = m_flCurTime - m_flDt;
+ // float t = ( flTime - flPrevTime ) / m_flDt;
+ // Which is mathematically equivalent to this:
+ // float t = ( flTime - ( m_flCurTime - m_flDt ) ) / m_flDt;
+ // However if m_flCurTime and flTime are large then significant precision
+ // is lost during subtraction -- catastrophic cancellation
+ // is the technical term. This starts out being an error of one part in
+ // ten million, but after running for just a few minutes it increases to
+ // one part in ten thousand -- and continues to get worse.
+ // This calculation even fails in the simple case where flTime == m_flCurTime,
+ // giving an answer that is not 1.0 and may be out of range.
+
+ // If the calculation is arranged as shown below then, because flTime and
+ // m_flCurTime are close to each other, the subtraction loses *no* precision.
+ // The subtraction will not necessarily be 'correct', since eventually flTime
+ // and m_flCurTime will not have enough precision, but it will give results
+ // that are as accurate as possible given the inputs.
+ // flHowLongAgo stores how far before the current time flTime is.
+ const float flHowLongAgo = m_flCurTime - flTime;
+ float t = ( m_flDt - flHowLongAgo ) / m_flDt;
+ // The original code had a comment saying:
+ // Precision errors can cause this problem
+ // in regards to issues that can cause t to go negative. Actually this function
+ // is just sometimes called (from InitNewParticlesScalar) with values that cause
+ // 't' to go massively negative. So I clamp it.
+ if ( t < 0.0f )
+ t = 0.0f;
+ Assert( t <= 1.0f );
+
+ VectorLerp( m_ControlPoints[nControlPoint].m_PrevPosition, m_ControlPoints[nControlPoint].m_Position, t, *pControlPoint );
+ Assert( IsFinite(pControlPoint->x) && IsFinite(pControlPoint->y) && IsFinite(pControlPoint->z) );
+}
+
+//-----------------------------------------------------------------------------
+// Used to retrieve the previous position of a control point
+//
+//-----------------------------------------------------------------------------
+void CParticleCollection::GetControlPointAtPrevTime( int nControlPoint, Vector *pControlPoint ) const
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+ *pControlPoint = m_ControlPoints[nControlPoint].m_PrevPosition;
+}
+
+void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, matrix3x4_t *pMat )
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+ const Vector &vecControlPoint = GetControlPointAtCurrentTime( nControlPoint );
+
+ // FIXME: Use quaternion lerp to get control point transform at time
+ Vector left;
+ VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
+ pMat->Init( m_ControlPoints[nControlPoint].m_ForwardVector, left, m_ControlPoints[nControlPoint].m_UpVector, vecControlPoint );
+}
+
+void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, VMatrix *pMat )
+{
+ GetControlPointTransformAtCurrentTime( nControlPoint, const_cast<matrix3x4_t *> ( &pMat->As3x4() ) );
+ pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
+}
+
+void CParticleCollection::GetControlPointOrientationAtTime( int nControlPoint, float flTime, Vector *pForward, Vector *pRight, Vector *pUp )
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+
+ // FIXME: Use quaternion lerp to get control point transform at time
+ *pForward = m_ControlPoints[nControlPoint].m_ForwardVector;
+ *pRight = m_ControlPoints[nControlPoint].m_RightVector;
+ *pUp = m_ControlPoints[nControlPoint].m_UpVector;
+}
+
+void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, matrix3x4_t *pMat )
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+ Vector vecControlPoint;
+ GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
+
+ // FIXME: Use quaternion lerp to get control point transform at time
+ Vector left;
+ VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
+ pMat->Init( m_ControlPoints[nControlPoint].m_ForwardVector, left, m_ControlPoints[nControlPoint].m_UpVector, vecControlPoint );
+}
+
+void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, VMatrix *pMat )
+{
+ GetControlPointTransformAtTime( nControlPoint, flTime, const_cast< matrix3x4_t * > ( &pMat->As3x4() ) );
+ pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
+}
+
+void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, CParticleSIMDTransformation *pXForm )
+{
+ Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
+ Vector vecControlPoint;
+ GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
+
+ pXForm->m_v4Fwd.DuplicateVector( m_ControlPoints[nControlPoint].m_ForwardVector );
+ pXForm->m_v4Up.DuplicateVector( m_ControlPoints[nControlPoint].m_UpVector );
+ //Vector left;
+ //VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
+ pXForm->m_v4Right.DuplicateVector( m_ControlPoints[nControlPoint].m_RightVector );
+
+}
+
+int CParticleCollection::GetHighestControlPoint( void ) const
+{
+ return m_nHighestCP;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the render bounds
+//-----------------------------------------------------------------------------
+void CParticleCollection::GetBounds( Vector *pMin, Vector *pMax )
+{
+ *pMin = m_MinBounds;
+ *pMax = m_MaxBounds;
+}
+
+//-----------------------------------------------------------------------------
+// Bloat the bounding box by bounds around the control point
+//-----------------------------------------------------------------------------
+void CParticleCollection::BloatBoundsUsingControlPoint()
+{
+ // more specifically, some particle systems were using "start" as an input, so it got set as control point 1,
+ // so other particle systems had an extra point in their bounding box, that generally remained at the world origin
+ RecomputeBounds();
+
+ // Don't do the bounding box fixup until after the second simulation (first real simulation)
+ // so that we know they're in their correct position.
+ if ( m_nSimulatedFrames > 2 )
+ {
+ // Include control points in the bbox.
+ for (int i = 0; i <= m_nHighestCP; ++i) {
+ if ( !m_pDef->ReadsControlPoint( i ) )
+ continue;
+
+ const Vector& cp = GetControlPointAtCurrentTime(i);
+ VectorMin( m_MinBounds, cp, m_MinBounds );
+ VectorMax( m_MaxBounds, cp, m_MaxBounds );
+ }
+ }
+
+ // Deal with children
+ // NOTE: Bounds have been recomputed for children prior to this call in Simulate
+ Vector vecMins, vecMaxs;
+ for( CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext )
+ {
+ i->GetBounds( &vecMins, &vecMaxs );
+ VectorMin( m_MinBounds, vecMins, m_MinBounds );
+ VectorMax( m_MaxBounds, vecMaxs, m_MaxBounds );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Recomputes the bounds
+//-----------------------------------------------------------------------------
+void CParticleCollection::RecomputeBounds( void )
+{
+ if ( m_nActiveParticles == 0.0f )
+ {
+ m_bBoundsValid = false;
+ m_MinBounds.Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ m_MaxBounds.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+ m_Center.Init();
+ return;
+ }
+
+ fltx4 min_x = ReplicateX4(1.0e23);
+ fltx4 min_y = min_x;
+ fltx4 min_z = min_x;
+ fltx4 max_x = ReplicateX4(-1.0e23);
+ fltx4 max_y = max_x;
+ fltx4 max_z = max_x;
+
+ fltx4 sum_x = Four_Zeros;
+ fltx4 sum_y = Four_Zeros;
+ fltx4 sum_z = Four_Zeros;
+
+ size_t xyz_stride;
+ const fltx4 *xyz = GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ int ctr = m_nActiveParticles/4;
+ while ( ctr-- )
+ {
+ min_x = MinSIMD( min_x, xyz[0] );
+ max_x = MaxSIMD( max_x, xyz[0] );
+ sum_x = AddSIMD( sum_x, xyz[0] );
+
+ min_y = MinSIMD( min_y, xyz[1] );
+ max_y = MaxSIMD( max_y, xyz[1] );
+ sum_y = AddSIMD( sum_y, xyz[1] );
+
+ min_z = MinSIMD( min_z, xyz[2] );
+ max_z = MaxSIMD( max_z, xyz[2] );
+ sum_z = AddSIMD( sum_z, xyz[2] );
+
+ xyz += xyz_stride;
+ }
+ m_bBoundsValid = true;
+ m_MinBounds.x = min( min( SubFloat( min_x, 0 ), SubFloat( min_x, 1 ) ), min( SubFloat( min_x, 2 ), SubFloat( min_x, 3 ) ) );
+ m_MinBounds.y = min( min( SubFloat( min_y, 0 ), SubFloat( min_y, 1 ) ), min( SubFloat( min_y, 2 ), SubFloat( min_y, 3 ) ) );
+ m_MinBounds.z = min( min( SubFloat( min_z, 0 ), SubFloat( min_z, 1 ) ), min( SubFloat( min_z, 2 ), SubFloat( min_z, 3 ) ) );
+
+ m_MaxBounds.x = max( max( SubFloat( max_x, 0 ), SubFloat( max_x, 1 ) ), max( SubFloat( max_x, 2 ), SubFloat( max_x, 3 ) ) );
+ m_MaxBounds.y = max( max( SubFloat( max_y, 0 ), SubFloat( max_y, 1 ) ), max( SubFloat( max_y, 2 ), SubFloat( max_y, 3 ) ) );
+ m_MaxBounds.z = max( max( SubFloat( max_z, 0 ), SubFloat( max_z, 1 ) ), max( SubFloat( max_z, 2 ), SubFloat( max_z, 3 ) ) );
+
+ float fsum_x = SubFloat( sum_x, 0 ) + SubFloat( sum_x, 1 ) + SubFloat( sum_x, 2 ) + SubFloat( sum_x, 3 );
+ float fsum_y = SubFloat( sum_y, 0 ) + SubFloat( sum_y, 1 ) + SubFloat( sum_y, 2 ) + SubFloat( sum_y, 3 );
+ float fsum_z = SubFloat( sum_z, 0 ) + SubFloat( sum_z, 1 ) + SubFloat( sum_z, 2 ) + SubFloat( sum_z, 3 );
+
+ // now, handle "tail" in a non-sse manner
+ for( int i=0; i < (m_nActiveParticles & 3); i++)
+ {
+ m_MinBounds.x = min( m_MinBounds.x, SubFloat( xyz[0], i ) );
+ m_MaxBounds.x = max( m_MaxBounds.x, SubFloat( xyz[0], i ) );
+ fsum_x += SubFloat( xyz[0], i );
+
+ m_MinBounds.y = min( m_MinBounds.y, SubFloat( xyz[1], i ) );
+ m_MaxBounds.y = max( m_MaxBounds.y, SubFloat( xyz[1], i ) );
+ fsum_y += SubFloat( xyz[1], i );
+
+ m_MinBounds.z = min( m_MinBounds.z, SubFloat( xyz[2], i ) );
+ m_MaxBounds.z = max( m_MaxBounds.z, SubFloat( xyz[2], i ) );
+ fsum_z += SubFloat( xyz[2], i );
+ }
+
+ VectorAdd( m_MinBounds, m_pDef->m_BoundingBoxMin, m_MinBounds );
+ VectorAdd( m_MaxBounds, m_pDef->m_BoundingBoxMax, m_MaxBounds );
+
+ // calculate center
+ float flOONumParticles = 1.0 / m_nActiveParticles;
+ m_Center.x = flOONumParticles * fsum_x;
+ m_Center.y = flOONumParticles * fsum_y;
+ m_Center.z = flOONumParticles * fsum_z;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the particle system finished emitting + all its particles are dead?
+//-----------------------------------------------------------------------------
+bool CParticleCollection::IsFinished( void )
+{
+ if ( !m_pDef )
+ return true;
+ if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
+ return false;
+ if ( m_nActiveParticles )
+ return false;
+ if ( m_bDormant )
+ return false;
+
+ // no particles. See if any emmitters intead to create more particles
+ int nEmitterCount = m_pDef->m_Emitters.Count();
+ for( int i=0; i < nEmitterCount; i++ )
+ {
+ if ( m_pDef->m_Emitters[i]->MayCreateMoreParticles( this, m_pOperatorContextData+m_pDef->m_nEmittersCtxOffsets[i] ) )
+ return false;
+ }
+
+ // make sure all children are finished
+ for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
+ {
+ if ( !i->IsFinished() )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop emitting particles
+//-----------------------------------------------------------------------------
+void CParticleCollection::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop )
+{
+ if ( !m_pDef )
+ return;
+
+ // Whenever we call stop emission, we clear out our dormancy. This ensures we
+ // get deleted if we're told to stop emission while dormant. SetDormant() ensures
+ // dormancy is set to true after stopping out emission.
+ m_bDormant = false;
+
+ if ( bWakeOnStop )
+ {
+ // Set next sleep time - an additional fudge factor is added over the normal time
+ // so that existing particles have a chance to go away.
+ m_flNextSleepTime = Max ( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + 10 ));
+ }
+
+ m_bEmissionStopped = true;
+
+ for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
+ {
+ m_pDef->m_Emitters[i]->StopEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
+ }
+
+ if ( bRemoveAllParticles )
+ {
+ SetNActiveParticles( 0 );
+ }
+
+ // Stop our children as well
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ p->StopEmission( bInfiniteOnly, bRemoveAllParticles );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop emitting particles
+//-----------------------------------------------------------------------------
+void CParticleCollection::StartEmission( bool bInfiniteOnly )
+{
+ if ( !m_pDef )
+ return;
+
+ m_bEmissionStopped = false;
+
+ for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
+ {
+ m_pDef->m_Emitters[i]->StartEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
+ }
+
+ // Stop our children as well
+ for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ p->StartEmission( bInfiniteOnly );
+ }
+
+ // Set our sleep time to some time in the future so we update again
+ m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dormant particle systems simulate their particles, but don't emit
+// new ones. Unlike particle systems that have StopEmission() called
+// dormant particle systems don't remove themselves when they're
+// out of particles, assuming they'll return at some point.
+//-----------------------------------------------------------------------------
+void CParticleCollection::SetDormant( bool bDormant )
+{
+ // Don't stop or start emission if we are not changing dormancy state
+ if ( bDormant == m_bDormant )
+ return;
+
+ // If emissions have already been stopped, don't go dormant, we're supposed to be dying.
+ if ( m_bEmissionStopped && bDormant )
+ return;
+
+ if ( bDormant )
+ {
+ StopEmission();
+ }
+ else
+ {
+ StartEmission();
+ }
+
+ m_bDormant = bDormant;
+}
+
+
+void CParticleCollection::MoveParticle( int nInitialIndex, int nNewIndex )
+{
+ // Copy the per-particle attributes
+ for( int p = 0; p < MAX_PARTICLE_ATTRIBUTES; ++p )
+ {
+ switch( m_nParticleFloatStrides[ p ] )
+ {
+ case 4: // move a float
+ m_pParticleAttributes[p][nNewIndex] = m_pParticleAttributes[p][nInitialIndex];
+ break;
+
+ case 12: // move a vec3
+ {
+ // sse weirdness
+ int oldidxsse = 12 * ( nNewIndex >> 2 );
+ int oldofs = oldidxsse + ( nNewIndex & 3 );
+ int lastidxsse = 12 * ( nInitialIndex >> 2 );
+ int lastofs = lastidxsse + ( nInitialIndex & 3 );
+
+ m_pParticleAttributes[p][oldofs] = m_pParticleAttributes[p][lastofs];
+ m_pParticleAttributes[p][4+oldofs] = m_pParticleAttributes[p][4+lastofs];
+ m_pParticleAttributes[p][8+oldofs] = m_pParticleAttributes[p][8+lastofs];
+ break;
+ }
+ }
+
+ switch( m_nParticleInitialFloatStrides[ p ] )
+ {
+ case 4: // move a float
+ m_pParticleInitialAttributes[p][nNewIndex] = m_pParticleInitialAttributes[p][nInitialIndex];
+ break;
+
+ case 12: // move a vec3
+ {
+ // sse weirdness
+ int oldidxsse = 12 * ( nNewIndex>>2 );
+ int oldofs = oldidxsse + ( nNewIndex & 3 );
+ int lastidxsse = 12 * ( nInitialIndex >> 2 );
+ int lastofs = lastidxsse + ( nInitialIndex & 3 );
+
+ m_pParticleInitialAttributes[p][oldofs] = m_pParticleInitialAttributes[p][lastofs];
+ m_pParticleInitialAttributes[p][4+oldofs] = m_pParticleInitialAttributes[p][4+lastofs];
+ m_pParticleInitialAttributes[p][8+oldofs] = m_pParticleInitialAttributes[p][8+lastofs];
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Kill List processing.
+//-----------------------------------------------------------------------------
+
+#define THREADED_PARTICLES 1
+
+#if THREADED_PARTICLES
+#define MAX_SIMULTANEOUS_KILL_LISTS 16
+static volatile int g_nKillBufferInUse[MAX_SIMULTANEOUS_KILL_LISTS];
+static int32 *g_pKillBuffers[MAX_SIMULTANEOUS_KILL_LISTS];
+
+void CParticleSystemMgr::DetachKillList( CParticleCollection *pParticles )
+{
+ if ( pParticles->m_pParticleKillList )
+ {
+ // find which it is
+ for(int i=0; i < NELEMS( g_pKillBuffers ); i++)
+ {
+ if ( g_pKillBuffers[i] == pParticles->m_pParticleKillList )
+ {
+ pParticles->m_pParticleKillList = NULL;
+ g_nKillBufferInUse[i] = 0; // no need to interlock
+ return;
+ }
+ }
+ Assert( 0 ); // how did we get here?
+ }
+}
+
+void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
+{
+ // look for a free slot
+ for(;;)
+ {
+ for(int i=0; i < NELEMS( g_nKillBufferInUse ); i++)
+ {
+ if ( ! g_nKillBufferInUse[i] ) // available?
+ {
+ // try to take it!
+ if ( ThreadInterlockedAssignIf( &( g_nKillBufferInUse[i]), 1, 0 ) )
+ {
+ if ( ! g_pKillBuffers[i] )
+ {
+ g_pKillBuffers[i] = new int32[MAX_PARTICLES_IN_A_SYSTEM];
+ }
+ pParticles->m_pParticleKillList = g_pKillBuffers[i];
+ return; // done!
+ }
+
+ }
+ }
+ Assert(0); // why don't we have enough buffers?
+ ThreadSleep();
+ }
+}
+#else
+// use one static kill list. no worries because of not threading
+static int g_nParticleKillList[MAX_PARTICLES_IN_A_SYSTEM];
+void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
+{
+ pParticles->m_pParticleKillList = g_nParticleKillList;
+}
+void CParticleCollection::DetachKillList( CParticleCollection *pParticles )
+{
+ Assert( pParticles->m_nNumParticlesToKill == 0 );
+ pParticles->m_pParticleKillList = NULL;
+}
+#endif
+
+
+void CParticleCollection::ApplyKillList( void )
+{
+ int nLeftInKillList = m_nNumParticlesToKill;
+ if ( nLeftInKillList == 0 )
+ return;
+
+ int nParticlesActiveNow = m_nActiveParticles;
+ const int *pCurKillListSlot = m_pParticleKillList;
+
+#ifdef _DEBUG
+ // This algorithm assumes the particles listed in the kill list are in ascending order
+ for ( int i = 1; i < nLeftInKillList; ++i )
+ {
+ Assert( pCurKillListSlot[i] > pCurKillListSlot[i-1] );
+ }
+#endif
+
+ // first, kill particles past bounds
+ while ( nLeftInKillList && pCurKillListSlot[nLeftInKillList - 1] >= nParticlesActiveNow )
+ {
+ nLeftInKillList--;
+ }
+
+ Assert( nLeftInKillList <= m_nActiveParticles );
+
+ // now, execute kill list
+ // Previously, this code would swap the last item that wasn't dead into this slot.
+ // However, some lists require order invariance, so we need to collapse over holes instead of
+ // doing the (cheaper) swap from the end to the hole.
+ if ( !m_bRequiresOrderInvariance )
+ {
+ while( nLeftInKillList )
+ {
+ int nKillIndex = *(pCurKillListSlot++);
+ nLeftInKillList--;
+
+ // now, we will move a particle from the end to where we are
+ // first, we have to find the last particle (which is not in the kill list)
+ while ( nLeftInKillList &&
+ ( pCurKillListSlot[ nLeftInKillList-1 ] == nParticlesActiveNow-1 ))
+ {
+ nLeftInKillList--;
+ nParticlesActiveNow--;
+ }
+
+ // we might be killing the last particle
+ if ( nKillIndex == nParticlesActiveNow-1 )
+ {
+ // killing last one
+ nParticlesActiveNow--;
+ break; // we are done
+ }
+
+ // move the last particle to this one and chop off the end of the list
+ MoveParticle( nParticlesActiveNow-1, nKillIndex );
+ nParticlesActiveNow--;
+ }
+ }
+ else
+ {
+ // The calling code may tell us to kill particles that are already out of bounds.
+ // That causes this code to kill more particles than we're supposed to (possibly even causing a crash).
+ // So remember how many particles we had left to kill so we can properly decrement the count below.
+ int decrementValue = nLeftInKillList;
+ int writeLoc = *(pCurKillListSlot++);
+ --nLeftInKillList;
+
+ for ( int readLoc = 1 + writeLoc; readLoc < nParticlesActiveNow; ++readLoc )
+ {
+ if ( nLeftInKillList > 0 && readLoc == *pCurKillListSlot )
+ {
+ pCurKillListSlot++;
+ --nLeftInKillList;
+ continue;
+ }
+
+ MoveParticle( readLoc, writeLoc );
+ ++writeLoc;
+ }
+
+ nParticlesActiveNow -= decrementValue;
+ }
+
+ // set count in system and wipe kill list
+ SetNActiveParticles( nParticlesActiveNow );
+ m_nNumParticlesToKill = 0;
+}
+
+void CParticleCollection::CalculatePathValues( CPathParameters const &PathIn,
+ float flTimeStamp,
+ Vector *pStartPnt,
+ Vector *pMidPnt,
+ Vector *pEndPnt
+ )
+{
+ Vector StartPnt;
+ GetControlPointAtTime( PathIn.m_nStartControlPointNumber, flTimeStamp, &StartPnt );
+ Vector EndPnt;
+ GetControlPointAtTime( PathIn.m_nEndControlPointNumber, flTimeStamp, &EndPnt );
+
+ Vector MidP;
+ VectorLerp(StartPnt, EndPnt, PathIn.m_flMidPoint, MidP);
+
+ if ( PathIn.m_nBulgeControl )
+ {
+ Vector vTarget=(EndPnt-StartPnt);
+ float flBulgeScale = 0.0;
+ int nCP=PathIn.m_nStartControlPointNumber;
+ if ( PathIn.m_nBulgeControl == 2)
+ nCP = PathIn.m_nEndControlPointNumber;
+ Vector Fwd = m_ControlPoints[nCP].m_ForwardVector;
+ float len=VectorLength( vTarget);
+ if ( len > 1.0e-6 )
+ {
+ vTarget *= (1.0/len); // normalize
+ flBulgeScale = 1.0-fabs( DotProduct( vTarget, Fwd )); // bulge inversely scaled
+ }
+ Vector Potential_MidP=Fwd;
+ float flOffsetDist = VectorLength( Potential_MidP );
+ if ( flOffsetDist > 1.0e-6 )
+ {
+ Potential_MidP *= (PathIn.m_flBulge*len*flBulgeScale)/flOffsetDist;
+ MidP += Potential_MidP;
+ }
+ }
+ else
+ {
+ Vector RndVector;
+ RandomVector( 0, -PathIn.m_flBulge, PathIn.m_flBulge, &RndVector);
+ MidP+=RndVector;
+ }
+
+ *pStartPnt = StartPnt;
+ *pMidPnt = MidP;
+ *pEndPnt = EndPnt;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Default impelemtation of the query
+//
+//-----------------------------------------------------------------------------
+
+class CDefaultParticleSystemQuery : public CBaseAppSystem< IParticleSystemQuery >
+{
+public:
+ virtual void GetLightingAtPoint( const Vector& vecOrigin, Color &tint )
+ {
+ tint.SetColor( 255, 255, 255, 255 );
+ }
+ virtual void TraceLine( const Vector& vecAbsStart,
+ const Vector& vecAbsEnd, unsigned int mask,
+ const class IHandleEntity *ignore,
+ int collisionGroup, CBaseTrace *ptr )
+ {
+ ptr->fraction = 1.0; // no hit
+ }
+
+ virtual void GetRandomPointsOnControllingObjectHitBox(
+ CParticleCollection *pParticles,
+ int nControlPointNumber,
+ int nNumPtsOut,
+ float flBBoxScale,
+ int nNumTrysToGetAPointInsideTheModel,
+ Vector *pPntsOut,
+ Vector vecDirectionBias,
+ Vector *pHitBoxRelativeCoordOut, int *pHitBoxIndexOut )
+ {
+ for ( int i = 0; i < nNumPtsOut; ++i )
+ {
+ pPntsOut[i].Init();
+ }
+ }
+
+ virtual float GetPixelVisibility( int *pQueryHandle, const Vector &vecOrigin, float flScale ) { return 0.0f; }
+};
+
+static CDefaultParticleSystemQuery s_DefaultParticleSystemQuery;
+
+
+//-----------------------------------------------------------------------------
+//
+// Particle system manager
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CParticleSystemMgr::CParticleSystemMgr()
+ // m_SheetList( DefLessFunc( ITexture * ) )
+{
+ m_pQuery = &s_DefaultParticleSystemQuery;
+ m_bDidInit = false;
+ m_bUsingDefaultQuery = true;
+ m_bShouldLoadSheets = true;
+ m_pParticleSystemDictionary = NULL;
+ m_nNumFramesMeasured = 0;
+ m_flLastSimulationTime = 0.0f;
+ m_nParticleVertexCount = m_nParticleIndexCount = 0;
+ m_bFrameWarningNeeded = false;
+
+ for ( int i = 0; i < c_nNumFramesTracked; i++ )
+ {
+ m_nParticleVertexCountHistory[i] = 0;
+ }
+ m_fParticleCountScaling = 1.0f;
+}
+
+CParticleSystemMgr::~CParticleSystemMgr()
+{
+ if ( m_pParticleSystemDictionary )
+ {
+ delete m_pParticleSystemDictionary;
+ m_pParticleSystemDictionary = NULL;
+ }
+ FlushAllSheets();
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialize the particle system
+//-----------------------------------------------------------------------------
+bool CParticleSystemMgr::Init( IParticleSystemQuery *pQuery )
+{
+ if ( !g_pMaterialSystem->QueryInterface( MATERIAL_SYSTEM_INTERFACE_VERSION ) )
+ {
+ Msg( "CParticleSystemMgr compiled using an old IMaterialSystem\n" );
+ return false;
+ }
+
+ if ( m_bUsingDefaultQuery && pQuery )
+ {
+ m_pQuery = pQuery;
+ m_bUsingDefaultQuery = false;
+ }
+
+ if ( !m_bDidInit )
+ {
+ m_pParticleSystemDictionary = new CParticleSystemDictionary;
+ // NOTE: This is for the editor only
+ AddParticleOperator( FUNCTION_CHILDREN, &s_ChildOperatorDefinition );
+
+ m_pShadowDepthMaterial = NULL;
+ if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 )
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
+ pVMTKeyValues->SetInt( "$no_fullbright", 1 );
+ pVMTKeyValues->SetInt( "$model", 0 );
+ pVMTKeyValues->SetInt( "$alphatest", 0 );
+ m_pShadowDepthMaterial = g_pMaterialSystem->CreateMaterial( "__particlesDepthWrite", pVMTKeyValues );
+ }
+
+ SeedRandSIMD( 12345678 );
+ m_bDidInit = true;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------------
+// Cache/uncache materials used by particle systems
+//----------------------------------------------------------------------------------
+void CParticleSystemMgr::PrecacheParticleSystem( const char *pName )
+{
+ if ( !pName || !pName[0] )
+ {
+ return;
+ }
+
+ CParticleSystemDefinition* pDef = FindParticleSystem( pName );
+ if ( !pDef )
+ {
+ Warning( "Attemped to precache unknown particle system \"%s\"!\n", pName );
+ return;
+ }
+
+ pDef->Precache();
+}
+
+void CParticleSystemMgr::UncacheAllParticleSystems()
+{
+ if ( !m_pParticleSystemDictionary )
+ return;
+
+ int nCount = m_pParticleSystemDictionary->Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_pParticleSystemDictionary->GetParticleSystem( i )->Uncache();
+ }
+
+ nCount = m_pParticleSystemDictionary->NameCount();
+ for ( ParticleSystemHandle_t h = 0; h < nCount; ++h )
+ {
+ m_pParticleSystemDictionary->FindParticleSystem( h )->Uncache();
+ }
+
+ // Flush sheets, as they can accumulate several MB of memory per map
+ FlushAllSheets();
+}
+
+
+//-----------------------------------------------------------------------------
+// return the particle field name
+//-----------------------------------------------------------------------------
+static const char *s_pParticleFieldNames[MAX_PARTICLE_ATTRIBUTES] =
+{
+ "Position", // XYZ, 0
+ "Life Duration", // LIFE_DURATION, 1 );
+ NULL, // PREV_XYZ is for internal use only
+ "Radius", // RADIUS, 3 );
+
+ "Roll", // ROTATION, 4 );
+ "Roll Speed", // ROTATION_SPEED, 5 );
+ "Color", // TINT_RGB, 6 );
+ "Alpha", // ALPHA, 7 );
+
+ "Creation Time", // CREATION_TIME, 8 );
+ "Sequence Number", // SEQUENCE_NUMBER, 9 );
+ "Trail Length", // TRAIL_LENGTH, 10 );
+ "Particle ID", // PARTICLE_ID, 11 );
+
+ "Yaw", // YAW, 12 );
+ "Sequence Number 1", // SEQUENCE_NUMBER1, 13 );
+ NULL, // HITBOX_INDEX is for internal use only
+ NULL, // HITBOX_XYZ_RELATIVE is for internal use only
+
+ "Alpha Alternate", // ALPHA2, 16
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+const char* CParticleSystemMgr::GetParticleFieldName( int nParticleField ) const
+{
+ return s_pParticleFieldNames[nParticleField];
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the available particle operators
+//-----------------------------------------------------------------------------
+void CParticleSystemMgr::AddParticleOperator( ParticleFunctionType_t nOpType,
+ IParticleOperatorDefinition *pOpFactory )
+{
+ m_ParticleOperators[nOpType].AddToTail( pOpFactory );
+}
+
+CUtlVector< IParticleOperatorDefinition *> &CParticleSystemMgr::GetAvailableParticleOperatorList( ParticleFunctionType_t nWhichList )
+{
+ return m_ParticleOperators[nWhichList];
+}
+
+const DmxElementUnpackStructure_t *CParticleSystemMgr::GetParticleSystemDefinitionUnpackStructure()
+{
+ return s_pParticleSystemDefinitionUnpack;
+}
+
+
+//------------------------------------------------------------------------------
+// custom allocators for operators so simd aligned
+//------------------------------------------------------------------------------
+#include "tier0/memdbgoff.h"
+void *CParticleOperatorInstance::operator new( size_t nSize )
+{
+ return MemAlloc_AllocAligned( nSize, 16 );
+}
+
+void* CParticleOperatorInstance::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
+}
+
+void CParticleOperatorInstance::operator delete(void *pData)
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+void CParticleOperatorInstance::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
+{
+ if ( pData )
+ {
+ MemAlloc_FreeAligned( pData );
+ }
+}
+
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Read the particle config file from a utlbuffer
+//-----------------------------------------------------------------------------
+bool CParticleSystemMgr::ReadParticleDefinitions( CUtlBuffer &buf, const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
+{
+ DECLARE_DMX_CONTEXT_DECOMMIT( bDecommitTempMemory );
+
+ CDmxElement *pRoot;
+ if ( !UnserializeDMX( buf, &pRoot, pFileName ) || !pRoot )
+ {
+ Warning( "Unable to read particle definition %s! UtlBuffer is the wrong type!\n", pFileName );
+ return false;
+ }
+
+ if ( !Q_stricmp( pRoot->GetTypeString(), "DmeParticleSystemDefinition" ) )
+ {
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( pRoot );
+ if ( pDef && bPrecache )
+ {
+ pDef->m_bAlwaysPrecache = true;
+ if ( IsPC() )
+ {
+ pDef->Precache();
+ }
+ }
+ CleanupDMX( pRoot );
+ return true;
+ }
+
+ const CDmxAttribute *pDefinitions = pRoot->GetAttribute( "particleSystemDefinitions" );
+ if ( !pDefinitions || pDefinitions->GetType() != AT_ELEMENT_ARRAY )
+ {
+ CleanupDMX( pRoot );
+ return false;
+ }
+
+ const CUtlVector< CDmxElement* >& definitions = pDefinitions->GetArray<CDmxElement*>( );
+ int nCount = definitions.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( definitions[i] );
+ if ( pDef && bPrecache )
+ {
+ pDef->m_bAlwaysPrecache = true;
+ if ( IsPC() )
+ {
+ pDef->Precache();
+ }
+ }
+ }
+
+ CleanupDMX( pRoot );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Decommits temporary memory
+//-----------------------------------------------------------------------------
+void CParticleSystemMgr::DecommitTempMemory()
+{
+ DecommitDMXMemory();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the last simulation time, used for particle system sleeping logic
+//-----------------------------------------------------------------------------
+void CParticleSystemMgr::SetLastSimulationTime( float flTime )
+{
+ m_flLastSimulationTime = flTime;
+
+ int nParticleVertexCountHistoryMax = 0;
+ for ( int i = 0; i < c_nNumFramesTracked-1; i++ )
+ {
+ m_nParticleVertexCountHistory[i] = m_nParticleVertexCountHistory[i+1];
+ nParticleVertexCountHistoryMax = Max ( nParticleVertexCountHistoryMax, m_nParticleVertexCountHistory[i] );
+ }
+ m_nParticleVertexCountHistory[c_nNumFramesTracked-1] = m_nParticleVertexCount;
+ nParticleVertexCountHistoryMax = Max ( nParticleVertexCountHistoryMax, m_nParticleVertexCount );
+
+ // We need to take an average over a decent number of frames because this throttling has a direct feedback effect. Worried about oscillation problems!
+ int nLower = CL_PARTICLE_SCALE_LOWER;//cl_particle_scale_lower.GetInt();
+ m_fParticleCountScaling = 1.0f;
+ int nHowManyOver = nParticleVertexCountHistoryMax - nLower;
+ if ( nHowManyOver > 0 )
+ {
+ int nUpper = CL_PARTICLE_SCALE_UPPER;//cl_particle_scale_upper.GetInt();
+ int nRange = nUpper - nLower;
+ m_fParticleCountScaling = 1.0f - ( (float)nHowManyOver / (float)nRange );
+ m_fParticleCountScaling = Clamp ( m_fParticleCountScaling, 0.0f, 1.0f );
+ }
+
+ m_nParticleVertexCount = m_nParticleIndexCount = 0;
+}
+
+float CParticleSystemMgr::GetLastSimulationTime() const
+{
+ return m_flLastSimulationTime;
+}
+
+bool CParticleSystemMgr::Debug_FrameWarningNeededTestAndReset()
+{
+ bool bTemp = m_bFrameWarningNeeded;
+ m_bFrameWarningNeeded = false;
+ return bTemp;
+}
+
+int CParticleSystemMgr::Debug_GetTotalParticleCount() const
+{
+ return m_nParticleVertexCountHistory[c_nNumFramesTracked-1];
+}
+
+float CParticleSystemMgr::ParticleThrottleScaling() const
+{
+ return m_fParticleCountScaling;
+}
+
+bool CParticleSystemMgr::ParticleThrottleRandomEnable() const
+{
+ if ( m_fParticleCountScaling == 1.0f )
+ {
+ // No throttling.
+ return true;
+ }
+ else if ( m_fParticleCountScaling > RandomFloat ( 0.0f, 1.0f ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Unserialization-related methods
+//-----------------------------------------------------------------------------
+void CParticleSystemMgr::AddParticleSystem( CDmxElement *pParticleSystem )
+{
+ m_pParticleSystemDictionary->AddParticleSystem( pParticleSystem );
+}
+
+CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const char *pName )
+{
+ return m_pParticleSystemDictionary->FindParticleSystem( pName );
+}
+
+CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const DmObjectId_t& id )
+{
+ return m_pParticleSystemDictionary->FindParticleSystem( id );
+}
+
+
+//-----------------------------------------------------------------------------
+// Read the particle config file from a utlbuffer
+//-----------------------------------------------------------------------------
+bool CParticleSystemMgr::ReadParticleConfigFile( CUtlBuffer &buf, bool bPrecache, bool bDecommitTempMemory, const char *pFileName )
+{
+ return ReadParticleDefinitions( buf, pFileName, bPrecache, bDecommitTempMemory );
+}
+
+
+//-----------------------------------------------------------------------------
+// Read the particle config file from a utlbuffer
+//-----------------------------------------------------------------------------
+bool CParticleSystemMgr::ReadParticleConfigFile( const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
+{
+ // Names starting with a '!' are always precached.
+ if ( pFileName[0] == '!' )
+ {
+ bPrecache = true;
+ ++pFileName;
+ }
+
+ if ( IsX360() )
+ {
+ char szTargetName[MAX_PATH];
+ CreateX360Filename( pFileName, szTargetName, sizeof( szTargetName ) );
+
+ CUtlBuffer fileBuffer;
+ bool bHaveParticles = g_pFullFileSystem->ReadFile( szTargetName, "GAME", fileBuffer );
+ if ( bHaveParticles )
+ {
+ fileBuffer.SetBigEndian( false );
+ return ReadParticleConfigFile( fileBuffer, bPrecache, bDecommitTempMemory, szTargetName );
+ }
+ else if ( g_pFullFileSystem->GetDVDMode() != DVDMODE_OFF )
+ {
+ // 360 version should have been there, 360 zips can only have binary particles
+ Warning( "Particles: Missing '%s'\n", szTargetName );
+ return false;
+ }
+ }
+
+ char pFallbackBuf[MAX_PATH];
+ if ( IsPC() )
+ {
+ // Look for fallback particle systems
+ char pTemp[MAX_PATH];
+ Q_StripExtension( pFileName, pTemp, sizeof(pTemp) );
+ const char *pExt = Q_GetFileExtension( pFileName );
+ if ( !pExt )
+ {
+ pExt = "pcf";
+ }
+
+ if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 )
+ {
+ Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx80.%s", pTemp, pExt );
+ if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
+ {
+ pFileName = pFallbackBuf;
+ }
+ }
+ else if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 90 && g_pMaterialSystemHardwareConfig->PreferReducedFillrate() )
+ {
+ Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx90_slow.%s", pTemp, pExt );
+ if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
+ {
+ pFileName = pFallbackBuf;
+ }
+ }
+ }
+
+ CUtlBuffer buf( 0, 0, 0 );
+ if ( IsX360() )
+ {
+ // fell through, load as pc particle resource file
+ buf.ActivateByteSwapping( true );
+ }
+
+ if ( g_pFullFileSystem->ReadFile( pFileName, "GAME", buf ) )
+ {
+ return ReadParticleConfigFile( buf, bPrecache, bDecommitTempMemory, pFileName );
+ }
+
+ Warning( "Particles: Missing '%s'\n", pFileName );
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Write a specific particle config to a utlbuffer
+//-----------------------------------------------------------------------------
+bool CParticleSystemMgr::WriteParticleConfigFile( const char *pParticleSystemName, CUtlBuffer &buf, bool bPreventNameBasedLookup )
+{
+ DECLARE_DMX_CONTEXT();
+ // Create DMX elements representing the particle system definition
+ CDmxElement *pParticleSystem = CreateParticleDmxElement( pParticleSystemName );
+ return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
+}
+
+bool CParticleSystemMgr::WriteParticleConfigFile( const DmObjectId_t& id, CUtlBuffer &buf, bool bPreventNameBasedLookup )
+{
+ DECLARE_DMX_CONTEXT();
+ // Create DMX elements representing the particle system definition
+ CDmxElement *pParticleSystem = CreateParticleDmxElement( id );
+ return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
+}
+
+bool CParticleSystemMgr::WriteParticleConfigFile( CDmxElement *pParticleSystem, CUtlBuffer &buf, bool bPreventNameBasedLookup )
+{
+ pParticleSystem->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
+
+ CDmxAttribute* pAttribute = pParticleSystem->GetAttribute( "children" );
+ const CUtlVector< CDmxElement* >& children = pAttribute->GetArray<CDmxElement*>( );
+ int nCount = children.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxElement *pChildRef = children[ i ];
+ CDmxElement *pChild = pChildRef->GetValue<CDmxElement*>( "child" );
+ pChild->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
+ }
+
+ // Now write the DMX elements out
+ bool bOk = SerializeDMX( buf, pParticleSystem );
+ CleanupDMX( pParticleSystem );
+ return bOk;
+}
+
+ParticleSystemHandle_t CParticleSystemMgr::GetParticleSystemIndex( const char *pParticleSystemName )
+{
+ if ( !pParticleSystemName )
+ return UTL_INVAL_SYMBOL;
+
+ return m_pParticleSystemDictionary->FindParticleSystemHandle( pParticleSystemName );
+}
+
+const char *CParticleSystemMgr::GetParticleSystemNameFromIndex( ParticleSystemHandle_t iIndex )
+{
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( iIndex );
+ return pDef ? pDef->GetName() : "Unknown";
+}
+
+int CParticleSystemMgr::GetParticleSystemCount( void )
+{
+ return m_pParticleSystemDictionary->NameCount();
+}
+
+
+//-----------------------------------------------------------------------------
+// Factory method for creating particle collections
+//-----------------------------------------------------------------------------
+CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const char *pParticleSystemName, float flDelay, int nRandomSeed )
+{
+ if ( !pParticleSystemName )
+ return NULL;
+
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
+ if ( !pDef )
+ {
+ Warning( "Attempted to create unknown particle system type %s\n", pParticleSystemName );
+ return NULL;
+ }
+
+ CParticleCollection *pParticleCollection = new CParticleCollection;
+ pParticleCollection->Init( pDef, flDelay, nRandomSeed );
+ return pParticleCollection;
+}
+
+
+CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const DmObjectId_t &id, float flDelay, int nRandomSeed )
+{
+ if ( !IsUniqueIdValid( id ) )
+ return NULL;
+
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
+ if ( !pDef )
+ {
+ char pBuf[256];
+ UniqueIdToString( id, pBuf, sizeof(pBuf) );
+ Warning( "Attempted to create unknown particle system id %s\n", pBuf );
+ return NULL;
+ }
+
+ CParticleCollection *pParticleCollection = new CParticleCollection;
+ pParticleCollection->Init( pDef, flDelay, nRandomSeed );
+ return pParticleCollection;
+}
+
+
+//--------------------------------------------------------------------------------
+// Is a particular particle system defined?
+//--------------------------------------------------------------------------------
+bool CParticleSystemMgr::IsParticleSystemDefined( const DmObjectId_t &id )
+{
+ if ( !IsUniqueIdValid( id ) )
+ return false;
+
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
+ return ( pDef != NULL );
+}
+
+
+//--------------------------------------------------------------------------------
+// Is a particular particle system defined?
+//--------------------------------------------------------------------------------
+bool CParticleSystemMgr::IsParticleSystemDefined( const char *pName )
+{
+ if ( !pName || !pName[0] )
+ return false;
+
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pName );
+ return ( pDef != NULL );
+}
+
+
+//--------------------------------------------------------------------------------
+// Particle kill list
+//--------------------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------------------
+// Serialization-related methods
+//--------------------------------------------------------------------------------
+CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const DmObjectId_t &id )
+{
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
+
+ // Create DMX elements representing the particle system definition
+ return pDef->Write( );
+}
+
+CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const char *pParticleSystemName )
+{
+ CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
+
+ // Create DMX elements representing the particle system definition
+ return pDef->Write( );
+}
+
+//--------------------------------------------------------------------------------
+// Client loads sheets for rendering, server doesn't need to.
+//--------------------------------------------------------------------------------
+void CParticleSystemMgr::ShouldLoadSheets( bool bLoadSheets )
+{
+ m_bShouldLoadSheets = bLoadSheets;
+}
+
+//--------------------------------------------------------------------------------
+// Particle sheets
+//--------------------------------------------------------------------------------
+CSheet *CParticleSystemMgr::FindOrLoadSheet( char const *pszFname, ITexture *pTexture )
+{
+ if ( !m_bShouldLoadSheets )
+ return NULL;
+
+ if ( m_SheetList.Defined( pszFname ) )
+ return m_SheetList[ pszFname ];
+
+ CSheet *pNewSheet = NULL;
+
+ size_t numBytes;
+ void const *pSheet = pTexture->GetResourceData( VTF_RSRC_SHEET, &numBytes );
+
+ if ( pSheet )
+ {
+ CUtlBuffer bufLoad( pSheet, numBytes, CUtlBuffer::READ_ONLY );
+ pNewSheet = new CSheet( bufLoad );
+ }
+
+ m_SheetList[ pszFname ] = pNewSheet;
+ return pNewSheet;
+}
+
+CSheet *CParticleSystemMgr::FindOrLoadSheet( IMaterial *pMaterial )
+{
+ if ( !pMaterial )
+ return NULL;
+
+ bool bFoundVar = false;
+ IMaterialVar *pVar = pMaterial->FindVar( "$basetexture", &bFoundVar, true );
+
+ if ( bFoundVar && pVar && pVar->IsDefined() )
+ {
+ ITexture *pTex = pVar->GetTextureValue();
+ if ( pTex && !pTex->IsError() )
+ return FindOrLoadSheet( pTex->GetName(), pTex );
+ }
+
+ return NULL;
+}
+
+void CParticleSystemMgr::FlushAllSheets( void )
+{
+// for( int i = 0, iEnd = m_SheetList.Count(); i < iEnd; i++ )
+// {
+// delete m_SheetList.Element(i);
+// }
+//
+// m_SheetList.RemoveAll();
+
+ m_SheetList.PurgeAndDeleteElements();
+}
+
+
+//-----------------------------------------------------------------------------
+// Render cache
+//-----------------------------------------------------------------------------
+void CParticleSystemMgr::ResetRenderCache( void )
+{
+ int nCount = m_RenderCache.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_RenderCache[i].m_ParticleCollections.RemoveAll();
+ }
+}
+
+void CParticleSystemMgr::AddToRenderCache( CParticleCollection *pParticles )
+{
+ if ( !pParticles->IsValid() || pParticles->m_pDef->GetMaterial()->IsTranslucent() )
+ return;
+
+ pParticles->m_flNextSleepTime = Max ( pParticles->m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + pParticles->m_pDef->m_flNoDrawTimeToGoToSleep ));
+ // Find the current rope list.
+ int iRenderCache = 0;
+ int nRenderCacheCount = m_RenderCache.Count();
+ for ( ; iRenderCache < nRenderCacheCount; ++iRenderCache )
+ {
+ if ( ( pParticles->m_pDef->GetMaterial() == m_RenderCache[iRenderCache].m_pMaterial ) )
+ break;
+ }
+
+ // A full rope list should have been generate in CreateRenderCache
+ // If we didn't find one, then allocate the mofo.
+ if ( iRenderCache == nRenderCacheCount )
+ {
+ iRenderCache = m_RenderCache.AddToTail();
+ m_RenderCache[iRenderCache].m_pMaterial = pParticles->m_pDef->GetMaterial();
+ }
+
+ m_RenderCache[iRenderCache].m_ParticleCollections.AddToTail( pParticles );
+
+ for( CParticleCollection *p = pParticles->m_Children.m_pHead; p; p = p->m_pNext )
+ {
+ AddToRenderCache( p );
+ }
+}
+
+
+void CParticleSystemMgr::BuildBatchList( int iRenderCache, IMatRenderContext *pRenderContext, CUtlVector< Batch_t >& batches )
+{
+ batches.RemoveAll();
+
+ IMaterial *pMaterial = m_RenderCache[iRenderCache].m_pMaterial;
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+
+ int nRemainingVertices = nMaxVertices;
+ int nRemainingIndices = nMaxIndices;
+
+ int i = batches.AddToTail();
+ Batch_t* pBatch = &batches[i];
+ pBatch->m_nVertCount = 0;
+ pBatch->m_nIndexCount = 0;
+
+ // Ask each renderer about the # of verts + ints it will draw
+ int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
+ for ( int iCache = 0; iCache < nCacheCount; ++iCache )
+ {
+ CParticleCollection *pParticles = m_RenderCache[iRenderCache].m_ParticleCollections[iCache];
+ if ( !pParticles->IsValid() )
+ continue;
+
+ int nRenderCount = pParticles->GetRendererCount();
+ for ( int j = 0; j < nRenderCount; ++j )
+ {
+ int nFirstParticle = 0;
+ while ( nFirstParticle < pParticles->m_nActiveParticles )
+ {
+ int iPart;
+ BatchStep_t step;
+ step.m_pParticles = pParticles;
+ step.m_pRenderer = pParticles->GetRenderer( j );
+ step.m_pContext = pParticles->GetRendererContext( j );
+ step.m_nFirstParticle = nFirstParticle;
+ step.m_nParticleCount = step.m_pRenderer->GetParticlesToRender( pParticles,
+ step.m_pContext, nFirstParticle, nRemainingVertices, nRemainingIndices, &step.m_nVertCount, &iPart );
+ nFirstParticle += step.m_nParticleCount;
+
+ if ( step.m_nParticleCount > 0 )
+ {
+ pBatch->m_nVertCount += step.m_nVertCount;
+ pBatch->m_nIndexCount += iPart;
+ pBatch->m_BatchStep.AddToTail( step );
+ Assert( pBatch->m_nVertCount <= nMaxVertices && pBatch->m_nIndexCount <= nMaxIndices );
+ }
+ else
+ {
+ if ( pBatch->m_nVertCount == 0 )
+ break;
+
+ // Not enough room
+ Assert( pBatch->m_nVertCount > 0 && pBatch->m_nIndexCount > 0 );
+ pBatch = &batches[batches.AddToTail()];
+ pBatch->m_nVertCount = 0;
+ pBatch->m_nIndexCount = 0;
+ nRemainingVertices = nMaxVertices;
+ nRemainingIndices = nMaxIndices;
+ }
+ }
+ }
+ }
+
+ if ( pBatch->m_nVertCount <= 0 || pBatch->m_nIndexCount <= 0 )
+ {
+ batches.FastRemove( batches.Count() - 1 );
+ }
+}
+
+void CParticleSystemMgr::DumpProfileInformation( void )
+{
+#if MEASURE_PARTICLE_PERF
+ FileHandle_t fh = g_pFullFileSystem->Open( "particle_profile.csv", "w" );
+ g_pFullFileSystem->FPrintf( fh, "numframes,%d\n", m_nNumFramesMeasured );
+ g_pFullFileSystem->FPrintf( fh, "name, total time, max time, max particles, allocated particles\n");
+ for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
+ {
+ CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
+ if ( p->m_nMaximumActiveParticles )
+ g_pFullFileSystem->FPrintf( fh, "%s,%f,%f,%d,%d\n", p->m_Name.Get(), p->m_flTotalSimTime, p->m_flMaxMeasuredSimTime, p->m_nMaximumActiveParticles, p->m_nMaxParticles );
+ }
+ g_pFullFileSystem->FPrintf( fh, "\n\nopname, total time, max time\n");
+ for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
+ {
+ for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
+ {
+ float flmax = m_ParticleOperators[i][j]->MaximumRecordedExecutionTime();
+ float fltotal = m_ParticleOperators[i][j]->TotalRecordedExecutionTime();
+ if ( fltotal > 0.0 )
+ g_pFullFileSystem->FPrintf( fh, "%s,%f,%f\n",
+ m_ParticleOperators[i][j]->GetName(), fltotal, flmax );
+ }
+ }
+ g_pFullFileSystem->Close( fh );
+#endif
+}
+
+void CParticleSystemMgr::CommitProfileInformation( bool bCommit )
+{
+#if MEASURE_PARTICLE_PERF
+ if ( 1 )
+ {
+ if ( bCommit )
+ m_nNumFramesMeasured++;
+ for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
+ {
+ CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
+ if ( bCommit )
+ p->m_flTotalSimTime += p->m_flUncomittedTotalSimTime;
+ p->m_flUncomittedTotalSimTime = 0.;
+ }
+ for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
+ {
+ for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
+ {
+ if ( bCommit )
+ m_ParticleOperators[i][j]->m_flTotalExecutionTime += m_ParticleOperators[i][j]->m_flUncomittedTime;
+ m_ParticleOperators[i][j]->m_flUncomittedTime = 0;
+ }
+ }
+ }
+#endif
+}
+
+void CParticleSystemMgr::DrawRenderCache( bool bShadowDepth )
+{
+ int nRenderCacheCount = m_RenderCache.Count();
+ if ( nRenderCacheCount == 0 )
+ return;
+
+ VPROF_BUDGET( "CParticleSystemMgr::DrawRenderCache", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CUtlVector< Batch_t > batches( 0, 8 );
+
+ for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache )
+ {
+ int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
+ if ( nCacheCount == 0 )
+ continue;
+
+ // FIXME: When rendering shadow depth, do it all in 1 batch
+ IMaterial *pMaterial = bShadowDepth ? m_pShadowDepthMaterial : m_RenderCache[iRenderCache].m_pMaterial;
+
+ BuildBatchList( iRenderCache, pRenderContext, batches );
+ int nBatchCount = batches.Count();
+ if ( nBatchCount == 0 )
+ continue;
+
+ pRenderContext->Bind( pMaterial );
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+
+ for ( int i = 0; i < nBatchCount; ++i )
+ {
+ const Batch_t& batch = batches[i];
+ Assert( batch.m_nVertCount > 0 && batch.m_nIndexCount > 0 );
+
+ g_pParticleSystemMgr->TallyParticlesRendered( batch.m_nVertCount * 3, batch.m_nIndexCount * 3 );
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, batch.m_nVertCount, batch.m_nIndexCount );
+
+ int nVertexOffset = 0;
+ int nBatchStepCount = batch.m_BatchStep.Count();
+ for ( int j = 0; j < nBatchStepCount; ++j )
+ {
+ const BatchStep_t &step = batch.m_BatchStep[j];
+ // FIXME: this will break if it ever calls into C_OP_RenderSprites::Render[TwoSequence]SpriteCard()
+ // (need to protect against that and/or split the meshBuilder batch to support that path here)
+ step.m_pRenderer->RenderUnsorted( step.m_pParticles, step.m_pContext, pRenderContext,
+ meshBuilder, nVertexOffset, step.m_nFirstParticle, step.m_nParticleCount );
+ nVertexOffset += step.m_nVertCount;
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+
+ ResetRenderCache( );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+}
+
+
+void CParticleSystemMgr::TallyParticlesRendered( int nVertexCount, int nIndexCount )
+{
+ m_nParticleIndexCount += nIndexCount;
+ m_nParticleVertexCount += nVertexCount;
+
+ if ( m_nParticleVertexCount > MAX_PARTICLE_VERTS )
+ {
+ m_bFrameWarningNeeded = true;
+ }
+}
+
+
+
+
+
+
+void IParticleSystemQuery::GetRandomPointsOnControllingObjectHitBox(
+ CParticleCollection *pParticles,
+ int nControlPointNumber,
+ int nNumPtsOut,
+ float flBBoxScale,
+ int nNumTrysToGetAPointInsideTheModel,
+ Vector *pPntsOut,
+ Vector vecDirectionalBias,
+ Vector *pHitBoxRelativeCoordOut,
+ int *pHitBoxIndexOut
+ )
+{
+ for(int i=0; i < nNumPtsOut; i++)
+ {
+ pPntsOut[i]=pParticles->m_ControlPoints[nControlPointNumber].m_Position;
+ if ( pHitBoxRelativeCoordOut )
+ pHitBoxRelativeCoordOut[i].Init();
+ if ( pHitBoxIndexOut )
+ pHitBoxIndexOut[i] = -1;
+ }
+}
+
+
+
+void CParticleCollection::UpdateHitBoxInfo( int nControlPointNumber )
+{
+ CModelHitBoxesInfo &hb = m_ControlPointHitBoxes[nControlPointNumber];
+
+ if ( hb.m_flLastUpdateTime == m_flCurTime )
+ return; // up to date
+
+ hb.m_flLastUpdateTime = m_flCurTime;
+
+ // make sure space allocated
+ if ( ! hb.m_pHitBoxes )
+ hb.m_pHitBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
+ if ( ! hb.m_pPrevBoxes )
+ hb.m_pPrevBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
+
+ // save current into prev
+ hb.m_nNumPrevHitBoxes = hb.m_nNumHitBoxes;
+ hb.m_flPrevLastUpdateTime = hb.m_flLastUpdateTime;
+ V_swap( hb.m_pHitBoxes, hb.m_pPrevBoxes );
+
+ // issue hitbox query
+ hb.m_nNumHitBoxes = g_pParticleSystemMgr->Query()->GetControllingObjectHitBoxInfo(
+ this, nControlPointNumber, MAXSTUDIOBONES, hb.m_pHitBoxes );
+
+}
diff --git a/particles/particles.vpc b/particles/particles.vpc
new file mode 100644
index 0000000..bdc1e65
--- /dev/null
+++ b/particles/particles.vpc
@@ -0,0 +1,32 @@
+//-----------------------------------------------------------------------------
+// PARTICLES.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Project "particles"
+{
+ $Folder "Source Files"
+ {
+ $File "builtin_constraints.cpp"
+ $File "builtin_initializers.cpp"
+ $File "builtin_particle_emitters.cpp"
+ $File "builtin_particle_forces.cpp"
+ $File "addbuiltin_ops.cpp"
+ $File "builtin_particle_ops.cpp"
+ $File "builtin_particle_render_ops.cpp"
+ $File "particle_sort.cpp"
+ $File "particles.cpp"
+ $File "psheet.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\particles\particles.h"
+ $File "random_floats.h"
+ $File "particles_internal.h"
+ }
+}
diff --git a/particles/particles_internal.h b/particles/particles_internal.h
new file mode 100644
index 0000000..c327cbf
--- /dev/null
+++ b/particles/particles_internal.h
@@ -0,0 +1,52 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: sheet code for particles and other sprite functions
+//
+//===========================================================================//
+
+#ifndef PARTICLES_INTERNAL_H
+#define PARTICLES_INTERNAL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "tier1/UtlStringMap.h"
+#include "tier1/utlbuffer.h"
+#include "tier2/fileutils.h"
+
+#define MAX_WORLD_PLANAR_CONSTRAINTS ( 26 + 5 + 10 )
+
+#define COLLISION_MODE_PER_PARTICLE_TRACE 0
+#define COLLISION_MODE_PER_FRAME_PLANESET 1
+#define COLLISION_MODE_INITIAL_TRACE_DOWN 2
+#define COLLISION_MODE_USE_NEAREST_TRACE 3
+
+struct CWorldCollideContextData
+{
+ FourVectors m_TraceStartPnt[MAX_WORLD_PLANAR_CONSTRAINTS];
+ FourVectors m_TraceEndPnt[MAX_WORLD_PLANAR_CONSTRAINTS];
+ FourVectors m_PointOnPlane[MAX_WORLD_PLANAR_CONSTRAINTS];
+ FourVectors m_PlaneNormal[MAX_WORLD_PLANAR_CONSTRAINTS];
+
+ int m_nActivePlanes;
+ int m_nNumFixedPlanes;
+ float m_flLastUpdateTime;
+ Vector m_vecLastUpdateOrigin;
+ bool m_bPlaneActive[MAX_WORLD_PLANAR_CONSTRAINTS];
+
+ void *operator new( size_t nSize );
+ void *operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine );
+ void operator delete(void *pData);
+ void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine );
+
+ void SetBaseTrace( int nIndex, Vector const &rayStart, Vector const &traceDir, int nCollisionGroup, bool bKeepMisses );
+
+ void CalculatePlanes( CParticleCollection *pParticles, int nCollisionMode, int nCollisionGroupNumber,
+ Vector const *pCpOffset = NULL, float flMovementTolerance = 0. );
+};
+
+#endif // PARTICLES_INTERNAL_H
+
+
diff --git a/particles/psheet.cpp b/particles/psheet.cpp
new file mode 100644
index 0000000..dfd243b
--- /dev/null
+++ b/particles/psheet.cpp
@@ -0,0 +1,146 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: sheet code for particles and other sprite functions
+//
+//===========================================================================//
+
+#include "psheet.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/utlbuffer.h"
+#include "tier2/fileutils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+CSheet::CSheet( void )
+{
+ memset( m_pSamples, 0, sizeof( m_pSamples ) );
+ memset( m_bClamp, 0, sizeof( m_bClamp ) );
+}
+
+CSheet::CSheet( CUtlBuffer &buf )
+{
+ memset( m_pSamples, 0, sizeof( m_pSamples ) );
+ memset( m_bClamp, 0, sizeof( m_bClamp ) );
+ memset( m_bSequenceIsCopyOfAnotherSequence, 0, sizeof( m_bSequenceIsCopyOfAnotherSequence ) );
+
+ // lets read a sheet
+ buf.ActivateByteSwappingIfBigEndian();
+ int nVersion = buf.GetInt(); // version#
+ int nNumCoordsPerFrame = (nVersion)?MAX_IMAGES_PER_FRAME_ON_DISK:1;
+
+ int nNumSequences = buf.GetInt();
+
+
+ while ( nNumSequences-- )
+ {
+ int nSequenceNumber = buf.GetInt();
+ if ( ( nSequenceNumber < 0 ) || (nSequenceNumber >= MAX_SEQUENCES ) )
+ {
+ Warning("sequence number %d too high in sheet file!!!\n", nSequenceNumber);
+ return;
+ }
+ m_bClamp[ nSequenceNumber ] = ( buf.GetInt() != 0 );
+ int nFrameCount = buf.GetInt();
+ // Save off how many frames we have for this sequence
+ m_nNumFrames[ nSequenceNumber ] = nFrameCount;
+ bool bSingleFrameSequence = ( nFrameCount == 1 );
+
+ int nTimeSamples = bSingleFrameSequence ? 1 : SEQUENCE_SAMPLE_COUNT;
+
+ m_pSamples[ nSequenceNumber ] =
+ new SheetSequenceSample_t[ nTimeSamples ];
+
+ int fTotalSequenceTime = buf.GetFloat();
+ float InterpKnot[SEQUENCE_SAMPLE_COUNT];
+ float InterpValue[SEQUENCE_SAMPLE_COUNT];
+ SheetSequenceSample_t Samples[SEQUENCE_SAMPLE_COUNT];
+ float fCurTime = 0.;
+ for( int nFrm = 0 ; nFrm < nFrameCount; nFrm++ )
+ {
+ float fThisDuration = buf.GetFloat();
+ InterpValue[ nFrm ] = nFrm;
+ InterpKnot [ nFrm ] = SEQUENCE_SAMPLE_COUNT*( fCurTime/ fTotalSequenceTime );
+ SheetSequenceSample_t &seq = Samples[ nFrm ];
+ seq.m_fBlendFactor = 0.0f;
+ for(int nImage = 0 ; nImage< nNumCoordsPerFrame; nImage++ )
+ {
+ SequenceSampleTextureCoords_t &s=seq.m_TextureCoordData[nImage];
+ s.m_fLeft_U0 = buf.GetFloat();
+ s.m_fTop_V0 = buf.GetFloat();
+ s.m_fRight_U0 = buf.GetFloat();
+ s.m_fBottom_V0 = buf.GetFloat();
+ }
+ if ( nNumCoordsPerFrame == 1 )
+ seq.CopyFirstFrameToOthers();
+ fCurTime += fThisDuration;
+ m_flFrameSpan[nSequenceNumber] = fCurTime;
+ }
+ // now, fill in the whole table
+ for( int nIdx = 0; nIdx < nTimeSamples; nIdx++ )
+ {
+ float flIdxA, flIdxB, flInterp;
+ GetInterpolationData( InterpKnot, InterpValue, nFrameCount,
+ SEQUENCE_SAMPLE_COUNT,
+ nIdx,
+ ! ( m_bClamp[nSequenceNumber] ),
+ &flIdxA, &flIdxB, &flInterp );
+ SheetSequenceSample_t sA = Samples[(int) flIdxA];
+ SheetSequenceSample_t sB = Samples[(int) flIdxB];
+ SheetSequenceSample_t &oseq = m_pSamples[nSequenceNumber][nIdx];
+
+ oseq.m_fBlendFactor = flInterp;
+ for(int nImage = 0 ; nImage< MAX_IMAGES_PER_FRAME_IN_MEMORY; nImage++ )
+ {
+ SequenceSampleTextureCoords_t &src0=sA.m_TextureCoordData[nImage];
+ SequenceSampleTextureCoords_t &src1=sB.m_TextureCoordData[nImage];
+ SequenceSampleTextureCoords_t &o=oseq.m_TextureCoordData[nImage];
+ o.m_fLeft_U0 = src0.m_fLeft_U0;
+ o.m_fTop_V0 = src0.m_fTop_V0;
+ o.m_fRight_U0 = src0.m_fRight_U0;
+ o.m_fBottom_V0 = src0.m_fBottom_V0;
+ o.m_fLeft_U1 = src1.m_fLeft_U0;
+ o.m_fTop_V1 = src1.m_fTop_V0;
+ o.m_fRight_U1 = src1.m_fRight_U0;
+ o.m_fBottom_V1 = src1.m_fBottom_V0;
+ }
+ }
+ }
+ // now, fill in all unseen sequences with copies of the first seen sequence to prevent crashes
+ // while editing
+ int nFirstSequence = -1;
+ for(int i=0 ; i<MAX_SEQUENCES ; i++)
+ {
+ if ( m_pSamples[i] )
+ {
+ nFirstSequence = i;
+ break;
+ }
+ }
+
+ if ( nFirstSequence != -1 )
+ {
+ for(int i=0 ; i<MAX_SEQUENCES ; i++)
+ {
+ if ( m_pSamples[i] == NULL )
+ {
+ m_pSamples[i] = m_pSamples[nFirstSequence];
+ m_bClamp[i]= m_bClamp[nFirstSequence];
+ m_nNumFrames[i] = m_nNumFrames[nFirstSequence];
+ m_bSequenceIsCopyOfAnotherSequence[i] = true;
+ }
+ }
+ }
+}
+
+CSheet::~CSheet( void )
+{
+ for( int i=0; i<NELEMS(m_pSamples); i++ )
+ {
+ if ( m_pSamples[i] && ( ! m_bSequenceIsCopyOfAnotherSequence[i] ) )
+ {
+ delete[] m_pSamples[i];
+ }
+ }
+}
diff --git a/particles/psheet.h b/particles/psheet.h
new file mode 100644
index 0000000..9614f75
--- /dev/null
+++ b/particles/psheet.h
@@ -0,0 +1,84 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: sheet definitions for particles and other sprite functions
+//
+//===========================================================================//
+
+#ifndef PSHEET_H
+#define PSHEET_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier1/utlobjectreference.h"
+
+class CUtlBuffer;
+
+// classes for keeping a dictionary of sheet files in memory. A sheet is a bunch of frames packewd
+// within one texture. Each sheet has 1 or more frame sequences stored for it.
+
+
+// for fast lookups to retrieve sequence data, we store the sequence information discretized into
+// a fixed # of frames. If this discretenesss is a visual problem, you can lerp the blend values to get it
+// perfect.
+#define SEQUENCE_SAMPLE_COUNT 1024
+
+#define MAX_SEQUENCES 64
+#define MAX_IMAGES_PER_FRAME_ON_DISK 4
+#define MAX_IMAGES_PER_FRAME_IN_MEMORY 2
+
+struct SequenceSampleTextureCoords_t
+{
+ float m_fLeft_U0;
+ float m_fTop_V0;
+ float m_fRight_U0;
+ float m_fBottom_V0;
+
+ float m_fLeft_U1;
+ float m_fTop_V1;
+ float m_fRight_U1;
+ float m_fBottom_V1;
+};
+
+struct SheetSequenceSample_t
+{
+ // coordinates of two rectangles (old and next frame coords)
+
+ SequenceSampleTextureCoords_t m_TextureCoordData[MAX_IMAGES_PER_FRAME_IN_MEMORY];
+
+ float m_fBlendFactor;
+
+ void CopyFirstFrameToOthers(void)
+ {
+ // for old format files only supporting one image per frame
+ for(int i=1; i < MAX_IMAGES_PER_FRAME_IN_MEMORY; i++)
+ {
+ m_TextureCoordData[i] = m_TextureCoordData[0];
+ }
+ }
+
+};
+
+class CSheet
+{
+public:
+ // read form a .sht file. This is the usual thing to do
+ CSheet( CUtlBuffer &buf );
+ CSheet( void );
+ ~CSheet( void );
+
+ // references for smart ptrs
+ CUtlReferenceList<CSheet> m_References;
+
+ SheetSequenceSample_t *m_pSamples[MAX_SEQUENCES];
+ bool m_bClamp[MAX_SEQUENCES];
+ bool m_bSequenceIsCopyOfAnotherSequence[MAX_SEQUENCES];
+ int m_nNumFrames[MAX_SEQUENCES];
+ float m_flFrameSpan[MAX_SEQUENCES];
+
+};
+
+
+#endif // PSHEET_H
+
diff --git a/particles/random_floats.h b/particles/random_floats.h
new file mode 100644
index 0000000..8054e3d
--- /dev/null
+++ b/particles/random_floats.h
@@ -0,0 +1,537 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: static random floats for deterministic random #s in particle system
+//
+// $Workfile: $
+// $NoKeywords: $
+//===========================================================================//
+//
+// **** DO NOT EDIT THIS FILE. GENERATED BY DATAGEN.PL ****
+//
+#ifndef RANDOM_FLOATS_H
+#define RANDOM_FLOATS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+ALIGN16 float s_pRandomFloats[]={
+ 0.336914,0.671387,0.539307,0.165039,0.258301,0.146973,0.475342,0.993408,
+ 0.882080,0.733887,0.152588,0.192871,0.873291,0.637939,0.859131,0.836914,
+ 0.277344,0.026367,0.864502,0.327393,0.428711,0.684082,0.916992,0.935547,
+ 0.643311,0.082764,0.033447,0.086182,0.575684,0.121826,0.342285,0.419189,
+ 0.409424,0.177490,0.049072,0.252930,0.459717,0.561035,0.581543,0.470703,
+ 0.662842,0.936035,0.912354,0.965332,0.523682,0.661865,0.484131,0.030029,
+ 0.958984,0.370605,0.626709,0.736084,0.241943,0.307129,0.969482,0.269287,
+ 0.215820,0.995605,0.300781,0.984375,0.621338,0.785645,0.432373,0.895264,
+ 0.098389,0.857178,0.220947,0.981689,0.667969,0.898682,0.603027,0.588623,
+ 0.206055,0.536133,0.245361,0.999023,0.758545,0.152100,0.015869,0.546387,
+ 0.101563,0.920410,0.566650,0.793457,0.077148,0.675049,0.032715,0.645264,
+ 0.654053,0.536377,0.599121,0.300537,0.404053,0.115967,0.646484,0.162842,
+ 0.886230,0.412109,0.552490,0.632080,0.623291,0.846436,0.313965,0.505859,
+ 0.487305,0.644287,0.517578,0.892090,0.146240,0.218750,0.484619,0.904785,
+ 0.551758,0.809814,0.721191,0.871582,0.847656,0.062256,0.647949,0.708252,
+ 0.690186,0.593750,0.061768,0.406982,0.169434,0.874756,0.139893,0.792236,
+ 0.783691,0.855957,0.198975,0.706299,0.594971,0.739746,0.307373,0.123291,
+ 0.480713,0.027100,0.697266,0.534424,0.070068,0.641602,0.472900,0.598145,
+ 0.211670,0.977051,0.145264,0.311279,0.975098,0.904297,0.999268,0.725586,
+ 0.039307,0.669434,0.578369,0.285889,0.867188,0.982666,0.797119,0.755371,
+ 0.950195,0.383789,0.426270,0.337402,0.075439,0.841553,0.794678,0.074707,
+ 0.021240,0.627686,0.036377,0.921143,0.229248,0.208740,0.698730,0.622803,
+ 0.943848,0.673828,0.826416,0.107910,0.366943,0.352539,0.256592,0.930176,
+ 0.556885,0.378418,0.205566,0.286133,0.623047,0.986328,0.781494,0.974609,
+ 0.239014,0.883789,0.859375,0.884277,0.856445,0.521484,0.332764,0.489746,
+ 0.880615,0.553955,0.961182,0.360352,0.489990,0.175781,0.777832,0.020020,
+ 0.346436,0.250732,0.094482,0.900146,0.185547,0.317139,0.686768,0.517334,
+ 0.350342,0.774902,0.978027,0.819580,0.967773,0.811523,0.038574,0.791504,
+ 0.196045,0.543701,0.705566,0.586426,0.585449,0.016846,0.413086,0.583496,
+ 0.772461,0.728271,0.289307,0.482666,0.389404,0.302002,0.331055,0.759277,
+ 0.885742,0.745605,0.444092,0.341797,0.607422,0.436279,0.102783,0.874023,
+ 0.868408,0.662109,0.287354,0.918701,0.892334,0.685791,0.500244,0.433594,
+ 0.664795,0.734863,0.933350,0.078369,0.808105,0.790039,0.404541,0.005371,
+ 0.976318,0.800537,0.482178,0.472412,0.656494,0.835449,0.713135,0.629395,
+ 0.468506,0.038330,0.143555,0.375000,0.140381,0.427734,0.822754,0.267090,
+ 0.253662,0.683350,0.788818,0.531738,0.214600,0.877930,0.659668,0.640381,
+ 0.954346,0.227539,0.167969,0.491211,0.084473,0.272949,0.961670,0.065430,
+ 0.696045,0.625732,0.177734,0.782715,0.934814,0.385742,0.401611,0.018555,
+ 0.686279,0.749023,0.114014,0.106689,0.501709,0.471924,0.151855,0.088623,
+ 0.677734,0.425293,0.910645,0.483398,0.872314,0.782471,0.163330,0.574463,
+ 0.119385,0.809570,0.947510,0.197510,0.887207,0.577637,0.021484,0.020508,
+ 0.641846,0.321533,0.957520,0.893555,0.625488,0.424072,0.612793,0.003906,
+ 0.060059,0.388184,0.724365,0.195068,0.263184,0.919434,0.094971,0.550781,
+ 0.997559,0.763916,0.300293,0.530029,0.583984,0.767578,0.745361,0.691650,
+ 0.170410,0.762695,0.949463,0.349365,0.757324,0.302490,0.498047,0.397217,
+ 0.213379,0.019775,0.587402,0.562988,0.573975,0.360840,0.320313,0.258545,
+ 0.948486,0.186279,0.976563,0.190430,0.865723,0.162598,0.547607,0.339111,
+ 0.392090,0.273193,0.714844,0.797363,0.403564,0.449219,0.747314,0.844238,
+ 0.835205,0.110596,0.134033,0.214355,0.766113,0.109131,0.013184,0.875000,
+ 0.737061,0.320801,0.639893,0.744141,0.751465,0.127197,0.011963,0.353027,
+ 0.693359,0.440674,0.264160,0.780029,0.529785,0.800781,0.671143,0.332031,
+ 0.400635,0.981445,0.770264,0.435059,0.298340,0.437988,0.163574,0.271973,
+ 0.039551,0.043457,0.866943,0.009766,0.651123,0.846680,0.653076,0.125732,
+ 0.530762,0.868896,0.268799,0.958008,0.142578,0.461914,0.303955,0.010986,
+ 0.620850,0.522217,0.510986,0.338135,0.914551,0.411865,0.604004,0.706543,
+ 0.238525,0.510254,0.103271,0.632324,0.422119,0.440186,0.265137,0.277100,
+ 0.221924,0.201172,0.599365,0.244629,0.177002,0.293945,0.368652,0.649902,
+ 0.100586,0.622559,0.905762,0.209961,0.257324,0.249512,0.428467,0.369141,
+ 0.166016,0.635986,0.798340,0.940918,0.993896,0.803467,0.320068,0.929443,
+ 0.650146,0.058594,0.065918,0.609375,0.616943,0.379639,0.221191,0.200684,
+ 0.486084,0.437500,0.871338,0.795898,0.863037,0.700684,0.517090,0.821533,
+ 0.319092,0.397461,0.852295,0.864014,0.591797,0.514404,0.925049,0.715820,
+ 0.384766,0.734131,0.899170,0.703125,0.210938,0.783203,0.687012,0.850586,
+ 0.532471,0.025635,0.975342,0.852051,0.612305,0.910889,0.159668,0.407227,
+ 0.669678,0.096924,0.733398,0.304688,0.015625,0.372803,0.329590,0.858398,
+ 0.033203,0.830811,0.914307,0.022949,0.451416,0.106201,0.664551,0.895752,
+ 0.366455,0.230225,0.595459,0.296631,0.291992,0.375488,0.952393,0.052734,
+ 0.132813,0.549805,0.828125,0.065674,0.318848,0.031494,0.605713,0.534912,
+ 0.611328,0.259766,0.445068,0.660156,0.848633,0.055176,0.323730,0.122803,
+ 0.697510,0.097412,0.989014,0.348877,0.536865,0.442139,0.269043,0.762451,
+ 0.926270,0.118896,0.458740,0.496094,0.253906,0.084717,0.648926,0.754639,
+ 0.648438,0.488281,0.533691,0.048828,0.050781,0.847168,0.431152,0.638184,
+ 0.938232,0.092285,0.970215,0.037354,0.852539,0.586670,0.729248,0.227295,
+ 0.482910,0.051025,0.135254,0.082520,0.812500,0.391602,0.598389,0.605957,
+ 0.949707,0.464600,0.863770,0.024902,0.989502,0.937012,0.435547,0.565186,
+ 0.370850,0.801758,0.143311,0.354492,0.715088,0.423828,0.796387,0.384033,
+ 0.093018,0.813965,0.356201,0.535889,0.229492,0.553711,0.080811,0.711426,
+ 0.082275,0.713623,0.097168,0.847900,0.197754,0.057617,0.970703,0.282959,
+ 0.252686,0.082031,0.847412,0.274658,0.806641,0.027588,0.844727,0.748535,
+ 0.667480,0.200195,0.814941,0.912598,0.463379,0.304199,0.815918,0.481445,
+ 0.345947,0.936279,0.851318,0.796631,0.119141,0.328369,0.185303,0.911621,
+ 0.527100,0.342529,0.652100,0.435303,0.537354,0.051758,0.998535,0.597168,
+ 0.031006,0.056396,0.003174,0.702637,0.262207,0.433838,0.052002,0.776367,
+ 0.993652,0.836182,0.055908,0.694092,0.768311,0.073242,0.908203,0.548584,
+ 0.873535,0.205078,0.551514,0.726074,0.302979,0.136475,0.381348,0.702393,
+ 0.054932,0.796143,0.079346,0.049561,0.539795,0.731201,0.175049,0.570068,
+ 0.171631,0.899902,0.467041,0.623779,0.913086,0.386230,0.451904,0.878418,
+ 0.880371,0.316406,0.468994,0.145752,0.920166,0.162354,0.944092,0.816162,
+ 0.887695,0.802246,0.546631,0.803955,0.491455,0.064453,0.284424,0.153564,
+ 0.291260,0.730713,0.173584,0.942383,0.640137,0.480469,0.552979,0.413818,
+ 0.222656,0.364502,0.736816,0.351074,0.245605,0.183105,0.697754,0.308350,
+ 0.467285,0.333740,0.400391,0.301514,0.601074,0.874512,0.571289,0.994873,
+ 0.438721,0.865234,0.795410,0.476807,0.409912,0.651855,0.375244,0.139404,
+ 0.776611,0.713379,0.406494,0.359619,0.635742,0.261963,0.210449,0.669922,
+ 0.612061,0.363281,0.013916,0.504639,0.113770,0.814697,0.826660,0.016602,
+ 0.079834,0.668945,0.453125,0.591553,0.258789,0.345703,0.271240,0.198486,
+ 0.239258,0.975830,0.203613,0.974121,0.408691,0.775391,0.071777,0.503418,
+ 0.483887,0.223877,0.805420,0.813721,0.326660,0.327148,0.616455,0.615479,
+ 0.430176,0.153076,0.979736,0.611572,0.543457,0.095459,0.897461,0.406250,
+ 0.657471,0.108887,0.002930,0.959961,0.396973,0.901367,0.572998,0.056152,
+ 0.630371,0.762939,0.910156,0.965820,0.397705,0.171143,0.401367,0.453857,
+ 0.493896,0.837158,0.636719,0.596436,0.645752,0.894043,0.990479,0.885254,
+ 0.863525,0.650391,0.761963,0.947266,0.573730,0.941650,0.212891,0.805664,
+ 0.153809,0.957275,0.044678,0.743408,0.579590,0.240967,0.172363,0.056641,
+ 0.327637,0.787842,0.431885,0.652344,0.616699,0.629150,0.893311,0.691406,
+ 0.054199,0.840088,0.930664,0.708008,0.398193,0.988770,0.959717,0.666992,
+ 0.000244,0.696777,0.365967,0.547119,0.127930,0.141113,0.348633,0.531494,
+ 0.584961,0.398926,0.728516,0.709229,0.391113,0.265869,0.244141,0.972900,
+ 0.663086,0.258057,0.317871,0.740723,0.704834,0.138672,0.819092,0.802002,
+ 0.563232,0.072754,0.085938,0.868652,0.521240,0.206543,0.696533,0.420654,
+ 0.897705,0.783936,0.067627,0.686035,0.537598,0.660400,0.784180,0.292725,
+ 0.444336,0.128662,0.862793,0.260742,0.789551,0.820557,0.319824,0.045166,
+ 0.483154,0.533203,0.022705,0.249756,0.201660,0.124023,0.966064,0.117920,
+ 0.129395,0.241699,0.585205,0.481201,0.904053,0.627441,0.979004,0.041992,
+ 0.619141,0.525879,0.682373,0.497803,0.907471,0.303711,0.562012,0.495117,
+ 0.500488,0.674072,0.552734,0.249023,0.088867,0.293213,0.221680,0.457031,
+ 0.187988,0.950439,0.453369,0.014160,0.881348,0.050049,0.686523,0.476318,
+ 0.524170,0.552246,0.077393,0.621826,0.829834,0.030518,0.295654,0.215088,
+ 0.438965,0.271729,0.384277,0.580322,0.191895,0.096680,0.281982,0.984131,
+ 0.675537,0.638672,0.329834,0.426025,0.768799,0.484375,0.287598,0.694824,
+ 0.822510,0.351563,0.736328,0.905029,0.755127,0.661377,0.138916,0.236572,
+ 0.662354,0.299316,0.906250,0.771240,0.680420,0.039063,0.029297,0.246338,
+ 0.876221,0.506348,0.474365,0.476074,0.289063,0.514648,0.751221,0.909424,
+ 0.921631,0.121094,0.628174,0.171875,0.881836,0.819824,0.002441,0.955811,
+ 0.407715,0.288818,0.690918,0.193359,0.090088,0.461670,0.057861,0.597412,
+ 0.247559,0.479492,0.222900,0.106934,0.147705,0.750977,0.322754,0.435791,
+ 0.672852,0.795654,0.036621,0.125488,0.036865,0.224854,0.012695,0.023438,
+ 0.191650,0.816650,0.381592,0.448975,0.112305,0.944336,0.017822,0.645020,
+ 0.113037,0.881104,0.157715,0.692383,0.331299,0.177979,0.771729,0.223145,
+ 0.854004,0.698242,0.821777,0.341553,0.638428,0.161133,0.040039,0.592529,
+ 0.679932,0.558350,0.363770,0.423584,0.710938,0.219482,0.738037,0.339844,
+ 0.433105,0.104736,0.653320,0.688965,0.087402,0.593994,0.387451,0.111328,
+ 0.254639,0.346680,0.615234,0.588379,0.273682,0.141602,0.418701,0.294678,
+ 0.569336,0.442383,0.598877,0.075684,0.677979,0.761230,0.919189,0.861572,
+ 0.320557,0.790771,0.064697,0.144531,0.934570,0.721436,0.684814,0.251465,
+ 0.762207,0.767090,0.528076,0.385498,0.269531,0.879639,0.576416,0.419434,
+ 0.875732,0.672607,0.823242,0.785156,0.877686,0.432861,0.416260,0.066406,
+ 0.233643,0.703857,0.719482,0.352783,0.837646,0.615967,0.593018,0.137451,
+ 0.199951,0.712891,0.885498,0.923828,0.777588,0.669189,0.546875,0.400146,
+ 0.374023,0.455322,0.577881,0.328125,0.954590,0.680908,0.836426,0.699219,
+ 0.803711,0.350586,0.259277,0.878174,0.798096,0.904541,0.314453,0.820313,
+ 0.949951,0.125000,0.782227,0.008057,0.303467,0.976074,0.189941,0.216064,
+ 0.069092,0.070557,0.680176,0.983643,0.182861,0.656250,0.133545,0.389893,
+ 0.277588,0.229004,0.148438,0.193115,0.562500,0.835938,0.172607,0.550293,
+ 0.290527,0.146729,0.334229,0.334717,0.707031,0.023193,0.969727,0.593262,
+ 0.708496,0.884766,0.329346,0.362305,0.305176,0.072021,0.591309,0.135010,
+ 0.434570,0.394287,0.239746,0.532715,0.751709,0.760986,0.083740,0.763184,
+ 0.765381,0.230713,0.312988,0.819336,0.338623,0.619385,0.152832,0.971436,
+ 0.656982,0.585693,0.089844,0.513184,0.732178,0.548828,0.980469,0.022461,
+ 0.340820,0.411377,0.829346,0.644531,0.804443,0.086914,0.468018,0.368164,
+ 0.243408,0.824463,0.350830,0.416748,0.025879,0.156494,0.989990,0.129150,
+ 0.814453,0.574707,0.790527,0.296387,0.510498,0.990234,0.480957,0.565430,
+ 0.550537,0.461182,0.359863,0.966553,0.316650,0.187500,0.032959,0.460693,
+ 0.692871,0.294434,0.857666,0.217041,0.128174,0.207764,0.309814,0.846924,
+ 0.531982,0.047607,0.555664,0.198730,0.049316,0.576904,0.709473,0.518799,
+ 0.069336,0.601563,0.228516,0.048096,0.479980,0.108643,0.080566,0.386719,
+ 0.535400,0.465088,0.472168,0.105957,0.310059,0.489014,0.987549,0.406006,
+ 0.673096,0.366211,0.681885,0.509766,0.600342,0.506592,0.558594,0.875488,
+ 0.413574,0.083984,0.015137,0.559082,0.093750,0.805176,0.933838,0.775635,
+ 0.826172,0.451660,0.779785,0.158691,0.346191,0.601318,0.407471,0.545410,
+ 0.712646,0.845947,0.741211,0.337891,0.393799,0.407959,0.095703,0.817139,
+ 0.198242,0.818359,0.272461,0.051270,0.350098,0.445557,0.485352,0.168945,
+ 0.855469,0.427246,0.189453,0.646973,0.648193,0.717041,0.737305,0.222168,
+ 0.704346,0.235352,0.126221,0.617676,0.108154,0.287842,0.237549,0.544678,
+ 0.761719,0.832764,0.781250,0.501221,0.168213,0.262451,0.325684,0.396240,
+ 0.970459,0.631348,0.134277,0.337158,0.006592,0.217529,0.333496,0.698486,
+ 0.810059,0.391846,0.361572,0.265625,0.919922,0.408447,0.299561,0.524414,
+ 0.076660,0.233887,0.147217,0.247803,0.720459,0.655273,0.178711,0.642822,
+ 0.076904,0.408936,0.113281,0.201416,0.730957,0.200439,0.885010,0.100342,
+ 0.369385,0.395264,0.542480,0.940430,0.684326,0.666504,0.228760,0.699463,
+ 0.127441,0.212646,0.835693,0.029785,0.656006,0.354736,0.278564,0.339600,
+ 0.549561,0.149658,0.593506,0.298096,0.492432,0.447754,0.264648,0.634766,
+ 0.166992,0.515137,0.382568,0.142822,0.783447,0.953125,0.439697,0.267822,
+ 0.121338,0.735840,0.087891,0.507568,0.595703,0.655518,0.610107,0.541748,
+ 0.645508,0.057373,0.624512,0.191406,0.832031,0.702148,0.330566,0.087646,
+ 0.242188,0.099854,0.307861,0.430908,0.684570,0.939453,0.031250,0.924805,
+ 0.462646,0.120361,0.545654,0.789063,0.719971,0.072266,0.353271,0.996826,
+ 0.627930,0.093994,0.920654,0.058105,0.430664,0.403076,0.448486,0.589111,
+ 0.524658,0.148682,0.671875,0.470947,0.851807,0.367188,0.749268,0.746582,
+ 0.429199,0.466064,0.519531,0.645996,0.125977,0.549072,0.547363,0.294189,
+ 0.254883,0.634277,0.663330,0.528320,0.178955,0.730225,0.063232,0.979248,
+ 0.733154,0.543945,0.668213,0.228027,0.896729,0.167480,0.551270,0.174316,
+ 0.271484,0.519043,0.310791,0.664307,0.984619,0.534668,0.541992,0.604492,
+ 0.946045,0.870117,0.161865,0.660889,0.587646,0.770996,0.573486,0.035156,
+ 0.895996,0.812988,0.452881,0.854248,0.404297,0.371094,0.561768,0.132324,
+ 0.009033,0.954834,0.522461,0.134521,0.538574,0.145996,0.968994,0.620117,
+ 0.582275,0.609863,0.325195,0.620361,0.070801,0.633301,0.116455,0.236084,
+ 0.485840,0.519287,0.264404,0.008301,0.632568,0.091553,0.142334,0.050537,
+ 0.358154,0.126953,0.572021,0.471436,0.017334,0.754883,0.735596,0.313232,
+ 0.780518,0.903809,0.314209,0.324951,0.188721,0.586182,0.421875,0.565918,
+ 0.179443,0.723389,0.242676,0.592773,0.818604,0.501465,0.092529,0.126709,
+ 0.250244,0.787354,0.104248,0.758301,0.395508,0.804932,0.090820,0.828857,
+ 0.014893,0.899414,0.716797,0.942871,0.839111,0.493164,0.625244,0.867432,
+ 0.504150,0.718750,0.725098,0.365479,0.256104,0.653564,0.355469,0.808838,
+ 0.933594,0.525146,0.808350,0.454346,0.775879,0.184326,0.052979,0.658936,
+ 0.243896,0.799072,0.603516,0.854980,0.947021,0.004639,0.503174,0.003418,
+ 0.682617,0.492920,0.887451,0.924316,0.475098,0.766602,0.917480,0.964111,
+ 0.971191,0.145020,0.071289,0.722900,0.144775,0.335205,0.613770,0.643066,
+ 0.186035,0.081299,0.393311,0.123047,0.261719,0.196533,0.433350,0.263916,
+ 0.935303,0.278076,0.732422,0.328613,0.937988,0.706787,0.467529,0.203857,
+ 0.279541,0.957764,0.600586,0.226074,0.213867,0.563721,0.132568,0.802490,
+ 0.318359,0.035400,0.606201,0.721680,0.735352,0.917725,0.045898,0.173828,
+ 0.486328,0.241211,0.243164,0.181152,0.637695,0.670166,0.964844,0.367676,
+ 0.826904,0.903076,0.457764,0.041260,0.161377,0.955322,0.752197,0.769043,
+ 0.163818,0.117188,0.971924,0.611084,0.560303,0.319580,0.194824,0.325928,
+ 0.477051,0.264893,0.043213,0.002686,0.690430,0.896240,0.409180,0.765137,
+ 0.741455,0.689941,0.860107,0.704590,0.983887,0.869629,0.452637,0.376953,
+ 0.839355,0.951416,0.378906,0.042969,0.287109,0.064209,0.195801,0.494385,
+ 0.720947,0.139648,0.570313,0.250000,0.181396,0.461426,0.921875,0.939209,
+ 0.690674,0.393555,0.693115,0.614502,0.322266,0.720215,0.636475,0.840820,
+ 0.958496,0.931641,0.063965,0.770020,0.298828,0.606445,0.886719,0.506104,
+ 0.958740,0.525635,0.297119,0.567871,0.358643,0.365234,0.187012,0.570801,
+ 0.116211,0.786621,0.336182,0.388428,0.913330,0.494141,0.696289,0.848145,
+ 0.699951,0.596680,0.276123,0.015381,0.685303,0.677490,0.463623,0.866699,
+ 0.283447,0.909912,0.215332,0.062012,0.495361,0.307617,0.465820,0.005127,
+ 0.937256,0.606934,0.465332,0.379150,0.685547,0.111084,0.753662,0.516113,
+ 0.412354,0.058350,0.553223,0.181885,0.963867,0.639648,0.502930,0.018799,
+ 0.588867,0.974365,0.657227,0.122559,0.773926,0.526367,0.243652,0.254150,
+ 0.618408,0.589355,0.657959,0.209229,0.670898,0.621582,0.523926,0.572510,
+ 0.053223,0.093262,0.187744,0.291504,0.083496,0.229980,0.109375,0.340332,
+ 0.496582,0.659424,0.626221,0.183350,0.059814,0.711914,0.526611,0.425781,
+ 0.995361,0.784424,0.170654,0.275146,0.402344,0.972656,0.624268,0.923584,
+ 0.744385,0.902100,0.361816,0.682861,0.971680,0.441895,0.186768,0.144043,
+ 0.801270,0.853760,0.099121,0.584717,0.443604,0.423096,0.855225,0.976807,
+ 0.326904,0.614014,0.337646,0.821289,0.109863,0.405762,0.000732,0.157471,
+ 0.377441,0.987793,0.766846,0.059570,0.434814,0.186523,0.332275,0.214844,
+ 0.072510,0.640625,0.280029,0.105469,0.280762,0.946533,0.845703,0.028320,
+ 0.044922,0.603760,0.059082,0.255371,0.143799,0.678711,0.182129,0.085205,
+ 0.915771,0.610352,0.457275,0.617188,0.060547,0.774658,0.829590,0.854492,
+ 0.313721,0.592041,0.156006,0.469482,0.743896,0.750732,0.004150,0.352051,
+ 0.853271,0.313477,0.456543,0.841797,0.007080,0.389648,0.849365,0.942627,
+ 0.561279,0.391357,0.214111,0.164551,0.554199,0.246582,0.012939,0.364746,
+ 0.104980,0.046387,0.002197,0.420410,0.802979,0.156738,0.028076,0.115479,
+ 0.405029,0.708984,0.782959,0.851074,0.748291,0.088135,0.681152,0.099365,
+ 0.624756,0.338379,0.642090,0.221436,0.997314,0.548340,0.259521,0.464111,
+ 0.016113,0.793945,0.773193,0.930908,0.316895,0.522705,0.755615,0.361328,
+ 0.923096,0.323242,0.487061,0.474854,0.800293,0.025391,0.417236,0.895020,
+ 0.700195,0.508789,0.031982,0.467773,0.890137,0.450684,0.710693,0.204834,
+ 0.724121,0.707764,0.425537,0.233154,0.716064,0.668701,0.255615,0.388672,
+ 0.893799,0.224609,0.978516,0.195557,0.145508,0.499268,0.890625,0.495850,
+ 0.362793,0.900879,0.902344,0.882324,0.811279,0.579346,0.128418,0.126465,
+ 0.283936,0.406738,0.588135,0.281738,0.358887,0.946289,0.641357,0.616211,
+ 0.747559,0.651611,0.872070,0.858887,0.834717,0.694336,0.005615,0.744873,
+ 0.836670,0.643799,0.602539,0.020996,0.107422,0.055664,0.961426,0.900391,
+ 0.867676,0.133057,0.995850,0.737549,0.807617,0.289795,0.354004,0.972168,
+ 0.139160,0.582520,0.497314,0.514893,0.759033,0.778564,0.128906,0.862549,
+ 0.600830,0.070313,0.966797,0.097900,0.252197,0.758789,0.529541,0.840576,
+ 0.373291,0.268066,0.728760,0.376221,0.394775,0.434082,0.840332,0.729004,
+ 0.759521,0.908691,0.884521,0.504883,0.460449,0.288086,0.983398,0.213623,
+ 0.816406,0.716553,0.629883,0.414307,0.158936,0.828369,0.008545,0.780762,
+ 0.364014,0.204102,0.465576,0.675781,0.666260,0.503906,0.299072,0.171387,
+ 0.511475,0.072998,0.809326,0.248291,0.182373,0.853516,0.364258,0.466797,
+ 0.399902,0.001465,0.722412,0.830566,0.115234,0.740967,0.566406,0.068115,
+ 0.140869,0.107178,0.242432,0.290039,0.079590,0.591064,0.620605,0.515381,
+ 0.229736,0.770752,0.707275,0.455811,0.114502,0.485596,0.628418,0.590088,
+ 0.175293,0.289551,0.077637,0.787109,0.042480,0.357666,0.790283,0.512451,
+ 0.447998,0.825195,0.724609,0.893066,0.769287,0.739014,0.101318,0.152344,
+ 0.096436,0.752441,0.494873,0.020752,0.297852,0.036133,0.907227,0.477295,
+ 0.869385,0.742920,0.952148,0.714600,0.053711,0.687744,0.660645,0.633545,
+ 0.439453,0.888428,0.908936,0.443848,0.030273,0.885986,0.284912,0.238281,
+ 0.933105,0.343750,0.963623,0.194580,0.996338,0.473877,0.545166,0.931885,
+ 0.399658,0.871826,0.398438,0.410400,0.344482,0.921387,0.850830,0.944580,
+ 0.250977,0.150879,0.834229,0.267578,0.734375,0.939697,0.913574,0.636230,
+ 0.283691,0.948730,0.306885,0.557861,0.764648,0.253418,0.582764,0.505615,
+ 0.340088,0.261230,0.727783,0.016357,0.658447,0.559326,0.102051,0.714111,
+ 0.535645,0.650635,0.622314,0.383545,0.916748,0.955566,0.116943,0.018311,
+ 0.750244,0.208252,0.773438,0.281006,0.095215,0.928467,0.426514,0.177246,
+ 0.219727,0.367920,0.495605,0.443115,0.380127,0.175537,0.452148,0.641113,
+ 0.786133,0.602295,0.784668,0.034912,0.061523,0.962891,0.715332,0.580078,
+ 0.363525,0.533936,0.009277,0.740479,0.285156,0.402832,0.821045,0.441406,
+ 0.568848,0.336670,0.185791,0.211182,0.218994,0.492676,0.205811,0.655029,
+ 0.784912,0.676270,0.709717,0.335449,0.081787,0.529053,0.794434,0.301270,
+ 0.449951,0.929932,0.232178,0.717773,0.548096,0.644775,0.590820,0.774170,
+ 0.458252,0.560059,0.117432,0.007324,0.630859,0.096191,0.182617,0.905273,
+ 0.848389,0.587158,0.699707,0.390137,0.618896,0.597656,0.812744,0.998291,
+ 0.590576,0.991699,0.838379,0.331543,0.166504,0.993164,0.525391,0.811768,
+ 0.629639,0.312744,0.285400,0.608643,0.512939,0.621094,0.953369,0.965576,
+ 0.223633,0.295410,0.133301,0.267334,0.678223,0.439941,0.010010,0.693604,
+ 0.619629,0.306641,0.475830,0.952637,0.074951,0.218262,0.873047,0.469238,
+ 0.236328,0.791992,0.310303,0.355957,0.450928,0.274170,0.509033,0.421143,
+ 0.683105,0.839600,0.466309,0.986084,0.516602,0.691895,0.105713,0.724854,
+ 0.695068,0.026123,0.579834,0.617432,0.107666,0.208984,0.496338,0.673584,
+ 0.448242,0.940674,0.999756,0.384521,0.053955,0.807373,0.165283,0.906006,
+ 0.838623,0.844482,0.335693,0.483643,0.133789,0.437012,0.778809,0.544434,
+ 0.755859,0.508301,0.712402,0.705322,0.634033,0.129883,0.013672,0.377686,
+ 0.661133,0.456787,0.888672,0.947998,0.043701,0.356445,0.366699,0.470215,
+ 0.176270,0.639160,0.239502,0.557373,0.342773,0.968506,0.185059,0.518311,
+ 0.378174,0.279053,0.663818,0.673340,0.001221,0.437744,0.188232,0.317383,
+ 0.203369,0.252441,0.226318,0.188477,0.160400,0.703613,0.265381,0.940186,
+ 0.833252,0.636963,0.429932,0.638916,0.208008,0.968262,0.343262,0.130127,
+ 0.650879,0.563965,0.689697,0.044434,0.387207,0.447021,0.773682,0.151611,
+ 0.463867,0.210693,0.889893,0.674316,0.261475,0.927734,0.903320,0.124268,
+ 0.713867,0.234619,0.992432,0.119629,0.606689,0.183838,0.555176,0.266113,
+ 0.618652,0.402100,0.204590,0.083008,0.995117,0.226563,0.417725,0.633789,
+ 0.581299,0.906494,0.948975,0.561523,0.679443,0.768555,0.095947,0.667725,
+ 0.376709,0.075928,0.827637,0.118164,0.502197,0.057129,0.871094,0.602051,
+ 0.891113,0.206299,0.890381,0.581787,0.353760,0.371338,0.653809,0.834473,
+ 0.917236,0.856201,0.939941,0.273926,0.834961,0.322021,0.382080,0.990967,
+ 0.281250,0.564453,0.633057,0.001709,0.614990,0.502686,0.838867,0.219238,
+ 0.266846,0.355225,0.062500,0.075195,0.703369,0.071045,0.907715,0.260254,
+ 0.160645,0.216553,0.863281,0.277832,0.623535,0.883301,0.099609,0.197998,
+ 0.000977,0.590332,0.488037,0.259033,0.918213,0.427979,0.599854,0.263428,
+ 0.913818,0.365723,0.327881,0.912842,0.837891,0.815186,0.255127,0.764893,
+ 0.760254,0.093506,0.739990,0.830322,0.067383,0.138428,0.535156,0.680664,
+ 0.103516,0.817871,0.200928,0.929688,0.248535,0.180664,0.781982,0.966309,
+ 0.860840,0.469727,0.576660,0.436035,0.167236,0.975586,0.450439,0.027832,
+ 0.897217,0.679688,0.666748,0.060303,0.131592,0.400879,0.125244,0.142090,
+ 0.706055,0.402588,0.963379,0.711182,0.831299,0.697998,0.898193,0.532959,
+ 0.530273,0.356689,0.244873,0.837402,0.300049,0.440918,0.157227,0.911377,
+ 0.625000,0.151367,0.917969,0.962646,0.179932,0.471191,0.286377,0.475586,
+ 0.034668,0.748779,0.208496,0.124512,0.471680,0.742432,0.777100,0.812012,
+ 0.991943,0.344727,0.822021,0.729736,0.559570,0.276367,0.906982,0.449707,
+ 0.066650,0.383301,0.769775,0.416504,0.654297,0.988525,0.358398,0.538086,
+ 0.401855,0.385986,0.225586,0.698975,0.909180,0.662598,0.061035,0.098145,
+ 0.136230,0.244385,0.542969,0.827881,0.978271,0.723145,0.922607,0.008789,
+ 0.571533,0.491699,0.498535,0.687256,0.884033,0.311768,0.042236,0.958252,
+ 0.604980,0.237061,0.122314,0.377930,0.575195,0.727295,0.950684,0.608398,
+ 0.231445,0.091064,0.630127,0.347168,0.973389,0.123779,0.817627,0.278320,
+ 0.150391,0.765869,0.368408,0.793701,0.615723,0.012451,0.956299,0.749512,
+ 0.509521,0.529297,0.649170,0.291016,0.956543,0.332520,0.513672,0.916504,
+ 0.012207,0.374512,0.932129,0.418945,0.607178,0.347656,0.825439,0.392578,
+ 0.667236,0.777344,0.123535,0.357422,0.967041,0.439209,0.248047,0.446289,
+ 0.218506,0.445801,0.135742,0.905518,0.915283,0.515869,0.063477,0.740234,
+ 0.910400,0.462158,0.614746,0.996582,0.250488,0.368896,0.555420,0.514160,
+ 0.102539,0.876953,0.464355,0.315186,0.333984,0.114258,0.110352,0.527832,
+ 0.296143,0.786377,0.605469,0.331787,0.377197,0.085449,0.549316,0.796875,
+ 0.110840,0.204346,0.419678,0.105225,0.521973,0.772705,0.670654,0.843750,
+ 0.830078,0.635254,0.280518,0.886963,0.293701,0.473389,0.507813,0.775146,
+ 0.813232,0.986572,0.042725,0.011475,0.134766,0.199463,0.906738,0.256836,
+ 0.044189,0.419922,0.815674,0.822998,0.977783,0.760498,0.541016,0.895508,
+ 0.681641,0.528564,0.632813,0.813477,0.513428,0.373535,0.305664,0.299805,
+ 0.659912,0.460205,0.047363,0.333252,0.372314,0.073486,0.505127,0.771484,
+ 0.336426,0.997803,0.158447,0.960938,0.347900,0.449463,0.502441,0.594238,
+ 0.132080,0.749756,0.941895,0.005859,0.304443,0.149902,0.564209,0.968018,
+ 0.371826,0.199219,0.260498,0.609619,0.089355,0.019287,0.359131,0.413330,
+ 0.817383,0.322510,0.806152,0.176025,0.041748,0.555908,0.950928,0.207520,
+ 0.898926,0.308594,0.367432,0.799805,0.792969,0.842041,0.742188,0.927979,
+ 0.952881,0.370361,0.534180,0.683594,0.380859,0.116699,0.827148,0.665771,
+ 0.292480,0.270508,0.676514,0.230957,0.474609,0.600098,0.428955,0.901855,
+ 0.199707,0.315674,0.349854,0.930420,0.811035,0.077881,0.064941,0.527344,
+ 0.427002,0.459961,0.308105,0.120605,0.468750,0.225830,0.944824,0.010498,
+ 0.718994,0.997070,0.726563,0.715576,0.493652,0.894287,0.338867,0.251221,
+ 0.789795,0.197021,0.114990,0.603271,0.213135,0.357178,0.902832,0.658691,
+ 0.459229,0.224121,0.687988,0.595947,0.862061,0.085693,0.092041,0.360107,
+ 0.347412,0.656738,0.379395,0.218018,0.753906,0.170166,0.390381,0.455566,
+ 0.858154,0.831787,0.779297,0.760010,0.027344,0.553467,0.328857,0.695801,
+ 0.572266,0.443359,0.500977,0.189209,0.424316,0.178467,0.094238,0.013428,
+ 0.792480,0.181641,0.998047,0.501953,0.383057,0.079102,0.721924,0.122070,
+ 0.870361,0.532227,0.424561,0.674805,0.192139,0.482422,0.306152,0.756836,
+ 0.493408,0.843506,0.765625,0.710449,0.321777,0.676025,0.164307,0.567627,
+ 0.081543,0.490479,0.571045,0.943115,0.161621,0.330322,0.537842,0.114746,
+ 0.450195,0.006836,0.392334,0.053467,0.598633,0.977539,0.111572,0.098633,
+ 0.511963,0.069580,0.387695,0.546143,0.941162,0.727051,0.490234,0.861084,
+ 0.928955,0.520508,0.115723,0.649658,0.642578,0.295166,0.965088,0.918457,
+ 0.918945,0.163086,0.312012,0.462891,0.447510,0.420166,0.665527,0.211914,
+ 0.520996,0.846191,0.473633,0.778320,0.736572,0.091797,0.988281,0.436523,
+ 0.040527,0.717529,0.318604,0.432617,0.011230,0.614258,0.810791,0.648682,
+ 0.569580,0.324707,0.750000,0.947754,0.155273,0.788086,0.010742,0.249268,
+ 0.144287,0.349609,0.520752,0.729980,0.806396,0.059326,0.303223,0.067139,
+ 0.421387,0.843994,0.637207,0.858643,0.814209,0.554932,0.251709,0.730469,
+ 0.109619,0.630615,0.379883,0.456055,0.274902,0.349121,0.580811,0.723633,
+ 0.018066,0.541260,0.387939,0.039795,0.040771,0.960205,0.458984,0.343994,
+ 0.141846,0.231934,0.017090,0.791748,0.931152,0.797852,0.731934,0.694580,
+ 0.691162,0.276855,0.785889,0.745850,0.942139,0.487793,0.228271,0.807861,
+ 0.023926,0.889160,0.140625,0.232666,0.110107,0.315918,0.372070,0.582031,
+ 0.121582,0.180176,0.386475,0.058838,0.038086,0.731445,0.202637,0.574951,
+ 0.446533,0.050293,0.542236,0.227051,0.194336,0.850098,0.857422,0.937744,
+ 0.886475,0.232422,0.542725,0.316162,0.880859,0.797607,0.575439,0.953613,
+ 0.066895,0.657715,0.753174,0.283203,0.705811,0.541504,0.220703,0.701172,
+ 0.454102,0.032471,0.452393,0.409668,0.926514,0.903564,0.263672,0.785400,
+ 0.422363,0.841064,0.410889,0.613281,0.004883,0.426758,0.106445,0.738770,
+ 0.291748,0.508545,0.578125,0.565674,0.480225,0.581055,0.415527,0.849854,
+ 0.104004,0.184814,0.418213,0.628662,0.324219,0.348145,0.831543,0.635010,
+ 0.986816,0.583008,0.124756,0.710205,0.382813,0.545898,0.727539,0.890869,
+ 0.869141,0.260986,0.170898,0.654785,0.801025,0.429443,0.442871,0.245117,
+ 0.756104,0.772949,0.577393,0.000488,0.631592,0.659180,0.048584,0.956787,
+ 0.850342,0.153320,0.627197,0.026611,0.025146,0.522949,0.928711,0.507324,
+ 0.028809,0.518066,0.512207,0.031738,0.945068,0.369873,0.692139,0.100830,
+ 0.752686,0.190674,0.510010,0.294922,0.741943,0.478271,0.757568,0.953857,
+ 0.232910,0.000000,0.746094,0.831055,0.915039,0.738525,0.275391,0.069824,
+ 0.354980,0.799561,0.611816,0.363037,0.101807,0.423340,0.934082,0.284668,
+ 0.859863,0.351807,0.652588,0.596191,0.519775,0.286865,0.451172,0.962158,
+ 0.612549,0.587891,0.887939,0.431641,0.169922,0.302246,0.117676,0.725342,
+ 0.049805,0.869873,0.127686,0.677246,0.429688,0.458008,0.172852,0.804688,
+ 0.308838,0.248779,0.491943,0.061279,0.037842,0.864746,0.326172,0.520264,
+ 0.925781,0.268311,0.412598,0.414551,0.352295,0.701904,0.866455,0.574219,
+ 0.074463,0.718018,0.375732,0.704102,0.592285,0.397949,0.239990,0.341309,
+ 0.768066,0.019531,0.174805,0.945313,0.290283,0.865967,0.877441,0.029541,
+ 0.478516,0.389160,0.827393,0.982910,0.868164,0.608154,0.180420,0.103760,
+ 0.257813,0.103027,0.902588,0.670410,0.100098,0.538818,0.045654,0.899658,
+ 0.405518,0.220215,0.381836,0.187256,0.321045,0.987061,0.411133,0.761475,
+ 0.150146,0.809082,0.440430,0.454590,0.155762,0.800049,0.666016,0.084229,
+ 0.810547,0.257080,0.278809,0.410645,0.949219,0.969971,0.948242,0.943604,
+ 0.298584,0.092773,0.370117,0.506836,0.499023,0.237793,0.739258,0.168701,
+ 0.510742,0.900635,0.460938,0.908447,0.539063,0.531006,0.380615,0.230469,
+ 0.159912,0.014648,0.823730,0.931396,0.824951,0.166748,0.324463,0.517822,
+ 0.536621,0.490723,0.279785,0.985352,0.165527,0.586914,0.226807,0.326416,
+ 0.604248,0.119873,0.148926,0.026855,0.689453,0.282471,0.631836,0.235596,
+ 0.828613,0.225342,0.394531,0.877197,0.901123,0.169678,0.399170,0.646729,
+ 0.737793,0.021729,0.135498,0.011719,0.911133,0.554688,0.810303,0.954102,
+ 0.339355,0.605225,0.361084,0.872803,0.360596,0.500000,0.159180,0.162109,
+ 0.108398,0.478760,0.227783,0.343018,0.486816,0.916016,0.861816,0.938965,
+ 0.174561,0.538330,0.078125,0.763428,0.757080,0.071533,0.457520,0.665039,
+ 0.194092,0.567383,0.270752,0.859619,0.791260,0.617920,0.888916,0.165771,
+ 0.202881,0.515625,0.486572,0.746826,0.470459,0.412842,0.714355,0.017578,
+ 0.396484,0.312256,0.311035,0.458496,0.417480,0.922363,0.646240,0.889648,
+ 0.607666,0.946777,0.321289,0.445313,0.479004,0.825684,0.272217,0.301758,
+ 0.274414,0.212402,0.503662,0.484863,0.364990,0.112061,0.302734,0.097656,
+ 0.499512,0.557617,0.742676,0.154541,0.280273,0.595215,0.689209,0.234375,
+ 0.247070,0.839844,0.215576,0.362061,0.853027,0.891846,0.262695,0.424805,
+ 0.193604,0.856689,0.487549,0.190186,0.881592,0.155029,0.076172,0.201904,
+ 0.712158,0.296875,0.314697,0.223389,0.951904,0.416992,0.060791,0.310547,
+ 0.981201,0.702881,0.540283,0.385254,0.297607,0.547852,0.168457,0.184082,
+ 0.870850,0.178223,0.685059,0.268555,0.088379,0.922852,0.418457,0.340576,
+ 0.179688,0.808594,0.041504,0.380371,0.874268,0.131104,0.855713,0.934326,
+ 0.747803,0.820801,0.101074,0.920898,0.774414,0.963135,0.732910,0.746338,
+ 0.728027,0.540039,0.207031,0.972412,0.983154,0.589844,0.589600,0.664063,
+ 0.919678,0.594482,0.174072,0.860596,0.551025,0.479736,0.889404,0.994629,
+ 0.562256,0.188965,0.189697,0.007568,0.430420,0.780273,0.038818,0.148193,
+ 0.743164,0.579102,0.404785,0.354248,0.150635,0.362549,0.982178,0.345215,
+ 0.619873,0.078857,0.212158,0.720703,0.233398,0.257568,0.697021,0.678955,
+ 0.748047,0.915527,0.497559,0.505371,0.160889,0.240479,0.718262,0.992676,
+ 0.021973,0.052490,0.833740,0.851563,0.138184,0.427490,0.104492,0.644043,
+ 0.584229,0.001953,0.991211,0.489502,0.932373,0.511719,0.862305,0.309570,
+ 0.068604,0.843018,0.959229,0.046875,0.823486,0.754395,0.560547,0.672363,
+ 0.028564,0.073730,0.346924,0.860352,0.731689,0.596924,0.539551,0.982422,
+ 0.945801,0.679199,0.481934,0.348389,0.477539,0.991455,0.794922,0.663574,
+ 0.490967,0.812256,0.476563,0.047119,0.892578,0.888184,0.793213,0.033691,
+ 0.312500,0.523193,0.883057,0.334473,0.492188,0.006348,0.990723,0.084961,
+ 0.757813,0.497070,0.172119,0.202148,0.709961,0.624023,0.275635,0.432129,
+ 0.752930,0.974854,0.438232,0.164063,0.388916,0.960693,0.878662,0.802734,
+ 0.192383,0.824707,0.977295,0.309082,0.513916,0.597900,0.305908,0.390869,
+ 0.447266,0.357910,0.754150,0.120117,0.203125,0.113525,0.089600,0.688477,
+ 0.335938,0.537109,0.572754,0.091309,0.098877,0.235107,0.956055,0.833008,
+ 0.196777,0.010254,0.955078,0.080322,0.390625,0.668457,0.701416,0.643555,
+ 0.601807,0.772217,0.035645,0.292236,0.196289,0.375977,0.967529,0.398682,
+ 0.732666,0.225098,0.393066,0.682129,0.938721,0.722168,0.907959,0.719727,
+ 0.681396,0.980957,0.594727,0.454834,0.531250,0.222412,0.717285,0.845459,
+ 0.892822,0.642334,0.479248,0.936523,0.083252,0.879395,0.856934,0.422852,
+ 0.442627,0.156250,0.849609,0.076416,0.550049,0.864990,0.926025,0.568115,
+ 0.989258,0.708740,0.578613,0.896484,0.805908,0.941406,0.778076,0.745117,
+ 0.688232,0.416016,0.245850,0.652832,0.169189,0.571777,0.459473,0.024170,
+ 0.879883,0.861328,0.319336,0.146484,0.700928,0.240234,0.154297,0.197266,
+ 0.472656,0.651367,0.716309,0.854736,0.544189,0.052246,0.981934,0.372559,
+ 0.266357,0.272705,0.035889,0.290771,0.741699,0.359375,0.566162,0.293457,
+ 0.431396,0.167725,0.970947,0.266602,0.875977,0.425049,0.743652,0.935059,
+ 0.173096,0.634521,0.677002,0.927490,0.988037,0.112793,0.494629,0.707520,
+ 0.086670,0.089111,0.943359,0.613037,0.776123,0.832275,0.045410,0.824219,
+ 0.062744,0.415771,0.206787,0.569824,0.507080,0.473145,0.401123,0.438477,
+ 0.345459,0.217773,0.033936,0.865479,0.376465,0.676758,0.330078,0.973145,
+ 0.516357,0.395020,0.464844,0.240723,0.967285,0.231689,0.022217,0.766357,
+ 0.776855,0.216309,0.305420,0.158203,0.019043,0.841309,0.054688,0.131348,
+ 0.270264,0.511230,0.073975,0.671631,0.718506,0.701660,0.009521,0.980713,
+ 0.355713,0.822266,0.023682,0.781006,0.973877,0.509277,0.288574,0.034180,
+ 0.500732,0.176514,0.968750,0.678467,0.334961,0.779541,0.985596,0.726807,
+ 0.006104,0.647705,0.928223,0.979492,0.477783,0.130371,0.209473,0.695313,
+ 0.311523,0.382324,0.504395,0.521729,0.147461,0.909668,0.585938,0.674561,
+ 0.747070,0.041016,0.207275,0.323486,0.558838,0.891357,0.526123,0.722656,
+ 0.985840,0.488770,0.961914,0.481689,0.801514,0.180908,0.788330,0.758057,
+ 0.891602,0.563477,0.466553,0.552002,0.760742,0.577148,0.705078,0.314941,
+ 0.852783,0.323975,0.738281,0.297363,0.136963,0.512695,0.356934,0.210205,
+ 0.378662,0.848877,0.411621,0.978760,0.723877,0.149170,0.635498,0.734619,
+ 0.779053,0.533447,0.610596,0.396729,0.242920,0.448730,0.251953,0.838135,
+ 0.695557,0.499756,0.935791,0.788574,0.247314,0.530518,0.524902,0.137695,
+ 0.231201,0.216797,0.130615,0.436768,0.980225,0.556641,0.807129,0.927246,
+ 0.927002,0.446777,0.599609,0.867920,0.969238,0.217285,0.792725,0.219971,
+ 0.444824,0.878906,0.996094,0.540527,0.341064,0.048340,0.688721,0.744629,
+ 0.604736,0.584473,0.700439,0.444580,0.037109,0.136719,0.131836,0.237305,
+ 0.756348,0.422607,0.901611,0.735107,0.568359,0.756592,0.410156,0.649414,
+ 0.875244,0.090576,0.876465,0.191162,0.626953,0.373779,0.864258,0.051514,
+ 0.999512,0.763672,0.994141,0.374756,0.911865,0.179199,0.415283,0.046143,
+ 0.381104,0.518555,0.675293,0.926758,0.260010,0.951660,0.608887,0.488525,
+ 0.580566,0.932861,0.879150,0.318115,0.613525,0.043945,0.040283,0.719238,
+ 0.353516,0.647461,0.570557,0.994385,0.602783,0.825928,0.394043,0.003662,
+ 0.951172,0.818848,0.786865,0.528809,0.750488,0.255859,0.154053,0.876709,
+ 0.154785,0.485107,0.007813,0.453613,0.034424,0.932617,0.897949,0.558105,
+ 0.030762,0.173340,0.753418,0.403809,0.434326,0.137207,0.282227,0.080078,
+ 0.849121,0.711670,0.065186,0.090332,0.925293,0.883545,0.456299,0.573242,
+ 0.583252,0.325439,0.074219,0.047852,0.392822,0.984863,0.330811,0.560791,
+ 0.441650,0.235840,0.559814,0.395752,0.234131,0.733643,0.520020,0.833984,
+ 0.764160,0.129639,0.446045,0.639404,0.068848,0.024414,0.478027,0.112549,
+ 0.276611,0.270020,0.844971,0.527588,0.566895,0.804199,0.896973,0.183594,
+ 0.631104,0.924072,0.246826,0.094727,0.304932,0.781738,0.118408,0.842529,
+ 0.269775,0.014404,0.626465,0.767822,0.729492,0.190918,0.544922,0.564697,
+ 0.992920,0.317627,0.024658,0.925537,0.414063,0.583740,0.569092,0.143066,
+ 0.655762,0.759766,0.799316,0.056885,0.273438,0.882813,0.281494,0.795166,
+ 0.020264,0.066162,0.523438,0.238037,0.880127,0.157959,0.236816,0.992188,
+ 0.924561,0.578857,0.498291,0.130859,0.344238,0.964600,0.798584,0.672119,
+ 0.622070,0.794189,0.661621,0.474121,0.399414,0.842285,0.166260,0.220459,
+ 0.640869,0.351318,0.764404,0.202393,0.726318,0.498779,0.959473,0.395996,
+ 0.929199,0.415039,0.246094,0.301025,0.516846,0.164795,0.141357,0.789307,
+ 0.286621,0.343506,0.625977,0.147949,0.654541,0.279297,0.770508,0.937500,
+ 0.282715,0.205322,0.833496,0.665283,0.751953,0.964355,0.462402,0.184570,
+ 0.195313,0.557129,0.791016,0.882568,0.610840,0.004395,0.787598,0.556396,
+ 0.209717,0.408203,0.816895,0.373047,0.369629,0.344971,0.870605,0.120850,
+ 0.987305,0.118652,0.658203,0.873779,0.292969,0.692627,0.568604,0.922119,
+ 0.469971,0.575928,0.985107,0.288330,0.192627,0.437256,0.414795,0.628906,
+ 0.647217,0.540771,0.111816,0.342041,0.420898,0.767334,0.224365,0.739502,
+ 0.843262,0.894531,0.916260,0.820068,0.564941,0.256348,0.238770,0.386963,
+ 0.960449,0.284180,0.385010,0.687500,0.683838,0.275879,0.945557,0.936768,
+ 0.032227,0.607910,0.062988,0.322998,0.241455,0.818115,0.567139,0.973633,
+ 0.829102,0.938477,0.046631,0.962402,0.576172,0.309326,0.979980,0.176758,
+ 0.421631,0.329102,0.315430,0.333008,0.160156,0.455078,0.562744,0.417969,
+ 0.898438,0.771973,0.725830,0.159424,0.254395,0.441162,0.371582,0.102295,
+ 0.151123,0.468262,0.637451,0.087158,0.405273,0.063721,0.155518,0.403320,
+ 0.989746,0.957031,0.137939,0.543213,0.428223,0.054443,0.872559,0.823975,
+ 0.815430,0.609131,0.912109,0.806885,0.067871,0.556152,0.923340,0.769531,
+ 0.618164,0.055420,0.803223,0.156982,0.262939,0.693848,0.894775,0.270996,
+ 0.463135,0.998779,0.845215,0.149414,0.081055,0.253174,0.037598,0.842773,
+ 0.526855,0.306396,0.068359,0.078613,0.211426,0.508057,0.295898,0.496826,
+ 0.374268,0.234863,0.832520,0.489258,0.140137,0.193848,0.029053,0.798828,
+ 0.914063,0.086426,0.866211,0.857910,0.914795,0.554443,0.285645,0.135986
+};
+
+#define MAX_RANDOM_FLOATS 4096
+#define RANDOM_FLOAT_MASK ( MAX_RANDOM_FLOATS - 1 )
+
+
+#endif // RANDOM_FLOATS