Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: particle system code
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "particles/particles.h"
  8. #include "filesystem.h"
  9. #include "tier2/tier2.h"
  10. #include "tier2/fileutils.h"
  11. #include "tier1/UtlStringMap.h"
  12. #include "tier1/strtools.h"
  13. #include "mathlib/halton.h"
  14. #include "bspflags.h"
  15. #include "const.h"
  16. #include "particles_internal.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. class C_OP_ConstrainDistance : public CParticleOperatorInstance
  20. {
  21. DECLARE_PARTICLE_OPERATOR( C_OP_ConstrainDistance );
  22. uint32 GetWrittenAttributes( void ) const
  23. {
  24. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  25. }
  26. uint32 GetReadAttributes( void ) const
  27. {
  28. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  29. }
  30. bool EnforceConstraint( int nStartBlock,
  31. int nEndBlock,
  32. CParticleCollection *pParticles,
  33. void *pContext,
  34. int nNumValidParticlesInLastChunk ) const;
  35. float m_fMinDistance, m_fMaxDistance;
  36. int m_nControlPointNumber;
  37. Vector m_CenterOffset;
  38. bool m_bGlobalCenter;
  39. };
  40. #ifdef NDEBUG
  41. #define CHECKSYSTEM( p ) 0
  42. #else
  43. static void CHECKSYSTEM( CParticleCollection *pParticles )
  44. {
  45. // Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
  46. for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
  47. {
  48. const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
  49. const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
  50. Assert( IsFinite( xyz[0] ) );
  51. Assert( IsFinite( xyz[4] ) );
  52. Assert( IsFinite( xyz[8] ) );
  53. Assert( IsFinite( xyz_prev[0] ) );
  54. Assert( IsFinite( xyz_prev[4] ) );
  55. Assert( IsFinite( xyz_prev[8] ) );
  56. }
  57. }
  58. #endif
  59. bool C_OP_ConstrainDistance::EnforceConstraint( int nStartBlock,
  60. int nNumBlocks,
  61. CParticleCollection *pParticles,
  62. void *pContext, int nNumValidParticlesInLastChunk ) const
  63. {
  64. size_t nStride;
  65. FourVectors *pXYZ=pParticles->Get4VAttributePtrForWrite( PARTICLE_ATTRIBUTE_XYZ,
  66. &nStride );
  67. pXYZ += nStride * nStartBlock;
  68. fltx4 SIMDMinDist=ReplicateX4( m_fMinDistance );
  69. fltx4 SIMDMaxDist=ReplicateX4( m_fMaxDistance );
  70. fltx4 SIMDMinDist2=ReplicateX4( m_fMinDistance*m_fMinDistance );
  71. fltx4 SIMDMaxDist2=ReplicateX4( m_fMaxDistance*m_fMaxDistance );
  72. Vector vecCenter;
  73. if ( m_bGlobalCenter )
  74. vecCenter = m_CenterOffset;
  75. else
  76. {
  77. pParticles->GetControlPointAtTime( m_nControlPointNumber, pParticles->m_flCurTime, &vecCenter );
  78. vecCenter += pParticles->TransformAxis( m_CenterOffset, true, m_nControlPointNumber );
  79. }
  80. FourVectors Center;
  81. Center.DuplicateVector( vecCenter );
  82. bool bChangedSomething = false;
  83. do
  84. {
  85. FourVectors pts = *(pXYZ);
  86. pts -= Center;
  87. fltx4 dist_squared= pts * pts;
  88. fltx4 TooFarMask = CmpGtSIMD( dist_squared, SIMDMaxDist2 );
  89. fltx4 TooCloseMask = CmpLtSIMD( dist_squared, SIMDMinDist2 );
  90. fltx4 NeedAdjust = OrSIMD( TooFarMask, TooCloseMask );
  91. if ( IsAnyNegative( NeedAdjust ) ) // any out of bounds?
  92. {
  93. // change squared distance into approximate rsqr root
  94. fltx4 guess = ReciprocalSqrtEstSaturateSIMD(dist_squared);
  95. // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2));
  96. guess=MulSIMD(guess,SubSIMD(Four_Threes,MulSIMD(dist_squared,MulSIMD(guess,guess))));
  97. guess=MulSIMD(Four_PointFives,guess);
  98. pts *= guess;
  99. FourVectors clamp_far=pts;
  100. clamp_far *= SIMDMaxDist;
  101. clamp_far += Center;
  102. FourVectors clamp_near=pts;
  103. clamp_near *= SIMDMinDist;
  104. clamp_near += Center;
  105. pts.x = MaskedAssign( TooCloseMask, clamp_near.x, MaskedAssign( TooFarMask, clamp_far.x, pXYZ->x ));
  106. pts.y = MaskedAssign( TooCloseMask, clamp_near.y, MaskedAssign( TooFarMask, clamp_far.y, pXYZ->y ));
  107. pts.z = MaskedAssign( TooCloseMask, clamp_near.z, MaskedAssign( TooFarMask, clamp_far.z, pXYZ->z ));
  108. *(pXYZ) = pts;
  109. bChangedSomething = true;
  110. }
  111. pXYZ += nStride;
  112. } while (--nNumBlocks);
  113. return bChangedSomething;
  114. }
  115. DEFINE_PARTICLE_OPERATOR( C_OP_ConstrainDistance, "Constrain distance to control point", OPERATOR_GENERIC );
  116. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistance )
  117. DMXELEMENT_UNPACK_FIELD( "minimum distance", "0", float, m_fMinDistance )
  118. DMXELEMENT_UNPACK_FIELD( "maximum distance", "100", float, m_fMaxDistance )
  119. DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
  120. DMXELEMENT_UNPACK_FIELD( "offset of center", "0 0 0", Vector, m_CenterOffset )
  121. DMXELEMENT_UNPACK_FIELD( "global center point", "0", bool, m_bGlobalCenter )
  122. END_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistance )
  123. class C_OP_ConstrainDistanceToPath : public CParticleOperatorInstance
  124. {
  125. DECLARE_PARTICLE_OPERATOR( C_OP_ConstrainDistanceToPath );
  126. uint32 GetWrittenAttributes( void ) const
  127. {
  128. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  129. }
  130. uint32 GetReadAttributes( void ) const
  131. {
  132. return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  133. }
  134. virtual uint64 GetReadControlPointMask() const
  135. {
  136. return ( 1ULL << m_PathParameters.m_nStartControlPointNumber ) |
  137. ( 1ULL << m_PathParameters.m_nEndControlPointNumber );
  138. }
  139. bool EnforceConstraint( int nStartBlock,
  140. int nEndBlock,
  141. CParticleCollection *pParticles,
  142. void *pContext, int nNumValidParticlesInLastChunk ) const;
  143. float m_fMinDistance;
  144. float m_flMaxDistance0, m_flMaxDistanceMid, m_flMaxDistance1;
  145. CPathParameters m_PathParameters;
  146. float m_flTravelTime;
  147. };
  148. bool C_OP_ConstrainDistanceToPath::EnforceConstraint( int nStartBlock,
  149. int nNumBlocks,
  150. CParticleCollection *pParticles,
  151. void *pContext,
  152. int nNumValidParticlesInLastChunk ) const
  153. {
  154. C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
  155. pXYZ += nStartBlock;
  156. CM128AttributeIterator pCreationTime( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles );
  157. pCreationTime += nStartBlock;
  158. Vector StartPnt, EndPnt, MidP;
  159. pParticles->CalculatePathValues( m_PathParameters, pParticles->m_flCurTime,
  160. &StartPnt, &MidP, &EndPnt );
  161. fltx4 CurTime = ReplicateX4( pParticles->m_flCurTime );
  162. fltx4 TimeScale= ReplicateX4( 1.0/(max(0.001f, m_flTravelTime ) ) );
  163. // calculate radius spline
  164. bool bConstantRadius = true;
  165. fltx4 Rad0=ReplicateX4(m_flMaxDistance0);
  166. fltx4 Radm=Rad0;
  167. if ( m_flMaxDistanceMid >= 0.0 )
  168. {
  169. bConstantRadius = ( m_flMaxDistanceMid == m_flMaxDistance0 );
  170. Radm=ReplicateX4( m_flMaxDistanceMid);
  171. }
  172. fltx4 Rad1=Radm;
  173. if ( m_flMaxDistance1 >= 0.0 )
  174. {
  175. bConstantRadius &= ( m_flMaxDistance1 == m_flMaxDistance0 );
  176. Rad1=ReplicateX4( m_flMaxDistance1 );
  177. }
  178. fltx4 RadmMinusRad0=SubSIMD( Radm, Rad0);
  179. fltx4 Rad1MinusRadm=SubSIMD( Rad1, Radm);
  180. fltx4 SIMDMinDist=ReplicateX4( m_fMinDistance );
  181. fltx4 SIMDMinDist2=ReplicateX4( m_fMinDistance*m_fMinDistance );
  182. fltx4 SIMDMaxDist=MaxSIMD( Rad0, MaxSIMD( Radm, Rad1 ) );
  183. fltx4 SIMDMaxDist2=MulSIMD( SIMDMaxDist, SIMDMaxDist);
  184. bool bChangedSomething = false;
  185. FourVectors StartP;
  186. StartP.DuplicateVector( StartPnt );
  187. FourVectors MiddleP;
  188. MiddleP.DuplicateVector( MidP );
  189. // form delta terms needed for quadratic bezier
  190. FourVectors Delta0;
  191. Delta0.DuplicateVector( MidP-StartPnt );
  192. FourVectors Delta1;
  193. Delta1.DuplicateVector( EndPnt-MidP );
  194. do
  195. {
  196. fltx4 TScale=MinSIMD(
  197. Four_Ones,
  198. MulSIMD( TimeScale, SubSIMD( CurTime, *pCreationTime ) ) );
  199. // bezier(a,b,c,t)=lerp( lerp(a,b,t),lerp(b,c,t),t)
  200. FourVectors L0 = Delta0;
  201. L0 *= TScale;
  202. L0 += StartP;
  203. FourVectors L1= Delta1;
  204. L1 *= TScale;
  205. L1 += MiddleP;
  206. FourVectors Center = L1;
  207. Center -= L0;
  208. Center *= TScale;
  209. Center += L0;
  210. FourVectors pts = *(pXYZ);
  211. pts -= Center;
  212. // calculate radius at the point. !!speed!! - use speical case for constant radius
  213. fltx4 dist_squared= pts * pts;
  214. fltx4 TooFarMask = CmpGtSIMD( dist_squared, SIMDMaxDist2 );
  215. if ( ( !bConstantRadius) && ( ! IsAnyNegative( TooFarMask ) ) )
  216. {
  217. // need to calculate and adjust for true radius =- we've only trivilally rejected note
  218. // voodoo here - we update simdmaxdist for true radius, but not max dist^2, since
  219. // that's used only for the trivial reject case, which we've already done
  220. fltx4 R0=AddSIMD( Rad0, MulSIMD( RadmMinusRad0, TScale ) );
  221. fltx4 R1=AddSIMD( Radm, MulSIMD( Rad1MinusRadm, TScale ) );
  222. SIMDMaxDist = AddSIMD( R0, MulSIMD( SubSIMD( R1, R0 ), TScale) );
  223. // now that we know the true radius, update our mask
  224. TooFarMask = CmpGtSIMD( dist_squared, MulSIMD( SIMDMaxDist, SIMDMaxDist ) );
  225. }
  226. fltx4 TooCloseMask = CmpLtSIMD( dist_squared, SIMDMinDist2 );
  227. fltx4 NeedAdjust = OrSIMD( TooFarMask, TooCloseMask );
  228. if ( IsAnyNegative( NeedAdjust ) ) // any out of bounds?
  229. {
  230. if ( ! bConstantRadius )
  231. {
  232. // need to calculate and adjust for true radius =- we've only trivilally rejected
  233. }
  234. // change squared distance into approximate rsqr root
  235. fltx4 guess=ReciprocalSqrtEstSIMD(dist_squared);
  236. // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2));
  237. guess=MulSIMD(guess,SubSIMD(Four_Threes,MulSIMD(dist_squared,MulSIMD(guess,guess))));
  238. guess=MulSIMD(Four_PointFives,guess);
  239. pts *= guess;
  240. FourVectors clamp_far=pts;
  241. clamp_far *= SIMDMaxDist;
  242. clamp_far += Center;
  243. FourVectors clamp_near=pts;
  244. clamp_near *= SIMDMinDist;
  245. clamp_near += Center;
  246. pts.x = MaskedAssign( TooCloseMask, clamp_near.x, MaskedAssign( TooFarMask, clamp_far.x, pXYZ->x ));
  247. pts.y = MaskedAssign( TooCloseMask, clamp_near.y, MaskedAssign( TooFarMask, clamp_far.y, pXYZ->y ));
  248. pts.z = MaskedAssign( TooCloseMask, clamp_near.z, MaskedAssign( TooFarMask, clamp_far.z, pXYZ->z ));
  249. *(pXYZ) = pts;
  250. bChangedSomething = true;
  251. }
  252. ++pXYZ;
  253. ++pCreationTime;
  254. } while (--nNumBlocks);
  255. return bChangedSomething;
  256. }
  257. DEFINE_PARTICLE_OPERATOR( C_OP_ConstrainDistanceToPath, "Constrain distance to path between two control points", OPERATOR_GENERIC );
  258. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistanceToPath )
  259. DMXELEMENT_UNPACK_FIELD( "minimum distance", "0", float, m_fMinDistance )
  260. DMXELEMENT_UNPACK_FIELD( "maximum distance", "100", float, m_flMaxDistance0 )
  261. DMXELEMENT_UNPACK_FIELD( "maximum distance middle", "-1", float, m_flMaxDistanceMid )
  262. DMXELEMENT_UNPACK_FIELD( "maximum distance end", "-1", float, m_flMaxDistance1 )
  263. DMXELEMENT_UNPACK_FIELD( "travel time", "10", float, m_flTravelTime )
  264. DMXELEMENT_UNPACK_FIELD( "random bulge", "0", float, m_PathParameters.m_flBulge )
  265. DMXELEMENT_UNPACK_FIELD( "start control point number", "0", int, m_PathParameters.m_nStartControlPointNumber )
  266. DMXELEMENT_UNPACK_FIELD( "end control point number", "0", int, m_PathParameters.m_nEndControlPointNumber )
  267. DMXELEMENT_UNPACK_FIELD( "bulge control 0=random 1=orientation of start pnt 2=orientation of end point", "0", int, m_PathParameters.m_nBulgeControl )
  268. DMXELEMENT_UNPACK_FIELD( "mid point position", "0.5", float, m_PathParameters.m_flMidPoint )
  269. END_PARTICLE_OPERATOR_UNPACK( C_OP_ConstrainDistanceToPath )
  270. class C_OP_PlanarConstraint : public CParticleOperatorInstance
  271. {
  272. DECLARE_PARTICLE_OPERATOR( C_OP_PlanarConstraint );
  273. uint32 GetWrittenAttributes( void ) const
  274. {
  275. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  276. }
  277. uint32 GetReadAttributes( void ) const
  278. {
  279. return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
  280. }
  281. virtual uint64 GetReadControlPointMask() const
  282. {
  283. return 1ULL << m_nControlPointNumber;
  284. }
  285. bool EnforceConstraint( int nStartBlock,
  286. int nEndBlock,
  287. CParticleCollection *pParticles,
  288. void *pContext, int nNumValidParticlesInLastChunk ) const;
  289. Vector m_PointOnPlane;
  290. Vector m_PlaneNormal;
  291. int m_nControlPointNumber;
  292. bool m_bGlobalOrigin;
  293. bool m_bGlobalNormal;
  294. };
  295. bool C_OP_PlanarConstraint::EnforceConstraint( int nStartBlock,
  296. int nNumBlocks,
  297. CParticleCollection *pParticles,
  298. void *pContext,
  299. int nNumValidParticlesInLastChunk ) const
  300. {
  301. C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
  302. pXYZ += nStartBlock;
  303. CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
  304. pRadius += nStartBlock;
  305. // now, transform and offset parameters
  306. FourVectors PlaneNormal;
  307. PlaneNormal.DuplicateVector(
  308. pParticles->TransformAxis( m_PlaneNormal, ! m_bGlobalNormal, m_nControlPointNumber ) );
  309. PlaneNormal.VectorNormalize();
  310. FourVectors PlanePoint;
  311. if ( m_bGlobalOrigin )
  312. {
  313. PlanePoint.DuplicateVector( m_PointOnPlane );
  314. }
  315. else
  316. {
  317. Vector ofs=pParticles->TransformAxis( m_PointOnPlane, true, m_nControlPointNumber );
  318. Vector vecCenter;
  319. pParticles->GetControlPointAtTime( m_nControlPointNumber,
  320. pParticles->m_flCurTime, &vecCenter );
  321. PlanePoint.DuplicateVector( ofs + vecCenter );
  322. }
  323. bool bChangedSomething = false;
  324. do
  325. {
  326. FourVectors pts = *pXYZ;
  327. pts -= PlanePoint;
  328. fltx4 PlaneEq=pts * PlaneNormal;
  329. // where planeeq<0, inside
  330. PlaneEq = SubSIMD( PlaneEq, *pRadius );
  331. fltx4 BadPts=CmpLtSIMD( PlaneEq, Four_Zeros );
  332. if ( IsAnyNegative( BadPts ) )
  333. {
  334. bChangedSomething = true;
  335. // project points to plane surface
  336. fltx4 PenetrationDistance=MinSIMD( Four_Zeros, PlaneEq );
  337. FourVectors PenetrationVector = PlaneNormal;
  338. PenetrationVector *= PenetrationDistance;
  339. (*pXYZ) -= PenetrationVector;
  340. }
  341. ++pXYZ;
  342. ++pRadius;
  343. } while (--nNumBlocks);
  344. return bChangedSomething;
  345. }
  346. DEFINE_PARTICLE_OPERATOR( C_OP_PlanarConstraint, "Prevent passing through a plane", OPERATOR_GENERIC );
  347. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_PlanarConstraint )
  348. DMXELEMENT_UNPACK_FIELD( "control point number", "0", int, m_nControlPointNumber )
  349. DMXELEMENT_UNPACK_FIELD( "plane point", "0 0 0", Vector, m_PointOnPlane )
  350. DMXELEMENT_UNPACK_FIELD( "plane normal", "0 0 1", Vector, m_PlaneNormal )
  351. DMXELEMENT_UNPACK_FIELD( "global origin", "0", bool, m_bGlobalOrigin )
  352. DMXELEMENT_UNPACK_FIELD( "global normal", "0", bool, m_bGlobalNormal )
  353. END_PARTICLE_OPERATOR_UNPACK( C_OP_PlanarConstraint )
  354. static Vector s_OrientationRelativeTraceVectors[] = {
  355. Vector( 0, .1962, .784929 ),
  356. Vector( -.1962, 0, .784929 ),
  357. Vector( .1962, 0, .784929 ),
  358. Vector( 0, -.1962, .78929 ),
  359. };
  360. void CWorldCollideContextData::SetBaseTrace( int nIndex, Vector const &rayStart, Vector const &traceDir, int nCollisionGroup, bool bKeepMisses )
  361. {
  362. CBaseTrace tr;
  363. Vector rayEnd = rayStart + traceDir;
  364. g_pParticleSystemMgr->Query()->TraceLine( rayStart, rayEnd, MASK_SOLID, NULL, nCollisionGroup, &tr );
  365. if ( tr.fraction < 1.0 )
  366. {
  367. m_bPlaneActive[nIndex] = true;
  368. m_PointOnPlane[nIndex].DuplicateVector( rayStart + tr.fraction * traceDir );
  369. m_PlaneNormal[nIndex].DuplicateVector( tr.plane.normal );
  370. m_TraceStartPnt[nIndex].DuplicateVector( rayStart );
  371. m_TraceEndPnt[nIndex].DuplicateVector( rayEnd );
  372. }
  373. else
  374. {
  375. if ( bKeepMisses )
  376. {
  377. m_PlaneNormal[nIndex].x = Four_Zeros;
  378. m_PlaneNormal[nIndex].y = Four_Zeros;
  379. m_PlaneNormal[nIndex].z = Four_Zeros;
  380. m_TraceStartPnt[nIndex].DuplicateVector( rayStart );
  381. m_TraceEndPnt[nIndex].DuplicateVector( rayEnd );
  382. m_bPlaneActive[nIndex] = true;
  383. }
  384. else
  385. m_bPlaneActive[nIndex] = false;
  386. }
  387. }
  388. void CWorldCollideContextData::CalculatePlanes( CParticleCollection *pParticles, int nCollisionMode,
  389. int nCollisionGroup, Vector const *pCPOffset,
  390. float flDistanceTolerance )
  391. {
  392. // fire some rays to find the convex around the control point
  393. if ( m_nActivePlanes && ( nCollisionMode == COLLISION_MODE_INITIAL_TRACE_DOWN ) )
  394. return;
  395. Vector rayStart = pParticles->GetControlPointAtCurrentTime( 0 ); // allow config + offset
  396. if ( pCPOffset )
  397. rayStart += *pCPOffset;
  398. if ( ( m_flLastUpdateTime > 0. ) && ( ( rayStart - m_vecLastUpdateOrigin ).LengthSqr() < Square( flDistanceTolerance ) ) )
  399. return;
  400. m_vecLastUpdateOrigin = rayStart;
  401. m_nActivePlanes = 0;
  402. switch( nCollisionMode )
  403. {
  404. case COLLISION_MODE_INITIAL_TRACE_DOWN:
  405. {
  406. SetBaseTrace( 0, rayStart, 1000.0 * Vector( -1, 0, 0 ), nCollisionGroup, false );
  407. m_nActivePlanes = 1;
  408. m_nNumFixedPlanes = 1;
  409. break;
  410. }
  411. case COLLISION_MODE_PER_FRAME_PLANESET:
  412. {
  413. int nIndexOut = 0;
  414. for( int i = -1; i <= 1; i++ )
  415. for( int j = -1; j <= 1; j++ )
  416. for( int k = -1; k <= 1; k++ )
  417. {
  418. if ( i || j || k )
  419. {
  420. SetBaseTrace( nIndexOut++, rayStart, 1000.0 * Vector( i, j, k ), nCollisionGroup, false );
  421. }
  422. }
  423. m_nNumFixedPlanes = nIndexOut;
  424. m_nActivePlanes = nIndexOut;
  425. }
  426. // Long missing break. Added to Source2 in change 700053.
  427. // It's a bug, but changing it now could cause regressions, so
  428. // leaving it for now until someone decides it's worth fixing.
  429. #ifdef FP_EXCEPTIONS_ENABLED
  430. // This break is necessary when exceptions are enabled because otherwise
  431. // m_bPlaneActive[21] is set even though that plane is filled with
  432. // NaNs. We should perhaps put this break in, but we need to do
  433. // careful particle testing.
  434. break;
  435. #endif
  436. case COLLISION_MODE_USE_NEAREST_TRACE:
  437. {
  438. int nIndexOut = 0;
  439. for( int i = -1; i <= 1; i++ )
  440. for( int j = -1; j <= 1; j++ )
  441. for( int k = -1; k <= 1; k++ )
  442. {
  443. if ( i || j || k )
  444. {
  445. SetBaseTrace( nIndexOut++, rayStart, 1000.0 * Vector( i, j, k ), nCollisionGroup, true );
  446. }
  447. }
  448. m_nNumFixedPlanes = nIndexOut;
  449. m_nActivePlanes = nIndexOut;
  450. }
  451. }
  452. }
  453. class C_OP_WorldCollideConstraint : public CParticleOperatorInstance
  454. {
  455. DECLARE_PARTICLE_OPERATOR( C_OP_WorldCollideConstraint );
  456. uint32 GetWrittenAttributes( void ) const
  457. {
  458. return PARTICLE_ATTRIBUTE_XYZ_MASK;
  459. }
  460. uint32 GetReadAttributes( void ) const
  461. {
  462. return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
  463. }
  464. virtual uint64 GetReadControlPointMask() const
  465. {
  466. return 1ULL << 0;
  467. }
  468. size_t GetRequiredContextBytes( ) const
  469. {
  470. return sizeof( CWorldCollideContextData );
  471. }
  472. bool EnforceConstraint( int nStartBlock,
  473. int nEndBlock,
  474. CParticleCollection *pParticles,
  475. void *pContext, int nNumValidParticlesInLastChunk ) const;
  476. void SetupConstraintPerFrameData( CParticleCollection *pParticles,
  477. void *pContext ) const;
  478. };
  479. void C_OP_WorldCollideConstraint::SetupConstraintPerFrameData( CParticleCollection *pParticles,
  480. void *pContext ) const
  481. {
  482. CWorldCollideContextData *pCtx =
  483. reinterpret_cast<CWorldCollideContextData *>( pContext );
  484. pCtx->CalculatePlanes( pParticles, COLLISION_MODE_PER_FRAME_PLANESET, COLLISION_GROUP_NONE );
  485. }
  486. bool C_OP_WorldCollideConstraint::EnforceConstraint( int nStartBlock,
  487. int nNumBlocks,
  488. CParticleCollection *pParticles,
  489. void *pContext,
  490. int nNumValidParticlesInLastChunk ) const
  491. {
  492. C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
  493. pXYZ += nStartBlock;
  494. CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
  495. pRadius += nStartBlock;
  496. CWorldCollideContextData *pCtx =
  497. reinterpret_cast<CWorldCollideContextData *>( pContext );
  498. bool bChangedSomething = false;
  499. do
  500. {
  501. for( int i=0; i < pCtx->m_nActivePlanes; i++ )
  502. {
  503. if ( pCtx->m_bPlaneActive[ i ] )
  504. {
  505. FourVectors pts = *pXYZ;
  506. pts -= pCtx->m_PointOnPlane[i];
  507. fltx4 PlaneEq=pts * pCtx->m_PlaneNormal[i];
  508. // where planeeq<0, inside
  509. PlaneEq = SubSIMD( PlaneEq, *pRadius );
  510. fltx4 BadPts=CmpLtSIMD( PlaneEq, Four_Zeros );
  511. if ( IsAnyNegative( BadPts ) )
  512. {
  513. bChangedSomething = true;
  514. // project points to plane surface
  515. fltx4 PenetrationDistance=MinSIMD( Four_Zeros, PlaneEq );
  516. FourVectors PenetrationVector = pCtx->m_PlaneNormal[i];
  517. PenetrationVector *= PenetrationDistance;
  518. (*pXYZ) -= PenetrationVector;
  519. }
  520. }
  521. }
  522. ++pXYZ;
  523. ++pRadius;
  524. } while (--nNumBlocks);
  525. return bChangedSomething;
  526. }
  527. DEFINE_PARTICLE_OPERATOR( C_OP_WorldCollideConstraint, "Prevent passing through static part of world", OPERATOR_GENERIC );
  528. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_WorldCollideConstraint )
  529. END_PARTICLE_OPERATOR_UNPACK( C_OP_WorldCollideConstraint )
  530. class C_OP_WorldTraceConstraint : public CParticleOperatorInstance
  531. {
  532. DECLARE_PARTICLE_OPERATOR( C_OP_WorldTraceConstraint );
  533. uint32 GetWrittenAttributes( void ) const
  534. {
  535. int nRet = PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ;
  536. if ( m_bKillonContact )
  537. nRet |= PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
  538. return nRet;
  539. }
  540. uint32 GetReadAttributes( void ) const
  541. {
  542. return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK;
  543. }
  544. virtual uint64 GetReadControlPointMask() const
  545. {
  546. return 1ULL << 0;
  547. }
  548. Vector m_vecCpOffset;
  549. int m_nCollisionMode;
  550. float m_flBounceAmount;
  551. float m_flSlideAmount;
  552. float m_flRadiusScale;
  553. float m_flCpMovementTolerance;
  554. float m_flTraceTolerance;
  555. bool m_bKillonContact;
  556. virtual bool IsFinalConstraint( void ) const
  557. {
  558. return ( m_flBounceAmount != 0. ) || ( m_flSlideAmount != 0. );
  559. }
  560. void InitializeContextData( CParticleCollection *pParticles,
  561. void *pContext ) const
  562. {
  563. }
  564. char m_CollisionGroupName[128];
  565. int m_nCollisionGroupNumber;
  566. bool m_bBrushOnly;
  567. void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
  568. bool EnforceConstraint( int nStartBlock,
  569. int nEndBlock,
  570. CParticleCollection *pParticles,
  571. void *pContext,
  572. int nNumValidParticlesInLastChunk ) const;
  573. template<bool bKillOnContact, bool bCached> bool EnforceConstraintInternal( int nStartBlock,
  574. int nEndBlock,
  575. CParticleCollection *pParticles,
  576. void *pContext, int nNumValidParticlesInLastChunk ) const;
  577. };
  578. void C_OP_WorldTraceConstraint::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
  579. {
  580. m_nCollisionGroupNumber = g_pParticleSystemMgr->Query()->GetCollisionGroupFromName( m_CollisionGroupName );
  581. }
  582. struct ISectData_t
  583. {
  584. fltx4 m_ISectT; // "t" of intersection
  585. fltx4 m_LeftOverT; // "left-over" amount
  586. FourVectors m_ISectNormal; // normal at intersection if any
  587. };
  588. static void WorldIntersectTNew( FourVectors const *pStartPnt, FourVectors const *pEndPnt,
  589. int nCollisionGroup, int nMask, ISectData_t *pISectData,
  590. int nCollisionMode, CWorldCollideContextData *pCtx, fltx4 const &fl4ParticleValidMask,
  591. float flTolerance = 0.0 )
  592. {
  593. pISectData->m_ISectT = Four_Zeros;
  594. pISectData->m_LeftOverT = Four_Zeros;
  595. pISectData->m_ISectNormal.x = Four_Zeros;
  596. pISectData->m_ISectNormal.y = Four_Zeros;
  597. pISectData->m_ISectNormal.z = Four_Zeros;
  598. if ( pCtx )
  599. {
  600. pISectData->m_ISectT = Four_Twos;
  601. // do simd interseciton against planes
  602. if ( nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE )
  603. {
  604. // find which of our traces is closest to our start / end points
  605. pISectData->m_ISectT = Four_Twos; // no hit
  606. FourVectors v4PointOnPlane;
  607. FourVectors v4PlaneNormal;
  608. fltx4 fl4ClosestDist = Four_FLT_MAX;
  609. for( int i = 0 ; i < pCtx->m_nActivePlanes; i++ )
  610. {
  611. if ( pCtx->m_bPlaneActive[i] )
  612. {
  613. fltx4 fl4TrialDistance = MaxSIMD(
  614. pStartPnt->DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ),
  615. pEndPnt->DistSqrToLineSegment( pCtx->m_TraceStartPnt[i], pCtx->m_TraceEndPnt[i] ) );
  616. fltx4 fl4Nearestmask = CmpLeSIMD( fl4TrialDistance, fl4ClosestDist );
  617. fl4ClosestDist = MaskedAssign( fl4ClosestDist, fl4TrialDistance, fl4Nearestmask );
  618. v4PointOnPlane.x = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].x, v4PointOnPlane.x );
  619. v4PointOnPlane.y = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].y, v4PointOnPlane.y );
  620. v4PointOnPlane.z = MaskedAssign( fl4Nearestmask, pCtx->m_PointOnPlane[i].z, v4PointOnPlane.z );
  621. v4PlaneNormal.x = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].x, v4PlaneNormal.x );
  622. v4PlaneNormal.y = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].y, v4PlaneNormal.y );
  623. v4PlaneNormal.z = MaskedAssign( fl4Nearestmask, pCtx->m_PlaneNormal[i].z, v4PlaneNormal.z );
  624. }
  625. }
  626. fltx4 fl4OutOfRange = AndSIMD( fl4ParticleValidMask,
  627. CmpGtSIMD( fl4ClosestDist, ReplicateX4( flTolerance ) ) );
  628. if ( IsAnyNegative( fl4OutOfRange ) )
  629. {
  630. nMask = TestSignSIMD( fl4OutOfRange );
  631. for(int i=0; i < 4; i++ )
  632. {
  633. if ( nMask & ( 1 << i ) )
  634. {
  635. Vector start = pStartPnt->Vec( i );
  636. Vector delta = pEndPnt->Vec( i ) - start;
  637. float ln = delta.Length();
  638. float traceScale = max( 5.0, 300.0 / ( ln + .01 ) );
  639. Vector end = start + delta * traceScale;
  640. CBaseTrace tr;
  641. g_pParticleSystemMgr->Query()->TraceLine( start, end,
  642. nMask, NULL, nCollisionGroup, &tr );
  643. if ( tr.fraction < 1.0 )
  644. {
  645. SubFloat( v4PointOnPlane.x, i ) = start.x + ( tr.fraction * ( end.x - start.x ) );
  646. SubFloat( v4PointOnPlane.y, i ) = start.y + ( tr.fraction * ( end.y - start.y ) );
  647. SubFloat( v4PointOnPlane.z, i ) = start.z + ( tr.fraction * ( end.z - start.z ) );
  648. SubFloat( v4PlaneNormal.x, i ) = tr.plane.normal.x;
  649. SubFloat( v4PlaneNormal.y, i ) = tr.plane.normal.y;
  650. SubFloat( v4PlaneNormal.z, i ) = tr.plane.normal.z;
  651. }
  652. else
  653. {
  654. // no hit. a normal of 0 will prevent the crossing check from ever
  655. // finding a crossing, since it will check for (p - origin ) dot normal
  656. // < 0
  657. SubFloat( v4PlaneNormal.x, i ) = 0;
  658. SubFloat( v4PlaneNormal.y, i ) = 0;
  659. SubFloat( v4PlaneNormal.z, i ) = 0;
  660. }
  661. }
  662. }
  663. }
  664. FourVectors v4StartD = *pStartPnt;
  665. FourVectors v4EndD = *pEndPnt;
  666. v4StartD -= v4PointOnPlane;
  667. v4EndD -= v4PointOnPlane;
  668. fltx4 fl4StartDist = v4StartD * v4PlaneNormal;
  669. fltx4 fl4EndDist = v4EndD * v4PlaneNormal;
  670. fltx4 fl4CrossMask = AndSIMD( CmpGeSIMD( fl4StartDist, Four_Zeros ), CmpLtSIMD( fl4EndDist, Four_Zeros ) );
  671. fl4CrossMask = AndSIMD( fl4CrossMask, fl4ParticleValidMask );
  672. if ( IsAnyNegative( fl4CrossMask ) )
  673. {
  674. // a hit!
  675. fltx4 fl4T = DivSIMD( fl4StartDist, SubSIMD( fl4StartDist, fl4EndDist ) );
  676. fl4CrossMask = AndSIMD( fl4CrossMask, CmpLtSIMD( fl4T, pISectData->m_ISectT ) );
  677. if ( IsAnyNegative( fl4CrossMask ) )
  678. {
  679. pISectData->m_ISectT = MaskedAssign( fl4CrossMask, fl4T, pISectData->m_ISectT );
  680. pISectData->m_ISectNormal.x = MaskedAssign( fl4CrossMask, v4PlaneNormal.x, pISectData->m_ISectNormal.x );
  681. pISectData->m_ISectNormal.y = MaskedAssign( fl4CrossMask, v4PlaneNormal.y, pISectData->m_ISectNormal.y );
  682. pISectData->m_ISectNormal.z = MaskedAssign( fl4CrossMask, v4PlaneNormal.z, pISectData->m_ISectNormal.z );
  683. }
  684. }
  685. }
  686. pISectData->m_LeftOverT = MaxSIMD( Four_Zeros, SubSIMD( Four_Ones, pISectData->m_ISectT ) );
  687. }
  688. }
  689. static void WorldIntersectT( FourVectors const *pStartPnt, FourVectors const *pEndPnt,
  690. int nCollisionGroup, int nMask, ISectData_t *pISectData,
  691. CWorldCollideContextData *pCtx )
  692. {
  693. pISectData->m_ISectT = Four_Zeros;
  694. pISectData->m_LeftOverT = Four_Zeros;
  695. pISectData->m_ISectNormal.x = Four_Zeros;
  696. pISectData->m_ISectNormal.y = Four_Zeros;
  697. pISectData->m_ISectNormal.z = Four_Zeros;
  698. if ( pCtx )
  699. {
  700. pISectData->m_ISectT = Four_Twos;
  701. // do simd interseciton against planes
  702. for( int i=0 ; i < pCtx->m_nActivePlanes; i++ )
  703. {
  704. if ( pCtx->m_bPlaneActive[ i ] )
  705. {
  706. FourVectors v4StartD = *pStartPnt;
  707. FourVectors v4EndD = *pEndPnt;
  708. v4StartD -= pCtx->m_PointOnPlane[i];
  709. v4EndD -= pCtx->m_PointOnPlane[i];
  710. fltx4 fl4StartDist = v4StartD * pCtx->m_PlaneNormal[i];
  711. fltx4 fl4EndDist = v4EndD * pCtx->m_PlaneNormal[i];
  712. fltx4 fl4CrossMask = AndSIMD( CmpGeSIMD( fl4StartDist, Four_Zeros ), CmpLtSIMD( fl4EndDist, Four_Zeros ) );
  713. if ( IsAnyNegative( fl4CrossMask ) )
  714. {
  715. #ifdef FP_EXCEPTIONS_ENABLED
  716. // Wherever fl4CrossMask is zero we need to ensure that fl4StartDist does
  717. // not equal fl4EndDist to avoid divide-by-zero.
  718. //fl4FadeWindow = OrSIMD( AndSIMD( fl4GoodMask, fl4EndTime ), AndNotSIMD( fl4GoodMask, fl4EndTime ) );
  719. fl4EndDist = AddSIMD( fl4EndDist, AndNotSIMD( fl4CrossMask, Four_Ones ) );
  720. #endif
  721. // a hit!
  722. fltx4 fl4T = DivSIMD( fl4StartDist, SubSIMD( fl4StartDist, fl4EndDist ) );
  723. fl4CrossMask = AndSIMD( fl4CrossMask, CmpLtSIMD( fl4T, pISectData->m_ISectT ) );
  724. if ( IsAnyNegative( fl4CrossMask ) )
  725. {
  726. pISectData->m_ISectT = MaskedAssign( fl4CrossMask, fl4T, pISectData->m_ISectT );
  727. pISectData->m_ISectNormal.x = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].x, pISectData->m_ISectNormal.x );
  728. pISectData->m_ISectNormal.y = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].y, pISectData->m_ISectNormal.y );
  729. pISectData->m_ISectNormal.z = MaskedAssign( fl4CrossMask, pCtx->m_PlaneNormal[i].z, pISectData->m_ISectNormal.z );
  730. }
  731. }
  732. }
  733. }
  734. pISectData->m_LeftOverT = MaxSIMD( Four_Zeros, SubSIMD( Four_Ones, pISectData->m_ISectT ) );
  735. }
  736. else
  737. {
  738. // assumes they don't start solid
  739. for(int i=0; i < 4; i++ )
  740. {
  741. Vector start=pStartPnt->Vec( i );
  742. Vector end=pEndPnt->Vec( i );
  743. Assert( start.IsValid() );
  744. Assert( end.IsValid() );
  745. CBaseTrace tr;
  746. g_pParticleSystemMgr->Query()->TraceLine( start, end,
  747. nMask, NULL, nCollisionGroup, &tr );
  748. SubFloat( pISectData->m_ISectT, i ) = tr.fraction;
  749. if ( tr.startsolid )
  750. {
  751. SubFloat( pISectData->m_LeftOverT, i ) = 0; // don't bounce if stuck
  752. }
  753. else
  754. {
  755. SubFloat( pISectData->m_LeftOverT, i ) = 1.0 - tr.fraction;
  756. }
  757. SubFloat( pISectData->m_ISectNormal.x, i ) = tr.plane.normal.x;
  758. SubFloat( pISectData->m_ISectNormal.y, i ) = tr.plane.normal.y;
  759. SubFloat( pISectData->m_ISectNormal.z, i ) = tr.plane.normal.z;
  760. }
  761. }
  762. }
  763. bool C_OP_WorldTraceConstraint::EnforceConstraint( int nStartBlock,
  764. int nNumBlocks,
  765. CParticleCollection *pParticles,
  766. void *pContext, int nNumValidParticlesInLastChunk ) const
  767. {
  768. if ( m_nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE )
  769. {
  770. if ( m_bKillonContact )
  771. return EnforceConstraintInternal<true, true>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
  772. else
  773. return EnforceConstraintInternal<false, true>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
  774. }
  775. else
  776. {
  777. if ( m_bKillonContact )
  778. return EnforceConstraintInternal<true, false>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
  779. else
  780. return EnforceConstraintInternal<false, false>( nStartBlock, nNumBlocks, pParticles, pContext, nNumValidParticlesInLastChunk );
  781. }
  782. }
  783. template<bool bKillonContact, bool bCached> bool C_OP_WorldTraceConstraint::EnforceConstraintInternal(
  784. int nStartBlock,
  785. int nNumBlocks,
  786. CParticleCollection *pParticles,
  787. void *pContext, int nNumValidParticlesInLastChunk ) const
  788. {
  789. C4VAttributeWriteIterator pPrevXYZ( PARTICLE_ATTRIBUTE_PREV_XYZ, pParticles );
  790. pPrevXYZ += nStartBlock;
  791. C4VAttributeWriteIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles );
  792. pXYZ += nStartBlock;
  793. CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles );
  794. pRadius += nStartBlock;
  795. CM128AttributeWriteIterator pLifetime;
  796. if ( bKillonContact )
  797. {
  798. pLifetime.Init( PARTICLE_ATTRIBUTE_LIFE_DURATION, pParticles );
  799. pLifetime += nStartBlock;
  800. }
  801. fltx4 bounceScale = ReplicateX4( m_flBounceAmount );
  802. fltx4 slideScale = ReplicateX4( m_flSlideAmount );
  803. bool bBouncingOrSliding = ( m_flBounceAmount != 0.0 ) || ( m_flSlideAmount != 0.0 );
  804. fltx4 radAdjustScale = ReplicateX4( m_flRadiusScale );
  805. bool bChangedSomething = false;
  806. int nMask = MASK_SOLID;
  807. if ( m_bBrushOnly )
  808. nMask = MASK_SOLID_BRUSHONLY;
  809. CWorldCollideContextData **ppCtx;
  810. if ( pParticles->m_pParent )
  811. ppCtx = &( pParticles->m_pParent->m_pCollisionCacheData[m_nCollisionMode] );
  812. else
  813. ppCtx = &( pParticles->m_pCollisionCacheData[m_nCollisionMode] );
  814. CWorldCollideContextData *pCtx = NULL;
  815. if ( ( m_nCollisionMode == COLLISION_MODE_PER_FRAME_PLANESET ) ||
  816. ( m_nCollisionMode == COLLISION_MODE_USE_NEAREST_TRACE ) ||
  817. ( m_nCollisionMode == COLLISION_MODE_INITIAL_TRACE_DOWN ) )
  818. {
  819. if ( ! *ppCtx )
  820. {
  821. *ppCtx = new CWorldCollideContextData;
  822. (*ppCtx)->m_nActivePlanes = 0;
  823. (*ppCtx)->m_flLastUpdateTime = -1.0;
  824. }
  825. pCtx = *ppCtx;
  826. if ( pCtx->m_flLastUpdateTime != pParticles->m_flCurTime )
  827. {
  828. pCtx->CalculatePlanes( pParticles, m_nCollisionMode, m_nCollisionGroupNumber, &m_vecCpOffset, m_flCpMovementTolerance );
  829. pCtx->m_flLastUpdateTime = pParticles->m_flCurTime;
  830. }
  831. }
  832. float flTol = m_flTraceTolerance * m_flTraceTolerance;
  833. do
  834. {
  835. // compute radius adjust factor for intersection
  836. fltx4 radiusFactor = MulSIMD( *pRadius, radAdjustScale );
  837. // compute movement delta
  838. FourVectors delta = *pXYZ;
  839. delta -= *pPrevXYZ;
  840. // now, add two components - the non-intersecting movement vector, and the
  841. // then the movement vector with the components normal to the plane removed.
  842. FourVectors deltanormalized = delta;
  843. fltx4 len2 = delta * delta;
  844. fltx4 bBadDeltas = CmpLeSIMD( len2, Four_Zeros );
  845. len2 = ReciprocalSqrtEstSIMD( len2 );
  846. deltanormalized *= AndNotSIMD( bBadDeltas, len2 );
  847. FourVectors endPnt = *pXYZ;
  848. FourVectors radadjust = deltanormalized;
  849. radadjust *= radiusFactor;
  850. endPnt += radadjust;
  851. ISectData_t iData;
  852. if ( bCached )
  853. {
  854. fltx4 fl4TailMask;
  855. if ( nNumBlocks > 1 )
  856. fl4TailMask = LoadAlignedIntSIMD( g_SIMD_AllOnesMask );
  857. else
  858. fl4TailMask = LoadAlignedIntSIMD( g_SIMD_SkipTailMask[nNumValidParticlesInLastChunk] );
  859. WorldIntersectTNew( pPrevXYZ, &endPnt, m_nCollisionGroupNumber, nMask, &iData, m_nCollisionMode, pCtx, fl4TailMask, flTol );
  860. }
  861. else
  862. WorldIntersectT( pPrevXYZ, &endPnt, m_nCollisionGroupNumber, nMask, &iData, pCtx );
  863. fltx4 didhit = CmpLtSIMD( iData.m_ISectT, Four_Ones );
  864. // mask off zero-length deltas
  865. didhit = AndNotSIMD( bBadDeltas, didhit );
  866. if ( IsAnyNegative( didhit ) ) // any penetration?
  867. {
  868. bChangedSomething = true;
  869. if ( bKillonContact )
  870. {
  871. *pLifetime = MaskedAssign( didhit, Four_Zeros, *pLifetime );
  872. }
  873. else
  874. {
  875. FourVectors newPnt = delta;
  876. newPnt *= iData.m_ISectT;
  877. newPnt += *pPrevXYZ;
  878. if ( bBouncingOrSliding )
  879. {
  880. // need to compute movement due to sliding and bouncing, and add it to the point,
  881. // and also compute the new velocity, adjust prev pnt to reflect that new velocity
  882. FourVectors bouncePart = VectorReflect( deltanormalized, iData.m_ISectNormal );
  883. bouncePart *= bounceScale;
  884. FourVectors newVel = bouncePart;
  885. bouncePart *= iData.m_LeftOverT;
  886. newPnt += bouncePart;
  887. FourVectors slidePart = VectorSlide( delta, iData.m_ISectNormal );
  888. slidePart *= slideScale;
  889. newVel += slidePart;
  890. slidePart *= iData.m_LeftOverT;
  891. newPnt += slidePart;
  892. FourVectors newPrev = newPnt;
  893. newPrev -= newVel;
  894. pPrevXYZ->x = MaskedAssign( didhit, newPrev.x, pPrevXYZ->x );
  895. pPrevXYZ->y = MaskedAssign( didhit, newPrev.y, pPrevXYZ->y );
  896. pPrevXYZ->z = MaskedAssign( didhit, newPrev.z, pPrevXYZ->z );
  897. }
  898. pXYZ->x = MaskedAssign( didhit, newPnt.x, pXYZ->x );
  899. pXYZ->y = MaskedAssign( didhit, newPnt.y, pXYZ->y );
  900. pXYZ->z = MaskedAssign( didhit, newPnt.z, pXYZ->z );
  901. }
  902. CHECKSYSTEM( pParticles );
  903. }
  904. ++pXYZ;
  905. ++pPrevXYZ;
  906. ++pRadius;
  907. if ( bKillonContact )
  908. ++pLifetime;
  909. } while (--nNumBlocks);
  910. return bChangedSomething;
  911. }
  912. DEFINE_PARTICLE_OPERATOR( C_OP_WorldTraceConstraint, "Collision via traces", OPERATOR_GENERIC );
  913. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_WorldTraceConstraint )
  914. DMXELEMENT_UNPACK_FIELD( "collision mode", "0", int, m_nCollisionMode )
  915. DMXELEMENT_UNPACK_FIELD( "amount of bounce", "0", float, m_flBounceAmount )
  916. DMXELEMENT_UNPACK_FIELD( "amount of slide", "0", float, m_flSlideAmount )
  917. DMXELEMENT_UNPACK_FIELD( "radius scale", "1", float, m_flRadiusScale )
  918. DMXELEMENT_UNPACK_FIELD( "brush only", "0", bool, m_bBrushOnly )
  919. DMXELEMENT_UNPACK_FIELD_STRING( "collision group", "NONE", m_CollisionGroupName )
  920. DMXELEMENT_UNPACK_FIELD( "control point offset for fast collisions", "0 0 0", Vector, m_vecCpOffset )
  921. DMXELEMENT_UNPACK_FIELD( "control point movement distance tolerance", "5", float, m_flCpMovementTolerance )
  922. DMXELEMENT_UNPACK_FIELD( "kill particle on collision", "0", bool, m_bKillonContact )
  923. DMXELEMENT_UNPACK_FIELD( "trace accuracy tolerance", "24", float, m_flTraceTolerance )
  924. END_PARTICLE_OPERATOR_UNPACK( C_OP_WorldTraceConstraint )
  925. void AddBuiltInParticleConstraints( void )
  926. {
  927. REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_ConstrainDistance );
  928. REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_PlanarConstraint );
  929. REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_WorldCollideConstraint );
  930. REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_WorldTraceConstraint );
  931. REGISTER_PARTICLE_OPERATOR( FUNCTION_CONSTRAINT, C_OP_ConstrainDistanceToPath );
  932. }