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.

3031 lines
84 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: BSP collision!
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cmodel_engine.h"
  8. #include "cmodel_private.h"
  9. #include "dispcoll_common.h"
  10. #include "coordsize.h"
  11. #include "quakedef.h"
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include "mathlib/mathlib.h"
  15. #include "common.h"
  16. #include "sysexternal.h"
  17. #include "zone.h"
  18. #include "utlvector.h"
  19. #include "const.h"
  20. #include "gl_model_private.h"
  21. #include "vphysics_interface.h"
  22. #include "icliententity.h"
  23. #include "engine/ICollideable.h"
  24. #include "enginethreads.h"
  25. #include "sys_dll.h"
  26. #include "collisionutils.h"
  27. #include "tier0/tslist.h"
  28. #include "tier0/vprof.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. CCollisionBSPData g_BSPData; // the global collision bsp
  32. #define g_BSPData dont_use_g_BSPData_directly
  33. #ifdef COUNT_COLLISIONS
  34. CCollisionCounts g_CollisionCounts; // collision test counters
  35. #endif
  36. csurface_t CCollisionBSPData::nullsurface = { "**empty**", 0, 0 }; // generic null collision model surface
  37. csurface_t *CCollisionBSPData::GetSurfaceAtIndex( unsigned short surfaceIndex )
  38. {
  39. if ( surfaceIndex == SURFACE_INDEX_INVALID )
  40. {
  41. return &nullsurface;
  42. }
  43. return &map_surfaces[surfaceIndex];
  44. }
  45. #if TEST_TRACE_POOL
  46. CTSPool<TraceInfo_t> g_TraceInfoPool;
  47. #else
  48. class CTraceInfoPool : public CTSList<TraceInfo_t *>
  49. {
  50. public:
  51. CTraceInfoPool()
  52. {
  53. }
  54. };
  55. CTraceInfoPool g_TraceInfoPool;
  56. #endif
  57. TraceInfo_t *BeginTrace()
  58. {
  59. #if TEST_TRACE_POOL
  60. TraceInfo_t *pTraceInfo = g_TraceInfoPool.GetObject();
  61. #else
  62. TraceInfo_t *pTraceInfo;
  63. if ( !g_TraceInfoPool.PopItem( &pTraceInfo ) )
  64. {
  65. pTraceInfo = new TraceInfo_t;
  66. }
  67. #endif
  68. if ( pTraceInfo->m_BrushCounters[0].Count() != GetCollisionBSPData()->numbrushes + 1 )
  69. {
  70. memset( pTraceInfo->m_Count, 0, sizeof( pTraceInfo->m_Count ) );
  71. pTraceInfo->m_nCheckDepth = -1;
  72. for ( int i = 0; i < MAX_CHECK_COUNT_DEPTH; i++ )
  73. {
  74. pTraceInfo->m_BrushCounters[i].SetCount( GetCollisionBSPData()->numbrushes + 1 );
  75. pTraceInfo->m_DispCounters[i].SetCount( g_DispCollTreeCount );
  76. memset( pTraceInfo->m_BrushCounters[i].Base(), 0, pTraceInfo->m_BrushCounters[i].Count() * sizeof(TraceCounter_t) );
  77. memset( pTraceInfo->m_DispCounters[i].Base(), 0, pTraceInfo->m_DispCounters[i].Count() * sizeof(TraceCounter_t) );
  78. }
  79. }
  80. PushTraceVisits( pTraceInfo );
  81. return pTraceInfo;
  82. }
  83. void PushTraceVisits( TraceInfo_t *pTraceInfo )
  84. {
  85. ++pTraceInfo->m_nCheckDepth;
  86. Assert( (pTraceInfo->m_nCheckDepth >= 0) && (pTraceInfo->m_nCheckDepth < MAX_CHECK_COUNT_DEPTH) );
  87. int i = pTraceInfo->m_nCheckDepth;
  88. pTraceInfo->m_Count[i]++;
  89. if ( pTraceInfo->m_Count[i] == 0 )
  90. {
  91. pTraceInfo->m_Count[i]++;
  92. memset( pTraceInfo->m_BrushCounters[i].Base(), 0, pTraceInfo->m_BrushCounters[i].Count() * sizeof(TraceCounter_t) );
  93. memset( pTraceInfo->m_DispCounters[i].Base(), 0, pTraceInfo->m_DispCounters[i].Count() * sizeof(TraceCounter_t) );
  94. }
  95. }
  96. void PopTraceVisits( TraceInfo_t *pTraceInfo )
  97. {
  98. --pTraceInfo->m_nCheckDepth;
  99. Assert( pTraceInfo->m_nCheckDepth >= -1 );
  100. }
  101. void EndTrace( TraceInfo_t *&pTraceInfo )
  102. {
  103. PopTraceVisits( pTraceInfo );
  104. Assert( pTraceInfo->m_nCheckDepth == -1 );
  105. #if TEST_TRACE_POOL
  106. g_TraceInfoPool.PutObject( pTraceInfo );
  107. #else
  108. g_TraceInfoPool.PushItem( pTraceInfo );
  109. #endif
  110. pTraceInfo = NULL;
  111. }
  112. static ConVar map_noareas( "map_noareas", "0", 0, "Disable area to area connection testing." );
  113. void FloodAreaConnections (CCollisionBSPData *pBSPData);
  114. //-----------------------------------------------------------------------------
  115. //-----------------------------------------------------------------------------
  116. vcollide_t *CM_GetVCollide( int modelIndex )
  117. {
  118. cmodel_t *pModel = CM_InlineModelNumber( modelIndex );
  119. if( !pModel )
  120. return NULL;
  121. // return the model's collision data
  122. return &pModel->vcollisionData;
  123. }
  124. //-----------------------------------------------------------------------------
  125. //-----------------------------------------------------------------------------
  126. cmodel_t *CM_InlineModel( const char *name )
  127. {
  128. // error checking!
  129. if( !name )
  130. return NULL;
  131. // JAYHL2: HACKHACK Get rid of this
  132. if( !strncmp( name, "maps/", 5 ) )
  133. return CM_InlineModelNumber( 0 );
  134. // check for valid name
  135. if( name[0] != '*' )
  136. Sys_Error( "CM_InlineModel: bad model name!" );
  137. // check for valid model
  138. int ndxModel = atoi( name + 1 );
  139. if( ( ndxModel < 1 ) || ( ndxModel >= GetCollisionBSPData()->numcmodels ) )
  140. Sys_Error( "CM_InlineModel: bad model number!" );
  141. return CM_InlineModelNumber( ndxModel );
  142. }
  143. //-----------------------------------------------------------------------------
  144. //-----------------------------------------------------------------------------
  145. cmodel_t *CM_InlineModelNumber( int index )
  146. {
  147. CCollisionBSPData *pBSPDataData = GetCollisionBSPData();
  148. if( ( index < 0 ) || ( index > pBSPDataData->numcmodels ) )
  149. return NULL;
  150. return ( &pBSPDataData->map_cmodels[ index ] );
  151. }
  152. int CM_BrushContents_r( CCollisionBSPData *pBSPData, int nodenum )
  153. {
  154. int contents = 0;
  155. while (1)
  156. {
  157. if (nodenum < 0)
  158. {
  159. int leafIndex = -1 - nodenum;
  160. cleaf_t &leaf = pBSPData->map_leafs[leafIndex];
  161. for ( int i = 0; i < leaf.numleafbrushes; i++ )
  162. {
  163. unsigned short brushIndex = pBSPData->map_leafbrushes[ leaf.firstleafbrush + i ];
  164. contents |= pBSPData->map_brushes[brushIndex].contents;
  165. }
  166. return contents;
  167. }
  168. cnode_t &node = pBSPData->map_rootnode[nodenum];
  169. contents |= CM_BrushContents_r( pBSPData, node.children[0] );
  170. nodenum = node.children[1];
  171. }
  172. return contents;
  173. }
  174. int CM_InlineModelContents( int index )
  175. {
  176. cmodel_t *pModel = CM_InlineModelNumber( index );
  177. if ( !pModel )
  178. return 0;
  179. return CM_BrushContents_r( GetCollisionBSPData(), pModel->headnode );
  180. }
  181. //-----------------------------------------------------------------------------
  182. //-----------------------------------------------------------------------------
  183. int CM_NumClusters( void )
  184. {
  185. return GetCollisionBSPData()->numclusters;
  186. }
  187. //-----------------------------------------------------------------------------
  188. //-----------------------------------------------------------------------------
  189. char *CM_EntityString( void )
  190. {
  191. return GetCollisionBSPData()->map_entitystring.Get();
  192. }
  193. void CM_DiscardEntityString( void )
  194. {
  195. GetCollisionBSPData()->map_entitystring.Discard();
  196. }
  197. //-----------------------------------------------------------------------------
  198. //-----------------------------------------------------------------------------
  199. int CM_LeafContents( int leafnum )
  200. {
  201. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  202. Assert( leafnum >= 0 );
  203. Assert( leafnum < pBSPData->numleafs );
  204. return pBSPData->map_leafs[leafnum].contents;
  205. }
  206. //-----------------------------------------------------------------------------
  207. //-----------------------------------------------------------------------------
  208. int CM_LeafCluster( int leafnum )
  209. {
  210. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  211. Assert( leafnum >= 0 );
  212. Assert( leafnum < pBSPData->numleafs );
  213. return pBSPData->map_leafs[leafnum].cluster;
  214. }
  215. int CM_LeafFlags( int leafnum )
  216. {
  217. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  218. Assert( leafnum >= 0 );
  219. Assert( leafnum < pBSPData->numleafs );
  220. return pBSPData->map_leafs[leafnum].flags;
  221. }
  222. //-----------------------------------------------------------------------------
  223. //-----------------------------------------------------------------------------
  224. int CM_LeafArea( int leafnum )
  225. {
  226. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  227. Assert( leafnum >= 0 );
  228. Assert( leafnum < pBSPData->numleafs );
  229. return pBSPData->map_leafs[leafnum].area;
  230. }
  231. //-----------------------------------------------------------------------------
  232. //-----------------------------------------------------------------------------
  233. void CM_FreeMap(void)
  234. {
  235. // get the current collision bsp -- there is only one!
  236. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  237. // free the collision bsp data
  238. CollisionBSPData_Destroy( pBSPData );
  239. }
  240. // This turns on all the area portals that are "always on" in the map.
  241. void CM_InitPortalOpenState( CCollisionBSPData *pBSPData )
  242. {
  243. for ( int i=0; i < pBSPData->numportalopen; i++ )
  244. {
  245. pBSPData->portalopen[i] = false;
  246. }
  247. }
  248. /*
  249. ==================
  250. CM_LoadMap
  251. Loads in the map and all submodels
  252. ==================
  253. */
  254. cmodel_t *CM_LoadMap( const char *name, bool allowReusePrevious, unsigned *checksum )
  255. {
  256. static unsigned int last_checksum = 0xFFFFFFFF;
  257. // get the current bsp -- there is currently only one!
  258. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  259. Assert( physcollision );
  260. if( !strcmp( pBSPData->map_name, name ) && allowReusePrevious )
  261. {
  262. *checksum = last_checksum;
  263. return &pBSPData->map_cmodels[0]; // still have the right version
  264. }
  265. // only pre-load if the map doesn't already exist
  266. CollisionBSPData_PreLoad( pBSPData );
  267. if ( !name || !name[0] )
  268. {
  269. *checksum = 0;
  270. return &pBSPData->map_cmodels[0]; // cinematic servers won't have anything at all
  271. }
  272. // read in the collision model data
  273. CMapLoadHelper::Init( 0, name );
  274. CollisionBSPData_Load( name, pBSPData );
  275. CMapLoadHelper::Shutdown( );
  276. // Push the displacement bounding boxes down the tree and set leaf data.
  277. CM_DispTreeLeafnum( pBSPData );
  278. CM_InitPortalOpenState( pBSPData );
  279. FloodAreaConnections(pBSPData);
  280. #ifdef COUNT_COLLISIONS
  281. // initialize counters
  282. CollisionCounts_Init( &g_CollisionCounts );
  283. #endif
  284. return &pBSPData->map_cmodels[0];
  285. }
  286. //-----------------------------------------------------------------------------
  287. //
  288. // Methods associated with colliding against the world + models
  289. //
  290. //-----------------------------------------------------------------------------
  291. //-----------------------------------------------------------------------------
  292. // returns a vcollide that can be used to collide against this model
  293. //-----------------------------------------------------------------------------
  294. vcollide_t* CM_VCollideForModel( int modelindex, const model_t* pModel )
  295. {
  296. if ( pModel )
  297. {
  298. switch( pModel->type )
  299. {
  300. case mod_brush:
  301. return CM_GetVCollide( modelindex-1 );
  302. case mod_studio:
  303. Assert( modelloader->IsLoaded( pModel ) );
  304. return g_pMDLCache->GetVCollide( pModel->studio );
  305. }
  306. }
  307. return 0;
  308. }
  309. //=======================================================================
  310. /*
  311. ==================
  312. CM_PointLeafnum_r
  313. ==================
  314. */
  315. int CM_PointLeafnumMinDistSqr_r( CCollisionBSPData *pBSPData, const Vector& p, int num, float &minDistSqr )
  316. {
  317. float d;
  318. cnode_t *node;
  319. cplane_t *plane;
  320. while (num >= 0)
  321. {
  322. node = pBSPData->map_rootnode + num;
  323. plane = node->plane;
  324. if (plane->type < 3)
  325. d = p[plane->type] - plane->dist;
  326. else
  327. d = DotProduct (plane->normal, p) - plane->dist;
  328. minDistSqr = fpmin( d*d, minDistSqr );
  329. if (d < 0)
  330. num = node->children[1];
  331. else
  332. num = node->children[0];
  333. }
  334. #ifdef COUNT_COLLISIONS
  335. g_CollisionCounts.m_PointContents++; // optimize counter
  336. #endif
  337. return -1 - num;
  338. }
  339. int CM_PointLeafnum_r( CCollisionBSPData *pBSPData, const Vector& p, int num)
  340. {
  341. float d;
  342. cnode_t *node;
  343. cplane_t *plane;
  344. while (num >= 0)
  345. {
  346. node = pBSPData->map_rootnode + num;
  347. plane = node->plane;
  348. if (plane->type < 3)
  349. d = p[plane->type] - plane->dist;
  350. else
  351. d = DotProduct (plane->normal, p) - plane->dist;
  352. if (d < 0)
  353. num = node->children[1];
  354. else
  355. num = node->children[0];
  356. }
  357. #ifdef COUNT_COLLISIONS
  358. g_CollisionCounts.m_PointContents++; // optimize counter
  359. #endif
  360. return -1 - num;
  361. }
  362. int CM_PointLeafnum (const Vector& p)
  363. {
  364. // get the current collision bsp -- there is only one!
  365. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  366. if (!pBSPData->numplanes)
  367. return 0; // sound may call this without map loaded
  368. return CM_PointLeafnum_r (pBSPData, p, 0);
  369. }
  370. void CM_SnapPointToReferenceLeaf_r( CCollisionBSPData *pBSPData, const Vector& p, int num, float tolerance, Vector *pSnapPoint )
  371. {
  372. float d, snapDist;
  373. cnode_t *node;
  374. cplane_t *plane;
  375. while (num >= 0)
  376. {
  377. node = pBSPData->map_rootnode + num;
  378. plane = node->plane;
  379. if (plane->type < 3)
  380. {
  381. d = p[plane->type] - plane->dist;
  382. snapDist = (*pSnapPoint)[plane->type] - plane->dist;
  383. }
  384. else
  385. {
  386. d = DotProduct (plane->normal, p) - plane->dist;
  387. snapDist = DotProduct (plane->normal, *pSnapPoint) - plane->dist;
  388. }
  389. if (d < 0)
  390. {
  391. num = node->children[1];
  392. if ( snapDist > 0 )
  393. {
  394. *pSnapPoint -= plane->normal * (snapDist + tolerance);
  395. }
  396. }
  397. else
  398. {
  399. num = node->children[0];
  400. if ( snapDist < 0 )
  401. {
  402. *pSnapPoint += plane->normal * (-snapDist + tolerance);
  403. }
  404. }
  405. }
  406. }
  407. void CM_SnapPointToReferenceLeaf(const Vector &referenceLeafPoint, float tolerance, Vector *pSnapPoint)
  408. {
  409. // get the current collision bsp -- there is only one!
  410. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  411. if (pBSPData->numplanes)
  412. {
  413. CM_SnapPointToReferenceLeaf_r(pBSPData, referenceLeafPoint, 0, tolerance, pSnapPoint);
  414. }
  415. }
  416. /*
  417. =============
  418. CM_BoxLeafnums
  419. Fills in a list of all the leafs touched
  420. =============
  421. */
  422. struct leafnums_t
  423. {
  424. int leafTopNode;
  425. int leafMaxCount;
  426. int *pLeafList;
  427. CCollisionBSPData *pBSPData;
  428. };
  429. int CM_BoxLeafnums( leafnums_t &context, const Vector &center, const Vector &extents, int nodenum )
  430. {
  431. int leafCount = 0;
  432. const int NODELIST_MAX = 1024;
  433. int nodeList[NODELIST_MAX];
  434. int nodeReadIndex = 0;
  435. int nodeWriteIndex = 0;
  436. cplane_t *plane;
  437. cnode_t *node;
  438. int prev_topnode = -1;
  439. while (1)
  440. {
  441. if (nodenum < 0)
  442. {
  443. // This handles the case when the box lies completely
  444. // within a single node. In that case, the top node should be
  445. // the parent of the leaf
  446. if (context.leafTopNode == -1)
  447. context.leafTopNode = prev_topnode;
  448. if (leafCount < context.leafMaxCount)
  449. {
  450. context.pLeafList[leafCount] = -1 - nodenum;
  451. leafCount++;
  452. }
  453. if ( nodeReadIndex == nodeWriteIndex )
  454. return leafCount;
  455. nodenum = nodeList[nodeReadIndex];
  456. nodeReadIndex = (nodeReadIndex+1) & (NODELIST_MAX-1);
  457. }
  458. else
  459. {
  460. node = &context.pBSPData->map_rootnode[nodenum];
  461. plane = node->plane;
  462. // s = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);
  463. // s = BOX_ON_PLANE_SIDE(*leaf_mins, *leaf_maxs, plane);
  464. float d0 = DotProduct( plane->normal, center ) - plane->dist;
  465. float d1 = DotProductAbs( plane->normal, extents );
  466. prev_topnode = nodenum;
  467. if (d0 >= d1)
  468. nodenum = node->children[0];
  469. else if (d0 < -d1)
  470. nodenum = node->children[1];
  471. else
  472. { // go down both
  473. if (context.leafTopNode == -1)
  474. context.leafTopNode = nodenum;
  475. nodeList[nodeWriteIndex] = node->children[0];
  476. nodeWriteIndex = (nodeWriteIndex+1) & (NODELIST_MAX-1);
  477. // check for overflow of the ring buffer
  478. Assert(nodeWriteIndex != nodeReadIndex);
  479. nodenum = node->children[1];
  480. }
  481. }
  482. }
  483. }
  484. int CM_BoxLeafnums ( const Vector& mins, const Vector& maxs, int *list, int listsize, int *topnode)
  485. {
  486. leafnums_t context;
  487. context.pLeafList = list;
  488. context.leafTopNode = -1;
  489. context.leafMaxCount = listsize;
  490. // get the current collision bsp -- there is only one!
  491. context.pBSPData = GetCollisionBSPData();
  492. Vector center = (mins+maxs)*0.5f;
  493. Vector extents = maxs - center;
  494. int leafCount = CM_BoxLeafnums(context, center, extents, context.pBSPData->map_cmodels[0].headnode );
  495. if (topnode)
  496. *topnode = context.leafTopNode;
  497. return leafCount;
  498. }
  499. // UNDONE: This is a version that returns only leaves with valid clusters
  500. // UNDONE: Use this in the PVS calcs for networking
  501. #if 0
  502. int CM_BoxClusters( leafnums_t * RESTRICT pContext, const Vector &center, const Vector &extents, int nodenum )
  503. {
  504. const int NODELIST_MAX = 1024;
  505. int nodeList[NODELIST_MAX];
  506. int nodeReadIndex = 0;
  507. int nodeWriteIndex = 0;
  508. cplane_t *RESTRICT plane;
  509. cnode_t *RESTRICT node;
  510. int prev_topnode = -1;
  511. int leafCount = 0;
  512. while (1)
  513. {
  514. if (nodenum < 0)
  515. {
  516. int leafIndex = -1 - nodenum;
  517. // This handles the case when the box lies completely
  518. // within a single node. In that case, the top node should be
  519. // the parent of the leaf
  520. if (pContext->leafTopNode == -1)
  521. pContext->leafTopNode = prev_topnode;
  522. if (leafCount < pContext->leafMaxCount)
  523. {
  524. cleaf_t *RESTRICT pLeaf = &pContext->pBSPData->map_leafs[leafIndex];
  525. if ( pLeaf->cluster >= 0 )
  526. {
  527. pContext->pLeafList[leafCount] = leafIndex;
  528. leafCount++;
  529. }
  530. }
  531. if ( nodeReadIndex == nodeWriteIndex )
  532. return leafCount;
  533. nodenum = nodeList[nodeReadIndex];
  534. nodeReadIndex = (nodeReadIndex+1) & (NODELIST_MAX-1);
  535. }
  536. else
  537. {
  538. node = &pContext->pBSPData->map_rootnode[nodenum];
  539. plane = node->plane;
  540. float d0 = DotProduct( plane->normal, center ) - plane->dist;
  541. float d1 = DotProductAbs( plane->normal, extents );
  542. prev_topnode = nodenum;
  543. if (d0 >= d1)
  544. nodenum = node->children[0];
  545. else if (d0 < -d1)
  546. nodenum = node->children[1];
  547. else
  548. { // go down both
  549. if (pContext->leafTopNode == -1)
  550. pContext->leafTopNode = nodenum;
  551. nodenum = node->children[0];
  552. nodeList[nodeWriteIndex] = node->children[1];
  553. nodeWriteIndex = (nodeWriteIndex+1) & (NODELIST_MAX-1);
  554. // check for overflow of the ring buffer
  555. Assert(nodeWriteIndex != nodeReadIndex);
  556. }
  557. }
  558. }
  559. }
  560. int CM_BoxClusters_headnode ( CCollisionBSPData *pBSPData, const Vector& mins, const Vector& maxs, int *list, int listsize, int nodenum, int *topnode)
  561. {
  562. leafnums_t context;
  563. context.pLeafList = list;
  564. context.leafTopNode = -1;
  565. context.leafMaxCount = listsize;
  566. Vector center = 0.5f * (mins + maxs);
  567. Vector extents = maxs - center;
  568. context.pBSPData = pBSPData;
  569. int leafCount = CM_BoxClusters( &context, center, extents, nodenum );
  570. if (topnode)
  571. *topnode = context.leafTopNode;
  572. return leafCount;
  573. }
  574. #endif
  575. static int FASTCALL CM_BrushBoxContents( CCollisionBSPData *pBSPData, const Vector &vMins, const Vector &vMaxs, cbrush_t *pBrush )
  576. {
  577. if ( pBrush->IsBox())
  578. {
  579. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[pBrush->GetBox()];
  580. if ( !IsBoxIntersectingBox( vMins, vMaxs, pBox->mins, pBox->maxs ) )
  581. return 0;
  582. }
  583. else
  584. {
  585. if (!pBrush->numsides)
  586. return 0;
  587. Vector vCenter = 0.5f *(vMins + vMaxs);
  588. Vector vExt = vMaxs - vCenter;
  589. int i, j;
  590. cplane_t *plane;
  591. float dist;
  592. Vector vOffset;
  593. float d1;
  594. cbrushside_t *side;
  595. for (i=0 ; i<pBrush->numsides ; i++)
  596. {
  597. side = &pBSPData->map_brushsides[pBrush->firstbrushside+i];
  598. plane = side->plane;
  599. // FIXME: special case for axial
  600. // general box case
  601. // push the plane out appropriately for mins/maxs
  602. // FIXME: use signbits into 8 way lookup for each mins/maxs
  603. for (j=0 ; j<3 ; j++)
  604. {
  605. if (plane->normal[j] < 0)
  606. vOffset[j] = vExt[j];
  607. else
  608. vOffset[j] = -vExt[j];
  609. }
  610. dist = DotProduct (vOffset, plane->normal);
  611. dist = plane->dist - dist;
  612. d1 = DotProduct (vCenter, plane->normal) - dist;
  613. // if completely in front of face, no intersection
  614. if (d1 > 0)
  615. return 0;
  616. }
  617. }
  618. // inside this brush
  619. return pBrush->contents;
  620. }
  621. static int FASTCALL CM_BrushPointContents( CCollisionBSPData *pBSPData, const Vector &vPos, cbrush_t *pBrush )
  622. {
  623. if ( pBrush->IsBox())
  624. {
  625. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[pBrush->GetBox()];
  626. if ( !IsPointInBox( vPos, pBox->mins, pBox->maxs ) )
  627. return 0;
  628. }
  629. else
  630. {
  631. if (!pBrush->numsides)
  632. return 0;
  633. cplane_t *plane;
  634. cbrushside_t *side;
  635. for ( int i = 0 ; i < pBrush->numsides; i++ )
  636. {
  637. side = &pBSPData->map_brushsides[pBrush->firstbrushside+i];
  638. plane = side->plane;
  639. float flDist = DotProduct (vPos, plane->normal) - plane->dist;
  640. // if completely in front of face, no intersection
  641. if (flDist > 0)
  642. return 0;
  643. }
  644. }
  645. // inside this brush
  646. return pBrush->contents;
  647. }
  648. /*
  649. ==================
  650. CM_PointContents
  651. ==================
  652. */
  653. int CM_PointContents ( const Vector &p, int headnode)
  654. {
  655. int l;
  656. // get the current collision bsp -- there is only one!
  657. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  658. if (!pBSPData->numnodes) // map not loaded
  659. return 0;
  660. l = CM_PointLeafnum_r (pBSPData, p, headnode);
  661. cleaf_t *pLeaf = &pBSPData->map_leafs[l];
  662. int nContents = pLeaf->contents;
  663. for ( int i = 0; i < pLeaf->numleafbrushes; i++ )
  664. {
  665. int nBrush = pBSPData->map_leafbrushes[pLeaf->firstleafbrush + i];
  666. cbrush_t * RESTRICT pBrush = &pBSPData->map_brushes[nBrush];
  667. nContents |= CM_BrushPointContents( pBSPData, p, pBrush );
  668. }
  669. return nContents;
  670. }
  671. /*
  672. ==================
  673. CM_TransformedPointContents
  674. Handles offseting and rotation of the end points for moving and
  675. rotating entities
  676. ==================
  677. */
  678. int CM_TransformedPointContents ( const Vector& p, int headnode, const Vector& origin, QAngle const& angles)
  679. {
  680. Vector p_l;
  681. Vector temp;
  682. Vector forward, right, up;
  683. int l;
  684. // get the current collision bsp -- there is only one!
  685. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  686. // subtract origin offset
  687. VectorSubtract (p, origin, p_l);
  688. // rotate start and end into the models frame of reference
  689. if ( angles[0] || angles[1] || angles[2] )
  690. {
  691. AngleVectors (angles, &forward, &right, &up);
  692. VectorCopy (p_l, temp);
  693. p_l[0] = DotProduct (temp, forward);
  694. p_l[1] = -DotProduct (temp, right);
  695. p_l[2] = DotProduct (temp, up);
  696. }
  697. l = CM_PointLeafnum_r (pBSPData, p_l, headnode);
  698. return pBSPData->map_leafs[l].contents;
  699. }
  700. /*
  701. ===============================================================================
  702. BOX TRACING
  703. ===============================================================================
  704. */
  705. // Custom SIMD implementation for box brushes
  706. const fltx4 Four_DistEpsilons={DIST_EPSILON,DIST_EPSILON,DIST_EPSILON,DIST_EPSILON};
  707. const int32 ALIGN16 g_CubeFaceIndex0[4] ALIGN16_POST = {0,1,2,-1};
  708. const int32 ALIGN16 g_CubeFaceIndex1[4] ALIGN16_POST = {3,4,5,-1};
  709. bool IntersectRayWithBoxBrush( TraceInfo_t *pTraceInfo, const cbrush_t *pBrush, cboxbrush_t *pBox )
  710. {
  711. // Suppress floating-point exceptions in this function because invDelta's
  712. // components can get arbitrarily large -- up to FLT_MAX -- and overflow
  713. // when multiplied. Only applicable when FP_EXCEPTIONS_ENABLED is defined.
  714. FPExceptionDisabler hideExceptions;
  715. // Load the unaligned ray/box parameters into SIMD registers
  716. fltx4 start = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  717. fltx4 extents = LoadUnaligned3SIMD(pTraceInfo->m_extents.Base());
  718. fltx4 delta = LoadUnaligned3SIMD(pTraceInfo->m_delta.Base());
  719. fltx4 boxMins = LoadAlignedSIMD( pBox->mins.Base() );
  720. fltx4 boxMaxs = LoadAlignedSIMD( pBox->maxs.Base() );
  721. // compute the mins/maxs of the box expanded by the ray extents
  722. // relocate the problem so that the ray start is at the origin.
  723. fltx4 offsetMins = SubSIMD(boxMins, start);
  724. fltx4 offsetMaxs = SubSIMD(boxMaxs, start);
  725. fltx4 offsetMinsExpanded = SubSIMD(offsetMins, extents);
  726. fltx4 offsetMaxsExpanded = AddSIMD(offsetMaxs, extents);
  727. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  728. // of any of the box sides - if so there can be no intersection
  729. fltx4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  730. fltx4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  731. fltx4 minsMask = AndSIMD( startOutMins, endOutMins );
  732. fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  733. fltx4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  734. fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  735. if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  736. return false;
  737. fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  738. // now build the per-axis interval of t for intersections
  739. fltx4 invDelta = LoadUnaligned3SIMD(pTraceInfo->m_invDelta.Base());
  740. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  741. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  742. // now sort the interval per axis
  743. fltx4 mint = MinSIMD( tmins, tmaxs );
  744. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  745. // only axes where we cross a plane are relevant
  746. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  747. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  748. // now find the intersection of the intervals on all axes
  749. fltx4 firstOut = FindLowestSIMD3(maxt);
  750. fltx4 lastIn = FindHighestSIMD3(mint);
  751. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  752. firstOut = MinSIMD(firstOut, Four_Ones);
  753. lastIn = MaxSIMD(lastIn, Four_Zeros);
  754. // If the final interval is valid lastIn<firstOut, check for separation
  755. fltx4 separation = CmpGtSIMD(lastIn, firstOut);
  756. if ( IsAllZeros(separation) )
  757. {
  758. bool bStartOut = IsAnyNegative(SetWToZeroSIMD(OrSIMD(startOutMins,startOutMaxs)));
  759. offsetMinsExpanded = SubSIMD(offsetMinsExpanded, Four_DistEpsilons);
  760. offsetMaxsExpanded = AddSIMD(offsetMaxsExpanded, Four_DistEpsilons);
  761. tmins = MulSIMD( offsetMinsExpanded, invDelta );
  762. tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  763. fltx4 minface0 = LoadAlignedSIMD( (float *) g_CubeFaceIndex0 );
  764. fltx4 minface1 = LoadAlignedSIMD( (float *) g_CubeFaceIndex1 );
  765. fltx4 faceMask = CmpLeSIMD( tmins, tmaxs );
  766. mint = MinSIMD( tmins, tmaxs );
  767. maxt = MaxSIMD( tmins, tmaxs );
  768. fltx4 faceId = MaskedAssign( faceMask, minface0, minface1 );
  769. // only axes where we cross a plane are relevant
  770. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  771. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  772. fltx4 firstOutTmp = FindLowestSIMD3(maxt);
  773. // implement FindHighest of 3, but use intermediate masks to find the
  774. // corresponding index in faceId to the highest at the same time
  775. fltx4 compareOne = RotateLeft( mint );
  776. faceMask = CmpGtSIMD(mint, compareOne);
  777. // compareOne is [y,z,G,x]
  778. fltx4 max_xy = MaxSIMD( mint, compareOne );
  779. fltx4 faceRot = RotateLeft(faceId);
  780. fltx4 faceId_xy = MaskedAssign(faceMask, faceId, faceRot);
  781. // max_xy is [max(x,y), ... ]
  782. compareOne = RotateLeft2( mint );
  783. faceRot = RotateLeft2(faceId);
  784. // compareOne is [z, G, x, y]
  785. faceMask = CmpGtSIMD( max_xy, compareOne );
  786. fltx4 max_xyz = MaxSIMD( max_xy, compareOne );
  787. faceId = MaskedAssign( faceMask, faceId_xy, faceRot );
  788. fltx4 lastInTmp = SplatXSIMD( max_xyz );
  789. firstOut = MinSIMD(firstOutTmp, Four_Ones);
  790. lastIn = MaxSIMD(lastInTmp, Four_Zeros);
  791. separation = CmpGtSIMD(lastIn, firstOut);
  792. Assert(IsAllZeros(separation));
  793. if ( IsAllZeros(separation) )
  794. {
  795. uint32 faceIndex = SubInt(faceId, 0);
  796. Assert(faceIndex<6);
  797. float t1 = SubFloat(lastIn,0);
  798. trace_t * RESTRICT pTrace = &pTraceInfo->m_trace;
  799. // this condition is copied from the brush case to avoid hitting an assert and
  800. // overwriting a previous start solid with a new shorter fraction
  801. if ( bStartOut && pTraceInfo->m_ispoint && pTrace->fractionleftsolid > t1 )
  802. {
  803. bStartOut = false;
  804. }
  805. if ( !bStartOut )
  806. {
  807. float t2 = SubFloat(firstOut,0);
  808. pTrace->startsolid = true;
  809. pTrace->contents = pBrush->contents;
  810. if ( t2 >= 1.0f )
  811. {
  812. pTrace->allsolid = true;
  813. pTrace->fraction = 0.0f;
  814. }
  815. else if ( t2 > pTrace->fractionleftsolid )
  816. {
  817. pTrace->fractionleftsolid = t2;
  818. if (pTrace->fraction <= t2)
  819. {
  820. pTrace->fraction = 1.0f;
  821. pTrace->surface = pTraceInfo->m_pBSPData->nullsurface;
  822. }
  823. }
  824. }
  825. else
  826. {
  827. static const int signbits[3]={1,2,4};
  828. if ( t1 < pTrace->fraction )
  829. {
  830. pTraceInfo->m_bDispHit = false;
  831. pTrace->fraction = t1;
  832. pTrace->plane.normal = vec3_origin;
  833. pTrace->surface = *pTraceInfo->m_pBSPData->GetSurfaceAtIndex( pBox->surfaceIndex[faceIndex] );
  834. if ( faceIndex >= 3 )
  835. {
  836. faceIndex -= 3;
  837. pTrace->plane.dist = pBox->maxs[faceIndex];
  838. pTrace->plane.normal[faceIndex] = 1.0f;
  839. pTrace->plane.signbits = 0;
  840. }
  841. else
  842. {
  843. pTrace->plane.dist = -pBox->mins[faceIndex];
  844. pTrace->plane.normal[faceIndex] = -1.0f;
  845. pTrace->plane.signbits = signbits[faceIndex];
  846. }
  847. pTrace->plane.type = faceIndex;
  848. pTrace->contents = pBrush->contents;
  849. return true;
  850. }
  851. }
  852. }
  853. }
  854. return false;
  855. }
  856. // slightly different version of the above. This folds in more of the trace_t output because CM_ComputeTraceEndpts() isn't called after this
  857. // so this routine needs to properly compute start/end points and fractions in all cases
  858. bool IntersectRayWithBox( const Ray_t &ray, const VectorAligned &inInvDelta, const VectorAligned &inBoxMins, const VectorAligned &inBoxMaxs, trace_t *RESTRICT pTrace )
  859. {
  860. // mark trace as not hitting
  861. pTrace->startsolid = false;
  862. pTrace->allsolid = false;
  863. pTrace->fraction = 1.0f;
  864. // Load the unaligned ray/box parameters into SIMD registers
  865. fltx4 start = LoadUnaligned3SIMD(ray.m_Start.Base());
  866. fltx4 extents = LoadUnaligned3SIMD(ray.m_Extents.Base());
  867. fltx4 delta = LoadUnaligned3SIMD(ray.m_Delta.Base());
  868. fltx4 boxMins = LoadAlignedSIMD( inBoxMins.Base() );
  869. fltx4 boxMaxs = LoadAlignedSIMD( inBoxMaxs.Base() );
  870. // compute the mins/maxs of the box expanded by the ray extents
  871. // relocate the problem so that the ray start is at the origin.
  872. fltx4 offsetMins = SubSIMD(boxMins, start);
  873. fltx4 offsetMaxs = SubSIMD(boxMaxs, start);
  874. fltx4 offsetMinsExpanded = SubSIMD(offsetMins, extents);
  875. fltx4 offsetMaxsExpanded = AddSIMD(offsetMaxs, extents);
  876. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  877. // of any of the box sides - if so there can be no intersection
  878. fltx4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  879. fltx4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  880. fltx4 minsMask = AndSIMD( startOutMins, endOutMins );
  881. fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  882. fltx4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  883. fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  884. if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  885. return false;
  886. fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  887. // now build the per-axis interval of t for intersections
  888. fltx4 invDelta = LoadAlignedSIMD(inInvDelta.Base());
  889. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  890. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  891. // now sort the interval per axis
  892. fltx4 mint = MinSIMD( tmins, tmaxs );
  893. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  894. // only axes where we cross a plane are relevant
  895. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  896. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  897. // now find the intersection of the intervals on all axes
  898. fltx4 firstOut = FindLowestSIMD3(maxt);
  899. fltx4 lastIn = FindHighestSIMD3(mint);
  900. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  901. firstOut = MinSIMD(firstOut, Four_Ones);
  902. lastIn = MaxSIMD(lastIn, Four_Zeros);
  903. // If the final interval is valid lastIn<firstOut, check for separation
  904. fltx4 separation = CmpGtSIMD(lastIn, firstOut);
  905. if ( IsAllZeros(separation) )
  906. {
  907. bool bStartOut = IsAnyNegative(SetWToZeroSIMD(OrSIMD(startOutMins,startOutMaxs)));
  908. offsetMinsExpanded = SubSIMD(offsetMinsExpanded, Four_DistEpsilons);
  909. offsetMaxsExpanded = AddSIMD(offsetMaxsExpanded, Four_DistEpsilons);
  910. tmins = MulSIMD( offsetMinsExpanded, invDelta );
  911. tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  912. fltx4 minface0 = LoadAlignedSIMD( (float *) g_CubeFaceIndex0 );
  913. fltx4 minface1 = LoadAlignedSIMD( (float *) g_CubeFaceIndex1 );
  914. fltx4 faceMask = CmpLeSIMD( tmins, tmaxs );
  915. mint = MinSIMD( tmins, tmaxs );
  916. maxt = MaxSIMD( tmins, tmaxs );
  917. fltx4 faceId = MaskedAssign( faceMask, minface0, minface1 );
  918. // only axes where we cross a plane are relevant
  919. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  920. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  921. fltx4 firstOutTmp = FindLowestSIMD3(maxt);
  922. //fltx4 lastInTmp = FindHighestSIMD3(mint);
  923. // implement FindHighest of 3, but use intermediate masks to find the
  924. // corresponding index in faceId to the highest at the same time
  925. fltx4 compareOne = RotateLeft( mint );
  926. faceMask = CmpGtSIMD(mint, compareOne);
  927. // compareOne is [y,z,G,x]
  928. fltx4 max_xy = MaxSIMD( mint, compareOne );
  929. fltx4 faceRot = RotateLeft(faceId);
  930. fltx4 faceId_xy = MaskedAssign(faceMask, faceId, faceRot);
  931. // max_xy is [max(x,y), ... ]
  932. compareOne = RotateLeft2( mint );
  933. faceRot = RotateLeft2(faceId);
  934. // compareOne is [z, G, x, y]
  935. faceMask = CmpGtSIMD( max_xy, compareOne );
  936. fltx4 max_xyz = MaxSIMD( max_xy, compareOne );
  937. faceId = MaskedAssign( faceMask, faceId_xy, faceRot );
  938. fltx4 lastInTmp = SplatXSIMD( max_xyz );
  939. firstOut = MinSIMD(firstOutTmp, Four_Ones);
  940. lastIn = MaxSIMD(lastInTmp, Four_Zeros);
  941. separation = CmpGtSIMD(lastIn, firstOut);
  942. Assert(IsAllZeros(separation));
  943. if ( IsAllZeros(separation) )
  944. {
  945. uint32 faceIndex = SubInt(faceId, 0);
  946. Assert(faceIndex<6);
  947. float t1 = SubFloat(lastIn,0);
  948. // this condition is copied from the brush case to avoid hitting an assert and
  949. // overwriting a previous start solid with a new shorter fraction
  950. if ( bStartOut && ray.m_IsRay && pTrace->fractionleftsolid > t1 )
  951. {
  952. bStartOut = false;
  953. }
  954. if ( !bStartOut )
  955. {
  956. float t2 = SubFloat(firstOut,0);
  957. pTrace->startsolid = true;
  958. pTrace->contents = CONTENTS_SOLID;
  959. pTrace->fraction = 0.0f;
  960. pTrace->startpos = ray.m_Start + ray.m_StartOffset;
  961. pTrace->endpos = pTrace->startpos;
  962. if ( t2 >= 1.0f )
  963. {
  964. pTrace->allsolid = true;
  965. }
  966. else if ( t2 > pTrace->fractionleftsolid )
  967. {
  968. pTrace->fractionleftsolid = t2;
  969. pTrace->startpos += ray.m_Delta * pTrace->fractionleftsolid;
  970. }
  971. return true;
  972. }
  973. else
  974. {
  975. static const int signbits[3]={1,2,4};
  976. if ( t1 <= 1.0f )
  977. {
  978. pTrace->fraction = t1;
  979. pTrace->plane.normal = vec3_origin;
  980. if ( faceIndex >= 3 )
  981. {
  982. faceIndex -= 3;
  983. pTrace->plane.dist = inBoxMaxs[faceIndex];
  984. pTrace->plane.normal[faceIndex] = 1.0f;
  985. pTrace->plane.signbits = 0;
  986. }
  987. else
  988. {
  989. pTrace->plane.dist = -inBoxMins[faceIndex];
  990. pTrace->plane.normal[faceIndex] = -1.0f;
  991. pTrace->plane.signbits = signbits[faceIndex];
  992. }
  993. pTrace->plane.type = faceIndex;
  994. pTrace->contents = CONTENTS_SOLID;
  995. Vector startVec;
  996. VectorAdd( ray.m_Start, ray.m_StartOffset, startVec );
  997. if (pTrace->fraction == 1)
  998. {
  999. VectorAdd( startVec, ray.m_Delta, pTrace->endpos);
  1000. }
  1001. else
  1002. {
  1003. VectorMA( startVec, pTrace->fraction, ray.m_Delta, pTrace->endpos );
  1004. }
  1005. return true;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. return false;
  1011. }
  1012. /*
  1013. ================
  1014. CM_ClipBoxToBrush
  1015. ================
  1016. */
  1017. template <bool IS_POINT>
  1018. void FASTCALL CM_ClipBoxToBrush( TraceInfo_t * RESTRICT pTraceInfo, const cbrush_t * RESTRICT brush )
  1019. {
  1020. if ( brush->IsBox() )
  1021. {
  1022. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[brush->GetBox()];
  1023. IntersectRayWithBoxBrush( pTraceInfo, brush, pBox );
  1024. return;
  1025. }
  1026. if (!brush->numsides)
  1027. return;
  1028. trace_t * RESTRICT trace = &pTraceInfo->m_trace;
  1029. const Vector& p1 = pTraceInfo->m_start;
  1030. const Vector& p2= pTraceInfo->m_end;
  1031. int brushContents = brush->contents;
  1032. #ifdef COUNT_COLLISIONS
  1033. g_CollisionCounts.m_BrushTraces++;
  1034. #endif
  1035. float enterfrac = NEVER_UPDATED;
  1036. float leavefrac = 1.f;
  1037. bool getout = false;
  1038. bool startout = false;
  1039. cbrushside_t* leadside = NULL;
  1040. float dist;
  1041. cbrushside_t * RESTRICT side = &pTraceInfo->m_pBSPData->map_brushsides[brush->firstbrushside];
  1042. for ( const cbrushside_t * const sidelimit = side + brush->numsides; side < sidelimit; side++ )
  1043. {
  1044. cplane_t *plane = side->plane;
  1045. const Vector &planeNormal = plane->normal;
  1046. if (!IS_POINT)
  1047. {
  1048. // general box case
  1049. // push the plane out apropriately for mins/maxs
  1050. dist = DotProductAbs( planeNormal, pTraceInfo->m_extents );
  1051. dist = plane->dist + dist;
  1052. }
  1053. else
  1054. {
  1055. // special point case
  1056. dist = plane->dist;
  1057. // don't trace rays against bevel planes
  1058. if ( side->bBevel )
  1059. continue;
  1060. }
  1061. float d1 = DotProduct (p1, planeNormal) - dist;
  1062. float d2 = DotProduct (p2, planeNormal) - dist;
  1063. // if completely in front of face, no intersection
  1064. if( d1 > 0.f )
  1065. {
  1066. startout = true;
  1067. // d1 > 0.f && d2 > 0.f
  1068. if( d2 > 0.f )
  1069. return;
  1070. }
  1071. else
  1072. {
  1073. // d1 <= 0.f && d2 <= 0.f
  1074. if( d2 <= 0.f )
  1075. continue;
  1076. // d2 > 0.f
  1077. getout = true;
  1078. }
  1079. // crosses face
  1080. if (d1 > d2)
  1081. { // enter
  1082. // NOTE: This could be negative if d1 is less than the epsilon.
  1083. // If the trace is short (d1-d2 is small) then it could produce a large
  1084. // negative fraction.
  1085. float f = (d1-DIST_EPSILON);
  1086. if ( f < 0.f )
  1087. f = 0.f;
  1088. f = f / (d1-d2);
  1089. if (f > enterfrac)
  1090. {
  1091. enterfrac = f;
  1092. leadside = side;
  1093. }
  1094. }
  1095. else
  1096. { // leave
  1097. float f = (d1+DIST_EPSILON) / (d1-d2);
  1098. if (f < leavefrac)
  1099. leavefrac = f;
  1100. }
  1101. }
  1102. // when this happens, we entered the brush *after* leaving the previous brush.
  1103. // Therefore, we're still outside!
  1104. // NOTE: We only do this test against points because fractionleftsolid is
  1105. // not possible to compute for brush sweeps without a *lot* more computation
  1106. // So, client code will never get fractionleftsolid for box sweeps
  1107. if (IS_POINT && startout)
  1108. {
  1109. // Add a little sludge. The sludge should already be in the fractionleftsolid
  1110. // (for all intents and purposes is a leavefrac value) and enterfrac values.
  1111. // Both of these values have +/- DIST_EPSILON values calculated in. Thus, I
  1112. // think the test should be against "0.0." If we experience new "left solid"
  1113. // problems you may want to take a closer look here!
  1114. // if ((trace->fractionleftsolid - enterfrac) > -1e-6)
  1115. if ((trace->fractionleftsolid - enterfrac) > 0.0f )
  1116. startout = false;
  1117. }
  1118. if (!startout)
  1119. { // original point was inside brush
  1120. trace->startsolid = true;
  1121. // return starting contents
  1122. trace->contents = brushContents;
  1123. if (!getout)
  1124. {
  1125. trace->allsolid = true;
  1126. trace->fraction = 0.0f;
  1127. trace->fractionleftsolid = 1.0f;
  1128. }
  1129. else
  1130. {
  1131. // if leavefrac == 1, this means it's never been updated or we're in allsolid
  1132. // the allsolid case was handled above
  1133. if ((leavefrac != 1) && (leavefrac > trace->fractionleftsolid))
  1134. {
  1135. trace->fractionleftsolid = leavefrac;
  1136. // This could occur if a previous trace didn't start us in solid
  1137. if (trace->fraction <= leavefrac)
  1138. {
  1139. trace->fraction = 1.0f;
  1140. trace->surface = pTraceInfo->m_pBSPData->nullsurface;
  1141. }
  1142. }
  1143. }
  1144. return;
  1145. }
  1146. // We haven't hit anything at all until we've left...
  1147. if (enterfrac < leavefrac)
  1148. {
  1149. if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
  1150. {
  1151. // WE HIT SOMETHING!!!!!
  1152. if (enterfrac < 0)
  1153. enterfrac = 0;
  1154. trace->fraction = enterfrac;
  1155. pTraceInfo->m_bDispHit = false;
  1156. trace->plane = *(leadside->plane);
  1157. trace->surface = *pTraceInfo->m_pBSPData->GetSurfaceAtIndex( leadside->surfaceIndex );
  1158. trace->contents = brushContents;
  1159. }
  1160. }
  1161. }
  1162. inline bool IsTraceBoxIntersectingBoxBrush( TraceInfo_t *pTraceInfo, cboxbrush_t *pBox )
  1163. {
  1164. fltx4 start = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  1165. fltx4 mins = LoadUnaligned3SIMD(pTraceInfo->m_mins.Base());
  1166. fltx4 maxs = LoadUnaligned3SIMD(pTraceInfo->m_maxs.Base());
  1167. fltx4 boxMins = LoadAlignedSIMD( pBox->mins.Base() );
  1168. fltx4 boxMaxs = LoadAlignedSIMD( pBox->maxs.Base() );
  1169. fltx4 offsetMins = AddSIMD(mins, start);
  1170. fltx4 offsetMaxs = AddSIMD(maxs,start);
  1171. fltx4 minsOut = MaxSIMD(boxMins, offsetMins);
  1172. fltx4 maxsOut = MinSIMD(boxMaxs, offsetMaxs);
  1173. fltx4 separated = CmpGtSIMD(minsOut, maxsOut);
  1174. fltx4 sep3 = SetWToZeroSIMD(separated);
  1175. return IsAllZeros(sep3);
  1176. }
  1177. /*
  1178. ================
  1179. CM_TestBoxInBrush
  1180. ================
  1181. */
  1182. static void FASTCALL CM_TestBoxInBrush( TraceInfo_t *pTraceInfo, const cbrush_t *brush )
  1183. {
  1184. if ( brush->IsBox())
  1185. {
  1186. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[brush->GetBox()];
  1187. if ( !IsTraceBoxIntersectingBoxBrush( pTraceInfo, pBox ) )
  1188. return;
  1189. }
  1190. else
  1191. {
  1192. if (!brush->numsides)
  1193. return;
  1194. const Vector& mins = pTraceInfo->m_mins;
  1195. const Vector& maxs = pTraceInfo->m_maxs;
  1196. const Vector& p1 = pTraceInfo->m_start;
  1197. int i, j;
  1198. cplane_t *plane;
  1199. float dist;
  1200. Vector ofs(0,0,0);
  1201. float d1;
  1202. cbrushside_t *side;
  1203. for (i=0 ; i<brush->numsides ; i++)
  1204. {
  1205. side = &pTraceInfo->m_pBSPData->map_brushsides[brush->firstbrushside+i];
  1206. plane = side->plane;
  1207. // FIXME: special case for axial
  1208. // general box case
  1209. // push the plane out appropriately for mins/maxs
  1210. // FIXME: use signbits into 8 way lookup for each mins/maxs
  1211. for (j=0 ; j<3 ; j++)
  1212. {
  1213. if (plane->normal[j] < 0)
  1214. ofs[j] = maxs[j];
  1215. else
  1216. ofs[j] = mins[j];
  1217. }
  1218. dist = DotProduct (ofs, plane->normal);
  1219. dist = plane->dist - dist;
  1220. d1 = DotProduct (p1, plane->normal) - dist;
  1221. // if completely in front of face, no intersection
  1222. if (d1 > 0)
  1223. return;
  1224. }
  1225. }
  1226. // inside this brush
  1227. trace_t *trace = &pTraceInfo->m_trace;
  1228. trace->startsolid = trace->allsolid = true;
  1229. trace->fraction = 0;
  1230. trace->fractionleftsolid = 1.0f;
  1231. trace->contents = brush->contents;
  1232. }
  1233. #if defined(_X360)
  1234. #define PREFETCH_ELEMENT(ofs,base) __dcbt(ofs,(void*)base)
  1235. #else
  1236. #define PREFETCH_ELEMENT(a,b)
  1237. #endif
  1238. /*
  1239. ================
  1240. CM_TraceToLeaf
  1241. ================
  1242. */
  1243. template <bool IS_POINT>
  1244. void FASTCALL CM_TraceToLeaf( TraceInfo_t * RESTRICT pTraceInfo, int ndxLeaf, float startFrac, float endFrac )
  1245. {
  1246. VPROF("CM_TraceToLeaf");
  1247. // get the leaf
  1248. cleaf_t * RESTRICT pLeaf = &pTraceInfo->m_pBSPData->map_leafs[ndxLeaf];
  1249. //
  1250. // trace ray/box sweep against all brushes in this leaf
  1251. //
  1252. const int numleafbrushes = pLeaf->numleafbrushes;
  1253. const int lastleafbrush = pLeaf->firstleafbrush + numleafbrushes;
  1254. const CRangeValidatedArray<unsigned short> &map_leafbrushes = pTraceInfo->m_pBSPData->map_leafbrushes;
  1255. CRangeValidatedArray<cbrush_t> & map_brushes = pTraceInfo->m_pBSPData->map_brushes;
  1256. TraceCounter_t * RESTRICT pCounters = pTraceInfo->GetBrushCounters();
  1257. TraceCounter_t count = pTraceInfo->GetCount();
  1258. for( int ndxLeafBrush = pLeaf->firstleafbrush; ndxLeafBrush < lastleafbrush; ndxLeafBrush++ )
  1259. {
  1260. // get the current brush
  1261. int ndxBrush = map_leafbrushes[ndxLeafBrush];
  1262. cbrush_t * RESTRICT pBrush = &map_brushes[ndxBrush];
  1263. // make sure we only check this brush once per trace/stab
  1264. if ( !pTraceInfo->Visit( pBrush, ndxBrush, count, pCounters ) )
  1265. continue;
  1266. const int traceContents = pTraceInfo->m_contents;
  1267. const int releventContents = ( pBrush->contents & traceContents );
  1268. // only collide with objects you are interested in
  1269. if( !releventContents )
  1270. continue;
  1271. // Many traces rely on CONTENTS_OPAQUE always being hit, even if it is nodraw. AI blocklos brushes
  1272. // need this, for instance. CS and Terror visibility checks don't want this behavior, since
  1273. // blocklight brushes also are CONTENTS_OPAQUE and SURF_NODRAW, and are actually in the playable
  1274. // area in several maps.
  1275. // NOTE: This is no longer true - no traces should rely on hitting CONTENTS_OPAQUE unless they
  1276. // actually want to hit blocklight brushes. No other brushes are marked with those bits
  1277. // so it should be renamed CONTENTS_BLOCKLIGHT. CONTENTS_BLOCKLOS has its own field now
  1278. // so there is no reason to ignore nodraw opaques since you can merely remove CONTENTS_OPAQUE to
  1279. // get that behavior
  1280. if ( releventContents == CONTENTS_OPAQUE && (traceContents & CONTENTS_IGNORE_NODRAW_OPAQUE) )
  1281. {
  1282. // if the only reason we hit this brush is because it is opaque, make sure it isn't nodraw
  1283. bool isNoDraw = false;
  1284. if ( pBrush->IsBox())
  1285. {
  1286. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[pBrush->GetBox()];
  1287. for (int i=0 ; i<6 && !isNoDraw ;i++)
  1288. {
  1289. csurface_t *surface = pTraceInfo->m_pBSPData->GetSurfaceAtIndex( pBox->surfaceIndex[i] );
  1290. if ( surface->flags & SURF_NODRAW )
  1291. {
  1292. isNoDraw = true;
  1293. break;
  1294. }
  1295. }
  1296. }
  1297. else
  1298. {
  1299. cbrushside_t *side = &pTraceInfo->m_pBSPData->map_brushsides[pBrush->firstbrushside];
  1300. for (int i=0 ; i<pBrush->numsides && !isNoDraw ;i++, side++)
  1301. {
  1302. csurface_t *surface = pTraceInfo->m_pBSPData->GetSurfaceAtIndex( side->surfaceIndex );
  1303. if ( surface->flags & SURF_NODRAW )
  1304. {
  1305. isNoDraw = true;
  1306. break;
  1307. }
  1308. }
  1309. }
  1310. if ( isNoDraw )
  1311. {
  1312. continue;
  1313. }
  1314. }
  1315. // trace against the brush and find impact point -- if any?
  1316. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  1317. CM_ClipBoxToBrush<IS_POINT>( pTraceInfo, pBrush );
  1318. if( !pTraceInfo->m_trace.fraction )
  1319. return;
  1320. }
  1321. // TODO: this may be redundant
  1322. if( pTraceInfo->m_trace.startsolid )
  1323. return;
  1324. // Collide (test) against displacement surfaces in this leaf.
  1325. if( pLeaf->dispCount )
  1326. {
  1327. VPROF("CM_TraceToLeafDisps");
  1328. //
  1329. // trace ray/swept box against all displacement surfaces in this leaf
  1330. //
  1331. pCounters = pTraceInfo->GetDispCounters();
  1332. count = pTraceInfo->GetCount();
  1333. if (IsX360())
  1334. {
  1335. // set up some relatively constant variables we'll use in the loop below
  1336. fltx4 traceStart = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  1337. fltx4 traceDelta = LoadUnaligned3SIMD(pTraceInfo->m_delta.Base());
  1338. fltx4 traceInvDelta = LoadUnaligned3SIMD(pTraceInfo->m_invDelta.Base());
  1339. static const fltx4 vecEpsilon = {DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON};
  1340. // only used in !IS_POINT version:
  1341. fltx4 extents;
  1342. if (!IS_POINT)
  1343. {
  1344. extents = LoadUnaligned3SIMD(pTraceInfo->m_extents.Base());
  1345. }
  1346. // TODO: this loop probably ought to be unrolled so that we can make a more efficient
  1347. // job of intersecting rays against boxes. The simple SIMD version used here,
  1348. // though about 6x faster than the fpu version, is slower still than intersecting
  1349. // against four boxes simultaneously.
  1350. for( int i = 0; i < pLeaf->dispCount; i++ )
  1351. {
  1352. int dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart + i];
  1353. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  1354. // only collide with objects you are interested in
  1355. if( !( pDispBounds->GetContents() & pTraceInfo->m_contents ) )
  1356. continue;
  1357. if( pTraceInfo->m_isswept )
  1358. {
  1359. // make sure we only check this brush once per trace/stab
  1360. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  1361. continue;
  1362. }
  1363. if ( IS_POINT )
  1364. {
  1365. if (!IsBoxIntersectingRay( LoadAlignedSIMD(pDispBounds->mins.Base()), LoadAlignedSIMD(pDispBounds->maxs.Base()),
  1366. traceStart, traceDelta, traceInvDelta, vecEpsilon ))
  1367. continue;
  1368. }
  1369. else
  1370. {
  1371. fltx4 mins = SubSIMD(LoadAlignedSIMD(pDispBounds->mins.Base()),extents);
  1372. fltx4 maxs = AddSIMD(LoadAlignedSIMD(pDispBounds->maxs.Base()),extents);
  1373. if (!IsBoxIntersectingRay( mins, maxs,
  1374. traceStart, traceDelta, traceInvDelta, vecEpsilon ))
  1375. continue;
  1376. }
  1377. CDispCollTree * RESTRICT pDispTree = &g_pDispCollTrees[dispIndex];
  1378. CM_TraceToDispTree<IS_POINT>( pTraceInfo, pDispTree, startFrac, endFrac );
  1379. if( !pTraceInfo->m_trace.fraction )
  1380. break;
  1381. }
  1382. }
  1383. else
  1384. {
  1385. // utterly nonoptimal FPU pathway
  1386. for( int i = 0; i < pLeaf->dispCount; i++ )
  1387. {
  1388. int dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart + i];
  1389. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  1390. // only collide with objects you are interested in
  1391. if( !( pDispBounds->GetContents() & pTraceInfo->m_contents ) )
  1392. continue;
  1393. if( pTraceInfo->m_isswept )
  1394. {
  1395. // make sure we only check this brush once per trace/stab
  1396. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  1397. continue;
  1398. }
  1399. if ( IS_POINT && !IsBoxIntersectingRay( pDispBounds->mins, pDispBounds->maxs, pTraceInfo->m_start, pTraceInfo->m_delta, pTraceInfo->m_invDelta, DISPCOLL_DIST_EPSILON ) )
  1400. {
  1401. continue;
  1402. }
  1403. if ( !IS_POINT && !IsBoxIntersectingRay( pDispBounds->mins - pTraceInfo->m_extents, pDispBounds->maxs + pTraceInfo->m_extents,
  1404. pTraceInfo->m_start, pTraceInfo->m_delta, pTraceInfo->m_invDelta, DISPCOLL_DIST_EPSILON ) )
  1405. {
  1406. continue;
  1407. }
  1408. CDispCollTree * RESTRICT pDispTree = &g_pDispCollTrees[dispIndex];
  1409. CM_TraceToDispTree<IS_POINT>( pTraceInfo, pDispTree, startFrac, endFrac );
  1410. if( !pTraceInfo->m_trace.fraction )
  1411. break;
  1412. }
  1413. }
  1414. CM_PostTraceToDispTree( pTraceInfo );
  1415. }
  1416. }
  1417. /*
  1418. ================
  1419. CM_TestInLeaf
  1420. ================
  1421. */
  1422. static void FASTCALL CM_TestInLeaf( TraceInfo_t *pTraceInfo, int ndxLeaf )
  1423. {
  1424. // get the leaf
  1425. cleaf_t *pLeaf = &pTraceInfo->m_pBSPData->map_leafs[ndxLeaf];
  1426. //
  1427. // trace ray/box sweep against all brushes in this leaf
  1428. //
  1429. TraceCounter_t *pCounters = pTraceInfo->GetBrushCounters();
  1430. TraceCounter_t count = pTraceInfo->GetCount();
  1431. for( int ndxLeafBrush = 0; ndxLeafBrush < pLeaf->numleafbrushes; ndxLeafBrush++ )
  1432. {
  1433. // get the current brush
  1434. int ndxBrush = pTraceInfo->m_pBSPData->map_leafbrushes[pLeaf->firstleafbrush+ndxLeafBrush];
  1435. cbrush_t *pBrush = &pTraceInfo->m_pBSPData->map_brushes[ndxBrush];
  1436. // make sure we only check this brush once per trace/stab
  1437. if ( !pTraceInfo->Visit( pBrush, ndxBrush, count, pCounters ) )
  1438. continue;
  1439. // only collide with objects you are interested in
  1440. if( !( pBrush->contents & pTraceInfo->m_contents ) )
  1441. continue;
  1442. //
  1443. // test to see if the point/box is inside of any solid
  1444. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  1445. //
  1446. CM_TestBoxInBrush( pTraceInfo, pBrush );
  1447. if( !pTraceInfo->m_trace.fraction )
  1448. return;
  1449. }
  1450. // TODO: this may be redundant
  1451. if( pTraceInfo->m_trace.startsolid )
  1452. return;
  1453. // if there are no displacement surfaces in this leaf -- we are done testing
  1454. if( pLeaf->dispCount )
  1455. {
  1456. // test to see if the point/box is inside of any of the displacement surface
  1457. CM_TestInDispTree( pTraceInfo, pLeaf, pTraceInfo->m_start, pTraceInfo->m_mins, pTraceInfo->m_maxs, pTraceInfo->m_contents, &pTraceInfo->m_trace );
  1458. }
  1459. }
  1460. //-----------------------------------------------------------------------------
  1461. // Computes the ray endpoints given a trace.
  1462. //-----------------------------------------------------------------------------
  1463. static inline void CM_ComputeTraceEndpoints( const Ray_t& ray, trace_t& tr )
  1464. {
  1465. // The ray start is the center of the extents; compute the actual start
  1466. Vector start;
  1467. VectorAdd( ray.m_Start, ray.m_StartOffset, start );
  1468. if (tr.fraction == 1)
  1469. VectorAdd(start, ray.m_Delta, tr.endpos);
  1470. else
  1471. VectorMA( start, tr.fraction, ray.m_Delta, tr.endpos );
  1472. if (tr.fractionleftsolid == 0)
  1473. {
  1474. VectorCopy (start, tr.startpos);
  1475. }
  1476. else
  1477. {
  1478. if (tr.fractionleftsolid == 1.0f)
  1479. {
  1480. tr.startsolid = tr.allsolid = 1;
  1481. tr.fraction = 0.0f;
  1482. VectorCopy( start, tr.endpos );
  1483. }
  1484. VectorMA( start, tr.fractionleftsolid, ray.m_Delta, tr.startpos );
  1485. }
  1486. }
  1487. //-----------------------------------------------------------------------------
  1488. // Purpose: Get a list of leaves for a trace.
  1489. //-----------------------------------------------------------------------------
  1490. void CM_RayLeafnums_r( const Ray_t &ray, CCollisionBSPData *pBSPData, int iNode,
  1491. float p1f, float p2f, const Vector &vecPoint1, const Vector &vecPoint2,
  1492. int *pLeafList, int nMaxLeafCount, int &nLeafCount )
  1493. {
  1494. cnode_t *pNode = NULL;
  1495. cplane_t *pPlane = NULL;
  1496. float flDist1 = 0.0f, flDist2 = 0.0f;
  1497. float flOffset = 0.0f;
  1498. float flDist;
  1499. float flFrac1, flFrac2;
  1500. int nSide;
  1501. float flMid;
  1502. Vector vecMid;
  1503. // A quick check so we don't flood the message on overflow - or keep testing beyond our means!
  1504. if ( nLeafCount >= nMaxLeafCount )
  1505. return;
  1506. // Find the point distances to the seperating plane and the offset for the size of the box.
  1507. // NJS: Hoisted loop invariant comparison to pTraceInfo->m_ispoint
  1508. if( ray.m_IsRay )
  1509. {
  1510. while( iNode >= 0 )
  1511. {
  1512. pNode = pBSPData->map_rootnode + iNode;
  1513. pPlane = pNode->plane;
  1514. if ( pPlane->type < 3 )
  1515. {
  1516. flDist1 = vecPoint1[pPlane->type] - pPlane->dist;
  1517. flDist2 = vecPoint2[pPlane->type] - pPlane->dist;
  1518. flOffset = ray.m_Extents[pPlane->type];
  1519. }
  1520. else
  1521. {
  1522. flDist1 = DotProduct( pPlane->normal, vecPoint1 ) - pPlane->dist;
  1523. flDist2 = DotProduct( pPlane->normal, vecPoint2 ) - pPlane->dist;
  1524. flOffset = 0.0f;
  1525. }
  1526. // See which sides we need to consider
  1527. if ( flDist1 > flOffset && flDist2 > flOffset )
  1528. {
  1529. iNode = pNode->children[0];
  1530. continue;
  1531. }
  1532. if ( flDist1 < -flOffset && flDist2 < -flOffset )
  1533. {
  1534. iNode = pNode->children[1];
  1535. continue;
  1536. }
  1537. break;
  1538. }
  1539. }
  1540. else
  1541. {
  1542. while( iNode >= 0 )
  1543. {
  1544. pNode = pBSPData->map_rootnode + iNode;
  1545. pPlane = pNode->plane;
  1546. if ( pPlane->type < 3 )
  1547. {
  1548. flDist1 = vecPoint1[pPlane->type] - pPlane->dist;
  1549. flDist2 = vecPoint2[pPlane->type] - pPlane->dist;
  1550. flOffset = ray.m_Extents[pPlane->type];
  1551. }
  1552. else
  1553. {
  1554. flDist1 = DotProduct( pPlane->normal, vecPoint1 ) - pPlane->dist;
  1555. flDist2 = DotProduct( pPlane->normal, vecPoint2 ) - pPlane->dist;
  1556. flOffset = fabs( ray.m_Extents[0] * pPlane->normal[0] ) +
  1557. fabs( ray.m_Extents[1] * pPlane->normal[1] ) +
  1558. fabs( ray.m_Extents[2] * pPlane->normal[2] );
  1559. }
  1560. // See which sides we need to consider
  1561. if ( flDist1 > flOffset && flDist2 > flOffset )
  1562. {
  1563. iNode = pNode->children[0];
  1564. continue;
  1565. }
  1566. if ( flDist1 < -flOffset && flDist2 < -flOffset )
  1567. {
  1568. iNode = pNode->children[1];
  1569. continue;
  1570. }
  1571. break;
  1572. }
  1573. }
  1574. // If < 0, we are in a leaf node.
  1575. if ( iNode < 0 )
  1576. {
  1577. if ( nLeafCount < nMaxLeafCount )
  1578. {
  1579. pLeafList[nLeafCount] = -1 - iNode;
  1580. nLeafCount++;
  1581. }
  1582. else
  1583. {
  1584. DevMsg( 1, "CM_RayLeafnums_r: Max leaf count along ray exceeded!\n" );
  1585. }
  1586. return;
  1587. }
  1588. // Put the crosspoint DIST_EPSILON pixels on the near side.
  1589. if ( flDist1 < flDist2 )
  1590. {
  1591. flDist = 1.0 / ( flDist1 - flDist2 );
  1592. nSide = 1;
  1593. flFrac2 = ( flDist1 + flOffset + DIST_EPSILON ) * flDist;
  1594. flFrac1 = ( flDist1 - flOffset - DIST_EPSILON ) * flDist;
  1595. }
  1596. else if ( flDist1 > flDist2 )
  1597. {
  1598. flDist = 1.0 / ( flDist1-flDist2 );
  1599. nSide = 0;
  1600. flFrac2 = ( flDist1 - flOffset - DIST_EPSILON ) * flDist;
  1601. flFrac1 = ( flDist1 + flOffset + DIST_EPSILON ) * flDist;
  1602. }
  1603. else
  1604. {
  1605. nSide = 0;
  1606. flFrac1 = 1.0f;
  1607. flFrac2 = 0.0f;
  1608. }
  1609. // Move up to the node
  1610. flFrac1 = clamp( flFrac1, 0.0f, 1.0f );
  1611. flMid = p1f + ( p2f - p1f ) * flFrac1;
  1612. VectorLerp( vecPoint1, vecPoint2, flFrac1, vecMid );
  1613. CM_RayLeafnums_r( ray, pBSPData, pNode->children[nSide], p1f, flMid, vecPoint1, vecMid, pLeafList, nMaxLeafCount, nLeafCount );
  1614. // Go past the node
  1615. flFrac2 = clamp( flFrac2, 0.0f, 1.0f );
  1616. flMid = p1f + ( p2f - p1f ) * flFrac2;
  1617. VectorLerp( vecPoint1, vecPoint2, flFrac2, vecMid );
  1618. CM_RayLeafnums_r( ray, pBSPData, pNode->children[nSide^1], flMid, p2f, vecMid, vecPoint2, pLeafList, nMaxLeafCount, nLeafCount );
  1619. }
  1620. //-----------------------------------------------------------------------------
  1621. // Purpose:
  1622. //-----------------------------------------------------------------------------
  1623. void CM_RayLeafnums( const Ray_t &ray, int *pLeafList, int nMaxLeafCount, int &nLeafCount )
  1624. {
  1625. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  1626. if ( !pBSPData->numnodes )
  1627. return;
  1628. Vector vecEnd;
  1629. VectorAdd( ray.m_Start, ray.m_Delta, vecEnd );
  1630. CM_RayLeafnums_r( ray, pBSPData, 0/*headnode*/, 0.0f, 1.0f, ray.m_Start, vecEnd, pLeafList, nMaxLeafCount, nLeafCount );
  1631. }
  1632. /*
  1633. ==================
  1634. CM_RecursiveHullCheck
  1635. ==================
  1636. Attempt to do whatever is nessecary to get this function to unroll at least once
  1637. */
  1638. template <bool IS_POINT>
  1639. static void FASTCALL CM_RecursiveHullCheckImpl( TraceInfo_t *pTraceInfo, int num, const float p1f, const float p2f, const Vector& p1, const Vector& p2)
  1640. {
  1641. if (pTraceInfo->m_trace.fraction <= p1f)
  1642. return; // already hit something nearer
  1643. cnode_t *node = NULL;
  1644. cplane_t *plane;
  1645. float t1 = 0, t2 = 0, offset = 0;
  1646. float frac, frac2;
  1647. float idist;
  1648. Vector mid;
  1649. int side;
  1650. float midf;
  1651. // find the point distances to the seperating plane
  1652. // and the offset for the size of the box
  1653. while( num >= 0 )
  1654. {
  1655. node = pTraceInfo->m_pBSPData->map_rootnode + num;
  1656. plane = node->plane;
  1657. byte type = plane->type;
  1658. float dist = plane->dist;
  1659. if (type < 3)
  1660. {
  1661. t1 = p1[type] - dist;
  1662. t2 = p2[type] - dist;
  1663. offset = pTraceInfo->m_extents[type];
  1664. }
  1665. else
  1666. {
  1667. t1 = DotProduct (plane->normal, p1) - dist;
  1668. t2 = DotProduct (plane->normal, p2) - dist;
  1669. if( IS_POINT )
  1670. {
  1671. offset = 0;
  1672. }
  1673. else
  1674. {
  1675. offset = fabsf(pTraceInfo->m_extents[0]*plane->normal[0]) +
  1676. fabsf(pTraceInfo->m_extents[1]*plane->normal[1]) +
  1677. fabsf(pTraceInfo->m_extents[2]*plane->normal[2]);
  1678. }
  1679. }
  1680. // see which sides we need to consider
  1681. if (t1 > offset && t2 > offset )
  1682. // if (t1 >= offset && t2 >= offset)
  1683. {
  1684. num = node->children[0];
  1685. continue;
  1686. }
  1687. if (t1 < -offset && t2 < -offset)
  1688. {
  1689. num = node->children[1];
  1690. continue;
  1691. }
  1692. break;
  1693. }
  1694. // if < 0, we are in a leaf node
  1695. if (num < 0)
  1696. {
  1697. CM_TraceToLeaf<IS_POINT>(pTraceInfo, -1-num, p1f, p2f);
  1698. return;
  1699. }
  1700. // put the crosspoint DIST_EPSILON pixels on the near side
  1701. if (t1 < t2)
  1702. {
  1703. idist = 1.0/(t1-t2);
  1704. side = 1;
  1705. frac2 = (t1 + offset + DIST_EPSILON)*idist;
  1706. frac = (t1 - offset - DIST_EPSILON)*idist;
  1707. }
  1708. else if (t1 > t2)
  1709. {
  1710. idist = 1.0/(t1-t2);
  1711. side = 0;
  1712. frac2 = (t1 - offset - DIST_EPSILON)*idist;
  1713. frac = (t1 + offset + DIST_EPSILON)*idist;
  1714. }
  1715. else
  1716. {
  1717. side = 0;
  1718. frac = 1;
  1719. frac2 = 0;
  1720. }
  1721. // move up to the node
  1722. frac = clamp( frac, 0.f, 1.f );
  1723. midf = p1f + (p2f - p1f)*frac;
  1724. VectorLerp( p1, p2, frac, mid );
  1725. CM_RecursiveHullCheckImpl<IS_POINT>(pTraceInfo, node->children[side], p1f, midf, p1, mid);
  1726. // go past the node
  1727. frac2 = clamp( frac2, 0.f, 1.f );
  1728. midf = p1f + (p2f - p1f)*frac2;
  1729. VectorLerp( p1, p2, frac2, mid );
  1730. CM_RecursiveHullCheckImpl<IS_POINT>(pTraceInfo, node->children[side^1], midf, p2f, mid, p2);
  1731. }
  1732. void FASTCALL CM_RecursiveHullCheck ( TraceInfo_t *pTraceInfo, int num, const float p1f, const float p2f )
  1733. {
  1734. const Vector& p1 = pTraceInfo->m_start;
  1735. const Vector& p2 = pTraceInfo->m_end;
  1736. if( pTraceInfo->m_ispoint )
  1737. {
  1738. CM_RecursiveHullCheckImpl<true>( pTraceInfo, num, p1f, p2f, p1, p2);
  1739. }
  1740. else
  1741. {
  1742. CM_RecursiveHullCheckImpl<false>( pTraceInfo, num, p1f, p2f, p1, p2);
  1743. }
  1744. }
  1745. void CM_ClearTrace( trace_t *trace )
  1746. {
  1747. memset( trace, 0, sizeof(*trace));
  1748. trace->fraction = 1.f;
  1749. trace->fractionleftsolid = 0;
  1750. trace->surface = CCollisionBSPData::nullsurface;
  1751. }
  1752. //-----------------------------------------------------------------------------
  1753. //
  1754. // The following versions use ray... gradually I'm gonna remove other versions
  1755. //
  1756. //-----------------------------------------------------------------------------
  1757. //-----------------------------------------------------------------------------
  1758. // Test an unswept box
  1759. //-----------------------------------------------------------------------------
  1760. static inline void CM_UnsweptBoxTrace( TraceInfo_t *pTraceInfo, const Ray_t& ray, int headnode, int brushmask )
  1761. {
  1762. int leafs[1024];
  1763. int i, numleafs;
  1764. leafnums_t context;
  1765. context.pLeafList = leafs;
  1766. context.leafTopNode = -1;
  1767. context.leafMaxCount = ARRAYSIZE(leafs);
  1768. context.pBSPData = pTraceInfo->m_pBSPData;
  1769. bool bFoundNonSolidLeaf = false;
  1770. numleafs = CM_BoxLeafnums ( context, ray.m_Start, ray.m_Extents+Vector(1,1,1), headnode);
  1771. for (i=0 ; i<numleafs ; i++)
  1772. {
  1773. if ((pTraceInfo->m_pBSPData->map_leafs[leafs[i]].contents & CONTENTS_SOLID) == 0)
  1774. {
  1775. bFoundNonSolidLeaf = true;
  1776. }
  1777. CM_TestInLeaf ( pTraceInfo, leafs[i] );
  1778. if (pTraceInfo->m_trace.allsolid)
  1779. break;
  1780. }
  1781. if (!bFoundNonSolidLeaf)
  1782. {
  1783. pTraceInfo->m_trace.allsolid = pTraceInfo->m_trace.startsolid = 1;
  1784. pTraceInfo->m_trace.fraction = 0.0f;
  1785. pTraceInfo->m_trace.fractionleftsolid = 1.0f;
  1786. }
  1787. }
  1788. //-----------------------------------------------------------------------------
  1789. // Purpose: Ray/Hull trace against the world without the RecursiveHullTrace
  1790. //-----------------------------------------------------------------------------
  1791. void CM_BoxTraceAgainstLeafList( const Ray_t &ray, int *pLeafList, int nLeafCount, int nBrushMask,
  1792. bool bComputeEndpoint, trace_t &trace )
  1793. {
  1794. // For multi-check avoidance.
  1795. TraceInfo_t *pTraceInfo = BeginTrace();
  1796. // Setup trace data.
  1797. CM_ClearTrace( &pTraceInfo->m_trace );
  1798. // Get the collision bsp tree.
  1799. pTraceInfo->m_pBSPData = GetCollisionBSPData();
  1800. // Check if the map is loaded.
  1801. if ( !pTraceInfo->m_pBSPData->numnodes )
  1802. {
  1803. trace = pTraceInfo->m_trace;
  1804. EndTrace( pTraceInfo );
  1805. return;
  1806. }
  1807. // Setup global trace data. (This is nasty! I hate this.)
  1808. pTraceInfo->m_bDispHit = false;
  1809. pTraceInfo->m_DispStabDir.Init();
  1810. pTraceInfo->m_contents = nBrushMask;
  1811. VectorCopy( ray.m_Start, pTraceInfo->m_start );
  1812. VectorAdd( ray.m_Start, ray.m_Delta, pTraceInfo->m_end );
  1813. VectorMultiply( ray.m_Extents, -1.0f, pTraceInfo->m_mins );
  1814. VectorCopy( ray.m_Extents, pTraceInfo->m_maxs );
  1815. VectorCopy( ray.m_Extents, pTraceInfo->m_extents );
  1816. pTraceInfo->m_delta = ray.m_Delta;
  1817. pTraceInfo->m_invDelta = ray.InvDelta();
  1818. pTraceInfo->m_ispoint = ray.m_IsRay;
  1819. pTraceInfo->m_isswept = ray.m_IsSwept;
  1820. if ( !ray.m_IsSwept )
  1821. {
  1822. Vector vecBoxMin( ( ray.m_Start.x - ray.m_Extents.x - 1 ), ( ray.m_Start.y - ray.m_Extents.y - 1 ), ( ray.m_Start.z - ray.m_Extents.z - 1 ) );
  1823. Vector vecBoxMax( ( ray.m_Start.x + ray.m_Extents.x + 1 ), ( ray.m_Start.y + ray.m_Extents.y + 1 ), ( ray.m_Start.z + ray.m_Extents.z + 1 ) );
  1824. bool bFoundNonSolidLeaf = false;
  1825. for ( int iLeaf = 0; iLeaf < nLeafCount; ++iLeaf )
  1826. {
  1827. if ( ( pTraceInfo->m_pBSPData->map_leafs[pLeafList[iLeaf]].contents & CONTENTS_SOLID ) == 0 )
  1828. {
  1829. bFoundNonSolidLeaf = true;
  1830. }
  1831. CM_TestInLeaf( pTraceInfo, pLeafList[iLeaf] );
  1832. if ( pTraceInfo->m_trace.allsolid )
  1833. break;
  1834. }
  1835. if ( !bFoundNonSolidLeaf )
  1836. {
  1837. pTraceInfo->m_trace.allsolid = pTraceInfo->m_trace.startsolid = 1;
  1838. pTraceInfo->m_trace.fraction = 0.0f;
  1839. pTraceInfo->m_trace.fractionleftsolid = 1.0f;
  1840. }
  1841. }
  1842. else
  1843. {
  1844. for ( int iLeaf = 0; iLeaf < nLeafCount; ++iLeaf )
  1845. {
  1846. // NOTE: startFrac and endFrac are not really used.
  1847. if ( pTraceInfo->m_ispoint )
  1848. CM_TraceToLeaf<true>( pTraceInfo, pLeafList[iLeaf], 1.0f, 1.0f );
  1849. else
  1850. CM_TraceToLeaf<false>( pTraceInfo, pLeafList[iLeaf], 1.0f, 1.0f );
  1851. }
  1852. }
  1853. // Compute the trace start and end points.
  1854. if ( bComputeEndpoint )
  1855. {
  1856. CM_ComputeTraceEndpoints( ray, pTraceInfo->m_trace );
  1857. }
  1858. // Copy off the results
  1859. trace = pTraceInfo->m_trace;
  1860. EndTrace( pTraceInfo );
  1861. Assert( !ray.m_IsRay || trace.allsolid || ( trace.fraction >= trace.fractionleftsolid ) );
  1862. }
  1863. void CM_BoxTrace( const Ray_t& ray, int headnode, int brushmask, bool computeEndpt, trace_t& tr )
  1864. {
  1865. VPROF("BoxTrace");
  1866. // for multi-check avoidance
  1867. TraceInfo_t *pTraceInfo = BeginTrace();
  1868. #ifdef COUNT_COLLISIONS
  1869. // for statistics, may be zeroed
  1870. g_CollisionCounts.m_Traces++;
  1871. #endif
  1872. // fill in a default trace
  1873. CM_ClearTrace( &pTraceInfo->m_trace );
  1874. pTraceInfo->m_pBSPData = GetCollisionBSPData();
  1875. // check if the map is not loaded
  1876. if (!pTraceInfo->m_pBSPData->numnodes)
  1877. {
  1878. tr = pTraceInfo->m_trace;
  1879. EndTrace( pTraceInfo );
  1880. return;
  1881. }
  1882. pTraceInfo->m_bDispHit = false;
  1883. pTraceInfo->m_DispStabDir.Init();
  1884. pTraceInfo->m_contents = brushmask;
  1885. VectorCopy (ray.m_Start, pTraceInfo->m_start);
  1886. VectorAdd (ray.m_Start, ray.m_Delta, pTraceInfo->m_end);
  1887. VectorMultiply (ray.m_Extents, -1.0f, pTraceInfo->m_mins);
  1888. VectorCopy (ray.m_Extents, pTraceInfo->m_maxs);
  1889. VectorCopy (ray.m_Extents, pTraceInfo->m_extents);
  1890. pTraceInfo->m_delta = ray.m_Delta;
  1891. pTraceInfo->m_invDelta = ray.InvDelta();
  1892. pTraceInfo->m_ispoint = ray.m_IsRay;
  1893. pTraceInfo->m_isswept = ray.m_IsSwept;
  1894. if (!ray.m_IsSwept)
  1895. {
  1896. // check for position test special case
  1897. CM_UnsweptBoxTrace( pTraceInfo, ray, headnode, brushmask );
  1898. }
  1899. else
  1900. {
  1901. // general sweeping through world
  1902. CM_RecursiveHullCheck( pTraceInfo, headnode, 0, 1 );
  1903. }
  1904. // Compute the trace start + end points
  1905. if (computeEndpt)
  1906. {
  1907. CM_ComputeTraceEndpoints( ray, pTraceInfo->m_trace );
  1908. }
  1909. // Copy off the results
  1910. tr = pTraceInfo->m_trace;
  1911. EndTrace( pTraceInfo );
  1912. Assert( !ray.m_IsRay || tr.allsolid || (tr.fraction >= tr.fractionleftsolid) );
  1913. }
  1914. void CM_TransformedBoxTrace( const Ray_t& ray, int headnode, int brushmask,
  1915. const Vector& origin, QAngle const& angles, trace_t& tr )
  1916. {
  1917. matrix3x4_t localToWorld;
  1918. Ray_t ray_l;
  1919. // subtract origin offset
  1920. VectorCopy( ray.m_StartOffset, ray_l.m_StartOffset );
  1921. VectorCopy( ray.m_Extents, ray_l.m_Extents );
  1922. // Are we rotated?
  1923. bool rotated = (angles[0] || angles[1] || angles[2]);
  1924. // rotate start and end into the models frame of reference
  1925. if (rotated)
  1926. {
  1927. // NOTE: In this case, the bbox is rotated into the space of the BSP as well
  1928. // to insure consistency at all orientations, we must rotate the origin of the ray
  1929. // and reapply the offset to the center of the box. That way all traces with the
  1930. // same box centering will have the same transformation into local space
  1931. Vector worldOrigin = ray.m_Start + ray.m_StartOffset;
  1932. AngleMatrix( angles, origin, localToWorld );
  1933. VectorIRotate( ray.m_Delta, localToWorld, ray_l.m_Delta );
  1934. VectorITransform( worldOrigin, localToWorld, ray_l.m_Start );
  1935. ray_l.m_Start -= ray.m_StartOffset;
  1936. }
  1937. else
  1938. {
  1939. VectorSubtract( ray.m_Start, origin, ray_l.m_Start );
  1940. VectorCopy( ray.m_Delta, ray_l.m_Delta );
  1941. }
  1942. ray_l.m_IsRay = ray.m_IsRay;
  1943. ray_l.m_IsSwept = ray.m_IsSwept;
  1944. // sweep the box through the model, don't compute endpoints
  1945. CM_BoxTrace( ray_l, headnode, brushmask, false, tr );
  1946. // If we hit, gotta fix up the normal...
  1947. if (( tr.fraction != 1 ) && rotated )
  1948. {
  1949. // transform the normal from the local space of this entity to world space
  1950. Vector temp;
  1951. VectorCopy (tr.plane.normal, temp);
  1952. VectorRotate( temp, localToWorld, tr.plane.normal );
  1953. }
  1954. CM_ComputeTraceEndpoints( ray, tr );
  1955. }
  1956. /*
  1957. ===============================================================================
  1958. PVS / PAS
  1959. ===============================================================================
  1960. */
  1961. //-----------------------------------------------------------------------------
  1962. // Purpose:
  1963. // Input : *pBSPData -
  1964. // *out -
  1965. //-----------------------------------------------------------------------------
  1966. void CM_NullVis( CCollisionBSPData *pBSPData, byte *out )
  1967. {
  1968. int numClusterBytes = (pBSPData->numclusters+7)>>3;
  1969. byte *out_p = out;
  1970. while (numClusterBytes)
  1971. {
  1972. *out_p++ = 0xff;
  1973. numClusterBytes--;
  1974. }
  1975. }
  1976. /*
  1977. ===================
  1978. CM_DecompressVis
  1979. ===================
  1980. */
  1981. void CM_DecompressVis( CCollisionBSPData *pBSPData, int cluster, int visType, byte *out )
  1982. {
  1983. int c;
  1984. byte *out_p;
  1985. int numClusterBytes;
  1986. if ( !pBSPData )
  1987. {
  1988. Assert( false ); // Shouldn't ever happen.
  1989. }
  1990. if ( cluster > pBSPData->numclusters || cluster < 0 )
  1991. {
  1992. // This can happen if this is called while the level is loading. See Map_VisCurrentCluster.
  1993. CM_NullVis( pBSPData, out );
  1994. return;
  1995. }
  1996. // no vis info, so make all visible
  1997. if ( !pBSPData->numvisibility || !pBSPData->map_vis )
  1998. {
  1999. CM_NullVis( pBSPData, out );
  2000. return;
  2001. }
  2002. byte *in = ((byte *)pBSPData->map_vis) + pBSPData->map_vis->bitofs[cluster][visType];
  2003. numClusterBytes = (pBSPData->numclusters+7)>>3;
  2004. out_p = out;
  2005. // no vis info, so make all visible
  2006. if ( !in )
  2007. {
  2008. CM_NullVis( pBSPData, out );
  2009. return;
  2010. }
  2011. do
  2012. {
  2013. if (*in)
  2014. {
  2015. *out_p++ = *in++;
  2016. continue;
  2017. }
  2018. c = in[1];
  2019. in += 2;
  2020. if ((out_p - out) + c > numClusterBytes)
  2021. {
  2022. c = numClusterBytes - (out_p - out);
  2023. ConMsg( "warning: Vis decompression overrun\n" );
  2024. }
  2025. while (c)
  2026. {
  2027. *out_p++ = 0;
  2028. c--;
  2029. }
  2030. } while (out_p - out < numClusterBytes);
  2031. }
  2032. //-----------------------------------------------------------------------------
  2033. // Purpose: Decompress the RLE bitstring for PVS or PAS of one cluster
  2034. // Input : *dest - buffer to store the decompressed data
  2035. // cluster - index of cluster of interest
  2036. // visType - DVIS_PAS or DVIS_PAS
  2037. // Output : byte * - pointer to the filled buffer
  2038. //-----------------------------------------------------------------------------
  2039. const byte *CM_Vis( byte *dest, int destlen, int cluster, int visType )
  2040. {
  2041. // get the current collision bsp -- there is only one!
  2042. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2043. if ( !dest || visType > 2 || visType < 0 )
  2044. {
  2045. Sys_Error( "CM_Vis: error");
  2046. return NULL;
  2047. }
  2048. if ( cluster == -1 )
  2049. {
  2050. int len = (pBSPData->numclusters+7)>>3;
  2051. if ( len > destlen )
  2052. {
  2053. Sys_Error( "CM_Vis: buffer not big enough (%i but need %i)\n",
  2054. destlen, len );
  2055. }
  2056. memset( dest, 0, (pBSPData->numclusters+7)>>3 );
  2057. }
  2058. else
  2059. {
  2060. CM_DecompressVis( pBSPData, cluster, visType, dest );
  2061. }
  2062. return dest;
  2063. }
  2064. static byte pvsrow[MAX_MAP_LEAFS/8];
  2065. int CM_ClusterPVSSize()
  2066. {
  2067. return sizeof( pvsrow );
  2068. }
  2069. const byte *CM_ClusterPVS (int cluster)
  2070. {
  2071. return CM_Vis( pvsrow, CM_ClusterPVSSize(), cluster, DVIS_PVS );
  2072. }
  2073. /*
  2074. ===============================================================================
  2075. AREAPORTALS
  2076. ===============================================================================
  2077. */
  2078. void FloodArea_r (CCollisionBSPData *pBSPData, carea_t *area, int floodnum)
  2079. {
  2080. int i;
  2081. dareaportal_t *p;
  2082. if (area->floodvalid == pBSPData->floodvalid)
  2083. {
  2084. if (area->floodnum == floodnum)
  2085. return;
  2086. Sys_Error( "FloodArea_r: reflooded");
  2087. }
  2088. area->floodnum = floodnum;
  2089. area->floodvalid = pBSPData->floodvalid;
  2090. p = &pBSPData->map_areaportals[area->firstareaportal];
  2091. for (i=0 ; i<area->numareaportals ; i++, p++)
  2092. {
  2093. if (pBSPData->portalopen[p->m_PortalKey])
  2094. {
  2095. FloodArea_r (pBSPData, &pBSPData->map_areas[p->otherarea], floodnum);
  2096. }
  2097. }
  2098. }
  2099. /*
  2100. ====================
  2101. FloodAreaConnections
  2102. ====================
  2103. */
  2104. void FloodAreaConnections ( CCollisionBSPData *pBSPData )
  2105. {
  2106. int i;
  2107. carea_t *area;
  2108. int floodnum;
  2109. // all current floods are now invalid
  2110. pBSPData->floodvalid++;
  2111. floodnum = 0;
  2112. // area 0 is not used
  2113. for (i=1 ; i<pBSPData->numareas ; i++)
  2114. {
  2115. area = &pBSPData->map_areas[i];
  2116. if (area->floodvalid == pBSPData->floodvalid)
  2117. continue; // already flooded into
  2118. floodnum++;
  2119. FloodArea_r (pBSPData, area, floodnum);
  2120. }
  2121. }
  2122. void CM_SetAreaPortalState( int portalnum, int isOpen )
  2123. {
  2124. // get the current collision bsp -- there is only one!
  2125. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2126. // Portalnums in the BSP file are 1-based instead of 0-based
  2127. if (portalnum > pBSPData->numareaportals)
  2128. {
  2129. Sys_Error( "portalnum > numareaportals");
  2130. }
  2131. pBSPData->portalopen[portalnum] = (isOpen != 0);
  2132. FloodAreaConnections (pBSPData);
  2133. }
  2134. void CM_SetAreaPortalStates( const int *portalnums, const int *isOpen, int nPortals )
  2135. {
  2136. if ( nPortals == 0 )
  2137. return;
  2138. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2139. // get the current collision bsp -- there is only one!
  2140. for ( int i=0; i < nPortals; i++ )
  2141. {
  2142. // Portalnums in the BSP file are 1-based instead of 0-based
  2143. if (portalnums[i] > pBSPData->numareaportals)
  2144. Sys_Error( "portalnum > numareaportals");
  2145. pBSPData->portalopen[portalnums[i]] = (isOpen[i] != 0);
  2146. }
  2147. FloodAreaConnections( pBSPData );
  2148. }
  2149. bool CM_AreasConnected (int area1, int area2)
  2150. {
  2151. // get the current collision bsp -- there is only one!
  2152. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2153. if (map_noareas.GetInt())
  2154. return true;
  2155. if (area1 >= pBSPData->numareas || area2 >= pBSPData->numareas)
  2156. {
  2157. Sys_Error( "area(1==%i, 2==%i) >= numareas (%i): Check if engine->ResetPVS() was called from ClientSetupVisibility", area1, area2, pBSPData->numareas );
  2158. }
  2159. return (pBSPData->map_areas[area1].floodnum == pBSPData->map_areas[area2].floodnum);
  2160. }
  2161. /*
  2162. =================
  2163. CM_WriteAreaBits
  2164. Writes a length byte followed by a bit vector of all the areas
  2165. that area in the same flood as the area parameter
  2166. This is used by the client refreshes to cull visibility
  2167. =================
  2168. */
  2169. int CM_WriteAreaBits ( byte *buffer, int buflen, int area )
  2170. {
  2171. int i;
  2172. int floodnum;
  2173. int bytes;
  2174. // get the current collision bsp -- there is only one!
  2175. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2176. bytes = (pBSPData->numareas+7)>>3;
  2177. if ( map_noareas.GetInt() )
  2178. {
  2179. // for debugging, send everything
  2180. Q_memset( buffer, 255, 3 );
  2181. }
  2182. else
  2183. {
  2184. if ( buflen < 32 )
  2185. {
  2186. Sys_Error( "CM_WriteAreaBits with buffer size < 32\n" );
  2187. }
  2188. Q_memset( buffer, 0, 32 );
  2189. floodnum = pBSPData->map_areas[area].floodnum;
  2190. for (i=0 ; i<pBSPData->numareas ; i++)
  2191. {
  2192. if (pBSPData->map_areas[i].floodnum == floodnum || !area)
  2193. buffer[i>>3] |= 1<<(i&7);
  2194. }
  2195. }
  2196. return bytes;
  2197. }
  2198. bool CM_GetAreaPortalPlane( const Vector &vViewOrigin, int portalKey, VPlane *pPlane )
  2199. {
  2200. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2201. // First, find the leaf and area the viewer is in.
  2202. int iLeaf = CM_PointLeafnum( vViewOrigin );
  2203. if( iLeaf < 0 || iLeaf >= pBSPData->numleafs )
  2204. return false;
  2205. int iArea = pBSPData->map_leafs[iLeaf].area;
  2206. if( iArea < 0 || iArea >= pBSPData->numareas )
  2207. return false;
  2208. carea_t *pArea = &pBSPData->map_areas[iArea];
  2209. for( int i=0; i < pArea->numareaportals; i++ )
  2210. {
  2211. dareaportal_t *pPortal = &pBSPData->map_areaportals[pArea->firstareaportal + i];
  2212. if( pPortal->m_PortalKey == portalKey )
  2213. {
  2214. cplane_t *pMapPlane = &pBSPData->map_planes[pPortal->planenum];
  2215. pPlane->m_Normal = pMapPlane->normal;
  2216. pPlane->m_Dist = pMapPlane->dist;
  2217. return true;
  2218. }
  2219. }
  2220. return false;
  2221. }
  2222. /*
  2223. =============
  2224. CM_HeadnodeVisible
  2225. Returns true if any leaf under headnode has a cluster that
  2226. is potentially visible
  2227. =============
  2228. */
  2229. bool CM_HeadnodeVisible (int nodenum, const byte *visbits, int vissize )
  2230. {
  2231. int leafnum;
  2232. int cluster;
  2233. cnode_t *node;
  2234. // get the current collision bsp -- there is only one!
  2235. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2236. if (nodenum < 0)
  2237. {
  2238. leafnum = -1-nodenum;
  2239. cluster = pBSPData->map_leafs[leafnum].cluster;
  2240. if (cluster == -1)
  2241. return false;
  2242. if (visbits[cluster>>3] & (1<<(cluster&7)))
  2243. return true;
  2244. return false;
  2245. }
  2246. node = &pBSPData->map_rootnode[nodenum];
  2247. if (CM_HeadnodeVisible(node->children[0], visbits, vissize ))
  2248. return true;
  2249. return CM_HeadnodeVisible(node->children[1], visbits, vissize );
  2250. }
  2251. //-----------------------------------------------------------------------------
  2252. // Purpose: returns true if the box is in a cluster that is visible in the visbits
  2253. // Input : mins - box extents
  2254. // maxs -
  2255. // *visbits - pvs or pas of some cluster
  2256. // Output : true if visible, false if not
  2257. //-----------------------------------------------------------------------------
  2258. #define MAX_BOX_LEAVES 256
  2259. int CM_BoxVisible( const Vector& mins, const Vector& maxs, const byte *visbits, int vissize )
  2260. {
  2261. int leafList[MAX_BOX_LEAVES];
  2262. int topnode;
  2263. // FIXME: Could save a loop here by traversing the tree in this routine like the code above
  2264. int count = CM_BoxLeafnums( mins, maxs, leafList, MAX_BOX_LEAVES, &topnode );
  2265. for ( int i = 0; i < count; i++ )
  2266. {
  2267. int cluster = CM_LeafCluster( leafList[i] );
  2268. int offset = cluster>>3;
  2269. if ( offset == -1 )
  2270. {
  2271. return false;
  2272. }
  2273. if ( offset > vissize || offset < 0 )
  2274. {
  2275. Sys_Error( "CM_BoxVisible: cluster %i, offset %i out of bounds %i\n", cluster, offset, vissize );
  2276. }
  2277. if (visbits[cluster>>3] & (1<<(cluster&7)))
  2278. {
  2279. return true;
  2280. }
  2281. }
  2282. return false;
  2283. }
  2284. //-----------------------------------------------------------------------------
  2285. // Returns the world-space center of an entity
  2286. //-----------------------------------------------------------------------------
  2287. void CM_WorldSpaceCenter( ICollideable *pCollideable, Vector *pCenter )
  2288. {
  2289. Vector vecLocalCenter;
  2290. VectorAdd( pCollideable->OBBMins(), pCollideable->OBBMaxs(), vecLocalCenter );
  2291. vecLocalCenter *= 0.5f;
  2292. if ( ( pCollideable->GetCollisionAngles() == vec3_angle ) || ( vecLocalCenter == vec3_origin ) )
  2293. {
  2294. VectorAdd( vecLocalCenter, pCollideable->GetCollisionOrigin(), *pCenter );
  2295. }
  2296. else
  2297. {
  2298. VectorTransform( vecLocalCenter, pCollideable->CollisionToWorldTransform(), *pCenter );
  2299. }
  2300. }
  2301. //-----------------------------------------------------------------------------
  2302. // Returns the world-align bounds of an entity
  2303. //-----------------------------------------------------------------------------
  2304. void CM_WorldAlignBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  2305. {
  2306. if ( pCollideable->GetCollisionAngles() == vec3_angle )
  2307. {
  2308. *pMins = pCollideable->OBBMins();
  2309. *pMaxs = pCollideable->OBBMaxs();
  2310. }
  2311. else
  2312. {
  2313. ITransformAABB( pCollideable->CollisionToWorldTransform(), pCollideable->OBBMins(), pCollideable->OBBMaxs(), *pMins, *pMaxs );
  2314. *pMins -= pCollideable->GetCollisionOrigin();
  2315. *pMaxs -= pCollideable->GetCollisionOrigin();
  2316. }
  2317. }
  2318. //-----------------------------------------------------------------------------
  2319. // Returns the world-space bounds of an entity
  2320. //-----------------------------------------------------------------------------
  2321. void CM_WorldSpaceBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  2322. {
  2323. if ( pCollideable->GetCollisionAngles() == vec3_angle )
  2324. {
  2325. VectorAdd( pCollideable->GetCollisionOrigin(), pCollideable->OBBMins(), *pMins );
  2326. VectorAdd( pCollideable->GetCollisionOrigin(), pCollideable->OBBMaxs(), *pMaxs );
  2327. }
  2328. else
  2329. {
  2330. TransformAABB( pCollideable->CollisionToWorldTransform(), pCollideable->OBBMins(), pCollideable->OBBMaxs(), *pMins, *pMaxs );
  2331. }
  2332. }
  2333. void CM_SetupAreaFloodNums( byte areaFloodNums[MAX_MAP_AREAS], int *pNumAreas )
  2334. {
  2335. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2336. *pNumAreas = pBSPData->numareas;
  2337. if ( pBSPData->numareas > MAX_MAP_AREAS )
  2338. Error( "pBSPData->numareas > MAX_MAP_AREAS" );
  2339. for ( int i=0; i < pBSPData->numareas; i++ )
  2340. {
  2341. Assert( pBSPData->map_areas[i].floodnum < MAX_MAP_AREAS );
  2342. areaFloodNums[i] = (byte)pBSPData->map_areas[i].floodnum;
  2343. }
  2344. }
  2345. // -----------------------------------------------------------------------------
  2346. // CFastLeafAccessor implementation.
  2347. // -----------------------------------------------------------------------------
  2348. CFastPointLeafNum::CFastPointLeafNum()
  2349. {
  2350. m_flDistToExitLeafSqr = -1;
  2351. m_vCachedPos.Init();
  2352. }
  2353. int CFastPointLeafNum::GetLeaf( const Vector &vPos )
  2354. {
  2355. if ( vPos.DistToSqr( m_vCachedPos ) > m_flDistToExitLeafSqr )
  2356. {
  2357. m_vCachedPos = vPos;
  2358. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2359. m_flDistToExitLeafSqr = 1e24;
  2360. m_iCachedLeaf = CM_PointLeafnumMinDistSqr_r( pBSPData, vPos, 0, m_flDistToExitLeafSqr );
  2361. }
  2362. return m_iCachedLeaf;
  2363. }
  2364. bool FASTCALL IsBoxIntersectingRayNoLowest( fltx4 boxMin, fltx4 boxMax,
  2365. const fltx4 & origin, const fltx4 & delta, const fltx4 & invDelta, // ray parameters
  2366. const fltx4 & vTolerance ///< eg from ReplicateX4(flTolerance)
  2367. )
  2368. {
  2369. /*
  2370. Assert( boxMin[0] <= boxMax[0] );
  2371. Assert( boxMin[1] <= boxMax[1] );
  2372. Assert( boxMin[2] <= boxMax[2] );
  2373. */
  2374. #if defined(_X360) && defined(DBGFLAG_ASSERT)
  2375. unsigned int r;
  2376. AssertMsg( (XMVectorGreaterOrEqualR(&r, SetWToZeroSIMD(boxMax),SetWToZeroSIMD(boxMin)), XMComparisonAllTrue(r)), "IsBoxIntersectingRay : boxmax < boxmin" );
  2377. #endif
  2378. // test if delta is tiny along any dimension
  2379. fltx4 bvDeltaTinyComponents = CmpInBoundsSIMD( delta, Four_Epsilons );
  2380. // push box extents out by tolerance (safe to do because pass by copy, not ref)
  2381. boxMin = SubSIMD(boxMin, vTolerance);
  2382. boxMax = AddSIMD(boxMax, vTolerance);
  2383. // for the very short components of the ray, check if the origin is inside the box;
  2384. // if not, then it doesn't intersect.
  2385. fltx4 bvOriginOutsideBox = OrSIMD( CmpLtSIMD(origin,boxMin), CmpGtSIMD(origin,boxMax) );
  2386. bvDeltaTinyComponents = SetWToZeroSIMD(bvDeltaTinyComponents);
  2387. // work out entry and exit points for the ray. This may produce strange results for
  2388. // very short delta components, but those will be masked out by bvDeltaTinyComponents
  2389. // anyway. We could early-out on bvOriginOutsideBox, but it won't be ready to branch
  2390. // on for fourteen cycles.
  2391. fltx4 vt1 = SubSIMD( boxMin, origin );
  2392. fltx4 vt2 = SubSIMD( boxMax, origin );
  2393. vt1 = MulSIMD( vt1, invDelta );
  2394. vt2 = MulSIMD( vt2, invDelta );
  2395. // ensure that vt1<vt2
  2396. {
  2397. fltx4 temp = MaxSIMD( vt1, vt2 );
  2398. vt1 = MinSIMD( vt1, vt2 );
  2399. vt2 = temp;
  2400. }
  2401. // Non-parallel case
  2402. // Find the t's corresponding to the entry and exit of
  2403. // the ray along x, y, and z. The find the furthest entry
  2404. // point, and the closest exit point. Once that is done,
  2405. // we know we don't collide if the closest exit point
  2406. // is behind the starting location. We also don't collide if
  2407. // the closest exit point is in front of the furthest entry point
  2408. fltx4 closestExit,furthestEntry;
  2409. {
  2410. VectorAligned temp;
  2411. StoreAlignedSIMD(temp.Base(),vt2);
  2412. closestExit = ReplicateX4( min( min(temp.x,temp.y), temp.z) );
  2413. StoreAlignedSIMD(temp.Base(),vt1);
  2414. furthestEntry = ReplicateX4( max( max(temp.x,temp.y), temp.z) );
  2415. }
  2416. // now start testing. We bail out if:
  2417. // any component with tiny delta has origin outside the box
  2418. if (!IsAllZeros(AndSIMD(bvOriginOutsideBox, bvDeltaTinyComponents)))
  2419. return false;
  2420. else
  2421. {
  2422. // however if there are tiny components inside the box, we
  2423. // know that they are good. (we didn't really need to run
  2424. // the other computations on them, but it was faster to do
  2425. // so than branching around them).
  2426. // now it's the origin INSIDE box (eg, tiny components & ~outside box)
  2427. bvOriginOutsideBox = AndNotSIMD(bvOriginOutsideBox,bvDeltaTinyComponents);
  2428. }
  2429. // closest exit is in front of furthest entry
  2430. fltx4 tminOverTmax = CmpGtSIMD( furthestEntry, closestExit );
  2431. // closest exit is behind start, or furthest entry after end
  2432. fltx4 outOfBounds = OrSIMD( CmpGtSIMD(furthestEntry, LoadOneSIMD()), CmpGtSIMD( LoadZeroSIMD(), closestExit ) );
  2433. fltx4 failedComponents = OrSIMD(tminOverTmax, outOfBounds); // any 1's here mean return false
  2434. // but, if a component is tiny and has its origin inside the box, ignore the computation against bogus invDelta.
  2435. failedComponents = AndNotSIMD(bvOriginOutsideBox,failedComponents);
  2436. return ( IsAllZeros( SetWToZeroSIMD( failedComponents ) ) );
  2437. }
  2438. // function to time IsBoxIntersectingRay
  2439. #if 0
  2440. /*
  2441. //-----------------------------------------------------------------------------
  2442. bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax,
  2443. fltx4 origin, fltx4 delta, fltx4 invDelta, // ray parameters
  2444. fltx4 vTolerance ///< eg from ReplicateX4(flTolerance)
  2445. )
  2446. {
  2447. */
  2448. CON_COMMAND( opt_test_collision, "Quick timing test in IsBoxIntersectingRay" )
  2449. {
  2450. int numIters = 100000;
  2451. if (args.ArgC() >= 1)
  2452. {
  2453. numIters = Q_atoi(args.Arg(1));
  2454. }
  2455. {
  2456. fltx4 boxMin = {1,1,1,0};
  2457. fltx4 boxMax = {2,2,2,0};
  2458. fltx4 origin = {0,0,0,0};
  2459. fltx4 delta = {3,4,3,0};
  2460. fltx4 invdelta = {1.0f/3.0f, 1.0f/4.0f, 1.0f/3.0f,0};
  2461. fltx4 flTolerance = ReplicateX4(.0001f);
  2462. double startTime = Plat_FloatTime();
  2463. for (int i = numIters ; i > 0 ; --i)
  2464. IsBoxIntersectingRayNoLowest(boxMin,boxMax,origin,delta,invdelta,flTolerance);
  2465. double endTime = Plat_FloatTime();
  2466. Msg("without FindLowest algorithm: %.4f secs for %d runs\n",endTime - startTime,numIters);
  2467. }
  2468. {
  2469. fltx4 boxMin = {1,1,1,0};
  2470. fltx4 boxMax = {2,2,2,0};
  2471. fltx4 origin = {0,0,0,0};
  2472. fltx4 delta = {3,4,3,0};
  2473. fltx4 invdelta = {1.0f/3.0f, 1.0f/4.0f, 1.0f/3.0f,0};
  2474. fltx4 flTolerance = ReplicateX4(.0001f);
  2475. double startTime = Plat_FloatTime();
  2476. for (int i = numIters ; i > 0 ; --i)
  2477. IsBoxIntersectingRay(boxMin,boxMax,origin,delta,invdelta,flTolerance);
  2478. double endTime = Plat_FloatTime();
  2479. Msg("using FindLowest algorithm: %.4f secs for %d runs\n",endTime - startTime,numIters);
  2480. }
  2481. }
  2482. CON_COMMAND( opt_test_rotation, "Quick timing test of vector rotation my m3x4" )
  2483. {
  2484. int numIters = 100000;
  2485. if (args.ArgC() >= 1)
  2486. {
  2487. numIters = Q_atoi(args.Arg(1));
  2488. }
  2489. // construct an array of 1024 random vectors
  2490. FourVectors testData[1024];
  2491. SeedRandSIMD(Plat_MSTime());
  2492. for (int i = 0 ; i < 1024 ; ++i)
  2493. {
  2494. testData[i].x = RandSIMD();
  2495. testData[i].y = RandSIMD();
  2496. testData[i].z = RandSIMD();
  2497. }
  2498. // for also testing store latency
  2499. FourVectors outScratch[16];
  2500. matrix3x4_t rota;
  2501. AngleIMatrix(QAngle(30,60,90), rota);
  2502. // THREE DOT PRODUCTS
  2503. {
  2504. double startTime = Plat_FloatTime();
  2505. for (int i = numIters ; i > 0 ; --i)
  2506. {
  2507. int in = i & 1023;
  2508. int out = i & 15;
  2509. outScratch[out].x = testData[in] * *reinterpret_cast<Vector *>(rota[0]);
  2510. outScratch[out].y = testData[in] * *reinterpret_cast<Vector *>(rota[1]);
  2511. outScratch[out].z = testData[in] * *reinterpret_cast<Vector *>(rota[2]);
  2512. }
  2513. double endTime = Plat_FloatTime();
  2514. Msg("THREE DOT PRODUCTS: %.4f secs for %d runs\n",endTime - startTime,numIters);
  2515. }
  2516. // REPEATED CALLS TO ROTATEBY
  2517. {
  2518. double startTime = Plat_FloatTime();
  2519. for (int i = numIters ; i > 0 ; --i)
  2520. {
  2521. int in = i & 1023;
  2522. int out = i & 15;
  2523. outScratch[out] = testData[in];
  2524. outScratch[out].RotateBy(rota);
  2525. }
  2526. double endTime = Plat_FloatTime();
  2527. Msg("REPEATED CALLS TO ROTATEBY: %.4f secs for %d runs\n",endTime - startTime,numIters);
  2528. }
  2529. // ROTATEBYMANY
  2530. {
  2531. double startTime = Plat_FloatTime();
  2532. int lastBatch = numIters - 1023;
  2533. int i;
  2534. for (i = 0 ; i < lastBatch ; i+=1024 )
  2535. {
  2536. FourVectors::RotateManyBy(testData, 1024, rota);
  2537. }
  2538. if (i < numIters)
  2539. {
  2540. FourVectors::RotateManyBy(testData, numIters-i, rota);
  2541. }
  2542. double endTime = Plat_FloatTime();
  2543. Msg("ROTATEBYMANY: %.4f secs for %d runs\n",endTime - startTime,numIters);
  2544. }
  2545. // test
  2546. FourVectors res1, res2;
  2547. res2 = testData[0];
  2548. res1.x = testData[0] * *reinterpret_cast<Vector *>(rota[0]);
  2549. res1.y = testData[0] * *reinterpret_cast<Vector *>(rota[1]);
  2550. res1.z = testData[0] * *reinterpret_cast<Vector *>(rota[2]);
  2551. res2.RotateBy(rota);
  2552. Msg("%.3f %.3f %.3f %.3f \t%.3f %.3f %.3f %.3f\n", SubFloat(res1.x, 0), SubFloat(res1.x, 1), SubFloat(res1.x, 2), SubFloat(res1.x, 3),
  2553. SubFloat(res2.x, 0), SubFloat(res2.x, 1), SubFloat(res2.x, 2), SubFloat(res2.x, 3));
  2554. Msg("%.3f %.3f %.3f %.3f \t%.3f %.3f %.3f %.3f\n", SubFloat(res1.y, 0), SubFloat(res1.y, 1), SubFloat(res1.y, 2), SubFloat(res1.y, 3),
  2555. SubFloat(res2.y, 0), SubFloat(res2.y, 1), SubFloat(res2.y, 2), SubFloat(res2.y, 3));
  2556. Msg("%.3f %.3f %.3f %.3f \t%.3f %.3f %.3f %.3f\n", SubFloat(res1.z, 0), SubFloat(res1.z, 1), SubFloat(res1.z, 2), SubFloat(res1.z, 3),
  2557. SubFloat(res2.z, 0), SubFloat(res2.z, 1), SubFloat(res2.z, 2), SubFloat(res2.z, 3));
  2558. }
  2559. #endif