summaryrefslogtreecommitdiff
path: root/particles/builtin_constraints.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /particles/builtin_constraints.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'particles/builtin_constraints.cpp')
-rw-r--r--particles/builtin_constraints.cpp1109
1 files changed, 1109 insertions, 0 deletions
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 );
+}
+
+