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.

1539 lines
47 KiB

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