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.

371 lines
12 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Local fast collision system for particles
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "particle_collision.h"
  9. #include "engine/ivdebugoverlay.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. #define __DEBUG_PARTICLE_COLLISION_RETEST 1
  13. #define __DEBUG_PARTICLE_COLLISION_OVERLAY 0
  14. #define __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME 0.1f
  15. #define NUM_DISCREET_STEPS 8.0f
  16. #define NUM_SIMULATION_SECONDS 2.0f
  17. #define COLLISION_EPSILON 0.01f
  18. //-----------------------------------------------------------------------------
  19. // Purpose:
  20. //-----------------------------------------------------------------------------
  21. CBaseSimpleCollision::CBaseSimpleCollision( void )
  22. {
  23. ClearActivePlanes();
  24. }
  25. //-----------------------------------------------------------------------------
  26. // Purpose:
  27. // Input : &origin -
  28. // radius -
  29. //-----------------------------------------------------------------------------
  30. void CBaseSimpleCollision::Setup( const Vector &origin, float speed, float gravity )
  31. {
  32. TestForPlane( origin, Vector( 1, 0, 0 ), speed, gravity );
  33. TestForPlane( origin, Vector( -1, 0, 0 ), speed, gravity );
  34. TestForPlane( origin, Vector( 0, 1, 0 ), speed, gravity );
  35. TestForPlane( origin, Vector( 0, -1, 0 ), speed, gravity );
  36. TestForPlane( origin, Vector( 0, 0, 1 ), speed, gravity );
  37. TestForPlane( origin, Vector( 0, 0, -1 ), speed, gravity );
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose: Trace line for super-simplified traces
  41. // Input : &start - start position
  42. // &end - end position
  43. // *pTrace - trace structure to fill
  44. // coarse - tests again with a real trace unless coarse is set
  45. //-----------------------------------------------------------------------------
  46. void CBaseSimpleCollision::TraceLine( const Vector &start, const Vector &end, trace_t *pTrace, bool coarse )
  47. {
  48. //Iterate over all active planes
  49. for ( int i = 0; i < m_nActivePlanes; i++ )
  50. {
  51. //Must be a valid plane
  52. if ( m_collisionPlanes[i].m_Dist == -1.0f )
  53. continue;
  54. //Get our information about the relation to this plane
  55. float dot1 = m_collisionPlanes[i].DistTo(start);
  56. float dot2 = m_collisionPlanes[i].DistTo(end);
  57. //Don't consider particles on the backside of planes
  58. if ( dot1 < -COLLISION_EPSILON )
  59. continue;
  60. //Must be crossing the plane's boundary
  61. if ( ( dot1 > COLLISION_EPSILON ) == ( dot2 > COLLISION_EPSILON ) )
  62. continue;
  63. //Find the intersection point
  64. float t = dot1 / (dot1 - dot2);
  65. Vector vIntersection = start + (end - start) * t;
  66. //Fake the collision info
  67. pTrace->endpos = vIntersection;
  68. pTrace->fraction = t - COLLISION_EPSILON;
  69. pTrace->plane.normal = m_collisionPlanes[i].m_Normal;
  70. pTrace->plane.dist = m_collisionPlanes[i].m_Dist;
  71. //If we need an exact trace, test again on a successful hit
  72. if ( ( coarse == false ) && ( pTrace->fraction < 1.0f ) )
  73. {
  74. UTIL_TraceLine( start, end, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, pTrace );
  75. }
  76. #if __DEBUG_PARTICLE_COLLISION_OVERLAY
  77. debugoverlay->AddBoxOverlay( vIntersection, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 0, 255, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
  78. #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
  79. //Done
  80. return;
  81. }
  82. //Fell through, so clear all the fields
  83. pTrace->plane.normal[0] = 0.0f;
  84. pTrace->plane.normal[1] = 0.0f;
  85. pTrace->plane.normal[2] = 0.0f;
  86. pTrace->plane.dist = 0.0f;
  87. pTrace->fraction = 1.0f;
  88. pTrace->allsolid = false;
  89. pTrace->startsolid = false;
  90. pTrace->m_pEnt = NULL;
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Tests the planes against all others for validity
  94. // Input : *plane - plane to test
  95. //-----------------------------------------------------------------------------
  96. void CBaseSimpleCollision::ConsiderPlane( cplane_t *plane )
  97. {
  98. //Test against all other active planes
  99. for ( int i = 0; i < m_nActivePlanes; i++ )
  100. {
  101. if ( m_collisionPlanes[i].m_Dist != -1.0f )
  102. {
  103. //Test for coplanar
  104. if ( ( m_collisionPlanes[i].m_Normal == plane->normal ) && ( m_collisionPlanes[i].m_Dist == plane->dist ) )
  105. return;
  106. }
  107. }
  108. //Don't overrun
  109. if ( m_nActivePlanes >= MAX_COLLISION_PLANES )
  110. return;
  111. //Take it
  112. m_collisionPlanes[m_nActivePlanes].m_Dist = plane->dist;
  113. m_collisionPlanes[m_nActivePlanes].m_Normal = plane->normal;
  114. m_nActivePlanes++;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Runs a simulation of the average particle's movement, looking for collisions along the way
  118. // Input : &start - start of the simulation
  119. // &dir - direction of travel
  120. // speed - speed of the particle
  121. // gravity - gravity being used
  122. //-----------------------------------------------------------------------------
  123. void CBaseSimpleCollision::TestForPlane( const Vector &start, const Vector &dir, float speed, float gravity )
  124. {
  125. trace_t tr;
  126. Vector testStart, testEnd;
  127. testStart = start;
  128. //Setup our step increments
  129. float dStepTime = (NUM_SIMULATION_SECONDS/NUM_DISCREET_STEPS);
  130. Vector vStepIncr = dir * ( speed * dStepTime );
  131. float flGravIncr = gravity*dStepTime;
  132. //Simulate collsions in discreet steps
  133. for ( int i = 1; i <= NUM_DISCREET_STEPS; i++ )
  134. {
  135. testEnd = testStart + vStepIncr;
  136. testEnd[2] -= flGravIncr * (0.5f*(dStepTime*i)*(dStepTime*i) );
  137. //Trace the line
  138. UTIL_TraceLine( testStart, testEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
  139. //See if we found one
  140. if ( tr.fraction != 1.0f )
  141. {
  142. #if __DEBUG_PARTICLE_COLLISION_OVERLAY
  143. debugoverlay->AddLineOverlay( testStart, tr.endpos, 255, 0, 0, true, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
  144. QAngle angles;
  145. VectorAngles( tr.plane.normal,angles );
  146. angles[PITCH] += 90;
  147. debugoverlay->AddBoxOverlay( tr.endpos, Vector(-64,-64,0), Vector(64,64,0), angles, 255, 0, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
  148. #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
  149. //Test the plane against a set of criteria
  150. ConsiderPlane( &tr.plane );
  151. return;
  152. }
  153. //We missed
  154. #if __DEBUG_PARTICLE_COLLISION_OVERLAY
  155. debugoverlay->AddLineOverlay( testStart, tr.endpos, 0, 128.0f+(128.0f*((float)i/(float)NUM_DISCREET_STEPS)), 0, true, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
  156. #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
  157. //Save that position for the next round
  158. testStart = testEnd;
  159. }
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. void CBaseSimpleCollision::ClearActivePlanes( void )
  165. {
  166. for ( int i = 0; i < MAX_COLLISION_PLANES; i++ )
  167. {
  168. m_collisionPlanes[i].m_Dist = -1.0f;
  169. m_collisionPlanes[i].m_Normal.Init();
  170. }
  171. m_nActivePlanes = 0;
  172. }
  173. //
  174. // CParticleCollision
  175. //
  176. //-----------------------------------------------------------------------------
  177. // Constructor
  178. //-----------------------------------------------------------------------------
  179. CParticleCollision::CParticleCollision( void )
  180. {
  181. m_flGravity = 800.0f;
  182. m_flCollisionDampen = 0.5f;
  183. m_flAngularCollisionDampen = 0.25f;
  184. ClearActivePlanes();
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Test for surrounding collision surfaces for quick collision testing for the particle system
  188. // Input : &origin - starting position
  189. // *dir - direction of movement (if NULL, will do a point emission test in four directions)
  190. // angularSpread - looseness of the spread
  191. // minSpeed - minimum speed
  192. // maxSpeed - maximum speed
  193. // gravity - particle gravity for the sytem
  194. // dampen - dampening amount on collisions
  195. //-----------------------------------------------------------------------------
  196. void CParticleCollision::Setup( const Vector &origin, const Vector *dir, float angularSpread, float minSpeed, float maxSpeed, float gravity, float dampen )
  197. {
  198. //Take the information for this simulation
  199. m_flGravity = gravity;
  200. m_flCollisionDampen = dampen;
  201. m_nActivePlanes = 0;
  202. //We take a rough estimation of the spray
  203. float speedAvg = (minSpeed+maxSpeed)*0.5f;
  204. //Point or directed?
  205. if ( dir == NULL )
  206. {
  207. //Test all around
  208. TestForPlane( origin, Vector( 1, 0, 0 ), speedAvg, gravity );
  209. TestForPlane( origin, Vector( -1, 0, 0 ), speedAvg, gravity );
  210. TestForPlane( origin, Vector( 0, 1, 0 ), speedAvg, gravity );
  211. TestForPlane( origin, Vector( 0, -1, 0 ), speedAvg, gravity );
  212. TestForPlane( origin, Vector( 0, 0, 1 ), speedAvg, gravity );
  213. TestForPlane( origin, Vector( 0, 0, -1 ), speedAvg, gravity );
  214. }
  215. else
  216. {
  217. Vector vSkewDir, vRight;
  218. QAngle vAngles;
  219. //FIXME: Quicker conversion?
  220. //FIXME: We need to factor in the angular spread instead
  221. VectorAngles( *dir, vAngles );
  222. AngleVectors( vAngles, NULL, &vRight, NULL );
  223. //Test straight
  224. TestForPlane( origin, *dir, speedAvg, gravity );
  225. vSkewDir = vRight;
  226. //Test right
  227. TestForPlane( origin, vSkewDir, speedAvg, gravity );
  228. vSkewDir *= -1.0f;
  229. //Test left
  230. TestForPlane( origin, vSkewDir, speedAvg, gravity );
  231. #if __DEBUG_PARTICLE_COLLISION_OVERLAY
  232. DevMsg( 1, "CParticleCollision: Found %d active plane(s)\n", m_nActivePlanes );
  233. #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
  234. }
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Simulate movement, with collision
  238. // Input : &origin - position of the particle
  239. // &velocity - velocity of the particle
  240. // &rollDelta - roll delta of the particle
  241. // timeDelta - time step
  242. //-----------------------------------------------------------------------------
  243. bool CParticleCollision::MoveParticle( Vector &origin, Vector &velocity, float *rollDelta, float timeDelta, trace_t *pTrace )
  244. {
  245. //Don't bother with non-moving particles
  246. if ( velocity == vec3_origin )
  247. return false;
  248. //Factor in gravity
  249. velocity[2] -= m_flGravity * timeDelta;
  250. //Move
  251. Vector testPosition = ( origin + ( velocity * timeDelta ) );
  252. //Only collide if we have active planes
  253. if ( m_nActivePlanes > 0 )
  254. {
  255. //Collide
  256. TraceLine( origin, testPosition, pTrace );
  257. //See if we hit something
  258. if ( pTrace->fraction != 1.0f )
  259. {
  260. #if __DEBUG_PARTICLE_COLLISION_RETEST
  261. //Retest the collision with a true trace line to avoid errant collisions
  262. UTIL_TraceLine( origin, testPosition, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, pTrace );
  263. #endif //__DEBUG_RETEST_COLLISION
  264. //Did we hit anything?
  265. if ( pTrace->fraction != 1.0f )
  266. {
  267. //See if we've settled
  268. if ( ( pTrace->plane.normal[2] >= 0.5f ) && ( fabs( velocity[2] ) <= 48.0f ) )
  269. {
  270. //Leave the particle at the collision point
  271. origin += velocity * ( (pTrace->fraction-COLLISION_EPSILON) * timeDelta );
  272. //Stop the particle
  273. velocity = vec3_origin;
  274. if ( rollDelta != NULL )
  275. {
  276. *rollDelta = 0.0f;
  277. }
  278. return false;
  279. }
  280. else
  281. {
  282. //Move the particle to the collision point
  283. origin += velocity * ( (pTrace->fraction-COLLISION_EPSILON) * timeDelta );
  284. //Find the reflection vector
  285. float proj = velocity.Dot( pTrace->plane.normal );
  286. velocity += pTrace->plane.normal * (-proj*2.0f);
  287. //Apply dampening
  288. velocity *= random->RandomFloat( (m_flCollisionDampen-0.1f), (m_flCollisionDampen+0.1f) );
  289. //Dampen the roll of the particles
  290. if ( rollDelta != NULL )
  291. {
  292. (*rollDelta) *= -0.25f;
  293. }
  294. return true;
  295. }
  296. }
  297. else
  298. {
  299. #if __DEBUG_PARTICLE_COLLISION_OVERLAY
  300. //Display a false hit
  301. debugoverlay->AddBoxOverlay( pTrace->endpos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 255, 0, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
  302. #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
  303. }
  304. }
  305. }
  306. //Simple move, no collision
  307. origin = testPosition;
  308. return false;
  309. }