You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1109 lines
36 KiB
1109 lines
36 KiB
//========= 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 );
|
|
}
|
|
|
|
|