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.

274 lines
8.1 KiB

  1. //========= Copyright � 1996-2012, Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. //=====================================================================================//
  5. #include <math.h>
  6. #include <float.h> // needed for flt_epsilon
  7. #include "basetypes.h"
  8. #include "tier0/dbg.h"
  9. #include "mathlib/vector4d.h"
  10. #include "mathlib/vector.h"
  11. #include "mathlib/volumeculler.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. // Returns true if the AABB is completely within the frustum.
  15. // Basic scalar approach derived from "Real Time Rendering" 2nd edition section 13.13.3.
  16. // TODO: Replace this a function similar to CFrustum::CheckBoxInline().
  17. static inline bool AABBInsideFrustum( const fltx4 *pPlanes, FLTX4 vCenter4, FLTX4 vDiagonal4 )
  18. {
  19. fltx4 mp0 = Dot4SIMD( vCenter4, pPlanes[0] );
  20. fltx4 mp1 = Dot4SIMD( vCenter4, pPlanes[1] );
  21. fltx4 mp2 = Dot4SIMD( vCenter4, pPlanes[2] );
  22. fltx4 mp3 = Dot4SIMD( vCenter4, pPlanes[3] );
  23. fltx4 mp4 = Dot4SIMD( vCenter4, pPlanes[4] );
  24. fltx4 mp5 = Dot4SIMD( vCenter4, pPlanes[5] );
  25. fltx4 np0 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[0] ) );
  26. fltx4 np1 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[1] ) );
  27. fltx4 np2 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[2] ) );
  28. fltx4 np3 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[3] ) );
  29. fltx4 np4 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[4] ) );
  30. fltx4 np5 = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[5] ) );
  31. fltx4 s0 = SubSIMD( mp0, np0 );
  32. fltx4 s1 = SubSIMD( mp1, np1 );
  33. fltx4 s2 = SubSIMD( mp2, np2 );
  34. fltx4 s3 = SubSIMD( mp3, np3 );
  35. fltx4 s4 = SubSIMD( mp4, np4 );
  36. fltx4 s5 = SubSIMD( mp5, np5 );
  37. fltx4 minS = MinSIMD( MinSIMD( MinSIMD( MinSIMD( MinSIMD( s0, s1 ), s2 ), s3 ), s4 ), s5 );
  38. if ( IsAnyNegative( minS ) )
  39. {
  40. return false;
  41. }
  42. // completely inside
  43. return true;
  44. }
  45. // Returns true if the AABB either touches or is completely within a convex volume defined by X planes.
  46. // Same basic approach as above.
  47. // TODO: Replace this a function similar to CFrustum::CheckBoxInline().
  48. static inline bool AABBTouchesOrInsideVolume( const fltx4 *pPlanes, uint nNumPlanes, FLTX4 vCenter4, FLTX4 vDiagonal4 )
  49. {
  50. fltx4 minA = Four_Ones;
  51. for ( uint i = 0; i < nNumPlanes; ++i )
  52. {
  53. fltx4 np = Dot3SIMD( vDiagonal4, AbsSIMD( pPlanes[i] ) );
  54. fltx4 mp = Dot4SIMD( vCenter4, pPlanes[i] );
  55. fltx4 a = AddSIMD( np, mp );
  56. minA = MinSIMD( minA, a );
  57. }
  58. if ( IsAnyNegative( minA ) )
  59. {
  60. return false;
  61. }
  62. return true;
  63. }
  64. bool AABBTouches( const fourplanes_t *planes, const fltx4 &fl4Center, const fltx4 &fl4Extents )
  65. {
  66. fltx4 centerx = SplatXSIMD(fl4Center);
  67. fltx4 centery = SplatYSIMD(fl4Center);
  68. fltx4 centerz = SplatZSIMD(fl4Center);
  69. fltx4 extx = SplatXSIMD(fl4Extents);
  70. fltx4 exty = SplatYSIMD(fl4Extents);
  71. fltx4 extz = SplatZSIMD(fl4Extents);
  72. // compute the dot product of the normal and the farthest corner
  73. for ( int i = 0; i < 2; i++ )
  74. {
  75. fltx4 xTotalBack = AddSIMD( MulSIMD( planes[i].nX, centerx ), MulSIMD(planes[i].nXAbs, extx ) );
  76. fltx4 yTotalBack = AddSIMD( MulSIMD( planes[i].nY, centery ), MulSIMD(planes[i].nYAbs, exty ) );
  77. fltx4 zTotalBack = AddSIMD( MulSIMD( planes[i].nZ, centerz ), MulSIMD(planes[i].nZAbs, extz ) );
  78. fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) );
  79. // if plane of the farthest corner is behind the plane, then the box is completely outside this plane
  80. if ( IsVector4LessThan( dotBack, planes[i].dist ) )
  81. return false;
  82. }
  83. return true;
  84. }
  85. bool CVolumeCuller::CheckBox( const VectorAligned &mins, const VectorAligned &maxs ) const
  86. {
  87. m_Stats.m_nTotalAABB++;
  88. if ( m_bCullSmallObjects )
  89. {
  90. VectorAligned diag( maxs - mins );
  91. // Not really box volume - hacked so one function is useful on zero thickness boxes too.
  92. float flVol = ( diag.x * diag.x ) + ( diag.y * diag.y ) + ( diag.z * diag.z );
  93. if ( flVol < m_flSmallObjectCullVolumeThreshold )
  94. return false;
  95. }
  96. fltx4 vMins4 = LoadAlignedSIMD( &mins.x );
  97. fltx4 vMaxs4 = LoadAlignedSIMD( &maxs.x );
  98. // Converts from 3D interval to center/diagonal form.
  99. fltx4 vCenter4 = MulSIMD( AddSIMD( vMaxs4, vMins4 ), Four_PointFives );
  100. fltx4 vDiagonal4 = SubSIMD( vMaxs4, vCenter4 );
  101. // Ensure vCenter.w is 1.0f.
  102. vCenter4 = SetWSIMD( vCenter4, Four_Ones );
  103. if ( m_bHasBaseFrustum )
  104. {
  105. if ( !AABBTouches( m_baseplanes, vCenter4, vDiagonal4 ) )
  106. return false;
  107. }
  108. if ( m_bHasExclusionFrustum )
  109. {
  110. if ( AABBInsideFrustum( m_ExclusionFrustumPlanes, vCenter4, vDiagonal4 ) )
  111. return false;
  112. }
  113. if ( m_nNumInclusionVolumePlanes )
  114. {
  115. if ( !AABBTouchesOrInsideVolume( m_InclusionVolumePlanes, m_nNumInclusionVolumePlanes, vCenter4, vDiagonal4 ) )
  116. return false;
  117. }
  118. m_Stats.m_nTotalAABBPassed++;
  119. return true;
  120. }
  121. bool CVolumeCuller::CheckBox( const Vector &mins, const Vector &maxs ) const
  122. {
  123. m_Stats.m_nTotalAABB++;
  124. if ( m_bCullSmallObjects )
  125. {
  126. Vector diag( maxs - mins );
  127. // Not really box volume - hacked so one function is useful on zero thickness boxes too.
  128. float flVol = ( diag.x * diag.x ) + ( diag.y * diag.y ) + ( diag.z * diag.z );
  129. if ( flVol < m_flSmallObjectCullVolumeThreshold )
  130. return false;
  131. }
  132. fltx4 vMins4 = LoadUnalignedSIMD( &mins.x );
  133. fltx4 vMaxs4 = LoadUnalignedSIMD( &maxs.x );
  134. // Converts from 3D interval to center/diagonal form.
  135. fltx4 vCenter4 = MulSIMD( AddSIMD( vMaxs4, vMins4 ), Four_PointFives );
  136. fltx4 vDiagonal4 = SubSIMD( vMaxs4, vCenter4 );
  137. // Ensure vCenter.w is 1.0f.
  138. vCenter4 = SetWSIMD( vCenter4, Four_Ones );
  139. if ( m_bHasBaseFrustum )
  140. {
  141. if ( !AABBTouches( m_baseplanes, vCenter4, vDiagonal4 ) )
  142. return false;
  143. }
  144. if ( m_bHasExclusionFrustum )
  145. {
  146. if ( AABBInsideFrustum( m_ExclusionFrustumPlanes, vCenter4, vDiagonal4 ) )
  147. return false;
  148. }
  149. if ( m_nNumInclusionVolumePlanes )
  150. {
  151. if ( !AABBTouchesOrInsideVolume( m_InclusionVolumePlanes, m_nNumInclusionVolumePlanes, vCenter4, vDiagonal4 ) )
  152. return false;
  153. }
  154. m_Stats.m_nTotalAABBPassed++;
  155. return true;
  156. }
  157. bool CVolumeCuller::CheckBoxCenterHalfDiagonal( const VectorAligned &center, const VectorAligned &halfDiagonal ) const
  158. {
  159. m_Stats.m_nTotalCenterHalfDiagonal++;
  160. fltx4 vCenter4 = LoadAlignedSIMD( &center.x );
  161. fltx4 vDiagonal4 = LoadAlignedSIMD( &halfDiagonal.x );
  162. // Ensure vCenter.w is 1.0f.
  163. vCenter4 = SetWSIMD( vCenter4, Four_Ones );
  164. if ( m_bHasBaseFrustum )
  165. {
  166. if ( !AABBTouches( m_baseplanes, vCenter4, vDiagonal4 ) )
  167. return false;
  168. }
  169. if ( m_bHasExclusionFrustum )
  170. {
  171. if ( AABBInsideFrustum( m_ExclusionFrustumPlanes, vCenter4, vDiagonal4 ) )
  172. return false;
  173. }
  174. if ( m_nNumInclusionVolumePlanes )
  175. {
  176. if ( !AABBTouchesOrInsideVolume( m_InclusionVolumePlanes, m_nNumInclusionVolumePlanes, vCenter4, vDiagonal4 ) )
  177. return false;
  178. }
  179. m_Stats.m_nTotalCenterHalfDiagonalPassed++;
  180. return true;
  181. }
  182. void CVolumeCuller::SetExclusionFrustumPlanes( const VPlane *pPlanes )
  183. {
  184. COMPILE_TIME_ASSERT( sizeof( VPlane ) == sizeof( fltx4 ) );
  185. if ( !pPlanes )
  186. {
  187. m_bHasExclusionFrustum = false;
  188. }
  189. else
  190. {
  191. for ( int i = 0; i < cNumExclusionFrustumPlanes; ++i )
  192. {
  193. // Convert VPlane to plane equation form.
  194. reinterpret_cast< Vector4D & >( m_ExclusionFrustumPlanes[i] ).Init( pPlanes[i].m_Normal.x, pPlanes[i].m_Normal.y, pPlanes[i].m_Normal.z, -pPlanes[i].m_Dist );
  195. }
  196. m_bHasExclusionFrustum = true;
  197. }
  198. }
  199. void CVolumeCuller::SetBaseFrustumPlanes( const VPlane *pPlanes )
  200. {
  201. COMPILE_TIME_ASSERT( sizeof( VPlane ) == sizeof( fltx4 ) );
  202. if ( !pPlanes )
  203. {
  204. m_bHasBaseFrustum = false;
  205. }
  206. else
  207. {
  208. m_baseplanes[0].Set4Planes( pPlanes );
  209. m_baseplanes[1].Set2Planes( pPlanes + 4 );
  210. m_bHasBaseFrustum = true;
  211. }
  212. }
  213. void CVolumeCuller::GetBaseFrustumPlanes( VPlane *pBasePlanes ) const
  214. {
  215. m_baseplanes[0].Get4Planes( pBasePlanes );
  216. m_baseplanes[1].Get2Planes( pBasePlanes + 4 );
  217. }
  218. void CVolumeCuller::SetInclusionVolumePlanes( const VPlane *pPlanes, uint nNumPlanes )
  219. {
  220. Assert( nNumPlanes <= cMaxInclusionVolumePlanes );
  221. nNumPlanes = MIN( nNumPlanes, cMaxInclusionVolumePlanes );
  222. m_nNumInclusionVolumePlanes = nNumPlanes;
  223. for ( uint i = 0; i < nNumPlanes; ++i )
  224. {
  225. // Convert VPlane to plane equation form.
  226. reinterpret_cast< Vector4D & >( m_InclusionVolumePlanes[i] ).Init( pPlanes[i].m_Normal.x, pPlanes[i].m_Normal.y, pPlanes[i].m_Normal.z, -pPlanes[i].m_Dist );
  227. }
  228. }