Counter Strike : Global Offensive Source Code
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.

1189 lines
39 KiB

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