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.

593 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <assert.h>
  8. #include <float.h>
  9. #include "cmodel_engine.h"
  10. #include "dispcoll_common.h"
  11. #include "cmodel_private.h"
  12. #include "builddisp.h"
  13. #include "collisionutils.h"
  14. #include "vstdlib/random.h"
  15. #include "tier0/fasttimer.h"
  16. #include "vphysics_interface.h"
  17. #include "vphysics/virtualmesh.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. int g_DispCollTreeCount = 0;
  21. CDispCollTree *g_pDispCollTrees = NULL;
  22. alignedbbox_t *g_pDispBounds = NULL;
  23. class CVirtualTerrain;
  24. //csurface_t dispSurf = { "terrain", 0, 0 };
  25. void CM_PreStab( TraceInfo_t *pTraceInfo, cleaf_t *pLeaf, Vector &vStabDir, int collisionMask, int &contents );
  26. void CM_Stab( TraceInfo_t *pTraceInfo, Vector const &start, Vector const &vStabDir, int contents );
  27. void CM_PostStab( TraceInfo_t *pTraceInfo );
  28. //-----------------------------------------------------------------------------
  29. //-----------------------------------------------------------------------------
  30. static void SetDispTraceSurfaceProps( trace_t *pTrace, CDispCollTree *pDisp )
  31. {
  32. // use the default surface properties
  33. pTrace->surface.name = "**displacement**";
  34. pTrace->surface.flags = 0;
  35. if ( pTrace->IsDispSurfaceProp2() )
  36. {
  37. pTrace->surface.surfaceProps = pDisp->GetSurfaceProps( 1 );
  38. }
  39. else
  40. {
  41. pTrace->surface.surfaceProps = pDisp->GetSurfaceProps( 0 );
  42. }
  43. }
  44. class CDispLeafBuilder
  45. {
  46. public:
  47. CDispLeafBuilder( CCollisionBSPData *pBSPData )
  48. {
  49. m_pBSPData = pBSPData;
  50. // don't want this to resize much, so make the backing buffer large
  51. m_dispList.EnsureCapacity( MAX_MAP_DISPINFO * 2 );
  52. // size both of these to the size of the array since there is exactly one per element
  53. m_leafCount.SetCount( g_DispCollTreeCount );
  54. m_firstIndex.SetCount( g_DispCollTreeCount );
  55. for ( int i = 0; i < g_DispCollTreeCount; i++ )
  56. {
  57. m_leafCount[i] = 0;
  58. m_firstIndex[i] = -1;
  59. }
  60. }
  61. void BuildLeafListForDisplacement( int index )
  62. {
  63. // get tree and see if it is real (power != 0)
  64. CDispCollTree *pDispTree = &g_pDispCollTrees[index];
  65. if( !pDispTree || ( pDispTree->GetPower() == 0 ) )
  66. return;
  67. m_firstIndex[index] = m_dispList.Count();
  68. m_leafCount[index] = 0;
  69. const int MAX_NODES = 1024;
  70. int nodeList[MAX_NODES];
  71. int listRead = 0;
  72. int listWrite = 1;
  73. nodeList[0] = m_pBSPData->map_cmodels[0].headnode;
  74. Vector mins, maxs;
  75. pDispTree->GetBounds( mins, maxs );
  76. // UNDONE: The rendering code did this, do we need it?
  77. // mins -= Vector( 0.5, 0.5, 0.5 );
  78. // maxs += Vector( 0.5, 0.5, 0.5 );
  79. while( listRead != listWrite )
  80. {
  81. int nodeIndex = nodeList[listRead];
  82. listRead = (listRead+1)%MAX_NODES;
  83. // if this is a leaf, add it to the array
  84. if( nodeIndex < 0 )
  85. {
  86. int leafIndex = -1 - nodeIndex;
  87. m_dispList.AddToTail(leafIndex);
  88. m_leafCount[index]++;
  89. }
  90. else
  91. {
  92. //
  93. // choose side(s) to traverse
  94. //
  95. cnode_t *pNode = &m_pBSPData->map_rootnode[nodeIndex];
  96. cplane_t *pPlane = pNode->plane;
  97. int sideResult = BOX_ON_PLANE_SIDE( mins, maxs, pPlane );
  98. // front side
  99. if( sideResult & 1 )
  100. {
  101. nodeList[listWrite] = pNode->children[0];
  102. listWrite = (listWrite+1)%MAX_NODES;
  103. Assert(listWrite != listRead);
  104. }
  105. // back side
  106. if( sideResult & 2 )
  107. {
  108. nodeList[listWrite] = pNode->children[1];
  109. listWrite = (listWrite+1)%MAX_NODES;
  110. Assert(listWrite != listRead);
  111. }
  112. }
  113. }
  114. }
  115. int GetDispListCount() { return m_dispList.Count(); }
  116. void WriteLeafList( unsigned short *pLeafList )
  117. {
  118. // clear current count if any
  119. for ( int i = 0; i < m_pBSPData->numleafs; i++ )
  120. {
  121. cleaf_t *pLeaf = &m_pBSPData->map_leafs[i];
  122. pLeaf->dispCount = 0;
  123. }
  124. // compute new count per leaf
  125. for ( int i = 0; i < m_dispList.Count(); i++ )
  126. {
  127. int leafIndex = m_dispList[i];
  128. cleaf_t *pLeaf = &m_pBSPData->map_leafs[leafIndex];
  129. pLeaf->dispCount++;
  130. }
  131. // point each leaf at the start of it's output range in the output array
  132. unsigned short firstDispIndex = 0;
  133. for ( int i = 0; i < m_pBSPData->numleafs; i++ )
  134. {
  135. cleaf_t *pLeaf = &m_pBSPData->map_leafs[i];
  136. pLeaf->dispListStart = firstDispIndex;
  137. firstDispIndex += pLeaf->dispCount;
  138. pLeaf->dispCount = 0;
  139. }
  140. // now iterate the references in disp order adding to each leaf's (now compact) list
  141. // for each displacement with leaves
  142. for ( int i = 0; i < m_leafCount.Count(); i++ )
  143. {
  144. // for each leaf in this disp's list
  145. int count = m_leafCount[i];
  146. for ( int j = 0; j < count; j++ )
  147. {
  148. int listIndex = m_firstIndex[i] + j; // index to per-disp list
  149. int leafIndex = m_dispList[listIndex]; // this reference is for one leaf
  150. cleaf_t *pLeaf = &m_pBSPData->map_leafs[leafIndex];
  151. int outListIndex = pLeaf->dispListStart + pLeaf->dispCount; // output position for this leaf
  152. pLeafList[outListIndex] = i; // write the reference there
  153. Assert(outListIndex < GetDispListCount());
  154. pLeaf->dispCount++; // move this leaf's output pointer
  155. }
  156. }
  157. }
  158. private:
  159. CCollisionBSPData *m_pBSPData;
  160. // this is a list of all of the leaf indices for each displacement
  161. CUtlVector<unsigned short> m_dispList;
  162. // this is the first entry into dispList for each displacement
  163. CUtlVector<int> m_firstIndex;
  164. // this is the # of leaf entries for each displacement
  165. CUtlVector<unsigned short> m_leafCount;
  166. };
  167. //-----------------------------------------------------------------------------
  168. //-----------------------------------------------------------------------------
  169. void CM_DispTreeLeafnum( CCollisionBSPData *pBSPData )
  170. {
  171. // check to see if there are any displacement trees to push down the bsp tree??
  172. if( g_DispCollTreeCount == 0 )
  173. return;
  174. for ( int i = 0; i < pBSPData->numleafs; i++ )
  175. {
  176. pBSPData->map_leafs[i].dispCount = 0;
  177. }
  178. //
  179. // get the number of displacements per leaf
  180. //
  181. CDispLeafBuilder leafBuilder( pBSPData );
  182. for ( int i = 0; i < g_DispCollTreeCount; i++ )
  183. {
  184. leafBuilder.BuildLeafListForDisplacement( i );
  185. }
  186. int count = leafBuilder.GetDispListCount();
  187. pBSPData->map_dispList.Attach( count, (unsigned short*)Hunk_Alloc( sizeof(unsigned short) * count, false ) );
  188. leafBuilder.WriteLeafList( pBSPData->map_dispList.Base() );
  189. }
  190. //-----------------------------------------------------------------------------
  191. //-----------------------------------------------------------------------------
  192. void DispCollTrees_FreeLeafList( CCollisionBSPData *pBSPData )
  193. {
  194. if ( pBSPData->map_dispList.Base() )
  195. {
  196. pBSPData->map_dispList.Detach();
  197. pBSPData->numdisplist = 0;
  198. }
  199. }
  200. // Virtual collision models for terrain
  201. class CVirtualTerrain : public IVirtualMeshEvent
  202. {
  203. public:
  204. CVirtualTerrain()
  205. {
  206. m_pDispHullData = NULL;
  207. }
  208. // Fill out the meshlist for this terrain patch
  209. virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
  210. {
  211. int index = (int)userData;
  212. Assert(index >= 0 && index < g_DispCollTreeCount );
  213. g_pDispCollTrees[index].GetVirtualMeshList( pList );
  214. pList->pHull = NULL;
  215. if ( m_pDispHullData )
  216. {
  217. if ( m_dispHullOffset[index] >= 0 )
  218. {
  219. pList->pHull = m_pDispHullData + m_dispHullOffset[index];
  220. }
  221. }
  222. }
  223. // returns the bounds for the terrain patch
  224. virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
  225. {
  226. int index = (int)userData;
  227. *pMins = g_pDispBounds[index].mins;
  228. *pMaxs = g_pDispBounds[index].maxs;
  229. }
  230. // Query against the AABB tree to find the list of triangles for this patch in a sphere
  231. virtual void GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList )
  232. {
  233. int index = (int)userData;
  234. pList->triangleCount = g_pDispCollTrees[index].AABBTree_GetTrisInSphere( center, radius, pList->triangleIndices, ARRAYSIZE(pList->triangleIndices) );
  235. }
  236. void LevelInit( dphysdisp_t *pLump, int lumpSize )
  237. {
  238. if ( !pLump )
  239. {
  240. m_pDispHullData = NULL;
  241. return;
  242. }
  243. int totalHullData = 0;
  244. m_dispHullOffset.SetCount(g_DispCollTreeCount);
  245. Assert(pLump->numDisplacements==g_DispCollTreeCount);
  246. // count the size of the lump
  247. unsigned short *pDataSize = (unsigned short *)(pLump+1);
  248. for ( int i = 0; i < pLump->numDisplacements; i++ )
  249. {
  250. if ( pDataSize[i] == (unsigned short)-1 )
  251. {
  252. m_dispHullOffset[i] = -1;
  253. continue;
  254. }
  255. m_dispHullOffset[i] = totalHullData;
  256. totalHullData += pDataSize[i];
  257. }
  258. m_pDispHullData = new byte[totalHullData];
  259. byte *pData = (byte *)(pDataSize + pLump->numDisplacements);
  260. memcpy( m_pDispHullData, pData, totalHullData );
  261. #if _DEBUG
  262. int offset = pData - ((byte *)pLump);
  263. Assert( offset + totalHullData == lumpSize );
  264. #endif
  265. }
  266. void LevelShutdown()
  267. {
  268. m_dispHullOffset.Purge();
  269. delete[] m_pDispHullData;
  270. m_pDispHullData = NULL;
  271. }
  272. private:
  273. byte *m_pDispHullData;
  274. CUtlVector<int> m_dispHullOffset;
  275. };
  276. // Singleton to implement the callbacks
  277. static CVirtualTerrain g_VirtualTerrain;
  278. // List of terrain collision models for the currently loaded level, indexed by terrain patch index
  279. static CUtlVector<CPhysCollide *> g_TerrainList;
  280. // Find or create virtual terrain collision model. Note that these will be shared by client & server
  281. CPhysCollide *CM_PhysCollideForDisp( int index )
  282. {
  283. if ( index < 0 || index >= g_DispCollTreeCount )
  284. return NULL;
  285. return g_TerrainList[index];
  286. }
  287. int CM_SurfacepropsForDisp( int index )
  288. {
  289. return g_pDispCollTrees[index].GetSurfaceProps(0);
  290. }
  291. void CM_CreateDispPhysCollide( dphysdisp_t *pDispLump, int dispLumpSize )
  292. {
  293. g_VirtualTerrain.LevelInit(pDispLump, dispLumpSize);
  294. g_TerrainList.SetCount( g_DispCollTreeCount );
  295. for ( int i = 0; i < g_DispCollTreeCount; i++ )
  296. {
  297. // Don't create a physics collision model for displacements that have been tagged as such.
  298. CDispCollTree *pDispTree = &g_pDispCollTrees[i];
  299. if ( pDispTree && pDispTree->CheckFlags( CCoreDispInfo::SURF_NOPHYSICS_COLL ) )
  300. {
  301. g_TerrainList[i] = NULL;
  302. continue;
  303. }
  304. virtualmeshparams_t params;
  305. params.pMeshEventHandler = &g_VirtualTerrain;
  306. params.userData = (void *)i;
  307. params.buildOuterHull = dispLumpSize > 0 ? false : true;
  308. g_TerrainList[i] = physcollision->CreateVirtualMesh( params );
  309. }
  310. }
  311. // End of level, free the collision models
  312. void CM_DestroyDispPhysCollide()
  313. {
  314. g_VirtualTerrain.LevelShutdown();
  315. for ( int i = g_TerrainList.Count()-1; i>=0; --i )
  316. {
  317. physcollision->DestroyCollide( g_TerrainList[i] );
  318. }
  319. g_TerrainList.Purge();
  320. }
  321. //-----------------------------------------------------------------------------
  322. // New Collision!
  323. //-----------------------------------------------------------------------------
  324. void CM_TestInDispTree( TraceInfo_t *pTraceInfo, cleaf_t *pLeaf, const Vector &traceStart,
  325. const Vector &boxMin, const Vector &boxMax, int collisionMask, trace_t *pTrace )
  326. {
  327. bool bIsBox = ( ( boxMin.x != 0.0f ) || ( boxMin.y != 0.0f ) || ( boxMin.z != 0.0f ) ||
  328. ( boxMax.x != 0.0f ) || ( boxMax.y != 0.0f ) || ( boxMax.z != 0.0f ) );
  329. // box test
  330. if( bIsBox )
  331. {
  332. // Box/Tree intersection test.
  333. Vector absMins = traceStart + boxMin;
  334. Vector absMaxs = traceStart + boxMax;
  335. // Test box against all displacements in the leaf.
  336. TraceCounter_t *pCounters = pTraceInfo->GetDispCounters();
  337. int count = pTraceInfo->GetCount();
  338. for( int i = 0; i < pLeaf->dispCount; i++ )
  339. {
  340. int dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart + i];
  341. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  342. // Respect trace contents
  343. if( !(pDispBounds->GetContents() & collisionMask) )
  344. continue;
  345. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  346. continue;
  347. if ( IsBoxIntersectingBox( absMins, absMaxs, pDispBounds->mins, pDispBounds->maxs ) )
  348. {
  349. CDispCollTree *pDispTree = &g_pDispCollTrees[dispIndex];
  350. if( pDispTree->AABBTree_IntersectAABB( absMins, absMaxs ) )
  351. {
  352. pTrace->startsolid = true;
  353. pTrace->allsolid = true;
  354. pTrace->fraction = 0.0f;
  355. pTrace->fractionleftsolid = 0.0f;
  356. pTrace->contents = pDispTree->GetContents();
  357. return;
  358. }
  359. }
  360. }
  361. }
  362. //
  363. // need to stab if is was a point test or the box test yeilded no intersection
  364. //
  365. Vector stabDir;
  366. int contents;
  367. CM_PreStab( pTraceInfo, pLeaf, stabDir, collisionMask, contents );
  368. CM_Stab( pTraceInfo, traceStart, stabDir, contents );
  369. CM_PostStab( pTraceInfo );
  370. }
  371. //-----------------------------------------------------------------------------
  372. // New Collision!
  373. //-----------------------------------------------------------------------------
  374. void CM_PreStab( TraceInfo_t *pTraceInfo, cleaf_t *pLeaf, Vector &vStabDir, int collisionMask, int &contents )
  375. {
  376. if( !pLeaf->dispCount )
  377. return;
  378. // if the point wasn't in the bounded area of any of the displacements -- stab in any
  379. // direction and set contents to "solid"
  380. int dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart];
  381. CDispCollTree *pDispTree = &g_pDispCollTrees[dispIndex];
  382. pDispTree->GetStabDirection( vStabDir );
  383. contents = CONTENTS_SOLID;
  384. //
  385. // if the point is inside a displacement's (in the leaf) bounded area
  386. // then get the direction to stab from it
  387. //
  388. for( int i = 0; i < pLeaf->dispCount; i++ )
  389. {
  390. dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart + i];
  391. pDispTree = &g_pDispCollTrees[dispIndex];
  392. // Respect trace contents
  393. if( !(pDispTree->GetContents() & collisionMask) )
  394. continue;
  395. if( pDispTree->PointInBounds( pTraceInfo->m_start, pTraceInfo->m_mins, pTraceInfo->m_maxs, pTraceInfo->m_ispoint ) )
  396. {
  397. pDispTree->GetStabDirection( vStabDir );
  398. contents = pDispTree->GetContents();
  399. return;
  400. }
  401. }
  402. }
  403. static Vector InvDelta(const Vector &delta)
  404. {
  405. Vector vecInvDelta;
  406. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  407. {
  408. if ( delta[iAxis] != 0.0f )
  409. {
  410. vecInvDelta[iAxis] = 1.0f / delta[iAxis];
  411. }
  412. else
  413. {
  414. vecInvDelta[iAxis] = FLT_MAX;
  415. }
  416. }
  417. return vecInvDelta;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // New Collision!
  421. //-----------------------------------------------------------------------------
  422. void CM_Stab( TraceInfo_t *pTraceInfo, const Vector &start, const Vector &vStabDir, int contents )
  423. {
  424. //
  425. // initialize the displacement trace parameters
  426. //
  427. pTraceInfo->m_trace.fraction = 1.0f;
  428. pTraceInfo->m_trace.fractionleftsolid = 0.0f;
  429. pTraceInfo->m_trace.surface = pTraceInfo->m_pBSPData->nullsurface;
  430. pTraceInfo->m_trace.startsolid = false;
  431. pTraceInfo->m_trace.allsolid = false;
  432. pTraceInfo->m_bDispHit = false;
  433. pTraceInfo->m_DispStabDir = vStabDir;
  434. Vector end = pTraceInfo->m_end;
  435. pTraceInfo->m_start = start;
  436. pTraceInfo->m_end = start + ( vStabDir * /* world extents * 2*/99999.9f );
  437. pTraceInfo->m_delta = pTraceInfo->m_end - pTraceInfo->m_start;
  438. pTraceInfo->m_invDelta = InvDelta(pTraceInfo->m_delta);
  439. // increment the checkcount -- so we can retest objects that may have been tested
  440. // previous to the stab
  441. PushTraceVisits( pTraceInfo );
  442. // increment the stab count -- statistics
  443. #ifdef COUNT_COLLISIONS
  444. g_CollisionCounts.m_Stabs++;
  445. #endif
  446. // stab
  447. CM_RecursiveHullCheck( pTraceInfo, 0 /*root*/, 0.0f, 1.0f );
  448. PopTraceVisits( pTraceInfo );
  449. pTraceInfo->m_end = end;
  450. }
  451. //-----------------------------------------------------------------------------
  452. // New Collision!
  453. //-----------------------------------------------------------------------------
  454. void CM_PostStab( TraceInfo_t *pTraceInfo )
  455. {
  456. //
  457. // only need to resolve things that impacted against a displacement surface,
  458. // this is partially resolved in the post trace phase -- so just use that
  459. // data to determine
  460. //
  461. if( pTraceInfo->m_bDispHit && pTraceInfo->m_trace.startsolid )
  462. {
  463. pTraceInfo->m_trace.allsolid = true;
  464. pTraceInfo->m_trace.fraction = 0.0f;
  465. pTraceInfo->m_trace.fractionleftsolid = 0.0f;
  466. }
  467. else
  468. {
  469. pTraceInfo->m_trace.startsolid = false;
  470. pTraceInfo->m_trace.allsolid = false;
  471. pTraceInfo->m_trace.contents = 0;
  472. pTraceInfo->m_trace.fraction = 1.0f;
  473. pTraceInfo->m_trace.fractionleftsolid = 0.0f;
  474. }
  475. }
  476. //-----------------------------------------------------------------------------
  477. // New Collision!
  478. //-----------------------------------------------------------------------------
  479. void CM_PostTraceToDispTree( TraceInfo_t *pTraceInfo )
  480. {
  481. // only resolve things that impacted against a displacement surface
  482. if( !pTraceInfo->m_bDispHit )
  483. return;
  484. //
  485. // determine whether or not we are in solid
  486. //
  487. if( DotProduct( pTraceInfo->m_trace.plane.normal, pTraceInfo->m_delta ) > 0.0f )
  488. {
  489. pTraceInfo->m_trace.startsolid = true;
  490. pTraceInfo->m_trace.allsolid = true;
  491. }
  492. }
  493. //-----------------------------------------------------------------------------
  494. // New Collision!
  495. //-----------------------------------------------------------------------------
  496. template <bool IS_POINT>
  497. void FASTCALL CM_TraceToDispTree( TraceInfo_t *pTraceInfo, CDispCollTree *pDispTree, float startFrac, float endFrac )
  498. {
  499. Ray_t ray;
  500. ray.m_Start = pTraceInfo->m_start;
  501. ray.m_Delta = pTraceInfo->m_delta;
  502. ray.m_IsSwept = true;
  503. trace_t *pTrace = &pTraceInfo->m_trace;
  504. // ray cast
  505. if( IS_POINT )
  506. {
  507. ray.m_Extents.Init();
  508. ray.m_IsRay = true;
  509. if( pDispTree->AABBTree_Ray( ray, pTraceInfo->m_invDelta, pTrace ) )
  510. {
  511. pTraceInfo->m_bDispHit = true;
  512. pTrace->contents = pDispTree->GetContents();
  513. SetDispTraceSurfaceProps( pTrace, pDispTree );
  514. }
  515. }
  516. // box sweep
  517. else
  518. {
  519. ray.m_Extents = pTraceInfo->m_extents;
  520. ray.m_IsRay = false;
  521. if( pDispTree->AABBTree_SweepAABB( ray, pTraceInfo->m_invDelta, pTrace ) )
  522. {
  523. pTraceInfo->m_bDispHit = true;
  524. pTrace->contents = pDispTree->GetContents();
  525. SetDispTraceSurfaceProps( pTrace, pDispTree );
  526. }
  527. }
  528. // CM_TraceToDispTreeTest( pDispTree, traceStart, traceEnd, boxMin, boxMax, startFrac, endFrac, pTrace, bRayCast );
  529. }
  530. template void FASTCALL CM_TraceToDispTree<true>( TraceInfo_t *pTraceInfo, CDispCollTree *pDispTree, float startFrac, float endFrac );
  531. template void FASTCALL CM_TraceToDispTree<false>( TraceInfo_t *pTraceInfo, CDispCollTree *pDispTree, float startFrac, float endFrac );