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.

603 lines
18 KiB

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