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.

238 lines
6.4 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "physics.h"
  10. #include "te_effect_dispatch.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. static int BestAxisMatchingNormal( matrix3x4_t &matrix, const Vector &normal )
  14. {
  15. float bestDot = -1;
  16. int best = 0;
  17. for ( int i = 0; i < 3; i++ )
  18. {
  19. Vector tmp;
  20. MatrixGetColumn( matrix, i, tmp );
  21. float dot = fabs(DotProduct( tmp, normal ));
  22. if ( dot > bestDot )
  23. {
  24. bestDot = dot;
  25. best = i;
  26. }
  27. }
  28. return best;
  29. }
  30. void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity )
  31. {
  32. Vector normal;
  33. float dist;
  34. pFluid->GetSurfacePlane( &normal, &dist );
  35. matrix3x4_t &matrix = pEntity->EntityToWorldTransform();
  36. // Find the local axis that best matches the water surface normal
  37. int bestAxis = BestAxisMatchingNormal( matrix, normal );
  38. Vector tangent, binormal;
  39. MatrixGetColumn( matrix, (bestAxis+1)%3, tangent );
  40. binormal = CrossProduct( normal, tangent );
  41. VectorNormalize( binormal );
  42. tangent = CrossProduct( binormal, normal );
  43. VectorNormalize( tangent );
  44. // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible
  45. // compute an OBB using this basis
  46. // Get object extents in basis
  47. Vector tanPts[2], binPts[2];
  48. tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent );
  49. tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent );
  50. binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal );
  51. binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal );
  52. // now compute the centered bbox
  53. float mins[2], maxs[2], center[2], extents[2];
  54. mins[0] = DotProduct( tanPts[0], tangent );
  55. maxs[0] = DotProduct( tanPts[1], tangent );
  56. mins[1] = DotProduct( binPts[0], binormal );
  57. maxs[1] = DotProduct( binPts[1], binormal );
  58. center[0] = 0.5 * (mins[0] + maxs[0]);
  59. center[1] = 0.5 * (mins[1] + maxs[1]);
  60. extents[0] = maxs[0] - center[0];
  61. extents[1] = maxs[1] - center[1];
  62. Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal;
  63. Vector axes[2];
  64. axes[0] = (maxs[0] - center[0]) * tangent;
  65. axes[1] = (maxs[1] - center[1]) * binormal;
  66. // visualize OBB hit
  67. /*
  68. Vector corner1 = centerPoint - axes[0] - axes[1];
  69. Vector corner2 = centerPoint + axes[0] - axes[1];
  70. Vector corner3 = centerPoint + axes[0] + axes[1];
  71. Vector corner4 = centerPoint - axes[0] + axes[1];
  72. NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 );
  73. NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 );
  74. NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 );
  75. NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 );
  76. */
  77. Vector corner[4];
  78. corner[0] = centerPoint - axes[0] - axes[1];
  79. corner[1] = centerPoint + axes[0] - axes[1];
  80. corner[2] = centerPoint + axes[0] + axes[1];
  81. corner[3] = centerPoint - axes[0] + axes[1];
  82. CEffectData data;
  83. if ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL )
  84. {
  85. /*
  86. data.m_vOrigin = centerPoint;
  87. data.m_vNormal = normal;
  88. VectorAngles( normal, data.m_vAngles );
  89. data.m_flScale = random->RandomFloat( 8, 10 );
  90. DispatchEffect( "watersplash", data );
  91. int splashes = 4;
  92. Vector point;
  93. for ( int i = 0; i < splashes; i++ )
  94. {
  95. point = RandomVector( -32.0f, 32.0f );
  96. point[2] = 0.0f;
  97. point += corner[i];
  98. data.m_vOrigin = point;
  99. data.m_vNormal = normal;
  100. VectorAngles( normal, data.m_vAngles );
  101. data.m_flScale = random->RandomFloat( 4, 6 );
  102. DispatchEffect( "watersplash", data );
  103. }
  104. */
  105. //FIXME: This code will not work correctly given how the ragdoll/fluid collision is acting currently
  106. return;
  107. }
  108. Vector vel;
  109. pObject->GetVelocity( &vel, NULL );
  110. float rawSpeed = -DotProduct( normal, vel );
  111. // proportional to cross-sectional area times velocity squared (fluid pressure)
  112. float speed = rawSpeed * rawSpeed * extents[0] * extents[1] * (1.0f / 2500000.0f) * pObject->GetMass() * (0.01f);
  113. speed = clamp( speed, 0.f, 50.f );
  114. bool bRippleOnly = false;
  115. // allow the entity to perform a custom splash effect
  116. if ( pEntity->PhysicsSplash( centerPoint, normal, rawSpeed, speed ) )
  117. return;
  118. //Deny really weak hits
  119. //FIXME: We still need to ripple the surface in this case
  120. if ( speed <= 0.35f )
  121. {
  122. if ( speed <= 0.1f )
  123. return;
  124. bRippleOnly = true;
  125. }
  126. float size = RemapVal( speed, 0.35, 50, 8, 18 );
  127. //Find the surface area
  128. float radius = extents[0] * extents[1];
  129. //int splashes = clamp ( radius / 128.0f, 1, 2 ); //One splash for every three square feet of area
  130. //Msg( "Speed: %.2f, Size: %.2f\n, Radius: %.2f, Splashes: %d", speed, size, radius, splashes );
  131. Vector point;
  132. data.m_fFlags = 0;
  133. data.m_vOrigin = centerPoint;
  134. data.m_vNormal = normal;
  135. VectorAngles( normal, data.m_vAngles );
  136. data.m_flScale = size + random->RandomFloat( 0, 2 );
  137. if ( pEntity->GetWaterType() & CONTENTS_SLIME )
  138. {
  139. data.m_fFlags |= FX_WATER_IN_SLIME;
  140. }
  141. if ( bRippleOnly )
  142. {
  143. DispatchEffect( "waterripple", data );
  144. }
  145. else
  146. {
  147. DispatchEffect( "watersplash", data );
  148. }
  149. if ( radius > 500.0f )
  150. {
  151. int splashes = random->RandomInt( 1, 4 );
  152. for ( int i = 0; i < splashes; i++ )
  153. {
  154. point = RandomVector( -4.0f, 4.0f );
  155. point[2] = 0.0f;
  156. point += corner[i];
  157. data.m_fFlags = 0;
  158. data.m_vOrigin = point;
  159. data.m_vNormal = normal;
  160. VectorAngles( normal, data.m_vAngles );
  161. data.m_flScale = size + random->RandomFloat( -3, 1 );
  162. if ( pEntity->GetWaterType() & CONTENTS_SLIME )
  163. {
  164. data.m_fFlags |= FX_WATER_IN_SLIME;
  165. }
  166. if ( bRippleOnly )
  167. {
  168. DispatchEffect( "waterripple", data );
  169. }
  170. else
  171. {
  172. DispatchEffect( "watersplash", data );
  173. }
  174. }
  175. }
  176. /*
  177. for ( i = 0; i < splashes; i++ )
  178. {
  179. point = RandomVector( -8.0f, 8.0f );
  180. point[2] = 0.0f;
  181. point += centerPoint + axes[0] * random->RandomFloat( -1, 1 ) + axes[1] * random->RandomFloat( -1, 1 );
  182. data.m_vOrigin = point;
  183. data.m_vNormal = normal;
  184. VectorAngles( normal, data.m_vAngles );
  185. data.m_flScale = size + random->RandomFloat( -2, 4 );
  186. DispatchEffect( "watersplash", data );
  187. }
  188. */
  189. }