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 ); |
|
} |
|
|
|
|
|
|