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.

1565 lines
48 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cmodel.h"
  8. #include "dispcoll_common.h"
  9. #include "collisionutils.h"
  10. #include "tier1/strtools.h"
  11. #include "tier0/vprof.h"
  12. #include "tier1/fmtstr.h"
  13. #include "tier1/utlhash.h"
  14. #include "tier1/generichash.h"
  15. #include "tier0/fasttimer.h"
  16. #include "vphysics/virtualmesh.h"
  17. #include "tier1/datamanager.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. //=============================================================================
  21. // Cache
  22. #ifdef ENGINE_DLL
  23. CDataManager<CDispCollTree, CDispCollTree *, bool, CThreadFastMutex> g_DispCollTriCache( 2048*1024 );
  24. #endif
  25. struct DispCollPlaneIndex_t
  26. {
  27. Vector vecPlane;
  28. int index;
  29. };
  30. class CPlaneIndexHashFuncs
  31. {
  32. public:
  33. // cppcheck-suppress noExplicitConstructor
  34. CPlaneIndexHashFuncs( int ) {}
  35. // Compare
  36. bool operator()( const DispCollPlaneIndex_t &lhs, const DispCollPlaneIndex_t &rhs ) const
  37. {
  38. return ( lhs.vecPlane == rhs.vecPlane || lhs.vecPlane == -rhs.vecPlane );
  39. }
  40. // Hash
  41. unsigned int operator()( const DispCollPlaneIndex_t &item ) const
  42. {
  43. return HashItem( item.vecPlane ) ^ HashItem( -item.vecPlane );
  44. }
  45. };
  46. CUtlHash<DispCollPlaneIndex_t, CPlaneIndexHashFuncs, CPlaneIndexHashFuncs> g_DispCollPlaneIndexHash( 512 );
  47. //=============================================================================
  48. // Displacement Collision Triangle
  49. //-----------------------------------------------------------------------------
  50. // Purpose:
  51. //-----------------------------------------------------------------------------
  52. CDispCollTri::CDispCollTri()
  53. {
  54. Init();
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. //-----------------------------------------------------------------------------
  59. void CDispCollTri::Init( void )
  60. {
  61. m_vecNormal.Init();
  62. m_flDist = 0.0f;
  63. m_TriData[0].m_IndexDummy = m_TriData[1].m_IndexDummy = m_TriData[2].m_IndexDummy = 0;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. //-----------------------------------------------------------------------------
  68. void CDispCollTri::CalcPlane( CDispVector<Vector> &m_aVerts )
  69. {
  70. Vector vecEdges[2];
  71. vecEdges[0] = m_aVerts[GetVert( 1 )] - m_aVerts[GetVert( 0 )];
  72. vecEdges[1] = m_aVerts[GetVert( 2 )] - m_aVerts[GetVert( 0 )];
  73. m_vecNormal = vecEdges[1].Cross( vecEdges[0] );
  74. VectorNormalize( m_vecNormal );
  75. m_flDist = m_vecNormal.Dot( m_aVerts[GetVert( 0 )] );
  76. // Calculate the signbits for the plane - fast test.
  77. m_ucSignBits = 0;
  78. m_ucPlaneType = PLANE_ANYZ;
  79. for ( int iAxis = 0; iAxis < 3 ; ++iAxis )
  80. {
  81. if ( m_vecNormal[iAxis] < 0.0f )
  82. {
  83. m_ucSignBits |= 1 << iAxis;
  84. }
  85. if ( m_vecNormal[iAxis] == 1.0f )
  86. {
  87. m_ucPlaneType = iAxis;
  88. }
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. //-----------------------------------------------------------------------------
  93. inline void FindMin( float v1, float v2, float v3, int &iMin )
  94. {
  95. float flMin = v1;
  96. iMin = 0;
  97. if( v2 < flMin ) { flMin = v2; iMin = 1; }
  98. if( v3 < flMin ) { flMin = v3; iMin = 2; }
  99. }
  100. //-----------------------------------------------------------------------------
  101. //-----------------------------------------------------------------------------
  102. inline void FindMax( float v1, float v2, float v3, int &iMax )
  103. {
  104. float flMax = v1;
  105. iMax = 0;
  106. if( v2 > flMax ) { flMax = v2; iMax = 1; }
  107. if( v3 > flMax ) { flMax = v3; iMax = 2; }
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose:
  111. //-----------------------------------------------------------------------------
  112. void CDispCollTri::FindMinMax( CDispVector<Vector> &m_aVerts )
  113. {
  114. int iMin, iMax;
  115. FindMin( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMin );
  116. FindMax( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMax );
  117. SetMin( 0, iMin );
  118. SetMax( 0, iMax );
  119. FindMin( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMin );
  120. FindMax( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMax );
  121. SetMin( 1, iMin );
  122. SetMax( 1, iMax );
  123. FindMin( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMin );
  124. FindMax( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMax );
  125. SetMin( 2, iMin );
  126. SetMax( 2, iMax );
  127. }
  128. // SIMD Routines for intersecting with the quad tree
  129. FORCEINLINE int IntersectRayWithFourBoxes( const FourVectors &rayStart, const FourVectors &invDelta, const FourVectors &rayExtents, const FourVectors &boxMins, const FourVectors &boxMaxs )
  130. {
  131. // SIMD Test ray against all four boxes at once
  132. // each node stores the bboxes of its four children
  133. FourVectors hitMins = boxMins;
  134. hitMins -= rayStart;
  135. FourVectors hitMaxs = boxMaxs;
  136. hitMaxs -= rayStart;
  137. // adjust for swept box by enlarging the child bounds to shrink the sweep down to a point
  138. hitMins -= rayExtents;
  139. hitMaxs += rayExtents;
  140. // compute the parametric distance along the ray of intersection in each dimension
  141. hitMins *= invDelta;
  142. hitMaxs *= invDelta;
  143. // Find the exit parametric intersection distance in each dimesion, for each box
  144. FourVectors exitT = maximum(hitMins,hitMaxs);
  145. // Find the entry parametric intersection distance in each dimesion, for each box
  146. FourVectors entryT = minimum(hitMins,hitMaxs);
  147. // now find the max overall entry distance across all dimensions for each box
  148. fltx4 minTemp = MaxSIMD(entryT.x, entryT.y);
  149. fltx4 boxEntryT = MaxSIMD(minTemp, entryT.z);
  150. // now find the min overall exit distance across all dimensions for each box
  151. fltx4 maxTemp = MinSIMD(exitT.x, exitT.y);
  152. fltx4 boxExitT = MinSIMD(maxTemp, exitT.z);
  153. boxEntryT = MaxSIMD(boxEntryT,Four_Zeros);
  154. boxExitT = MinSIMD(boxExitT,Four_Ones);
  155. // if entry<=exit for the box, we've got a hit
  156. bi32x4 active = CmpLeSIMD(boxEntryT,boxExitT); // mask of which boxes are active
  157. // hit at least one box?
  158. return TestSignSIMD(active);
  159. }
  160. // This does 4 simultaneous box intersections
  161. // NOTE: This can be used as a 1 vs 4 test by replicating a single box into the one side
  162. FORCEINLINE int IntersectFourBoxPairs( const FourVectors &mins0, const FourVectors &maxs0, const FourVectors &mins1, const FourVectors &maxs1 )
  163. {
  164. // find the max mins and min maxs in each dimension
  165. FourVectors intersectMins = maximum(mins0,mins1);
  166. FourVectors intersectMaxs = minimum(maxs0,maxs1);
  167. // if intersectMins <= intersectMaxs then the boxes overlap in this dimension
  168. bi32x4 overlapX = CmpLeSIMD(intersectMins.x,intersectMaxs.x);
  169. bi32x4 overlapY = CmpLeSIMD(intersectMins.y,intersectMaxs.y);
  170. bi32x4 overlapZ = CmpLeSIMD(intersectMins.z,intersectMaxs.z);
  171. // if the boxes overlap in all three dimensions, they intersect
  172. bi32x4 tmp = AndSIMD( overlapX, overlapY );
  173. bi32x4 active = AndSIMD( tmp, overlapZ );
  174. // hit at least one box?
  175. return TestSignSIMD(active);
  176. }
  177. // This does 4 simultaneous box vs. sphere intersections
  178. // NOTE: This can be used as a 1 vs 4 test by replicating a single sphere/box into one side
  179. FORCEINLINE int IntersectFourBoxSpherePairs( const FourVectors &center, const fltx4 &radiusSq, const FourVectors &mins, const FourVectors &maxs )
  180. {
  181. // for each dimension of each box, compute the clamped distance from the mins side to the center (must be >= 0)
  182. FourVectors minDist = mins;
  183. minDist -= center;
  184. FourVectors dist;
  185. dist.x = MaxSIMD(Four_Zeros, minDist.x);
  186. dist.y = MaxSIMD(Four_Zeros, minDist.y);
  187. dist.z = MaxSIMD(Four_Zeros, minDist.z);
  188. // now compute the distance from the maxs side to the center
  189. FourVectors maxDist = center;
  190. maxDist -= maxs;
  191. // NOTE: Don't need to clamp here because we clamp against the minDist which must be >= 0, so the two clamps
  192. // get folded together
  193. FourVectors totalDist;
  194. totalDist.x = MaxSIMD(dist.x, maxDist.x);
  195. totalDist.y = MaxSIMD(dist.y, maxDist.y);
  196. totalDist.z = MaxSIMD(dist.z, maxDist.z);
  197. // get the total squred distance between each box & sphere center by summing the squares of each
  198. // component/dimension
  199. fltx4 distSq = totalDist * totalDist;
  200. // if squared distance between each sphere center & box is less than the radiusSquared for that sphere
  201. // we have an intersection
  202. bi32x4 active = CmpLeSIMD(distSq,radiusSq);
  203. // at least one intersection?
  204. return TestSignSIMD(active);
  205. }
  206. int FORCEINLINE CDispCollTree::BuildRayLeafList( int iNode, rayleaflist_t &list )
  207. {
  208. list.nodeList[0] = iNode;
  209. int listIndex = 0;
  210. list.maxIndex = 0;
  211. while ( listIndex <= list.maxIndex )
  212. {
  213. iNode = list.nodeList[listIndex];
  214. // the rest are all leaves
  215. if ( IsLeafNode(iNode) )
  216. return listIndex;
  217. listIndex++;
  218. const CDispCollNode &node = m_nodes[iNode];
  219. int mask = IntersectRayWithFourBoxes( list.rayStart, list.invDelta, list.rayExtents, node.m_mins, node.m_maxs );
  220. if ( mask )
  221. {
  222. int child = Nodes_GetChild( iNode, 0 );
  223. if ( mask & 1 )
  224. {
  225. ++list.maxIndex;
  226. list.nodeList[list.maxIndex] = child;
  227. }
  228. if ( mask & 2 )
  229. {
  230. ++list.maxIndex;
  231. list.nodeList[list.maxIndex] = child+1;
  232. }
  233. if ( mask & 4 )
  234. {
  235. ++list.maxIndex;
  236. list.nodeList[list.maxIndex] = child+2;
  237. }
  238. if ( mask & 8 )
  239. {
  240. ++list.maxIndex;
  241. list.nodeList[list.maxIndex] = child+3;
  242. }
  243. Assert(list.maxIndex < MAX_AABB_LIST);
  244. }
  245. }
  246. return listIndex;
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Create the AABB tree.
  250. //-----------------------------------------------------------------------------
  251. bool CDispCollTree::AABBTree_Create( CCoreDispInfo *pDisp )
  252. {
  253. // Copy the flags.
  254. m_nFlags = pDisp->GetSurface()->GetFlags();
  255. // Copy necessary displacement data.
  256. AABBTree_CopyDispData( pDisp );
  257. // Setup/create the leaf nodes first so the recusion can use this data to stop.
  258. AABBTree_CreateLeafs();
  259. // Create the bounding box of the displacement surface + the base face.
  260. AABBTree_CalcBounds();
  261. // Successful.
  262. return true;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CDispCollTree::AABBTree_CopyDispData( CCoreDispInfo *pDisp )
  268. {
  269. // Displacement size.
  270. m_nPower = pDisp->GetPower();
  271. // Displacement base surface data.
  272. CCoreDispSurface *pSurf = pDisp->GetSurface();
  273. m_nContents = pSurf->GetContents();
  274. pSurf->GetNormal( m_vecStabDir );
  275. for ( int iPoint = 0; iPoint < 4; iPoint++ )
  276. {
  277. pSurf->GetPoint( iPoint, m_vecSurfPoints[iPoint] );
  278. }
  279. // Allocate collision tree data.
  280. HUNK_ALLOC_CREDIT_( "AABBTree_CopyDispData" );
  281. m_aVerts.SetSize( GetSize() );
  282. m_aTris.SetSize( GetTriSize() );
  283. int numLeaves = (GetWidth()-1) * (GetHeight()-1);
  284. m_leaves.SetCount(numLeaves);
  285. int numNodes = Nodes_CalcCount( m_nPower );
  286. numNodes -= numLeaves;
  287. m_nodes.SetCount(numNodes);
  288. // Setup size.
  289. m_nSize = sizeof( this );
  290. m_nSize += sizeof( Vector ) * GetSize();
  291. m_nSize += sizeof( CDispCollTri ) * GetTriSize();
  292. #if OLD_DISP_AABB
  293. m_nSize += sizeof( CDispCollAABBNode ) * Nodes_CalcCount( m_nPower );
  294. #endif
  295. m_nSize += sizeof(m_nodes[0]) * m_nodes.Count();
  296. m_nSize += sizeof(m_leaves[0]) * m_leaves.Count();
  297. m_nSize += sizeof( CDispCollTri* ) * DISPCOLL_TREETRI_SIZE;
  298. // Copy vertex data.
  299. for ( int iVert = 0; iVert < m_aVerts.Count(); iVert++ )
  300. {
  301. pDisp->GetVert( iVert, m_aVerts[iVert] );
  302. }
  303. // Copy and setup triangle data.
  304. unsigned short iVerts[3];
  305. for ( int iTri = 0; iTri < m_aTris.Count(); ++iTri )
  306. {
  307. pDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] );
  308. m_aTris[iTri].SetVert( 0, iVerts[0] );
  309. m_aTris[iTri].SetVert( 1, iVerts[1] );
  310. m_aTris[iTri].SetVert( 2, iVerts[2] );
  311. m_aTris[iTri].m_uiFlags = pDisp->GetTriTagValue( iTri );
  312. // Calculate the surface props and set flags.
  313. if ( ( pDisp->GetFlags() & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
  314. {
  315. float flTotalAlpha[3] = { 0,0,0 };
  316. for ( int iVert = 0; iVert < 3; ++iVert )
  317. {
  318. Vector4D vBlend;
  319. pDisp->GetMultiBlend( m_aTris[iTri].GetVert( iVert ), vBlend );
  320. flTotalAlpha[0] += vBlend.y;
  321. flTotalAlpha[1] += vBlend.z;
  322. flTotalAlpha[2] += vBlend.w;
  323. }
  324. if ( ( flTotalAlpha[2] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[2] >= flTotalAlpha[1] ) && ( flTotalAlpha[2] >= flTotalAlpha[0] ) )
  325. {
  326. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP4;
  327. }
  328. else if ( ( flTotalAlpha[1] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[1] >= flTotalAlpha[0] ) )
  329. {
  330. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP3;
  331. }
  332. else if ( flTotalAlpha[0] > DISP_MULTIBLEND_PROP_THRESHOLD )
  333. {
  334. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP2;
  335. }
  336. else
  337. {
  338. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP1;
  339. }
  340. }
  341. else
  342. {
  343. float flTotalAlpha = 0.0f;
  344. for ( int iVert = 0; iVert < 3; ++iVert )
  345. {
  346. flTotalAlpha += pDisp->GetAlpha( m_aTris[iTri].GetVert( iVert ) );
  347. }
  348. if ( flTotalAlpha > DISP_ALPHA_PROP_DELTA )
  349. {
  350. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP2;
  351. }
  352. else
  353. {
  354. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP1;
  355. }
  356. }
  357. // Add the displacement surface flag.
  358. m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFACE;
  359. // Calculate the plane normal and the min max.
  360. m_aTris[iTri].CalcPlane( m_aVerts );
  361. m_aTris[iTri].FindMinMax( m_aVerts );
  362. }
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. //-----------------------------------------------------------------------------
  367. void CDispCollTree::AABBTree_CreateLeafs( void )
  368. {
  369. int numLeaves = (GetWidth()-1) * (GetHeight()-1);
  370. m_leaves.SetCount(numLeaves);
  371. int numNodes = Nodes_CalcCount( m_nPower );
  372. numNodes -= numLeaves;
  373. m_nodes.SetCount(numNodes);
  374. // Get the width and height of the displacement.
  375. int nWidth = GetWidth() - 1;
  376. int nHeight = GetHeight() - 1;
  377. for ( int iHgt = 0; iHgt < nHeight; ++iHgt )
  378. {
  379. for ( int iWid = 0; iWid < nWidth; ++iWid )
  380. {
  381. int iLeaf = Nodes_GetIndexFromComponents( iWid, iHgt );
  382. int iIndex = iHgt * nWidth + iWid;
  383. int iTri = iIndex * 2;
  384. m_leaves[iLeaf].m_tris[0] = iTri;
  385. m_leaves[iLeaf].m_tris[1] = iTri + 1;
  386. }
  387. }
  388. }
  389. void CDispCollTree::AABBTree_GenerateBoxes_r( int nodeIndex, Vector *pMins, Vector *pMaxs )
  390. {
  391. // leaf
  392. ClearBounds( *pMins, *pMaxs );
  393. if ( nodeIndex >= m_nodes.Count() )
  394. {
  395. int iLeaf = nodeIndex - m_nodes.Count();
  396. for ( int iTri = 0; iTri < 2; ++iTri )
  397. {
  398. int triIndex = m_leaves[iLeaf].m_tris[iTri];
  399. const CDispCollTri &tri = m_aTris[triIndex];
  400. AddPointToBounds( m_aVerts[tri.GetVert( 0 )], *pMins, *pMaxs );
  401. AddPointToBounds( m_aVerts[tri.GetVert( 1 )], *pMins, *pMaxs );
  402. AddPointToBounds( m_aVerts[tri.GetVert( 2 )], *pMins, *pMaxs );
  403. }
  404. }
  405. else // node
  406. {
  407. Vector childMins[4], childMaxs[4];
  408. for ( int i = 0; i < 4; i++ )
  409. {
  410. int child = Nodes_GetChild( nodeIndex, i );
  411. AABBTree_GenerateBoxes_r( child, &childMins[i], &childMaxs[i] );
  412. AddPointToBounds( childMins[i], *pMins, *pMaxs );
  413. AddPointToBounds( childMaxs[i], *pMins, *pMaxs );
  414. }
  415. m_nodes[nodeIndex].m_mins.LoadAndSwizzle( childMins[0], childMins[1], childMins[2], childMins[3] );
  416. m_nodes[nodeIndex].m_maxs.LoadAndSwizzle( childMaxs[0], childMaxs[1], childMaxs[2], childMaxs[3] );
  417. }
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose:
  421. //-----------------------------------------------------------------------------
  422. void CDispCollTree::AABBTree_CalcBounds( void )
  423. {
  424. // Check data.
  425. if ( ( m_aVerts.Count() == 0 ) || ( m_nodes.Count() == 0 ) )
  426. return;
  427. AABBTree_GenerateBoxes_r( 0, &m_mins, &m_maxs );
  428. #if INCLUDE_SURFACE_IN_BOUNDS
  429. // Add surface points to bounds.
  430. for ( int iPoint = 0; iPoint < 4; ++iPoint )
  431. {
  432. VectorMin( m_vecSurfPoints[iPoint], m_mins, m_mins );
  433. VectorMax( m_vecSurfPoints[iPoint], m_maxs, m_maxs );
  434. }
  435. #endif
  436. // Bloat a little.
  437. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  438. {
  439. m_mins[iAxis] -= 1.0f;
  440. m_maxs[iAxis] += 1.0f;
  441. }
  442. }
  443. static CThreadFastMutex s_CacheMutex;
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. //-----------------------------------------------------------------------------
  447. inline void CDispCollTree::LockCache()
  448. {
  449. #ifdef ENGINE_DLL
  450. if ( !g_DispCollTriCache.LockResource( m_hCache ) )
  451. {
  452. AUTO_LOCK_FM( s_CacheMutex );
  453. // Cache may have just been created, so check once more
  454. if ( !g_DispCollTriCache.LockResource( m_hCache ) )
  455. {
  456. Cache();
  457. m_hCache = g_DispCollTriCache.CreateResource( this );
  458. g_DispCollTriCache.LockResource( m_hCache );
  459. //Msg( "Adding 0x%x to cache (actual %d) [%d, %d --> %.2f] %d total, %d unique\n", this, GetCacheMemorySize(), GetTriSize(), m_aEdgePlanes.Count(), (float)m_aEdgePlanes.Count()/(float)GetTriSize(), totals, uniques );
  460. }
  461. }
  462. #else
  463. Cache();
  464. #endif
  465. }
  466. inline void CDispCollTree::UnlockCache()
  467. {
  468. #ifdef ENGINE_DLL
  469. g_DispCollTriCache.UnlockResource( m_hCache );
  470. #endif
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose:
  474. //-----------------------------------------------------------------------------
  475. void CDispCollTree::Cache( void )
  476. {
  477. if ( m_aTrisCache.Count() == GetTriSize() )
  478. {
  479. return;
  480. }
  481. VPROF( "CDispCollTree::Cache" );
  482. // Alloc.
  483. // int nSize = sizeof( CDispCollTriCache ) * GetTriSize();
  484. int nTriCount = GetTriSize();
  485. {
  486. MEM_ALLOC_CREDIT();
  487. m_aTrisCache.SetSize( nTriCount );
  488. }
  489. for ( int iTri = 0; iTri < nTriCount; ++iTri )
  490. {
  491. Cache_Create( &m_aTris[iTri], iTri );
  492. }
  493. g_DispCollPlaneIndexHash.Purge();
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. //-----------------------------------------------------------------------------
  498. bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, RayDispOutput_t &output )
  499. {
  500. if ( IsBoxIntersectingRay( m_mins, m_maxs, ray.m_Start, ray.m_Delta, DISPCOLL_DIST_EPSILON ) )
  501. {
  502. return AABBTree_Ray( ray, ray.InvDelta(), output );
  503. }
  504. return false;
  505. }
  506. bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, RayDispOutput_t &output )
  507. {
  508. VPROF( "DispRayTest" );
  509. // Check for ray test.
  510. if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) )
  511. return false;
  512. // Check for opacity.
  513. if ( !( m_nContents & MASK_OPAQUE ) )
  514. return false;
  515. // Pre-calc the inverse delta for perf.
  516. CDispCollTri *pImpactTri = NULL;
  517. AABBTree_TreeTrisRayBarycentricTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, output, &pImpactTri );
  518. if ( pImpactTri )
  519. {
  520. // Collision.
  521. output.ndxVerts[0] = pImpactTri->GetVert( 0 );
  522. output.ndxVerts[1] = pImpactTri->GetVert( 2 );
  523. output.ndxVerts[2] = pImpactTri->GetVert( 1 );
  524. Assert( (output.u <= 1.0f ) && ( output.v <= 1.0f ) );
  525. Assert( (output.u >= 0.0f ) && ( output.v >= 0.0f ) );
  526. return true;
  527. }
  528. // No collision.
  529. return false;
  530. }
  531. void CDispCollTree::AABBTree_TreeTrisRayBarycentricTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, RayDispOutput_t &output, CDispCollTri **pImpactTri )
  532. {
  533. rayleaflist_t list;
  534. // NOTE: This part is loop invariant - should be hoisted up as far as possible
  535. list.invDelta.DuplicateVector(vecInvDelta);
  536. list.rayStart.DuplicateVector(ray.m_Start);
  537. Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON);
  538. list.rayExtents.DuplicateVector(ext);
  539. int listIndex = BuildRayLeafList( iNode, list );
  540. float flU, flV, flT;
  541. for ( ; listIndex <= list.maxIndex; listIndex++ )
  542. {
  543. int leafIndex = list.nodeList[listIndex] - m_nodes.Count();
  544. CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]];
  545. CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]];
  546. if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], flU, flV, &flT ) )
  547. {
  548. // Make sure it's inside the range
  549. if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) )
  550. {
  551. if( ( flT > 0.0f ) && ( flT < output.dist ) )
  552. {
  553. (*pImpactTri) = pTri0;
  554. output.u = flU;
  555. output.v = flV;
  556. output.dist = flT;
  557. }
  558. }
  559. }
  560. if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], flU, flV, &flT ) )
  561. {
  562. // Make sure it's inside the range
  563. if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) )
  564. {
  565. if( ( flT > 0.0f ) && ( flT < output.dist ) )
  566. {
  567. (*pImpactTri) = pTri1;
  568. output.u = flU;
  569. output.v = flV;
  570. output.dist = flT;
  571. }
  572. }
  573. }
  574. }
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose:
  578. //-----------------------------------------------------------------------------
  579. bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace, bool bSide )
  580. {
  581. VPROF("AABBTree_Ray");
  582. // VPROF_BUDGET( "DispRayTraces", VPROF_BUDGETGROUP_DISP_RAYTRACES );
  583. // Check for ray test.
  584. if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) )
  585. return false;
  586. // Check for opacity.
  587. if ( !( m_nContents & MASK_OPAQUE ) )
  588. return false;
  589. // Pre-calc the inverse delta for perf.
  590. CDispCollTri *pImpactTri = NULL;
  591. AABBTree_TreeTrisRayTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, pTrace, bSide, &pImpactTri );
  592. if ( pImpactTri )
  593. {
  594. // Collision.
  595. VectorCopy( pImpactTri->m_vecNormal, pTrace->plane.normal );
  596. pTrace->plane.dist = pImpactTri->m_flDist;
  597. pTrace->dispFlags = pImpactTri->m_uiFlags;
  598. return true;
  599. }
  600. // No collision.
  601. return false;
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose:
  605. //-----------------------------------------------------------------------------
  606. void CDispCollTree::AABBTree_TreeTrisRayTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, CBaseTrace *pTrace, bool bSide, CDispCollTri **pImpactTri )
  607. {
  608. rayleaflist_t list;
  609. // NOTE: This part is loop invariant - should be hoisted up as far as possible
  610. list.invDelta.DuplicateVector(vecInvDelta);
  611. list.rayStart.DuplicateVector(ray.m_Start);
  612. Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON);
  613. list.rayExtents.DuplicateVector(ext);
  614. int listIndex = BuildRayLeafList( iNode, list );
  615. for ( ;listIndex <= list.maxIndex; listIndex++ )
  616. {
  617. int leafIndex = list.nodeList[listIndex] - m_nodes.Count();
  618. CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]];
  619. CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]];
  620. float flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], bSide );
  621. if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) )
  622. {
  623. pTrace->fraction = flFrac;
  624. (*pImpactTri) = pTri0;
  625. }
  626. flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], bSide );
  627. if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) )
  628. {
  629. pTrace->fraction = flFrac;
  630. (*pImpactTri) = pTri1;
  631. }
  632. }
  633. }
  634. //-----------------------------------------------------------------------------
  635. // Purpose:
  636. //-----------------------------------------------------------------------------
  637. int CDispCollTree::AABBTree_GetTrisInSphere( const Vector &center, float radius, unsigned short *pIndexOut, int indexMax )
  638. {
  639. return AABBTree_BuildTreeTrisInSphere_r( center, radius, DISPCOLL_ROOTNODE_INDEX, pIndexOut, indexMax );
  640. }
  641. int CDispCollTree::AABBTree_BuildTreeTrisInSphere_r( const Vector &center, float radius, int iNode, unsigned short *pIndexOut, unsigned short indexMax )
  642. {
  643. int nodeList[MAX_AABB_LIST];
  644. nodeList[0] = iNode;
  645. int nTriCount = 0;
  646. int listIndex = 0;
  647. int maxIndex = 0;
  648. // NOTE: This part is loop invariant - should be hoisted up as far as possible
  649. FourVectors sphereCenters;
  650. sphereCenters.DuplicateVector(center);
  651. float radiusSq = radius * radius;
  652. fltx4 sphereRadSq = ReplicateX4(radiusSq);
  653. while ( listIndex <= maxIndex )
  654. {
  655. iNode = nodeList[listIndex];
  656. listIndex++;
  657. // the rest are all leaves
  658. if ( IsLeafNode(iNode) )
  659. {
  660. VPROF("Tris");
  661. for ( --listIndex; listIndex <= maxIndex; listIndex++ )
  662. {
  663. if ( (nTriCount+2) <= indexMax )
  664. {
  665. int leafIndex = nodeList[listIndex] - m_nodes.Count();
  666. pIndexOut[nTriCount] = m_leaves[leafIndex].m_tris[0];
  667. pIndexOut[nTriCount+1] = m_leaves[leafIndex].m_tris[1];
  668. nTriCount += 2;
  669. }
  670. }
  671. break;
  672. }
  673. else
  674. {
  675. const CDispCollNode &node = m_nodes[iNode];
  676. int mask = IntersectFourBoxSpherePairs( sphereCenters, sphereRadSq, node.m_mins, node.m_maxs );
  677. if ( mask )
  678. {
  679. int child = Nodes_GetChild( iNode, 0 );
  680. if ( mask & 1 )
  681. {
  682. ++maxIndex;
  683. nodeList[maxIndex] = child;
  684. }
  685. if ( mask & 2 )
  686. {
  687. ++maxIndex;
  688. nodeList[maxIndex] = child+1;
  689. }
  690. if ( mask & 4 )
  691. {
  692. ++maxIndex;
  693. nodeList[maxIndex] = child+2;
  694. }
  695. if ( mask & 8 )
  696. {
  697. ++maxIndex;
  698. nodeList[maxIndex] = child+3;
  699. }
  700. Assert(maxIndex < MAX_AABB_LIST);
  701. }
  702. }
  703. }
  704. return nTriCount;
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Purpose:
  708. //-----------------------------------------------------------------------------
  709. bool CDispCollTree::AABBTree_IntersectAABB( const Vector &absMins, const Vector &absMaxs )
  710. {
  711. // Check for hull test.
  712. if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) )
  713. return false;
  714. cplane_t plane;
  715. Vector center = 0.5f *(absMins + absMaxs);
  716. Vector extents = absMaxs - center;
  717. int nodeList[MAX_AABB_LIST];
  718. nodeList[0] = 0;
  719. int listIndex = 0;
  720. int maxIndex = 0;
  721. // NOTE: This part is loop invariant - should be hoisted up as far as possible
  722. FourVectors mins0;
  723. mins0.DuplicateVector(absMins);
  724. FourVectors maxs0;
  725. maxs0.DuplicateVector(absMaxs);
  726. FourVectors rayExtents;
  727. while ( listIndex <= maxIndex )
  728. {
  729. int iNode = nodeList[listIndex];
  730. listIndex++;
  731. // the rest are all leaves
  732. if ( IsLeafNode(iNode) )
  733. {
  734. VPROF("Tris");
  735. for ( --listIndex; listIndex <= maxIndex; listIndex++ )
  736. {
  737. int leafIndex = nodeList[listIndex] - m_nodes.Count();
  738. CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]];
  739. CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]];
  740. VectorCopy( pTri0->m_vecNormal, plane.normal );
  741. plane.dist = pTri0->m_flDist;
  742. plane.signbits = pTri0->m_ucSignBits;
  743. plane.type = pTri0->m_ucPlaneType;
  744. if ( IsBoxIntersectingTriangle( center, extents,
  745. m_aVerts[pTri0->GetVert( 0 )],
  746. m_aVerts[pTri0->GetVert( 2 )],
  747. m_aVerts[pTri0->GetVert( 1 )],
  748. plane, 0.0f ) )
  749. return true;
  750. VectorCopy( pTri1->m_vecNormal, plane.normal );
  751. plane.dist = pTri1->m_flDist;
  752. plane.signbits = pTri1->m_ucSignBits;
  753. plane.type = pTri1->m_ucPlaneType;
  754. if ( IsBoxIntersectingTriangle( center, extents,
  755. m_aVerts[pTri1->GetVert( 0 )],
  756. m_aVerts[pTri1->GetVert( 2 )],
  757. m_aVerts[pTri1->GetVert( 1 )],
  758. plane, 0.0f ) )
  759. return true;
  760. }
  761. break;
  762. }
  763. else
  764. {
  765. const CDispCollNode &node = m_nodes[iNode];
  766. int mask = IntersectFourBoxPairs( mins0, maxs0, node.m_mins, node.m_maxs );
  767. if ( mask )
  768. {
  769. int child = Nodes_GetChild( iNode, 0 );
  770. if ( mask & 1 )
  771. {
  772. ++maxIndex;
  773. nodeList[maxIndex] = child;
  774. }
  775. if ( mask & 2 )
  776. {
  777. ++maxIndex;
  778. nodeList[maxIndex] = child+1;
  779. }
  780. if ( mask & 4 )
  781. {
  782. ++maxIndex;
  783. nodeList[maxIndex] = child+2;
  784. }
  785. if ( mask & 8 )
  786. {
  787. ++maxIndex;
  788. nodeList[maxIndex] = child+3;
  789. }
  790. Assert(maxIndex < MAX_AABB_LIST);
  791. }
  792. }
  793. }
  794. // no collision
  795. return false;
  796. }
  797. static const Vector g_Vec3DispCollEpsilons(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON);
  798. //-----------------------------------------------------------------------------
  799. // Purpose:
  800. //-----------------------------------------------------------------------------
  801. bool CDispCollTree::AABBTree_SweepAABB( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace )
  802. {
  803. VPROF( "Disp_AABBTree_SweepAABB" );
  804. // VPROF_BUDGET( "DispHullTraces", VPROF_BUDGETGROUP_DISP_HULLTRACES );
  805. // Check for hull test.
  806. if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) )
  807. return false;
  808. // Test ray against the triangles in the list.
  809. Vector rayDir;
  810. VectorCopy( ray.m_Delta, rayDir );
  811. VectorNormalize( rayDir );
  812. // Save fraction.
  813. float flFrac = pTrace->fraction;
  814. rayleaflist_t list;
  815. // NOTE: This part is loop invariant - should be hoisted up as far as possible
  816. list.invDelta.DuplicateVector(vecInvDelta);
  817. list.rayStart.DuplicateVector(ray.m_Start);
  818. Vector ext = ray.m_Extents + g_Vec3DispCollEpsilons;
  819. list.rayExtents.DuplicateVector(ext);
  820. int listIndex = BuildRayLeafList( 0, list );
  821. if ( listIndex <= list.maxIndex )
  822. {
  823. VPROF( "DispHullTest_Tris" );
  824. LockCache();
  825. for ( ; listIndex <= list.maxIndex; listIndex++ )
  826. {
  827. int leafIndex = list.nodeList[listIndex] - m_nodes.Count();
  828. int iTri0 = m_leaves[leafIndex].m_tris[0];
  829. int iTri1 = m_leaves[leafIndex].m_tris[1];
  830. CDispCollTri *pTri0 = &m_aTris[iTri0];
  831. CDispCollTri *pTri1 = &m_aTris[iTri1];
  832. SweepAABBTriIntersect( ray, rayDir, iTri0, pTri0, pTrace );
  833. SweepAABBTriIntersect( ray, rayDir, iTri1, pTri1, pTrace );
  834. }
  835. UnlockCache();
  836. }
  837. // Collision.
  838. if ( pTrace->fraction < flFrac )
  839. return true;
  840. // No collision.
  841. return false;
  842. }
  843. //-----------------------------------------------------------------------------
  844. //-----------------------------------------------------------------------------
  845. bool CDispCollTree::ResolveRayPlaneIntersect( float flStart, float flEnd, const Vector &vecNormal, float flDist, CDispCollHelper *pHelper )
  846. {
  847. if( ( flStart > 0.0f ) && ( flEnd > 0.0f ) )
  848. return false;
  849. if( ( flStart < 0.0f ) && ( flEnd < 0.0f ) )
  850. return true;
  851. float flDenom = flStart - flEnd;
  852. bool bDenomIsZero = ( flDenom == 0.0f );
  853. if( ( flStart >= 0.0f ) && ( flEnd <= 0.0f ) )
  854. {
  855. // Find t - the parametric distance along the trace line.
  856. float t = ( !bDenomIsZero ) ? ( flStart - DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f;
  857. if( t > pHelper->m_flStartFrac )
  858. {
  859. pHelper->m_flStartFrac = t;
  860. VectorCopy( vecNormal, pHelper->m_vecImpactNormal );
  861. pHelper->m_flImpactDist = flDist;
  862. }
  863. }
  864. else
  865. {
  866. // Find t - the parametric distance along the trace line.
  867. float t = ( !bDenomIsZero ) ? ( flStart + DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f;
  868. if( t < pHelper->m_flEndFrac )
  869. {
  870. pHelper->m_flEndFrac = t;
  871. }
  872. }
  873. return true;
  874. }
  875. //-----------------------------------------------------------------------------
  876. // Purpose:
  877. //-----------------------------------------------------------------------------
  878. inline bool CDispCollTree::FacePlane( const Ray_t &ray, const Vector &rayDir, CDispCollTri *pTri, CDispCollHelper *pHelper )
  879. {
  880. // Calculate the closest point on box to plane (get extents in that direction).
  881. Vector vecExtent;
  882. CalcClosestExtents( pTri->m_vecNormal, ray.m_Extents, vecExtent );
  883. float flExpandDist = pTri->m_flDist - pTri->m_vecNormal.Dot( vecExtent );
  884. float flStart = pTri->m_vecNormal.Dot( ray.m_Start ) - flExpandDist;
  885. float flEnd = pTri->m_vecNormal.Dot( ( ray.m_Start + ray.m_Delta ) ) - flExpandDist;
  886. return ResolveRayPlaneIntersect( flStart, flEnd, pTri->m_vecNormal, pTri->m_flDist, pHelper );
  887. }
  888. //-----------------------------------------------------------------------------
  889. // Purpose:
  890. //-----------------------------------------------------------------------------
  891. bool FORCEINLINE CDispCollTree::AxisPlanesXYZ( const Ray_t &ray, CDispCollTri *pTri, CDispCollHelper *pHelper )
  892. {
  893. static const TableVector g_ImpactNormalVecs[2][3] =
  894. {
  895. {
  896. { -1, 0, 0 },
  897. { 0, -1, 0 },
  898. { 0, 0, -1 },
  899. },
  900. {
  901. { 1, 0, 0 },
  902. { 0, 1, 0 },
  903. { 0, 0, 1 },
  904. }
  905. };
  906. Vector vecImpactNormal;
  907. float flDist, flExpDist, flStart, flEnd;
  908. int iAxis;
  909. for ( iAxis = 2; iAxis >= 0; --iAxis )
  910. {
  911. const float rayStart = ray.m_Start[iAxis];
  912. const float rayExtent = ray.m_Extents[iAxis];
  913. const float rayDelta = ray.m_Delta[iAxis];
  914. // Min
  915. flDist = m_aVerts[pTri->GetVert(pTri->GetMin(iAxis))][iAxis];
  916. flExpDist = flDist - rayExtent;
  917. flStart = flExpDist - rayStart;
  918. flEnd = flStart - rayDelta;
  919. if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[0][iAxis], flDist, pHelper ) )
  920. return false;
  921. // Max
  922. flDist = m_aVerts[pTri->GetVert(pTri->GetMax(iAxis))][iAxis];
  923. flExpDist = flDist + rayExtent;
  924. flStart = rayStart - flExpDist;
  925. flEnd = flStart + rayDelta;
  926. if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[1][iAxis], flDist, pHelper ) )
  927. return false;
  928. }
  929. return true;
  930. }
  931. //-----------------------------------------------------------------------------
  932. // Purpose: Testing!
  933. //-----------------------------------------------------------------------------
  934. void CDispCollTree::Cache_Create( CDispCollTri *pTri, int iTri )
  935. {
  936. MEM_ALLOC_CREDIT();
  937. Vector *pVerts[3];
  938. pVerts[0] = &m_aVerts[pTri->GetVert( 0 )];
  939. pVerts[1] = &m_aVerts[pTri->GetVert( 1 )];
  940. pVerts[2] = &m_aVerts[pTri->GetVert( 2 )];
  941. CDispCollTriCache *pCache = &m_aTrisCache[iTri];
  942. Vector vecEdge;
  943. // Edge 1
  944. VectorSubtract( *pVerts[1], *pVerts[0], vecEdge );
  945. Cache_EdgeCrossAxisX( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossX[0] );
  946. Cache_EdgeCrossAxisY( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossY[0] );
  947. Cache_EdgeCrossAxisZ( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossZ[0] );
  948. // Edge 2
  949. VectorSubtract( *pVerts[2], *pVerts[1], vecEdge );
  950. Cache_EdgeCrossAxisX( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossX[1] );
  951. Cache_EdgeCrossAxisY( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossY[1] );
  952. Cache_EdgeCrossAxisZ( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossZ[1] );
  953. // Edge 3
  954. VectorSubtract( *pVerts[0], *pVerts[2], vecEdge );
  955. Cache_EdgeCrossAxisX( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossX[2] );
  956. Cache_EdgeCrossAxisY( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossY[2] );
  957. Cache_EdgeCrossAxisZ( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossZ[2] );
  958. }
  959. //-----------------------------------------------------------------------------
  960. //-----------------------------------------------------------------------------
  961. int CDispCollTree::AddPlane( const Vector &vecNormal )
  962. {
  963. UtlHashHandle_t handle;
  964. DispCollPlaneIndex_t planeIndex;
  965. bool bDidInsert;
  966. planeIndex.vecPlane = vecNormal;
  967. planeIndex.index = m_aEdgePlanes.Count();
  968. handle = g_DispCollPlaneIndexHash.Insert( planeIndex, &bDidInsert );
  969. if ( !bDidInsert )
  970. {
  971. DispCollPlaneIndex_t &existingEntry = g_DispCollPlaneIndexHash[handle];
  972. if ( existingEntry.vecPlane == vecNormal )
  973. {
  974. return existingEntry.index;
  975. }
  976. else
  977. {
  978. return ( existingEntry.index | 0x8000 );
  979. }
  980. }
  981. return m_aEdgePlanes.AddToTail( vecNormal );
  982. }
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. // NOTE: The plane distance get stored in the normal x position since it isn't
  986. // used.
  987. //-----------------------------------------------------------------------------
  988. bool CDispCollTree::Cache_EdgeCrossAxisX( const Vector &vecEdge, const Vector &vecOnEdge,
  989. const Vector &vecOffEdge, CDispCollTri *pTri,
  990. unsigned short &iPlane )
  991. {
  992. // Calculate the normal - edge x axisX = ( 0.0, edgeZ, -edgeY )
  993. Vector vecNormal( 0.0f, vecEdge.z, -vecEdge.y );
  994. VectorNormalize( vecNormal );
  995. // Check for zero length normals.
  996. if( ( vecNormal.y == 0.0f ) || ( vecNormal.z == 0.0f ) )
  997. {
  998. iPlane = DISPCOLL_NORMAL_UNDEF;
  999. return false;
  1000. }
  1001. // if ( pTri->m_vecNormal.Dot( vecNormal ) )
  1002. // {
  1003. // iPlane = DISPCOLL_NORMAL_UNDEF;
  1004. // return false;
  1005. // }
  1006. // Finish the plane definition - get distance.
  1007. float flDist = ( vecNormal.y * vecOnEdge.y ) + ( vecNormal.z * vecOnEdge.z );
  1008. // Special case the point off edge in plane
  1009. float flOffDist = ( vecNormal.y * vecOffEdge.y ) + ( vecNormal.z * vecOffEdge.z );
  1010. if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) )
  1011. {
  1012. // Adjust plane facing - triangle should be behind the plane.
  1013. vecNormal.x = -flDist;
  1014. vecNormal.y = -vecNormal.y;
  1015. vecNormal.z = -vecNormal.z;
  1016. }
  1017. else
  1018. {
  1019. vecNormal.x = flDist;
  1020. }
  1021. // Add edge plane to edge plane list.
  1022. iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) );
  1023. // Created the cached edge.
  1024. return true;
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. // Purpose:
  1028. // NOTE: The plane distance get stored in the normal y position since it isn't
  1029. // used.
  1030. //-----------------------------------------------------------------------------
  1031. bool CDispCollTree::Cache_EdgeCrossAxisY( const Vector &vecEdge, const Vector &vecOnEdge,
  1032. const Vector &vecOffEdge, CDispCollTri *pTri,
  1033. unsigned short &iPlane )
  1034. {
  1035. // Calculate the normal - edge x axisY = ( -edgeZ, 0.0, edgeX )
  1036. Vector vecNormal( -vecEdge.z, 0.0f, vecEdge.x );
  1037. VectorNormalize( vecNormal );
  1038. // Check for zero length normals
  1039. if( ( vecNormal.x == 0.0f ) || ( vecNormal.z == 0.0f ) )
  1040. {
  1041. iPlane = DISPCOLL_NORMAL_UNDEF;
  1042. return false;
  1043. }
  1044. // if ( pTri->m_vecNormal.Dot( vecNormal ) )
  1045. // {
  1046. // iPlane = DISPCOLL_NORMAL_UNDEF;
  1047. // return false;
  1048. // }
  1049. // Finish the plane definition - get distance.
  1050. float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.z * vecOnEdge.z );
  1051. // Special case the point off edge in plane
  1052. float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.z * vecOffEdge.z );
  1053. if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) )
  1054. {
  1055. // Adjust plane facing if necessay - triangle should be behind the plane.
  1056. vecNormal.x = -vecNormal.x;
  1057. vecNormal.y = -flDist;
  1058. vecNormal.z = -vecNormal.z;
  1059. }
  1060. else
  1061. {
  1062. vecNormal.y = flDist;
  1063. }
  1064. // Add edge plane to edge plane list.
  1065. iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) );
  1066. // Created the cached edge.
  1067. return true;
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Purpose:
  1071. //-----------------------------------------------------------------------------
  1072. bool CDispCollTree::Cache_EdgeCrossAxisZ( const Vector &vecEdge, const Vector &vecOnEdge,
  1073. const Vector &vecOffEdge, CDispCollTri *pTri,
  1074. unsigned short &iPlane )
  1075. {
  1076. // Calculate the normal - edge x axisY = ( edgeY, -edgeX, 0.0 )
  1077. Vector vecNormal( vecEdge.y, -vecEdge.x, 0.0f );
  1078. VectorNormalize( vecNormal );
  1079. // Check for zero length normals
  1080. if( ( vecNormal.x == 0.0f ) || ( vecNormal.y == 0.0f ) )
  1081. {
  1082. iPlane = DISPCOLL_NORMAL_UNDEF;
  1083. return false;
  1084. }
  1085. // if ( pTri->m_vecNormal.Dot( vecNormal ) )
  1086. // {
  1087. // iPlane = DISPCOLL_NORMAL_UNDEF;
  1088. // return false;
  1089. // }
  1090. // Finish the plane definition - get distance.
  1091. float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.y * vecOnEdge.y );
  1092. // Special case the point off edge in plane
  1093. float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.y * vecOffEdge.y );
  1094. if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) )
  1095. {
  1096. // Adjust plane facing if necessay - triangle should be behind the plane.
  1097. vecNormal.x = -vecNormal.x;
  1098. vecNormal.y = -vecNormal.y;
  1099. vecNormal.z = -flDist;
  1100. }
  1101. else
  1102. {
  1103. vecNormal.z = flDist;
  1104. }
  1105. // Add edge plane to edge plane list.
  1106. iPlane = static_cast<unsigned short>( AddPlane( vecNormal ) );
  1107. // Created the cached edge.
  1108. return true;
  1109. }
  1110. //-----------------------------------------------------------------------------
  1111. // Purpose:
  1112. //-----------------------------------------------------------------------------
  1113. template <int AXIS>
  1114. bool CDispCollTree::EdgeCrossAxis( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper )
  1115. {
  1116. if ( iPlane == DISPCOLL_NORMAL_UNDEF )
  1117. return true;
  1118. // Get the edge plane.
  1119. Vector vecNormal;
  1120. if ( ( iPlane & 0x8000 ) != 0 )
  1121. {
  1122. VectorCopy( m_aEdgePlanes[(iPlane&0x7fff)], vecNormal );
  1123. vecNormal.Negate();
  1124. }
  1125. else
  1126. {
  1127. VectorCopy( m_aEdgePlanes[iPlane], vecNormal );
  1128. }
  1129. const int OTHER_AXIS1 = ( AXIS + 1 ) % 3;
  1130. const int OTHER_AXIS2 = ( AXIS + 2 ) % 3;
  1131. // Get the pland distance are "fix" the normal.
  1132. float flDist = vecNormal[AXIS];
  1133. vecNormal[AXIS] = 0.0f;
  1134. // Calculate the closest point on box to plane (get extents in that direction).
  1135. Vector vecExtent;
  1136. //vecExtent[AXIS] = 0.0f;
  1137. vecExtent[OTHER_AXIS1] = ( vecNormal[OTHER_AXIS1] < 0.0f ) ? ray.m_Extents[OTHER_AXIS1] : -ray.m_Extents[OTHER_AXIS1];
  1138. vecExtent[OTHER_AXIS2] = ( vecNormal[OTHER_AXIS2] < 0.0f ) ? ray.m_Extents[OTHER_AXIS2] : -ray.m_Extents[OTHER_AXIS2];
  1139. // Expand the plane by the extents of the box to reduce the swept box/triangle
  1140. // test to a ray/extruded triangle test (one of the triangles extruded planes
  1141. // was just calculated above).
  1142. Vector vecEnd;
  1143. vecEnd[AXIS] = 0;
  1144. vecEnd[OTHER_AXIS1] = ray.m_Start[OTHER_AXIS1] + ray.m_Delta[OTHER_AXIS1];
  1145. vecEnd[OTHER_AXIS2] = ray.m_Start[OTHER_AXIS2] + ray.m_Delta[OTHER_AXIS2];
  1146. float flExpandDist = flDist - ( ( vecNormal[OTHER_AXIS1] * vecExtent[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecExtent[OTHER_AXIS2] ) );
  1147. float flStart = ( vecNormal[OTHER_AXIS1] * ray.m_Start[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * ray.m_Start[OTHER_AXIS2] ) - flExpandDist;
  1148. float flEnd = ( vecNormal[OTHER_AXIS1] * vecEnd[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecEnd[OTHER_AXIS2] ) - flExpandDist;
  1149. return ResolveRayPlaneIntersect( flStart, flEnd, vecNormal, flDist, pHelper );
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Purpose:
  1153. //-----------------------------------------------------------------------------
  1154. inline bool CDispCollTree::EdgeCrossAxisX( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper )
  1155. {
  1156. return EdgeCrossAxis<0>( ray, iPlane, pHelper );
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. // Purpose:
  1160. //-----------------------------------------------------------------------------
  1161. inline bool CDispCollTree::EdgeCrossAxisY( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper )
  1162. {
  1163. return EdgeCrossAxis<1>( ray, iPlane, pHelper );
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. // Purpose:
  1167. //-----------------------------------------------------------------------------
  1168. inline bool CDispCollTree::EdgeCrossAxisZ( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper )
  1169. {
  1170. return EdgeCrossAxis<2>( ray, iPlane, pHelper );
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. // Purpose:
  1174. //-----------------------------------------------------------------------------
  1175. void CDispCollTree::SweepAABBTriIntersect( const Ray_t &ray, const Vector &rayDir, int iTri, CDispCollTri *pTri, CBaseTrace *pTrace )
  1176. {
  1177. // Init test data.
  1178. CDispCollHelper helper;
  1179. helper.m_flEndFrac = 1.0f;
  1180. helper.m_flStartFrac = DISPCOLL_INVALID_FRAC;
  1181. // Make sure objects are traveling toward one another.
  1182. float flDistAlongNormal = pTri->m_vecNormal.Dot( ray.m_Delta );
  1183. if( flDistAlongNormal > DISPCOLL_DIST_EPSILON )
  1184. return;
  1185. // Test against the axis planes.
  1186. if ( !AxisPlanesXYZ( ray, pTri, &helper ) )
  1187. {
  1188. return;
  1189. }
  1190. //
  1191. // There are 9 edge tests - edges 1, 2, 3 cross with the box edges (symmetry) 1, 2, 3. However, the box
  1192. // is axis-aligned resulting in axially directional edges -- thus each test is edges 1, 2, and 3 vs.
  1193. // axial planes x, y, and z
  1194. //
  1195. // There are potentially 9 more tests with edges, the edge's edges and the direction of motion!
  1196. // NOTE: I don't think these tests are necessary for a manifold surface.
  1197. //
  1198. CDispCollTriCache *pCache = &m_aTrisCache[iTri];
  1199. // Edges 1-3, interleaved - axis tests are 2d tests
  1200. if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[0], &helper ) ) { return; }
  1201. if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[1], &helper ) ) { return; }
  1202. if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[2], &helper ) ) { return; }
  1203. if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[0], &helper ) ) { return; }
  1204. if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[1], &helper ) ) { return; }
  1205. if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[2], &helper ) ) { return; }
  1206. if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[0], &helper ) ) { return; }
  1207. if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[1], &helper ) ) { return; }
  1208. if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[2], &helper ) ) { return; }
  1209. // Test against the triangle face plane.
  1210. if ( !FacePlane( ray, rayDir, pTri, &helper ) )
  1211. return;
  1212. if ( ( helper.m_flStartFrac < helper.m_flEndFrac ) || ( FloatMakePositive( helper.m_flStartFrac - helper.m_flEndFrac ) < 0.001f ) )
  1213. {
  1214. if ( ( helper.m_flStartFrac != DISPCOLL_INVALID_FRAC ) && ( helper.m_flStartFrac < pTrace->fraction ) )
  1215. {
  1216. // Clamp -- shouldn't really ever be here!???
  1217. if ( helper.m_flStartFrac < 0.0f )
  1218. {
  1219. helper.m_flStartFrac = 0.0f;
  1220. }
  1221. pTrace->fraction = helper.m_flStartFrac;
  1222. VectorCopy( helper.m_vecImpactNormal, pTrace->plane.normal );
  1223. pTrace->plane.dist = helper.m_flImpactDist;
  1224. pTrace->dispFlags = pTri->m_uiFlags;
  1225. }
  1226. }
  1227. }
  1228. //-----------------------------------------------------------------------------
  1229. // Purpose: constructor
  1230. //-----------------------------------------------------------------------------
  1231. CDispCollTree::CDispCollTree()
  1232. {
  1233. m_nPower = 0;
  1234. m_nFlags = 0;
  1235. for ( int iPoint = 0; iPoint < 4; ++iPoint )
  1236. {
  1237. m_vecSurfPoints[iPoint].Init();
  1238. }
  1239. m_nContents = -1;
  1240. m_nSurfaceProps[0] = 0;
  1241. m_nSurfaceProps[1] = 0;
  1242. m_nSurfaceProps[2] = 0;
  1243. m_nSurfaceProps[3] = 0;
  1244. m_vecStabDir.Init();
  1245. m_mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  1246. m_maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  1247. m_iCounter = 0;
  1248. m_aVerts.Purge();
  1249. m_aTris.Purge();
  1250. m_aEdgePlanes.Purge();
  1251. #ifdef ENGINE_DLL
  1252. m_hCache = INVALID_MEMHANDLE;
  1253. #endif
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose: deconstructor
  1257. //-----------------------------------------------------------------------------
  1258. CDispCollTree::~CDispCollTree()
  1259. {
  1260. #ifdef ENGINE_DLL
  1261. if ( m_hCache != INVALID_MEMHANDLE )
  1262. g_DispCollTriCache.DestroyResource( m_hCache );
  1263. #endif
  1264. m_aVerts.Purge();
  1265. m_aTris.Purge();
  1266. m_aEdgePlanes.Purge();
  1267. }
  1268. //-----------------------------------------------------------------------------
  1269. // Purpose:
  1270. //-----------------------------------------------------------------------------
  1271. bool CDispCollTree::Create( CCoreDispInfo *pDisp )
  1272. {
  1273. // Create the AABB Tree.
  1274. return AABBTree_Create( pDisp );
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. // Purpose:
  1278. //-----------------------------------------------------------------------------
  1279. bool CDispCollTree::PointInBounds( const Vector &vecBoxCenter, const Vector &vecBoxMin,
  1280. const Vector &vecBoxMax, bool bPoint )
  1281. {
  1282. // Point test inside bounds.
  1283. if( bPoint )
  1284. {
  1285. return IsPointInBox( vecBoxCenter, m_mins, m_maxs );
  1286. }
  1287. // Box test inside bounds
  1288. Vector vecExtents;
  1289. VectorSubtract( vecBoxMax, vecBoxMin, vecExtents );
  1290. vecExtents *= 0.5f;
  1291. Vector vecExpandBounds[2];
  1292. vecExpandBounds[0] = m_mins - vecExtents;
  1293. vecExpandBounds[1] = m_maxs + vecExtents;
  1294. return IsPointInBox( vecBoxCenter, vecExpandBounds[0], vecExpandBounds[1] );
  1295. }
  1296. void CDispCollTree::GetVirtualMeshList( virtualmeshlist_t *pList )
  1297. {
  1298. int i;
  1299. int triangleCount = GetTriSize();
  1300. pList->indexCount = triangleCount * 3;
  1301. pList->triangleCount = triangleCount;
  1302. pList->vertexCount = m_aVerts.Count();
  1303. pList->pVerts = m_aVerts.Base();
  1304. pList->pHull = NULL;
  1305. pList->surfacePropsIndex = GetSurfaceProps(0);
  1306. int index = 0;
  1307. for ( i = 0 ; i < triangleCount; i++ )
  1308. {
  1309. pList->indices[index+0] = m_aTris[i].GetVert(0);
  1310. pList->indices[index+1] = m_aTris[i].GetVert(1);
  1311. pList->indices[index+2] = m_aTris[i].GetVert(2);
  1312. index += 3;
  1313. }
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. //-----------------------------------------------------------------------------
  1317. #ifdef ENGINE_DLL
  1318. static int g_nTrees;
  1319. #endif
  1320. CDispCollTree *DispCollTrees_Alloc( int count )
  1321. {
  1322. CDispCollTree *pTrees = NULL;
  1323. #ifdef ENGINE_DLL
  1324. pTrees = (CDispCollTree *)Hunk_AllocName( count * sizeof(CDispCollTree), "DispCollTrees_Alloc", false );
  1325. g_nTrees = count;
  1326. for ( int i = 0; i < g_nTrees; i++ )
  1327. {
  1328. Construct( pTrees + i );
  1329. }
  1330. #else
  1331. pTrees = new CDispCollTree[count];
  1332. #endif
  1333. if( !pTrees )
  1334. return NULL;
  1335. for ( int i = 0; i < count; i++ )
  1336. {
  1337. pTrees[i].m_iCounter = i;
  1338. }
  1339. return pTrees;
  1340. }
  1341. //-----------------------------------------------------------------------------
  1342. //-----------------------------------------------------------------------------
  1343. void DispCollTrees_Free( CDispCollTree *pTrees )
  1344. {
  1345. #ifdef ENGINE_DLL
  1346. for ( int i = 0; i < g_nTrees; i++ )
  1347. {
  1348. Destruct( pTrees + i );
  1349. }
  1350. g_nTrees = 0;
  1351. #else
  1352. if( pTrees )
  1353. {
  1354. delete [] pTrees;
  1355. pTrees = NULL;
  1356. }
  1357. #endif
  1358. }