Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4067 lines
126 KiB

  1. //========= Copyright � 1996-2005, 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. #include "tier0/microprofiler.h"
  30. #include "keyvalues.h"
  31. #include "paint.h"
  32. #include "tier1/fmtstr.h"
  33. #include "bsplog.h"
  34. #include "edict.h"
  35. #include "debugoverlay.h"
  36. #include "engine/IEngineTrace.h"
  37. // memdbgon must be the last include file in a .cpp file!!!
  38. #include "tier0/memdbgon.h"
  39. CCollisionBSPData g_BSPData; // the global collision bsp
  40. #define g_BSPData dont_use_g_BSPData_directly
  41. #ifdef COUNT_COLLISIONS
  42. CCollisionCounts g_CollisionCounts; // collision test counters
  43. #endif
  44. static const float kBoxCheckFloatEpsilon = 0.01f; // Used for box trace assert checks below.
  45. csurface_t CCollisionBSPData::nullsurface = { "**empty**", 0, 0 }; // generic null collision model surface
  46. csurface_t *CCollisionBSPData::GetSurfaceAtIndex( unsigned short surfaceIndex )
  47. {
  48. if ( surfaceIndex == SURFACE_INDEX_INVALID )
  49. {
  50. return &nullsurface;
  51. }
  52. return &map_surfaces[surfaceIndex];
  53. }
  54. CTSPool<TraceInfo_t> g_TraceInfoPool;
  55. TraceInfo_t *BeginTrace()
  56. {
  57. TraceInfo_t *pTraceInfo = g_TraceInfoPool.GetObject();
  58. if ( pTraceInfo->m_BrushCounters[0].Count() != GetCollisionBSPData()->numbrushes + 1 )
  59. {
  60. memset( pTraceInfo->m_Count, 0, sizeof( pTraceInfo->m_Count ) );
  61. pTraceInfo->m_nCheckDepth = -1;
  62. for ( int i = 0; i < MAX_CHECK_COUNT_DEPTH; i++ )
  63. {
  64. pTraceInfo->m_BrushCounters[i].SetCount( GetCollisionBSPData()->numbrushes + 1 );
  65. pTraceInfo->m_DispCounters[i].SetCount( g_DispCollTreeCount );
  66. memset( pTraceInfo->m_BrushCounters[i].Base(), 0, pTraceInfo->m_BrushCounters[i].Count() * sizeof(TraceCounter_t) );
  67. memset( pTraceInfo->m_DispCounters[i].Base(), 0, pTraceInfo->m_DispCounters[i].Count() * sizeof(TraceCounter_t) );
  68. }
  69. }
  70. PushTraceVisits( pTraceInfo );
  71. pTraceInfo->m_pBSPData = GetCollisionBSPData();
  72. return pTraceInfo;
  73. }
  74. void PushTraceVisits( TraceInfo_t *pTraceInfo )
  75. {
  76. ++pTraceInfo->m_nCheckDepth;
  77. Assert( (pTraceInfo->m_nCheckDepth >= 0) && (pTraceInfo->m_nCheckDepth < MAX_CHECK_COUNT_DEPTH) );
  78. int i = pTraceInfo->m_nCheckDepth;
  79. pTraceInfo->m_Count[i]++;
  80. if ( pTraceInfo->m_Count[i] == 0 )
  81. {
  82. pTraceInfo->m_Count[i]++;
  83. memset( pTraceInfo->m_BrushCounters[i].Base(), 0, pTraceInfo->m_BrushCounters[i].Count() * sizeof(TraceCounter_t) );
  84. memset( pTraceInfo->m_DispCounters[i].Base(), 0, pTraceInfo->m_DispCounters[i].Count() * sizeof(TraceCounter_t) );
  85. }
  86. }
  87. void PopTraceVisits( TraceInfo_t *pTraceInfo )
  88. {
  89. --pTraceInfo->m_nCheckDepth;
  90. Assert( pTraceInfo->m_nCheckDepth >= -1 );
  91. }
  92. void EndTrace( TraceInfo_t *&pTraceInfo )
  93. {
  94. PopTraceVisits( pTraceInfo );
  95. Assert( pTraceInfo->m_nCheckDepth == -1 );
  96. g_TraceInfoPool.PutObject( pTraceInfo );
  97. pTraceInfo = NULL;
  98. }
  99. static ConVar map_noareas( "map_noareas", "0", 0, "Disable area to area connection testing." );
  100. void FloodAreaConnections (CCollisionBSPData *pBSPData);
  101. //-----------------------------------------------------------------------------
  102. //-----------------------------------------------------------------------------
  103. vcollide_t *CM_GetVCollide( int modelIndex )
  104. {
  105. cmodel_t *pModel = CM_InlineModelNumber( modelIndex );
  106. if( !pModel )
  107. return NULL;
  108. // return the model's collision data
  109. return &pModel->vcollisionData;
  110. }
  111. //-----------------------------------------------------------------------------
  112. //-----------------------------------------------------------------------------
  113. cmodel_t *CM_InlineModel( const char *name )
  114. {
  115. // error checking!
  116. if( !name )
  117. return NULL;
  118. // JAYHL2: HACKHACK Get rid of this
  119. if( StringHasPrefix( name, "maps/" ) )
  120. return CM_InlineModelNumber( 0 );
  121. // check for valid name
  122. if( name[0] != '*' )
  123. Sys_Error( "CM_InlineModel: bad model name!" );
  124. // check for valid model
  125. int ndxModel = atoi( name + 1 );
  126. if( ( ndxModel < 1 ) || ( ndxModel >= GetCollisionBSPData()->numcmodels ) )
  127. Sys_Error( "CM_InlineModel: bad model number!" );
  128. return CM_InlineModelNumber( ndxModel );
  129. }
  130. //-----------------------------------------------------------------------------
  131. //-----------------------------------------------------------------------------
  132. cmodel_t *CM_InlineModelNumber( int index )
  133. {
  134. CCollisionBSPData *pBSPDataData = GetCollisionBSPData();
  135. if( ( index < 0 ) || ( index > pBSPDataData->numcmodels ) )
  136. return NULL;
  137. return ( &pBSPDataData->map_cmodels[ index ] );
  138. }
  139. int CM_BrushContents_r( CCollisionBSPData *pBSPData, int nodenum )
  140. {
  141. int contents = 0;
  142. while (1)
  143. {
  144. if (nodenum < 0)
  145. {
  146. int leafIndex = -1 - nodenum;
  147. cleaf_t &leaf = pBSPData->map_leafs[leafIndex];
  148. for ( int i = 0; i < leaf.numleafbrushes; i++ )
  149. {
  150. unsigned short brushIndex = pBSPData->map_leafbrushes[ leaf.firstleafbrush + i ];
  151. contents |= pBSPData->map_brushes[brushIndex].contents;
  152. }
  153. return contents;
  154. }
  155. cnode_t &node = pBSPData->map_rootnode[nodenum];
  156. contents |= CM_BrushContents_r( pBSPData, node.children[0] );
  157. nodenum = node.children[1];
  158. }
  159. return contents;
  160. }
  161. int CM_InlineModelContents( int index )
  162. {
  163. cmodel_t *pModel = CM_InlineModelNumber( index );
  164. if ( !pModel )
  165. return 0;
  166. return CM_BrushContents_r( GetCollisionBSPData(), pModel->headnode );
  167. }
  168. //-----------------------------------------------------------------------------
  169. //-----------------------------------------------------------------------------
  170. int CM_NumClusters( void )
  171. {
  172. return GetCollisionBSPData()->numclusters;
  173. }
  174. //-----------------------------------------------------------------------------
  175. //-----------------------------------------------------------------------------
  176. char *CM_EntityString( void )
  177. {
  178. return GetCollisionBSPData()->map_entitystring.Get();
  179. }
  180. void CM_DiscardEntityString( void )
  181. {
  182. GetCollisionBSPData()->map_entitystring.Discard();
  183. }
  184. //-----------------------------------------------------------------------------
  185. //-----------------------------------------------------------------------------
  186. int CM_LeafContents( int leafnum )
  187. {
  188. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  189. Assert( leafnum >= 0 );
  190. Assert( leafnum < pBSPData->numleafs );
  191. return pBSPData->map_leafs[leafnum].contents;
  192. }
  193. //-----------------------------------------------------------------------------
  194. //-----------------------------------------------------------------------------
  195. int CM_LeafCluster( int leafnum )
  196. {
  197. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  198. Assert( leafnum >= 0 );
  199. Assert( leafnum < pBSPData->numleafs );
  200. return pBSPData->map_leafs[leafnum].cluster;
  201. }
  202. int CM_LeafFlags( int leafnum )
  203. {
  204. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  205. Assert( leafnum >= 0 );
  206. Assert( leafnum < pBSPData->numleafs );
  207. return pBSPData->map_leafs[leafnum].flags;
  208. }
  209. //-----------------------------------------------------------------------------
  210. //-----------------------------------------------------------------------------
  211. int CM_LeafArea( int leafnum )
  212. {
  213. const CCollisionBSPData *pBSPData = GetCollisionBSPData();
  214. Assert( leafnum >= 0 );
  215. Assert( leafnum < pBSPData->numleafs );
  216. return pBSPData->map_leafs[leafnum].area;
  217. }
  218. //-----------------------------------------------------------------------------
  219. //-----------------------------------------------------------------------------
  220. void CM_FreeMap(void)
  221. {
  222. // get the current collision bsp -- there is only one!
  223. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  224. // free the collision bsp data
  225. CollisionBSPData_Destroy( pBSPData );
  226. }
  227. // This turns on all the area portals that are "always on" in the map.
  228. void CM_InitPortalOpenState( CCollisionBSPData *pBSPData )
  229. {
  230. for ( int i=0; i < pBSPData->numportalopen; i++ )
  231. {
  232. pBSPData->portalopen[i] = false;
  233. }
  234. }
  235. void CM_RegisterPaintMap( CCollisionBSPData *pBSPData )
  236. {
  237. char* paintStr = V_stristr( pBSPData->map_entitystring.Get(), "paintinmap" );
  238. bool bMapHasPaint = false;
  239. if ( paintStr )
  240. {
  241. KeyValues *kv = KeyValues::FromString( "", paintStr );
  242. KeyValues * firstVal = kv->GetFirstValue();
  243. const char* val = firstVal->GetString();
  244. bMapHasPaint = val[1] == '1';
  245. kv->deleteThis();
  246. }
  247. g_PaintManager.m_bShouldRegister = bMapHasPaint;
  248. }
  249. /*
  250. ==================
  251. CM_LoadMap
  252. Loads in the map and all submodels
  253. ==================
  254. */
  255. cmodel_t *CM_LoadMap( const char *pPathName, bool allowReusePrevious, texinfo_t *pTexinfo, int texinfoCount, unsigned *checksum )
  256. {
  257. static unsigned int last_checksum = 0xFFFFFFFF;
  258. // get the current bsp -- there is currently only one!
  259. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  260. Assert( physcollision );
  261. if( !strcmp( pBSPData->mapPathName, pPathName ) && allowReusePrevious )
  262. {
  263. *checksum = last_checksum;
  264. return &pBSPData->map_cmodels[0]; // still have the right version
  265. }
  266. // only pre-load if the map doesn't already exist
  267. CollisionBSPData_PreLoad( pBSPData );
  268. if ( !pPathName || !pPathName[0] )
  269. {
  270. *checksum = 0;
  271. return &pBSPData->map_cmodels[0]; // cinematic servers won't have anything at all
  272. }
  273. // read in the collision model data
  274. CMapLoadHelper::Init( 0, pPathName );
  275. CollisionBSPData_Load( pPathName, pBSPData, pTexinfo, texinfoCount );
  276. CMapLoadHelper::Shutdown( );
  277. // Push the displacement bounding boxes down the tree and set leaf data.
  278. CM_DispTreeLeafnum( pBSPData );
  279. CM_InitPortalOpenState( pBSPData );
  280. FloodAreaConnections( pBSPData );
  281. CM_RegisterPaintMap( pBSPData );
  282. #ifdef COUNT_COLLISIONS
  283. // initialize counters
  284. CollisionCounts_Init( &g_CollisionCounts );
  285. #endif
  286. return &pBSPData->map_cmodels[0];
  287. }
  288. //-----------------------------------------------------------------------------
  289. //
  290. // Methods associated with colliding against the world + models
  291. //
  292. //-----------------------------------------------------------------------------
  293. //-----------------------------------------------------------------------------
  294. // returns a vcollide that can be used to collide against this model
  295. //-----------------------------------------------------------------------------
  296. vcollide_t* CM_VCollideForModel( int modelindex, const model_t* 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. return 0;
  307. }
  308. //=======================================================================
  309. /*
  310. ==================
  311. CM_PointLeafnum_r
  312. ==================
  313. */
  314. int CM_PointLeafnumMinDistSqr_r( CCollisionBSPData *pBSPData, const Vector& p, int num, float &minDistSqr )
  315. {
  316. float d;
  317. cnode_t *node;
  318. cplane_t *plane;
  319. while (num >= 0)
  320. {
  321. node = pBSPData->map_rootnode + num;
  322. plane = node->plane;
  323. if (plane->type < 3)
  324. d = p[plane->type] - plane->dist;
  325. else
  326. d = DotProduct (plane->normal, p) - plane->dist;
  327. minDistSqr = fpmin( d*d, minDistSqr );
  328. if (d < 0)
  329. num = node->children[1];
  330. else
  331. num = node->children[0];
  332. }
  333. #ifdef COUNT_COLLISIONS
  334. g_CollisionCounts.m_PointContents++; // optimize counter
  335. #endif
  336. return -1 - num;
  337. }
  338. int CM_PointLeafnum_r( CCollisionBSPData *pBSPData, const Vector& p, int num)
  339. {
  340. float d;
  341. cnode_t *node;
  342. cplane_t *plane;
  343. while (num >= 0)
  344. {
  345. node = pBSPData->map_rootnode + num;
  346. plane = node->plane;
  347. if (plane->type < 3)
  348. d = p[plane->type] - plane->dist;
  349. else
  350. d = DotProduct (plane->normal, p) - plane->dist;
  351. if (d < 0)
  352. num = node->children[1];
  353. else
  354. num = node->children[0];
  355. }
  356. #ifdef COUNT_COLLISIONS
  357. g_CollisionCounts.m_PointContents++; // optimize counter
  358. #endif
  359. return -1 - num;
  360. }
  361. int CM_PointLeafnum (const Vector& p)
  362. {
  363. // get the current collision bsp -- there is only one!
  364. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  365. if (!pBSPData->numplanes)
  366. return 0; // sound may call this without map loaded
  367. return CM_PointLeafnum_r (pBSPData, p, 0);
  368. }
  369. void CM_SnapPointToReferenceLeaf_r( CCollisionBSPData *pBSPData, const Vector& p, int num, float tolerance, Vector *pSnapPoint )
  370. {
  371. float d, snapDist;
  372. cnode_t *node;
  373. cplane_t *plane;
  374. while (num >= 0)
  375. {
  376. node = pBSPData->map_rootnode + num;
  377. plane = node->plane;
  378. if (plane->type < 3)
  379. {
  380. d = p[plane->type] - plane->dist;
  381. snapDist = (*pSnapPoint)[plane->type] - plane->dist;
  382. }
  383. else
  384. {
  385. d = DotProduct (plane->normal, p) - plane->dist;
  386. snapDist = DotProduct (plane->normal, *pSnapPoint) - plane->dist;
  387. }
  388. if (d < 0)
  389. {
  390. num = node->children[1];
  391. if ( snapDist > 0 )
  392. {
  393. *pSnapPoint -= plane->normal * (snapDist + tolerance);
  394. }
  395. }
  396. else
  397. {
  398. num = node->children[0];
  399. if ( snapDist < 0 )
  400. {
  401. *pSnapPoint += plane->normal * (-snapDist + tolerance);
  402. }
  403. }
  404. }
  405. }
  406. void CM_SnapPointToReferenceLeaf(const Vector &referenceLeafPoint, float tolerance, Vector *pSnapPoint)
  407. {
  408. // get the current collision bsp -- there is only one!
  409. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  410. if (pBSPData->numplanes)
  411. {
  412. CM_SnapPointToReferenceLeaf_r(pBSPData, referenceLeafPoint, 0, tolerance, pSnapPoint);
  413. }
  414. }
  415. /*
  416. =============
  417. CM_BoxLeafnums
  418. Fills in a list of all the leafs touched
  419. =============
  420. */
  421. struct leafnums_t
  422. {
  423. int leafTopNode;
  424. int leafMaxCount;
  425. int *pLeafList;
  426. CCollisionBSPData *pBSPData;
  427. };
  428. int CM_BoxLeafnums( leafnums_t &context, const Vector &center, const Vector &extents, int nodenum )
  429. {
  430. int leafCount = 0;
  431. const int NODELIST_MAX = 1024;
  432. int nodeList[NODELIST_MAX];
  433. int nodeReadIndex = 0;
  434. int nodeWriteIndex = 0;
  435. cplane_t *plane;
  436. cnode_t *node;
  437. int prev_topnode = -1;
  438. while (1)
  439. {
  440. if (nodenum < 0)
  441. {
  442. // This handles the case when the box lies completely
  443. // within a single node. In that case, the top node should be
  444. // the parent of the leaf
  445. if (context.leafTopNode == -1)
  446. context.leafTopNode = prev_topnode;
  447. if (leafCount < context.leafMaxCount)
  448. {
  449. context.pLeafList[leafCount] = -1 - nodenum;
  450. leafCount++;
  451. }
  452. if ( nodeReadIndex == nodeWriteIndex )
  453. return leafCount;
  454. nodenum = nodeList[nodeReadIndex];
  455. nodeReadIndex = (nodeReadIndex+1) & (NODELIST_MAX-1);
  456. }
  457. else
  458. {
  459. node = &context.pBSPData->map_rootnode[nodenum];
  460. plane = node->plane;
  461. // s = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);
  462. // s = BOX_ON_PLANE_SIDE(*leaf_mins, *leaf_maxs, plane);
  463. float d0 = DotProduct( plane->normal, center ) - plane->dist;
  464. float d1 = DotProductAbs( plane->normal, extents );
  465. prev_topnode = nodenum;
  466. if (d0 >= d1)
  467. nodenum = node->children[0];
  468. else if (d0 < -d1)
  469. nodenum = node->children[1];
  470. else
  471. { // go down both
  472. if (context.leafTopNode == -1)
  473. context.leafTopNode = nodenum;
  474. nodeList[nodeWriteIndex] = node->children[0];
  475. nodeWriteIndex = (nodeWriteIndex+1) & (NODELIST_MAX-1);
  476. // check for overflow of the ring buffer
  477. Assert(nodeWriteIndex != nodeReadIndex);
  478. nodenum = node->children[1];
  479. }
  480. }
  481. }
  482. }
  483. int CM_BoxLeafnums ( const Vector& mins, const Vector& maxs, int *list, int listsize, int *topnode, int cmodelIndex )
  484. {
  485. leafnums_t context;
  486. context.pLeafList = list;
  487. context.leafTopNode = -1;
  488. context.leafMaxCount = listsize;
  489. // get the current collision bsp -- there is only one!
  490. context.pBSPData = GetCollisionBSPData();
  491. Vector center = (mins+maxs)*0.5f;
  492. Vector extents = maxs - center;
  493. AssertMsg( cmodelIndex >= 0 && cmodelIndex < context.pBSPData->numcmodels, "Collision model index out of bounds." );
  494. int leafCount = 0;
  495. if( cmodelIndex >= 0 && cmodelIndex < context.pBSPData->numcmodels )
  496. leafCount = CM_BoxLeafnums(context, center, extents, context.pBSPData->map_cmodels[cmodelIndex].headnode );
  497. if( topnode )
  498. *topnode = context.leafTopNode;
  499. return leafCount;
  500. }
  501. // UNDONE: This is a version that returns only leaves with valid clusters
  502. // UNDONE: Use this in the PVS calcs for networking
  503. #if 0
  504. int CM_BoxClusters( leafnums_t * RESTRICT pContext, const Vector &center, const Vector &extents, int nodenum )
  505. {
  506. const int NODELIST_MAX = 1024;
  507. int nodeList[NODELIST_MAX];
  508. int nodeReadIndex = 0;
  509. int nodeWriteIndex = 0;
  510. cplane_t *RESTRICT plane;
  511. cnode_t *RESTRICT node;
  512. int prev_topnode = -1;
  513. int leafCount = 0;
  514. while (1)
  515. {
  516. if (nodenum < 0)
  517. {
  518. int leafIndex = -1 - nodenum;
  519. // This handles the case when the box lies completely
  520. // within a single node. In that case, the top node should be
  521. // the parent of the leaf
  522. if (pContext->leafTopNode == -1)
  523. pContext->leafTopNode = prev_topnode;
  524. if (leafCount < pContext->leafMaxCount)
  525. {
  526. cleaf_t *RESTRICT pLeaf = &pContext->pBSPData->map_leafs[leafIndex];
  527. if ( pLeaf->cluster >= 0 )
  528. {
  529. pContext->pLeafList[leafCount] = leafIndex;
  530. leafCount++;
  531. }
  532. }
  533. if ( nodeReadIndex == nodeWriteIndex )
  534. return leafCount;
  535. nodenum = nodeList[nodeReadIndex];
  536. nodeReadIndex = (nodeReadIndex+1) & (NODELIST_MAX-1);
  537. }
  538. else
  539. {
  540. node = &pContext->pBSPData->map_rootnode[nodenum];
  541. plane = node->plane;
  542. float d0 = DotProduct( plane->normal, center ) - plane->dist;
  543. float d1 = DotProductAbs( plane->normal, extents );
  544. prev_topnode = nodenum;
  545. if (d0 >= d1)
  546. nodenum = node->children[0];
  547. else if (d0 < -d1)
  548. nodenum = node->children[1];
  549. else
  550. { // go down both
  551. if (pContext->leafTopNode == -1)
  552. pContext->leafTopNode = nodenum;
  553. nodenum = node->children[0];
  554. nodeList[nodeWriteIndex] = node->children[1];
  555. nodeWriteIndex = (nodeWriteIndex+1) & (NODELIST_MAX-1);
  556. // check for overflow of the ring buffer
  557. Assert(nodeWriteIndex != nodeReadIndex);
  558. }
  559. }
  560. }
  561. }
  562. int CM_BoxClusters_headnode ( CCollisionBSPData *pBSPData, const Vector& mins, const Vector& maxs, int *list, int listsize, int nodenum, int *topnode)
  563. {
  564. leafnums_t context;
  565. context.pLeafList = list;
  566. context.leafTopNode = -1;
  567. context.leafMaxCount = listsize;
  568. Vector center = 0.5f * (mins + maxs);
  569. Vector extents = maxs - center;
  570. context.pBSPData = pBSPData;
  571. int leafCount = CM_BoxClusters( &context, center, extents, nodenum );
  572. if (topnode)
  573. *topnode = context.leafTopNode;
  574. return leafCount;
  575. }
  576. #endif
  577. /*
  578. ==================
  579. CM_PointContents
  580. ==================
  581. */
  582. int CM_PointContents ( const Vector &p, int headnode, int contentsMask )
  583. {
  584. int l;
  585. // get the current collision bsp -- there is only one!
  586. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  587. if (!pBSPData->numnodes ) // map not loaded
  588. return 0;
  589. if ( !(pBSPData->allcontents & contentsMask) )
  590. {
  591. return 0;
  592. }
  593. l = CM_PointLeafnum_r (pBSPData, p, headnode);
  594. // iterate the leaf brushes and check for intersection with each one
  595. cleaf_t &leaf = pBSPData->map_leafs[l];
  596. if ( leaf.cluster < 0 )
  597. return leaf.contents;
  598. int nContents = 0;
  599. const unsigned short *pBrushList = &pBSPData->map_leafbrushes[leaf.firstleafbrush];
  600. for ( int i = 0; i < leaf.numleafbrushes; i++ )
  601. {
  602. cbrush_t *pBrush = &pBSPData->map_brushes[ pBrushList[i] ];
  603. // only consider brushes that have contents
  604. if ( !pBrush->contents )
  605. continue;
  606. if ( pBrush->IsBox() )
  607. {
  608. // special case for box brush
  609. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[pBrush->GetBox()];
  610. if ( IsPointInBox( p, pBox->mins, pBox->maxs ) )
  611. {
  612. nContents |= pBrush->contents;
  613. }
  614. }
  615. else
  616. {
  617. // must be on the back of each brush side to be inside, skip bevels because they aren't necessary for testing points
  618. cbrushside_t * RESTRICT pSide = &pBSPData->map_brushsides[pBrush->firstbrushside];
  619. bool bInside = true;
  620. for ( const cbrushside_t * const pSideLimit = pSide + pBrush->numsides; pSide < pSideLimit; pSide++ )
  621. {
  622. if ( pSide->bBevel )
  623. continue;
  624. float flDist = DotProduct( pSide->plane->normal, p ) - pSide->plane->dist;
  625. // outside plane, no intersection
  626. if ( flDist > 0.0f )
  627. {
  628. bInside = false;
  629. break;
  630. }
  631. }
  632. if ( bInside )
  633. {
  634. nContents |= pBrush->contents;
  635. }
  636. }
  637. }
  638. // point wasn't inside any brushes so return empty
  639. return nContents;
  640. }
  641. /*
  642. ==================
  643. CM_TransformedPointContents
  644. Handles offseting and rotation of the end points for moving and
  645. rotating entities
  646. ==================
  647. */
  648. int CM_TransformedPointContents ( const Vector& p, int headnode, const Vector& origin, QAngle const& angles)
  649. {
  650. Vector p_l;
  651. Vector temp;
  652. Vector forward, right, up;
  653. int l;
  654. // get the current collision bsp -- there is only one!
  655. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  656. // subtract origin offset
  657. VectorSubtract (p, origin, p_l);
  658. // rotate start and end into the models frame of reference
  659. if ( angles[0] || angles[1] || angles[2] )
  660. {
  661. AngleVectors (angles, &forward, &right, &up);
  662. VectorCopy (p_l, temp);
  663. p_l[0] = DotProduct (temp, forward);
  664. p_l[1] = -DotProduct (temp, right);
  665. p_l[2] = DotProduct (temp, up);
  666. }
  667. l = CM_PointLeafnum_r (pBSPData, p_l, headnode);
  668. return pBSPData->map_leafs[l].contents;
  669. }
  670. // does this box brush occlude the remaining polygon completely?
  671. bool OccludeWithBoxBrush( COcclusionInfo &oi, const cbrush_t *pBrush, cboxbrush_t *pBox, const char * &pCodePath)
  672. {
  673. // This version works with expanded box. TODO: Implement checking 8 rays
  674. // Constrain the brush to the local region of the cast. To be more conservative, it should be extents...deltaPos-extents, but 0..deltaPos should be good enough, since players shouldn't penetrate walls
  675. if ( pBox->mins.x < oi.m_traceMins.x
  676. && pBox->mins.y < oi.m_traceMins.y
  677. && pBox->mins.z < oi.m_traceMins.z )
  678. {
  679. pCodePath = "before_start";
  680. return false; // brush is somewhere outside of our region of interest: if it occludes the view, it would mean we're partially embedded into this brush, so we'll just say we ignore brushes we're embedded into
  681. }
  682. if ( pBox->maxs.x > oi.m_traceMaxs.x
  683. && pBox->maxs.y > oi.m_traceMaxs.y
  684. && pBox->maxs.z > oi.m_traceMaxs.z )
  685. {
  686. pCodePath = "after_end";
  687. return false; // brush is somewhere outside of our region of interest: if it occludes the view, it would mean we're partially embedded into this brush, so we'll just say we ignore brushes we're embedded into
  688. }
  689. Vector vDeltaBrush = ( pBox->maxs + pBox->mins ) * 0.5f - oi.m_start;
  690. // we operate in the +++ octant
  691. Vector vDeltaBrushPos = ScaleVector( vDeltaBrush, oi.m_deltaSigns );
  692. Vector vBrushExtent = ( pBox->maxs - pBox->mins ) * 0.5f;
  693. //float xy = fabsf( oi.m_delta.x * oi.m_delta.y ) , yz = fabsf( oi.m_delta.y * oi.m_delta.z ), zx = fabsf( oi.m_delta.z * oi.m_delta.x ); // +++ octant values
  694. Vector uvwBrushDist = CrossProduct( oi.m_deltaPos, vDeltaBrushPos ); // this is {(x^d)*b, (y^d)*b, (z^d)*b} = {x*(d^b),y*(d^b),z*(d^b)} where b = delta brush, d = delta (in positive octant)
  695. Vector uvwBrushExtents( oi.m_deltaPos.y * vBrushExtent.z + oi.m_deltaPos.z * vBrushExtent.y, oi.m_deltaPos.z * vBrushExtent.x + oi.m_deltaPos.x * vBrushExtent.z, oi.m_deltaPos.x * vBrushExtent.y + oi.m_deltaPos.y * vBrushExtent.x ); // all extents and their abs projections should be positive or zero
  696. /*
  697. // does the brush completely occlude oi.m_extents?
  698. if ( fabsf( uvwBrushDist.x ) > uvwBrushExtents.x - oi.m_uvwExtents.x
  699. || fabsf( uvwBrushDist.y ) > uvwBrushExtents.y - oi.m_uvwExtents.y
  700. || fabsf( uvwBrushDist.z ) > uvwBrushExtents.z - oi.m_uvwExtents.z
  701. )
  702. {
  703. return false;
  704. }
  705. */
  706. Vector uvwBrushMins = uvwBrushDist - uvwBrushExtents, uvwBrushMaxs = uvwBrushDist + uvwBrushExtents;
  707. // boolean subtraction: oi.uvw - uvwBrush. If we have nothing left in U,V and W, then we've got a full occluder - return true!
  708. // if we have something left in only one dimension, cut that dimension and continue - we have a simple partial occluder that can be considered an infinite stripe
  709. // if we have something left in more than one dimension, then we can't cut anything because the result is a concave polygon, and it's expensive to deal with concave polygons
  710. Vector uvwNewMins = oi.m_uvwMins;
  711. Vector uvwNewMaxs = oi.m_uvwMaxs;
  712. int nEmpties = 0;
  713. for ( int i = 0; i < 3; ++i )
  714. {
  715. if ( uvwNewMins[ i ] >= uvwBrushMins[ i ] )
  716. uvwNewMins[ i ] = Max( uvwNewMins[ i ], uvwBrushMaxs[ i ] );
  717. else if ( uvwNewMaxs[ i ] <= uvwBrushMaxs[ i ] )
  718. uvwNewMaxs[ i ] = Min( uvwNewMaxs[ i ], uvwBrushMins[ i ] );
  719. else
  720. continue;
  721. if ( uvwNewMins[ i ] >= uvwNewMaxs[ i ] )
  722. nEmpties |= 1 << i;
  723. }
  724. if ( oi.m_pResults )
  725. {
  726. // The shadow is ended by this box brush, so the end is enclosed in it... very conservative, horrible approximation! this will crash and burn because the boxes are so big, so I need to write another code path for this case
  727. oi.m_pResults->vEndMin = pBox->mins;
  728. oi.m_pResults->vEndMax = pBox->maxs;
  729. }
  730. switch ( nEmpties )
  731. {
  732. case 7:
  733. pCodePath = "full_occluder";
  734. return true; // Success - we found our full occluder!
  735. case 6:
  736. pCodePath = "partial_occluder_Uyz";
  737. oi.m_uvwMins.x = uvwNewMins.x;
  738. oi.m_uvwMaxs.x = uvwNewMaxs.x;
  739. break;
  740. case 5:
  741. pCodePath = "partial_occluder_Vzx";
  742. oi.m_uvwMins.y = uvwNewMins.y;
  743. oi.m_uvwMaxs.y = uvwNewMaxs.y;
  744. break;
  745. case 3:
  746. pCodePath = "partial_occluder_Wxy";
  747. oi.m_uvwMins.z = uvwNewMins.z;
  748. oi.m_uvwMaxs.z = uvwNewMaxs.z;
  749. break;
  750. default:
  751. pCodePath = "skipped";
  752. return false;
  753. }
  754. return false;
  755. }
  756. /*
  757. ===============================================================================
  758. BOX TRACING
  759. ===============================================================================
  760. */
  761. // Custom SIMD implementation for box brushes
  762. const fltx4 Four_DistEpsilons={DIST_EPSILON,DIST_EPSILON,DIST_EPSILON,DIST_EPSILON};
  763. const int32 ALIGN16 g_CubeFaceIndex0[4] ALIGN16_POST = {0,1,2,-1};
  764. const int32 ALIGN16 g_CubeFaceIndex1[4] ALIGN16_POST = {3,4,5,-1};
  765. bool IntersectRayWithBoxBrush( TraceInfo_t *pTraceInfo, const cbrush_t *pBrush, cboxbrush_t *pBox )
  766. {
  767. // Load the unaligned ray/box parameters into SIMD registers
  768. fltx4 start = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  769. fltx4 extents = LoadUnaligned3SIMD(pTraceInfo->m_extents.Base());
  770. fltx4 delta = LoadUnaligned3SIMD(pTraceInfo->m_delta.Base());
  771. fltx4 boxMins = LoadAlignedSIMD( pBox->mins.Base() );
  772. fltx4 boxMaxs = LoadAlignedSIMD( pBox->maxs.Base() );
  773. // compute the mins/maxs of the box expanded by the ray extents
  774. // relocate the problem so that the ray start is at the origin.
  775. fltx4 offsetMins = SubSIMD(boxMins, start);
  776. fltx4 offsetMaxs = SubSIMD(boxMaxs, start);
  777. fltx4 offsetMinsExpanded = SubSIMD( offsetMins, extents );
  778. fltx4 offsetMaxsExpanded = AddSIMD( offsetMaxs, extents );
  779. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  780. // of any of the box sides - if so there can be no intersection
  781. bi32x4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  782. bi32x4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  783. bi32x4 minsMask = AndSIMD( startOutMins, endOutMins );
  784. bi32x4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  785. bi32x4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  786. bi32x4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  787. if ( IsAnyTrue(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  788. return false;
  789. bi32x4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  790. // now build the per-axis interval of t for intersections
  791. fltx4 invDelta = LoadUnaligned3SIMD(pTraceInfo->m_invDelta.Base());
  792. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  793. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  794. // now sort the interval per axis
  795. fltx4 mint = MinSIMD( tmins, tmaxs );
  796. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  797. // only axes where we cross a plane are relevant
  798. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  799. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  800. // now find the intersection of the intervals on all axes
  801. fltx4 firstOut = FindLowestSIMD3(maxt);
  802. fltx4 lastIn = FindHighestSIMD3(mint);
  803. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  804. firstOut = MinSIMD(firstOut, Four_Ones);
  805. lastIn = MaxSIMD(lastIn, Four_Zeros);
  806. // If the final interval is valid lastIn<firstOut, check for separation
  807. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  808. if ( IsAllZeros(separation) )
  809. {
  810. bool bStartOut = IsAnyTrue(SetWToZeroSIMD(OrSIMD(startOutMins,startOutMaxs)));
  811. offsetMinsExpanded = SubSIMD(offsetMinsExpanded, Four_DistEpsilons);
  812. offsetMaxsExpanded = AddSIMD(offsetMaxsExpanded, Four_DistEpsilons);
  813. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  814. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  815. fltx4 minface0 = LoadAlignedSIMD( (float *) g_CubeFaceIndex0 );
  816. fltx4 minface1 = LoadAlignedSIMD( (float *) g_CubeFaceIndex1 );
  817. bi32x4 faceMask = CmpLeSIMD( tmins, tmaxs );
  818. fltx4 mint = MinSIMD( tmins, tmaxs );
  819. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  820. fltx4 faceId = MaskedAssign( faceMask, minface0, minface1 );
  821. // only axes where we cross a plane are relevant
  822. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  823. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  824. fltx4 firstOutTmp = FindLowestSIMD3(maxt);
  825. // implement FindHighest of 3, but use intermediate masks to find the
  826. // corresponding index in faceId to the highest at the same time
  827. fltx4 compareOne = RotateLeft( mint );
  828. faceMask = CmpGtSIMD(mint, compareOne);
  829. // compareOne is [y,z,G,x]
  830. fltx4 max_xy = MaxSIMD( mint, compareOne );
  831. fltx4 faceRot = RotateLeft(faceId);
  832. fltx4 faceId_xy = MaskedAssign(faceMask, faceId, faceRot);
  833. // max_xy is [max(x,y), ... ]
  834. compareOne = RotateLeft2( mint );
  835. faceRot = RotateLeft2(faceId);
  836. // compareOne is [z, G, x, y]
  837. faceMask = CmpGtSIMD( max_xy, compareOne );
  838. fltx4 max_xyz = MaxSIMD( max_xy, compareOne );
  839. faceId = MaskedAssign( faceMask, faceId_xy, faceRot );
  840. fltx4 lastInTmp = SplatXSIMD( max_xyz );
  841. fltx4 firstOut = MinSIMD(firstOutTmp, Four_Ones);
  842. fltx4 lastIn = MaxSIMD(lastInTmp, Four_Zeros);
  843. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  844. Assert(IsAllZeros(separation));
  845. if ( IsAllZeros(separation) )
  846. {
  847. uint32 faceIndex = SubInt(faceId, 0);
  848. Assert(faceIndex<6);
  849. float t1 = SubFloat(lastIn,0);
  850. trace_t * RESTRICT pTrace = &pTraceInfo->m_trace;
  851. // this condition is copied from the brush case to avoid hitting an assert and
  852. // overwriting a previous start solid with a new shorter fraction
  853. if ( bStartOut && pTraceInfo->m_ispoint && pTrace->fractionleftsolid > t1 )
  854. {
  855. bStartOut = false;
  856. }
  857. if ( !bStartOut )
  858. {
  859. float t2 = SubFloat(firstOut,0);
  860. pTrace->startsolid = true;
  861. pTrace->contents = pBrush->contents;
  862. if ( t2 >= 1.0f )
  863. {
  864. pTrace->allsolid = true;
  865. pTrace->fraction = 0.0f;
  866. }
  867. else if ( t2 > pTrace->fractionleftsolid )
  868. {
  869. pTrace->fractionleftsolid = t2;
  870. if (pTrace->fraction <= t2)
  871. {
  872. pTrace->fraction = 1.0f;
  873. pTrace->surface = pTraceInfo->m_pBSPData->nullsurface;
  874. }
  875. }
  876. }
  877. else
  878. {
  879. static const int signbits[3]={1,2,4};
  880. if ( t1 < pTrace->fraction )
  881. {
  882. pTraceInfo->m_bDispHit = false;
  883. pTrace->fraction = t1;
  884. pTrace->plane.normal = vec3_origin;
  885. pTrace->surface = *pTraceInfo->m_pBSPData->GetSurfaceAtIndex( pBox->surfaceIndex[faceIndex] );
  886. pTrace->worldSurfaceIndex = pBox->surfaceIndex[ faceIndex ];
  887. if ( faceIndex >= 3 )
  888. {
  889. faceIndex -= 3;
  890. pTrace->plane.dist = pBox->maxs[faceIndex];
  891. pTrace->plane.normal[faceIndex] = 1.0f;
  892. pTrace->plane.signbits = 0;
  893. }
  894. else
  895. {
  896. pTrace->plane.dist = -pBox->mins[faceIndex];
  897. pTrace->plane.normal[faceIndex] = -1.0f;
  898. pTrace->plane.signbits = signbits[faceIndex];
  899. }
  900. pTrace->plane.type = faceIndex;
  901. pTrace->contents = pBrush->contents;
  902. return true;
  903. }
  904. }
  905. }
  906. }
  907. return false;
  908. }
  909. // slightly different version of the above. This folds in more of the trace_t output because CM_ComputeTraceEndpts() isn't called after this
  910. // so this routine needs to properly compute start/end points and fractions in all cases
  911. bool IntersectRayWithBox( const Ray_t &ray, const VectorAligned &inInvDelta, const VectorAligned &inBoxMins, const VectorAligned &inBoxMaxs, trace_t *RESTRICT pTrace )
  912. {
  913. // mark trace as not hitting
  914. pTrace->startsolid = false;
  915. pTrace->allsolid = false;
  916. pTrace->fraction = 1.0f;
  917. // Load the unaligned ray/box parameters into SIMD registers
  918. fltx4 start = LoadUnaligned3SIMD(ray.m_Start.Base());
  919. fltx4 extents = LoadUnaligned3SIMD(ray.m_Extents.Base());
  920. fltx4 delta = LoadUnaligned3SIMD(ray.m_Delta.Base());
  921. fltx4 boxMins = LoadAlignedSIMD( inBoxMins.Base() );
  922. fltx4 boxMaxs = LoadAlignedSIMD( inBoxMaxs.Base() );
  923. // compute the mins/maxs of the box expanded by the ray extents
  924. // relocate the problem so that the ray start is at the origin.
  925. fltx4 offsetMins = SubSIMD(boxMins, start);
  926. fltx4 offsetMaxs = SubSIMD(boxMaxs, start);
  927. fltx4 offsetMinsExpanded = SubSIMD(offsetMins, extents);
  928. fltx4 offsetMaxsExpanded = AddSIMD(offsetMaxs, extents);
  929. // Check to see if both the origin (start point) and the end point (delta) are on the front side
  930. // of any of the box sides - if so there can be no intersection
  931. bi32x4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded);
  932. bi32x4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded);
  933. bi32x4 minsMask = AndSIMD( startOutMins, endOutMins );
  934. bi32x4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded);
  935. bi32x4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded);
  936. bi32x4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs );
  937. if ( IsAnyTrue(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask))))
  938. return false;
  939. bi32x4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs));
  940. // now build the per-axis interval of t for intersections
  941. fltx4 invDelta = LoadAlignedSIMD(inInvDelta.Base());
  942. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  943. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  944. // now sort the interval per axis
  945. fltx4 mint = MinSIMD( tmins, tmaxs );
  946. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  947. // only axes where we cross a plane are relevant
  948. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  949. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  950. // now find the intersection of the intervals on all axes
  951. fltx4 firstOut = FindLowestSIMD3(maxt);
  952. fltx4 lastIn = FindHighestSIMD3(mint);
  953. // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut]
  954. firstOut = MinSIMD(firstOut, Four_Ones);
  955. lastIn = MaxSIMD(lastIn, Four_Zeros);
  956. // If the final interval is valid lastIn<firstOut, check for separation
  957. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  958. if ( IsAllZeros(separation) )
  959. {
  960. bool bStartOut = IsAnyTrue(SetWToZeroSIMD(OrSIMD(startOutMins,startOutMaxs)));
  961. offsetMinsExpanded = SubSIMD(offsetMinsExpanded, Four_DistEpsilons);
  962. offsetMaxsExpanded = AddSIMD(offsetMaxsExpanded, Four_DistEpsilons);
  963. fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta );
  964. fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta );
  965. fltx4 minface0 = LoadAlignedSIMD( (float *) g_CubeFaceIndex0 );
  966. fltx4 minface1 = LoadAlignedSIMD( (float *) g_CubeFaceIndex1 );
  967. bi32x4 faceMask = CmpLeSIMD( tmins, tmaxs );
  968. fltx4 mint = MinSIMD( tmins, tmaxs );
  969. fltx4 maxt = MaxSIMD( tmins, tmaxs );
  970. fltx4 faceId = MaskedAssign( faceMask, minface0, minface1 );
  971. // only axes where we cross a plane are relevant
  972. mint = MaskedAssign( crossPlane, mint, Four_Negative_FLT_MAX );
  973. maxt = MaskedAssign( crossPlane, maxt, Four_FLT_MAX );
  974. fltx4 firstOutTmp = FindLowestSIMD3(maxt);
  975. //fltx4 lastInTmp = FindHighestSIMD3(mint);
  976. // implement FindHighest of 3, but use intermediate masks to find the
  977. // corresponding index in faceId to the highest at the same time
  978. fltx4 compareOne = RotateLeft( mint );
  979. faceMask = CmpGtSIMD(mint, compareOne);
  980. // compareOne is [y,z,G,x]
  981. fltx4 max_xy = MaxSIMD( mint, compareOne );
  982. fltx4 faceRot = RotateLeft(faceId);
  983. fltx4 faceId_xy = MaskedAssign(faceMask, faceId, faceRot);
  984. // max_xy is [max(x,y), ... ]
  985. compareOne = RotateLeft2( mint );
  986. faceRot = RotateLeft2(faceId);
  987. // compareOne is [z, G, x, y]
  988. faceMask = CmpGtSIMD( max_xy, compareOne );
  989. fltx4 max_xyz = MaxSIMD( max_xy, compareOne );
  990. faceId = MaskedAssign( faceMask, faceId_xy, faceRot );
  991. fltx4 lastInTmp = SplatXSIMD( max_xyz );
  992. fltx4 firstOut = MinSIMD(firstOutTmp, Four_Ones);
  993. fltx4 lastIn = MaxSIMD(lastInTmp, Four_Zeros);
  994. bi32x4 separation = CmpGtSIMD(lastIn, firstOut);
  995. Assert(IsAllZeros(separation));
  996. if ( IsAllZeros(separation) )
  997. {
  998. uint32 faceIndex = SubInt(faceId, 0);
  999. Assert(faceIndex<6);
  1000. float t1 = SubFloat(lastIn,0);
  1001. // this condition is copied from the brush case to avoid hitting an assert and
  1002. // overwriting a previous start solid with a new shorter fraction
  1003. if ( bStartOut && ray.m_IsRay && pTrace->fractionleftsolid > t1 )
  1004. {
  1005. bStartOut = false;
  1006. }
  1007. if ( !bStartOut )
  1008. {
  1009. float t2 = SubFloat(firstOut,0);
  1010. pTrace->startsolid = true;
  1011. pTrace->contents = CONTENTS_SOLID;
  1012. pTrace->fraction = 0.0f;
  1013. pTrace->startpos = ray.m_Start + ray.m_StartOffset;
  1014. pTrace->endpos = pTrace->startpos;
  1015. if ( t2 >= 1.0f )
  1016. {
  1017. pTrace->allsolid = true;
  1018. }
  1019. else if ( t2 > pTrace->fractionleftsolid )
  1020. {
  1021. pTrace->fractionleftsolid = t2;
  1022. pTrace->startpos += ray.m_Delta * pTrace->fractionleftsolid;
  1023. }
  1024. return true;
  1025. }
  1026. else
  1027. {
  1028. static const int signbits[3]={1,2,4};
  1029. if ( t1 <= 1.0f )
  1030. {
  1031. pTrace->fraction = t1;
  1032. pTrace->plane.normal = vec3_origin;
  1033. if ( faceIndex >= 3 )
  1034. {
  1035. faceIndex -= 3;
  1036. pTrace->plane.dist = inBoxMaxs[faceIndex];
  1037. pTrace->plane.normal[faceIndex] = 1.0f;
  1038. pTrace->plane.signbits = 0;
  1039. }
  1040. else
  1041. {
  1042. pTrace->plane.dist = -inBoxMins[faceIndex];
  1043. pTrace->plane.normal[faceIndex] = -1.0f;
  1044. pTrace->plane.signbits = signbits[faceIndex];
  1045. }
  1046. pTrace->plane.type = faceIndex;
  1047. pTrace->contents = CONTENTS_SOLID;
  1048. Vector start;
  1049. VectorAdd( ray.m_Start, ray.m_StartOffset, start );
  1050. if (pTrace->fraction == 1)
  1051. {
  1052. VectorAdd(start, ray.m_Delta, pTrace->endpos);
  1053. }
  1054. else
  1055. {
  1056. VectorMA( start, pTrace->fraction, ray.m_Delta, pTrace->endpos );
  1057. }
  1058. return true;
  1059. }
  1060. }
  1061. }
  1062. }
  1063. return false;
  1064. }
  1065. //const fltx4 Four_DistEpsilons = { DIST_EPSILON, DIST_EPSILON, DIST_EPSILON, DIST_EPSILON };
  1066. const fltx4 Four_NegDistEpsilons = { -DIST_EPSILON, -DIST_EPSILON, -DIST_EPSILON, -DIST_EPSILON };
  1067. const fltx4 Four_NegEpsilons = { -FLT_EPSILON, -FLT_EPSILON, -FLT_EPSILON, -FLT_EPSILON };
  1068. const fltx4 Four_NegOnes = { -1.0, -1.0, -1.0, -1.0 };
  1069. FORCEINLINE fltx4 ShuffleABXY( const fltx4 &abcd, const fltx4 &xyzw )
  1070. {
  1071. return _mm_shuffle_ps( abcd, xyzw, MM_SHUFFLE_REV( 0, 1, 0, 1 ) );
  1072. }
  1073. FORCEINLINE fltx4 ShuffleCDZW( const fltx4 &abcd, const fltx4 &xyzw )
  1074. {
  1075. return _mm_shuffle_ps( abcd, xyzw, MM_SHUFFLE_REV( 2,3,2,3 ) );
  1076. }
  1077. FORCEINLINE fltx4 ShuffleXZYW( const fltx4 &xyzw )
  1078. {
  1079. return _mm_shuffle_ps( xyzw, xyzw, MM_SHUFFLE_REV( 0,2,1,3 ) );
  1080. }
  1081. FORCEINLINE fltx4 ShuffleYWXZ( const fltx4 &xyzw )
  1082. {
  1083. return _mm_shuffle_ps( xyzw, xyzw, MM_SHUFFLE_REV( 1, 3, 0, 2 ) );
  1084. }
  1085. FORCEINLINE fltx4 ShuffleZWXY( const fltx4 &xyzw )
  1086. {
  1087. return _mm_shuffle_ps( xyzw, xyzw, MM_SHUFFLE_REV( 2, 3, 0, 1 ) );
  1088. }
  1089. FORCEINLINE fltx4 ShuffleYZWX( const fltx4 &xyzw )
  1090. {
  1091. return _mm_shuffle_ps( xyzw, xyzw, MM_SHUFFLE_REV( 1, 2, 3, 0 ) );
  1092. }
  1093. FORCEINLINE fltx4 ShuffleAAXX( const fltx4 &abcd, const fltx4 &xyzw )
  1094. {
  1095. return _mm_shuffle_ps( abcd, xyzw, MM_SHUFFLE_REV( 0,0, 0,0 ) );
  1096. }
  1097. FORCEINLINE fltx4 ShuffleCCZZ( const fltx4 &abcd, const fltx4 &xyzw )
  1098. {
  1099. return _mm_shuffle_ps( abcd, xyzw, MM_SHUFFLE_REV( 2,2,2,2 ) );
  1100. }
  1101. FORCEINLINE int IntersectOcclusionInterval( fltx4 d0[ 2 ], fltx4 d1[ 2 ], fltx4 f4EnterNum[ 2 ], fltx4 f4EnterDenum[ 2 ], fltx4 f4LeaveNum[ 2 ], fltx4 f4LeaveDenum[ 2 ] )
  1102. {
  1103. // We enter the polytope if d0-d1 >= 0, and leave if d0-d1 < 0. Note that the math works when d0-d1==0: we enter at -inf or +inf correctly. -inf is ignored, +inf means we never enter, the line is outside of the polytope and we can terminate. The same line of reasoning works with the Leave side
  1104. fltx4 delta[ 2 ] = { d0[ 0 ] - d1[ 0 ], d0[ 1 ] - d1[ 1 ] };
  1105. fltx4 isLeaving[ 2 ] = { CmpLtSIMD( delta[ 0 ], Four_Zeros ), CmpLtSIMD( delta[ 1 ], Four_Zeros ) }; // is the ray leaving the hemispace (moving outside the plane, moving along the normal; movement is from d0 to d1)?
  1106. fltx4 gtEnter[ 2 ] = { CmpLtSIMD( f4EnterNum[ 0 ] * delta[ 0 ], d0[ 0 ] * f4EnterDenum[ 0 ] ), CmpLtSIMD( f4EnterNum[ 1 ] * delta[ 1 ], d0[ 1 ] * f4EnterDenum[ 1 ] ) }; // valid when is Entering (!isLeaving) only
  1107. fltx4 ltLeave[ 2 ] = { CmpGtSIMD( f4LeaveNum[ 0 ] * delta[ 0 ], d0[ 0 ] * f4LeaveDenum[ 0 ] ), CmpGtSIMD( f4LeaveNum[ 1 ] * delta[ 1 ], d0[ 1 ] * f4LeaveDenum[ 1 ] ) }; // valid when isLeaving only : is the d0/delta < Leave? then we'll update the Leave value
  1108. fltx4 maskNewLeave[ 2 ] = { AndSIMD( isLeaving[ 0 ], ltLeave[ 0 ] ), AndSIMD( isLeaving[ 1 ], ltLeave[ 1 ] ) };
  1109. f4LeaveNum[ 0 ] = MaskedAssign( maskNewLeave[ 0 ], d0[ 0 ], f4LeaveNum[ 0 ] );
  1110. f4LeaveNum[ 1 ] = MaskedAssign( maskNewLeave[ 1 ], d0[ 1 ], f4LeaveNum[ 1 ] );
  1111. f4LeaveDenum[ 0 ] = MaskedAssign( maskNewLeave[ 0 ], delta[ 0 ], f4LeaveDenum[ 0 ] );
  1112. f4LeaveDenum[ 1 ] = MaskedAssign( maskNewLeave[ 1 ], delta[ 1 ], f4LeaveDenum[ 1 ] );
  1113. fltx4 maskNewEnter[ 2 ] = { AndNotSIMD( isLeaving[ 0 ], gtEnter[ 0 ] ), AndNotSIMD( isLeaving[ 1 ], gtEnter[ 1 ] ) };
  1114. f4EnterNum[ 0 ] = MaskedAssign( maskNewEnter[ 0 ], d0[ 0 ], f4EnterNum[ 0 ] );
  1115. f4EnterNum[ 1 ] = MaskedAssign( maskNewEnter[ 1 ], d0[ 1 ], f4EnterNum[ 1 ] );
  1116. f4EnterDenum[ 0 ] = MaskedAssign( maskNewEnter[ 0 ], delta[ 0 ], f4EnterDenum[ 0 ] );
  1117. f4EnterDenum[ 1 ] = MaskedAssign( maskNewEnter[ 1 ], delta[ 1 ], f4EnterDenum[ 1 ] );
  1118. fltx4 maskEnterAfterLeave[ 2 ] = { CmpLtSIMD( f4EnterNum[ 0 ] * f4LeaveDenum[ 0 ], f4EnterDenum[ 0 ] * f4LeaveNum[ 0 ] ), CmpLtSIMD( f4EnterNum[ 1 ] * f4LeaveDenum[ 1 ], f4EnterDenum[ 1 ] * f4LeaveNum[ 1 ] ) };
  1119. return TestSignSIMD( OrSIMD( maskEnterAfterLeave[ 0 ], maskEnterAfterLeave[ 1 ] ) );
  1120. // non-0 means one of the rays enters after it leaves the polytope, i.e. it's not intersected by polytope, so exit early (ish)
  1121. // non-0 means "not occluded"
  1122. }
  1123. static fltx4 Four_OneAndRcpMargin = { 1.001f, 1.001f, 1.001f, 1.001f };
  1124. static fltx4 Four_NegRcpMargin = { -0.001f, -0.001f, -0.001f, -0.001f };
  1125. bool CM_BrushOcclusionPass( COcclusionInfo &oi, const cbrush_t * RESTRICT brush )
  1126. {
  1127. fltx4 fracHit[ 2 ] = { Four_Ones, Four_Ones };
  1128. if ( brush->IsBox() )
  1129. {
  1130. cboxbrush_t *pBox = &oi.m_pBSPData->map_boxbrushes[ brush->GetBox() ];
  1131. if ( oi.m_pDebugLog )
  1132. {
  1133. oi.m_pDebugLog->AddBox( CFmtStr( "hit%04d_box_brush%d", oi.m_pDebugLog->GetPrimCount(), brush->GetBox() ).Get(), "relevant", pBox->mins, pBox->maxs );
  1134. }
  1135. /*
  1136. if ( !oi.m_pResults )
  1137. {
  1138. const char *pCodePath;
  1139. bool bFullyOccluded = OccludeWithBoxBrush( oi, brush, pBox, pCodePath );
  1140. return bFullyOccluded;
  1141. }
  1142. */
  1143. fltx4 f4Mins = LoadUnaligned3SIMD( &pBox->mins );
  1144. fltx4 f4Maxs = LoadUnaligned3SIMD( &pBox->maxs );
  1145. // d12_XnXYnY means d1 (the height of the end point), above the plane 2 (+Y: {+X,-X,+Y,-Y,+Z,-Z}[2]), kept in SIMD in the order {+X,-X,+Y,-Y}
  1146. // for +X plane: (+Y is the same)
  1147. // d0 = start.x - max.x, d1 = end.x - max.x
  1148. fltx4 maxXXYY = ShuffleXXYY( f4Maxs );
  1149. fltx4 d0U = oi.m_StartXnXYnY - maxXXYY, d1U = oi.m_EndXnXYnY - maxXXYY; // U = 0_XnXYnY, plane {+X,+X,+Y,+Y} dot point {posX, negX, posY, negY}
  1150. // for -X plane: (-Y is the same)
  1151. // d0 = min.x - start.x, d1 = min.x - end.x
  1152. fltx4 minXXYY = ShuffleXXYY( f4Mins );
  1153. fltx4 d0V = minXXYY - oi.m_StartXnXYnY, d1V = minXXYY - oi.m_EndXnXYnY; // V = 1_XnXYnY, plane {-X,-X,-Y,-Y} dot point {posX, negX, posY, negY}
  1154. // for +Z plane:
  1155. // d01 = {start.Z - max.z, start.nZ - max.z, end.Z - max.z, end.nZ - max.z }
  1156. fltx4 d01Z = oi.m_StartEndZnZ - SplatZSIMD( f4Maxs ), d01nZ = SplatZSIMD( f4Mins ) - oi.m_StartEndZnZ;
  1157. fltx4 d0ZnZ = ShuffleABXY(d01Z, d01nZ), d1ZnZ = ShuffleCDZW(d01Z, d01nZ); // ZnZZnZ, plane { +Z, +Z, -Z, -Z } dot point {posZ, negZ, posZ, negZ}
  1158. // every single ray/interval must lie, at least partially, behind each plane (negatively). At least one of d0 and d1, for both X and nX shifts, must be negative.
  1159. // If there's a single one interval with both d0 >= 0 and d1 >= 0 it means there's a ray that misses this box completely, and we don't have full occlusion
  1160. if ( TestSignSIMD( AndSIMD( AndSIMD( OrSIMD( d0U, d1U ), OrSIMD( d0V, d1V ) ), OrSIMD( d0ZnZ, d1ZnZ ) ) ) != 15 )
  1161. {
  1162. // there's a separating axis along one of the orts, both d0 and d1 lie on one side of it
  1163. return false;
  1164. }
  1165. // PERF: Even though the following code is gigantic, it's not executed for many boxes
  1166. // We enter the polytope if d1-d2 >= 0, and leave if d1-d2 < 0. Note that the math works when d1-d2==0: we enter at -inf or +inf correctly. -inf is ignored, +inf means we never enter, the line is outside of the polytope and we can terminate. The same line of reasoning works with the Leave side
  1167. {
  1168. fltx4 deltaU = d0U - d1U; // this should be the same as deltaV, with flipped signs
  1169. Assert( IsAllGreaterThanOrEq( Four_DistEpsilons, AbsSIMD( deltaU + ( d0V - d1V ) ) ) );
  1170. fltx4 deltaZ = d0ZnZ - d1ZnZ; // same with +Z and -Z planes
  1171. Assert( IsAllGreaterThanOrEq( Four_DistEpsilons, AbsSIMD( deltaZ + ShuffleZWXY( deltaZ ) ) ) );
  1172. //fltx4 isDirPosU = CmpGtSIMD( deltaU, Four_Zeros );// , isDirNegU = CmpLtSIMD( deltaU, Four_NegEpsilons ); // Pos => use U over V; Neg => use V over U
  1173. //fltx4 isDirPosZ = CmpGtSIMD( deltaZ, Four_Zeros );// , isDirNegZ = CmpLtSIMD( deltaZ, Four_NegEpsilons ); // note: this mask is valid in the XY and inverted in ZW
  1174. //fltx4 deltaUabs = AbsSIMD( deltaU ), deltaZabs = AbsSIMD( deltaZ );
  1175. //fltx4 isNonParallelU = CmpGtSIMD( deltaUabs, Four_Epsilons ), isNonParallelZ = CmpGtSIMD( deltaZabs, Four_Epsilons );
  1176. fltx4 deltaUrcp = ReciprocalEstSIMD( deltaU ), deltaZrcp = ReciprocalEstSIMD( deltaZ );
  1177. //fltx4 fracU = AndSIMD( isNonParallelU, MaskedAssign( isDirPosU, d0U, d0V ) * deltaUrcp ), fracZ = AndSIMD( isNonParallelZ, MaskedAssign( isDirPosZ, d0ZnZ, ShuffleZWXY( d0ZnZ ) ) * deltaZrcp );
  1178. fltx4 f4Size = f4Maxs - f4Mins;
  1179. fltx4 fracUa = d0U * deltaUrcp, fracUb = ( d0U + ShuffleXXYY( f4Size ) ) * deltaUrcp;
  1180. fltx4 fracUmin = MinSIMD( fracUa, fracUb ), fracUmax = MaxSIMD( fracUa, fracUb );
  1181. fltx4 fracZa = d01Z * deltaZrcp, fracZb = ( d01Z + SplatZSIMD( f4Size ) ) * deltaZrcp;
  1182. fltx4 fracZmin = MinSIMD( fracZa, fracZb ), fracZmax = MaxSIMD( fracZa, fracZb );
  1183. Assert( IsAllGreaterThanOrEq( Four_OneAndRcpMargin, MaxSIMD( fracUmin, fracZmin ) ) ); // at least one ray starts further than 1 along at least one dimension? This shouldn't happen
  1184. // There may be large-ish round-off errors due to using reciprocal estimates (deltaUrcp and deltaZrcp)
  1185. // There are 8 rays, in all combinations of +- with X, Y, Z - those are the intervals we'll need to intersect:
  1186. // +-,+-,+-: fracU01, fracU23, fracZ01
  1187. // so, fracMin[2] = { { max(u0,u2,z0),max(u1,u2,z0),max(u0,u3,z0), max(u1, u3, z0) }, { max(u0,u2,z1),max(u1,u2,z1),max(u0,u3,z1), max(u1, u3, z1) } }
  1188. fltx4 fracUminTmp = MaxSIMD( ShuffleXYXY( fracUmin ), ShuffleZZWW( fracUmin ) ), fracUmaxTmp = MinSIMD( ShuffleXYXY( fracUmax ), ShuffleZZWW( fracUmax ) );
  1189. fracHit[ 0 ] = MaxSIMD( Four_Zeros, MaxSIMD( fracUminTmp, SplatXSIMD( fracZmin ) ) ); fracHit[ 1 ] = MaxSIMD( Four_Zeros, MaxSIMD( fracUminTmp, SplatYSIMD( fracZmin ) ) );
  1190. fltx4 fracHitMax[ 2 ] = { MinSIMD( fracUmaxTmp, SplatXSIMD( fracZmax ) ), MinSIMD( fracUmaxTmp, SplatYSIMD( fracZmax ) ) };
  1191. // now we need to intersect all found intervals, and if we end up with empty intersection or starting beyond +1, then there's at least one ray that doesn't get blocked and we have no full occlusion
  1192. if ( TestSignSIMD( OrSIMD( fracHitMax[ 0 ] - fracHit[ 0 ], fracHitMax[ 1 ] - fracHit[ 1 ] ) ) )
  1193. {
  1194. // one of the intervals is empty, ray hasn't hit the box, exit
  1195. return false;
  1196. }
  1197. }
  1198. }
  1199. else// support for non-box brushes
  1200. {
  1201. cbrushside_t * RESTRICT pSidesBegin = &oi.m_pBSPData->map_brushsides[ brush->firstbrushside ], *pSide = pSidesBegin;
  1202. if ( oi.m_pDebugLog )
  1203. {
  1204. oi.m_pDebugLog->AddBrush( CFmtStr( "hit%04d_brush%d_%dside", oi.m_pDebugLog->GetPrimCount(), brush->firstbrushside, brush->numsides ).Get(), "relevant", pSidesBegin, brush->numsides );
  1205. }
  1206. fltx4 f4EnterNum[ 2 ] = { Four_Zeros, Four_Zeros }, f4EnterDenum[ 2 ] = { Four_Ones, Four_Ones };
  1207. fltx4 f4LeaveNum[ 2 ] = { Four_NegOnes, Four_NegOnes }, f4LeaveDenum[ 2 ] = { Four_NegOnes, Four_NegOnes };
  1208. for ( const cbrushside_t * const pSidesEnd = pSide + brush->numsides; pSide < pSidesEnd; pSide++ )
  1209. {
  1210. // don't trace rays against bevel planes
  1211. if ( pSide->bBevel )
  1212. continue;
  1213. DbgAssert( uintp( &pSide->plane->dist ) - uintp( &pSide->plane->normal ) == 12 ); // dist must follow the normal exactly
  1214. fltx4 plane = LoadUnalignedSIMD( &pSide->plane->normal );
  1215. //fltx4 dist = SplatWSIMD( plane );
  1216. //fltx4 planeNormalX = SplatXSIMD( plane ), planeNormalY = SplatYSIMD( plane ), planeNormalZ = SplatZSIMD( plane );
  1217. // fltx4 d0[ 2 ] = { oi.GetStartX() * planeNormalX + oi.GetStartY() * planeNormalY + oi.GetStartZ0() * planeNormalZ - dist, oi.GetStartX() * planeNormalX + oi.GetStartY() * planeNormalY + oi.GetStartZ1() * planeNormalZ - dist };
  1218. fltx4 planeXXYY = ShuffleXXYY( plane );
  1219. fltx4 startPlaneXnXYnY = oi.m_StartXnXYnY * planeXXYY, endPlaneXnXYnY = oi.m_EndXnXYnY * planeXXYY, startEndPlaneZnZ_dist = oi.m_StartEndZnZ * SplatZSIMD( plane ) - SplatWSIMD( plane );
  1220. fltx4 d0xy = ShuffleXYXY( startPlaneXnXYnY ) + ShuffleZZWW( startPlaneXnXYnY );
  1221. fltx4 d0[ 2 ] = { d0xy + SplatXSIMD( startEndPlaneZnZ_dist ), d0xy + SplatYSIMD( startEndPlaneZnZ_dist ) };
  1222. fltx4 d1xy = ShuffleXYXY( endPlaneXnXYnY ) + ShuffleZZWW( endPlaneXnXYnY );
  1223. fltx4 d1[ 2 ] = { d1xy + SplatZSIMD( startEndPlaneZnZ_dist ), d1xy + SplatWSIMD( startEndPlaneZnZ_dist ) };
  1224. if ( ( TestSignSIMD( OrSIMD( d0[ 0 ], d1[ 0 ] ) ) & TestSignSIMD( OrSIMD( d0[ 1 ], d1[ 1 ] ) ) ) != 15 )
  1225. {
  1226. // we found the separating plane for one of the 8 rays.
  1227. // That ray's d1 >= 0 && d2 >= 0, it lies completely outside the brush side, so it doesn't intersect the brush, guaranteed.
  1228. // Therefore the whole probe is not occluded.
  1229. // Therefore we can return false ("not occluded")
  1230. return false;
  1231. }
  1232. if ( IntersectOcclusionInterval( d0, d1, f4EnterNum, f4EnterDenum, f4LeaveNum, f4LeaveDenum ) )
  1233. {
  1234. // non-0 means one of the rays enters after it leaves the polytope, i.e. it's not intersected by polytope, so exit early (ish)
  1235. // non-0 means "not occluded"
  1236. return false;
  1237. }
  1238. }
  1239. fracHit[ 0 ] = f4EnterNum[ 0 ] * ReciprocalEstSIMD( f4EnterDenum[ 0 ] );
  1240. fracHit[ 1 ] = f4EnterNum[ 1 ] * ReciprocalEstSIMD( f4EnterDenum[ 1 ] );
  1241. };
  1242. if ( oi.m_pResults )
  1243. {
  1244. // compute the bbox of the ends of the rays. This is only executed at most once per occlusion query, so it's not a critical path
  1245. fltx4 fracAft[ 2 ] = { Four_Ones - fracHit[ 0 ], Four_Ones - fracHit[ 1 ] };
  1246. Assert( IsAllGreaterThanOrEq( fracHit[ 0 ], Four_Zeros ) && IsAllGreaterThanOrEq( fracHit[ 1 ], Four_Zeros ) && IsAllGreaterThanOrEq( Four_OneAndRcpMargin, fracHit[ 0 ] ) && IsAllGreaterThanOrEq( Four_OneAndRcpMargin, fracHit[ 1 ] ) );
  1247. Assert( IsAllGreaterThanOrEq( fracAft[ 0 ], Four_NegRcpMargin ) && IsAllGreaterThanOrEq( fracAft[ 1 ], Four_NegRcpMargin ) && IsAllGreaterThanOrEq( Four_Ones, fracAft[ 0 ] ) && IsAllGreaterThanOrEq( Four_Ones, fracAft[ 1 ] ) );
  1248. // the 8 start points' coordinates will need to multiply by enter; the end points' coordinates will need to multiply by 1-enter
  1249. fltx4 startX = oi.GetStartX(), endX = oi.GetEndX();
  1250. fltx4 hitX[ 2 ] = { fracHit[ 0 ] * endX + fracAft[ 0 ] * startX, fracHit[ 1 ] * endX + fracAft[ 1 ] * startX };
  1251. fltx4 minXXXX = MinSIMD( hitX[ 0 ], hitX[ 1 ] ), maxXXXX = MaxSIMD( hitX[ 0 ], hitX[ 1 ] ); // 4 values will need to be collapsed to the horizontal min/max in each register
  1252. fltx4 startY = oi.GetStartY(), endY = oi.GetEndY();
  1253. fltx4 hitY[ 2 ] = { fracHit[ 0 ] * endY + fracAft[ 0 ] * startY, fracHit[ 1 ] * endY + fracAft[ 1 ] * startY };
  1254. fltx4 minYYYY = MinSIMD( hitY[ 0 ], hitY[ 1 ] ), maxYYYY = MaxSIMD( hitY[ 0 ], hitY[ 1 ] ); // 4 values will need to be collapsed to the horizontal min/max in each register
  1255. fltx4 minXXYY = MinSIMD( ShuffleABXY( minXXXX, minYYYY ), ShuffleCDZW( minXXXX, minYYYY ) ), maxXXYY = MaxSIMD( ShuffleABXY( maxXXXX, maxYYYY ), ShuffleCDZW( maxXXXX, maxYYYY ) );
  1256. fltx4 minXYXY = MinSIMD( ShuffleXZYW( minXXYY ), ShuffleYWXZ( minXXYY ) ), maxXYXY = MaxSIMD( ShuffleXZYW( maxXXYY ), ShuffleYWXZ( maxXXYY ) );
  1257. fltx4 hitZ[ 2 ] = { fracHit[ 0 ] * oi.GetEndZ0() + fracAft[ 0 ] * oi.GetStartZ0(), fracHit[ 1 ] * oi.GetEndZ1() + fracAft[ 1 ] * oi.GetStartZ1() };
  1258. fltx4 minZZZZ = MinSIMD( hitZ[ 0 ], hitZ[ 1 ] ), maxZZZZ = MaxSIMD( hitZ[ 0 ], hitZ[ 1 ] ); // 4 values will need to be collapsed to the horizontal min/max in each register
  1259. fltx4 minZZ = MinSIMD( minZZZZ, ShuffleZWXY( minZZZZ ) ), maxZZ = MaxSIMD( maxZZZZ, ShuffleZWXY( maxZZZZ ) );
  1260. fltx4 minZ = MinSIMD( minZZ, ShuffleYZWX( minZZ ) ), maxZ = MaxSIMD( maxZZ, ShuffleYZWX( maxZZ ) );
  1261. fltx4 minXYZZ = ShuffleABXY( minXYXY, minZ );
  1262. StoreAligned3SIMD( &oi.m_pResults->vEndMin, minXYZZ );
  1263. fltx4 maxXYZZ = ShuffleABXY( maxXYXY, maxZ );
  1264. StoreAligned3SIMD( &oi.m_pResults->vEndMax, maxXYZZ );
  1265. Assert( IsAllGreaterThanOrEq( maxXYZZ, minXYZZ ) );
  1266. }
  1267. return true; // yes, we didn't exit early, so it means all rays are intersected by the polytope, which means we're completely occluded
  1268. }
  1269. /*
  1270. ================
  1271. CM_ClipBoxToBrush
  1272. ================
  1273. */
  1274. template <bool IS_POINT>
  1275. void FASTCALL CM_ClipBoxToBrush( TraceInfo_t * RESTRICT pTraceInfo, const cbrush_t * RESTRICT brush )
  1276. {
  1277. if ( brush->IsBox() )
  1278. {
  1279. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[brush->GetBox()];
  1280. IntersectRayWithBoxBrush( pTraceInfo, brush, pBox );
  1281. return;
  1282. }
  1283. if (!brush->numsides)
  1284. return;
  1285. trace_t * RESTRICT trace = &pTraceInfo->m_trace;
  1286. const Vector& p1 = pTraceInfo->m_start;
  1287. const Vector& p2= pTraceInfo->m_end;
  1288. int brushContents = brush->contents;
  1289. #ifdef COUNT_COLLISIONS
  1290. g_CollisionCounts.m_BrushTraces++;
  1291. #endif
  1292. float enterfrac = NEVER_UPDATED;
  1293. float leavefrac = 1.f;
  1294. bool getout = false;
  1295. bool startout = false;
  1296. cbrushside_t* leadside = NULL;
  1297. float dist;
  1298. cbrushside_t * RESTRICT side = &pTraceInfo->m_pBSPData->map_brushsides[brush->firstbrushside];
  1299. for ( const cbrushside_t * const sidelimit = side + brush->numsides; side < sidelimit; side++ )
  1300. {
  1301. cplane_t *plane = side->plane;
  1302. const Vector &planeNormal = plane->normal;
  1303. if (!IS_POINT)
  1304. {
  1305. // general box case
  1306. // push the plane out apropriately for mins/maxs
  1307. dist = DotProductAbs( planeNormal, pTraceInfo->m_extents );
  1308. dist = plane->dist + dist;
  1309. }
  1310. else
  1311. {
  1312. // special point case
  1313. dist = plane->dist;
  1314. // don't trace rays against bevel planes
  1315. if ( side->bBevel )
  1316. continue;
  1317. }
  1318. float d1 = DotProduct (p1, planeNormal) - dist;
  1319. float d2 = DotProduct (p2, planeNormal) - dist;
  1320. // if completely in front of face, no intersection
  1321. if( d1 > 0.f )
  1322. {
  1323. startout = true;
  1324. // d1 > 0.f && d2 > 0.f
  1325. if( d2 > 0.f )
  1326. return;
  1327. }
  1328. else
  1329. {
  1330. // d1 <= 0.f && d2 <= 0.f
  1331. if( d2 <= 0.f )
  1332. continue;
  1333. // d2 > 0.f
  1334. getout = true;
  1335. }
  1336. // crosses face
  1337. if (d1 > d2)
  1338. { // enter
  1339. // NOTE: This could be negative if d1 is less than the epsilon.
  1340. // If the trace is short (d1-d2 is small) then it could produce a large
  1341. // negative fraction.
  1342. float f = (d1-DIST_EPSILON);
  1343. if ( f < 0.f )
  1344. f = 0.f;
  1345. f = f / (d1-d2);
  1346. if (f > enterfrac)
  1347. {
  1348. enterfrac = f;
  1349. leadside = side;
  1350. }
  1351. }
  1352. else
  1353. { // leave
  1354. float f = (d1+DIST_EPSILON) / (d1-d2);
  1355. if (f < leavefrac)
  1356. leavefrac = f;
  1357. }
  1358. }
  1359. // when this happens, we entered the brush *after* leaving the previous brush.
  1360. // Therefore, we're still outside!
  1361. // NOTE: We only do this test against points because fractionleftsolid is
  1362. // not possible to compute for brush sweeps without a *lot* more computation
  1363. // So, client code will never get fractionleftsolid for box sweeps
  1364. if (IS_POINT && startout)
  1365. {
  1366. // Add a little sludge. The sludge should already be in the fractionleftsolid
  1367. // (for all intents and purposes is a leavefrac value) and enterfrac values.
  1368. // Both of these values have +/- DIST_EPSILON values calculated in. Thus, I
  1369. // think the test should be against "0.0." If we experience new "left solid"
  1370. // problems you may want to take a closer look here!
  1371. // if ((trace->fractionleftsolid - enterfrac) > -1e-6)
  1372. if ((trace->fractionleftsolid - enterfrac) > 0.0f )
  1373. startout = false;
  1374. }
  1375. if (!startout)
  1376. { // original point was inside brush
  1377. trace->startsolid = true;
  1378. // return starting contents
  1379. trace->contents = brushContents;
  1380. if (!getout)
  1381. {
  1382. trace->allsolid = true;
  1383. trace->fraction = 0.0f;
  1384. trace->fractionleftsolid = 1.0f;
  1385. }
  1386. else
  1387. {
  1388. // if leavefrac == 1, this means it's never been updated or we're in allsolid
  1389. // the allsolid case was handled above
  1390. if ((leavefrac != 1) && (leavefrac > trace->fractionleftsolid))
  1391. {
  1392. trace->fractionleftsolid = leavefrac;
  1393. // This could occur if a previous trace didn't start us in solid
  1394. if (trace->fraction <= leavefrac)
  1395. {
  1396. trace->fraction = 1.0f;
  1397. trace->surface = pTraceInfo->m_pBSPData->nullsurface;
  1398. }
  1399. }
  1400. }
  1401. return;
  1402. }
  1403. // We haven't hit anything at all until we've left...
  1404. if (enterfrac < leavefrac)
  1405. {
  1406. if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
  1407. {
  1408. // WE HIT SOMETHING!!!!!
  1409. if (enterfrac < 0)
  1410. enterfrac = 0;
  1411. trace->fraction = enterfrac;
  1412. pTraceInfo->m_bDispHit = false;
  1413. trace->plane = *(leadside->plane);
  1414. trace->surface = *pTraceInfo->m_pBSPData->GetSurfaceAtIndex( leadside->surfaceIndex );
  1415. trace->worldSurfaceIndex = leadside->surfaceIndex;
  1416. trace->contents = brushContents;
  1417. }
  1418. }
  1419. }
  1420. inline bool IsTraceBoxIntersectingBoxBrush( TraceInfo_t *pTraceInfo, cboxbrush_t *pBox )
  1421. {
  1422. fltx4 start = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  1423. fltx4 mins = LoadUnaligned3SIMD(pTraceInfo->m_mins.Base());
  1424. fltx4 maxs = LoadUnaligned3SIMD(pTraceInfo->m_maxs.Base());
  1425. fltx4 boxMins = LoadAlignedSIMD( pBox->mins.Base() );
  1426. fltx4 boxMaxs = LoadAlignedSIMD( pBox->maxs.Base() );
  1427. fltx4 offsetMins = AddSIMD(mins, start);
  1428. fltx4 offsetMaxs = AddSIMD(maxs,start);
  1429. fltx4 minsOut = MaxSIMD(boxMins, offsetMins);
  1430. fltx4 maxsOut = MinSIMD(boxMaxs, offsetMaxs);
  1431. bi32x4 separated = CmpGtSIMD(minsOut, maxsOut);
  1432. bi32x4 sep3 = SetWToZeroSIMD(separated);
  1433. return IsAllZeros(sep3);
  1434. }
  1435. /*
  1436. ================
  1437. CM_TestBoxInBrush
  1438. ================
  1439. */
  1440. void FASTCALL CM_TestBoxInBrush( TraceInfo_t *pTraceInfo, const cbrush_t *brush )
  1441. {
  1442. if ( brush->IsBox())
  1443. {
  1444. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[brush->GetBox()];
  1445. if ( !IsTraceBoxIntersectingBoxBrush( pTraceInfo, pBox ) )
  1446. return;
  1447. }
  1448. else
  1449. {
  1450. if (!brush->numsides)
  1451. return;
  1452. const Vector& mins = pTraceInfo->m_mins;
  1453. const Vector& maxs = pTraceInfo->m_maxs;
  1454. const Vector& p1 = pTraceInfo->m_start;
  1455. int i, j;
  1456. cplane_t *plane;
  1457. float dist;
  1458. Vector ofs(0,0,0);
  1459. float d1;
  1460. cbrushside_t *side;
  1461. for (i=0 ; i<brush->numsides ; i++)
  1462. {
  1463. side = &pTraceInfo->m_pBSPData->map_brushsides[brush->firstbrushside+i];
  1464. plane = side->plane;
  1465. // FIXME: special case for axial
  1466. // general box case
  1467. // push the plane out appropriately for mins/maxs
  1468. // FIXME: use signbits into 8 way lookup for each mins/maxs
  1469. for (j=0 ; j<3 ; j++)
  1470. {
  1471. if (plane->normal[j] < 0)
  1472. ofs[j] = maxs[j];
  1473. else
  1474. ofs[j] = mins[j];
  1475. }
  1476. dist = DotProduct (ofs, plane->normal);
  1477. dist = plane->dist - dist;
  1478. d1 = DotProduct (p1, plane->normal) - dist;
  1479. // if completely in front of face, no intersection
  1480. if (d1 > 0)
  1481. return;
  1482. }
  1483. }
  1484. // inside this brush
  1485. trace_t *trace = &pTraceInfo->m_trace;
  1486. trace->startsolid = trace->allsolid = true;
  1487. trace->fraction = 0;
  1488. trace->fractionleftsolid = 1.0f;
  1489. trace->contents = brush->contents;
  1490. }
  1491. template<bool IS_POINT, bool CHECK_COUNTERS>
  1492. FORCEINLINE_TEMPLATE void CM_TraceToDispList( TraceInfo_t * RESTRICT pTraceInfo, const unsigned short *pDispList, int dispListCount, float startFrac, float endFrac )
  1493. {
  1494. VPROF("CM_TraceToDispList");
  1495. //
  1496. // trace ray/swept box against all displacement surfaces in this leaf
  1497. //
  1498. TraceCounter_t * RESTRICT pCounters = 0;
  1499. TraceCounter_t count = 0;
  1500. if ( CHECK_COUNTERS )
  1501. {
  1502. pCounters = pTraceInfo->GetDispCounters();
  1503. count = pTraceInfo->GetCount();
  1504. }
  1505. if ( IsX360() || IsPS3() )
  1506. {
  1507. // set up some relatively constant variables we'll use in the loop below
  1508. fltx4 traceStart = LoadUnaligned3SIMD(pTraceInfo->m_start.Base());
  1509. fltx4 traceDelta = LoadUnaligned3SIMD(pTraceInfo->m_delta.Base());
  1510. fltx4 traceInvDelta = LoadUnaligned3SIMD(pTraceInfo->m_invDelta.Base());
  1511. static const fltx4 vecEpsilon = {DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON};
  1512. // only used in !IS_POINT version:
  1513. fltx4 extents;
  1514. if (!IS_POINT)
  1515. {
  1516. extents = LoadUnaligned3SIMD(pTraceInfo->m_extents.Base());
  1517. }
  1518. // TODO: this loop probably ought to be unrolled so that we can make a more efficient
  1519. // job of intersecting rays against boxes. The simple SIMD version used here,
  1520. // though about 6x faster than the fpu version, is slower still than intersecting
  1521. // against four boxes simultaneously.
  1522. for( int i = 0; i < dispListCount; i++ )
  1523. {
  1524. int dispIndex = pDispList[i];
  1525. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  1526. // only collide with objects you are interested in
  1527. if( !( pDispBounds->GetContents() & pTraceInfo->m_contents ) )
  1528. continue;
  1529. if( CHECK_COUNTERS && pTraceInfo->m_isswept )
  1530. {
  1531. // make sure we only check this brush once per trace/stab
  1532. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  1533. continue;
  1534. }
  1535. if ( IS_POINT )
  1536. {
  1537. if (!IsBoxIntersectingRay( LoadAlignedSIMD(pDispBounds->mins.Base()), LoadAlignedSIMD(pDispBounds->maxs.Base()),
  1538. traceStart, traceDelta, traceInvDelta, vecEpsilon ))
  1539. continue;
  1540. }
  1541. else
  1542. {
  1543. fltx4 mins = SubSIMD(LoadAlignedSIMD(pDispBounds->mins.Base()),extents);
  1544. fltx4 maxs = AddSIMD(LoadAlignedSIMD(pDispBounds->maxs.Base()),extents);
  1545. if (!IsBoxIntersectingRay( mins, maxs,
  1546. traceStart, traceDelta, traceInvDelta, vecEpsilon ))
  1547. continue;
  1548. }
  1549. CDispCollTree * RESTRICT pDispTree = &g_pDispCollTrees[dispIndex];
  1550. CM_TraceToDispTree<IS_POINT>( pTraceInfo, pDispTree, startFrac, endFrac );
  1551. if( !pTraceInfo->m_trace.fraction )
  1552. break;
  1553. }
  1554. }
  1555. else
  1556. {
  1557. // utterly nonoptimal FPU pathway
  1558. for( int i = 0; i < dispListCount; i++ )
  1559. {
  1560. int dispIndex = pDispList[i];
  1561. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  1562. // only collide with objects you are interested in
  1563. if( !( pDispBounds->GetContents() & pTraceInfo->m_contents ) )
  1564. continue;
  1565. if( CHECK_COUNTERS && pTraceInfo->m_isswept )
  1566. {
  1567. // make sure we only check this brush once per trace/stab
  1568. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  1569. continue;
  1570. }
  1571. if ( IS_POINT && !IsBoxIntersectingRay( pDispBounds->mins, pDispBounds->maxs, pTraceInfo->m_start, pTraceInfo->m_delta, pTraceInfo->m_invDelta, DISPCOLL_DIST_EPSILON ) )
  1572. {
  1573. continue;
  1574. }
  1575. if ( !IS_POINT && !IsBoxIntersectingRay( pDispBounds->mins - pTraceInfo->m_extents, pDispBounds->maxs + pTraceInfo->m_extents,
  1576. pTraceInfo->m_start, pTraceInfo->m_delta, pTraceInfo->m_invDelta, DISPCOLL_DIST_EPSILON ) )
  1577. {
  1578. continue;
  1579. }
  1580. CDispCollTree * RESTRICT pDispTree = &g_pDispCollTrees[dispIndex];
  1581. CM_TraceToDispTree<IS_POINT>( pTraceInfo, pDispTree, startFrac, endFrac );
  1582. if( !pTraceInfo->m_trace.fraction )
  1583. break;
  1584. }
  1585. }
  1586. CM_PostTraceToDispTree( pTraceInfo );
  1587. }
  1588. bool IsNoDrawBrush( CCollisionBSPData *pBSPData, const int relevantContents, const int traceContents, const cbrush_t * RESTRICT pBrush )
  1589. {
  1590. // Many traces rely on CONTENTS_OPAQUE always being hit, even if it is nodraw. AI blocklos brushes
  1591. // need this, for instance. CS and Terror visibility checks don't want this behavior, since
  1592. // blocklight brushes also are CONTENTS_OPAQUE and SURF_NODRAW, and are actually in the playable
  1593. // area in several maps.
  1594. // NOTE: This is no longer true - no traces should rely on hitting CONTENTS_OPAQUE unless they
  1595. // actually want to hit blocklight brushes. No other brushes are marked with those bits
  1596. // so it should be renamed CONTENTS_BLOCKLIGHT. CONTENTS_BLOCKLOS has its own field now
  1597. // so there is no reason to ignore nodraw opaques since you can merely remove CONTENTS_OPAQUE to
  1598. // get that behavior
  1599. if ( relevantContents == CONTENTS_OPAQUE && ( traceContents & CONTENTS_IGNORE_NODRAW_OPAQUE ) )
  1600. {
  1601. // if the only reason we hit this brush is because it is opaque, make sure it isn't nodraw
  1602. if ( pBrush->IsBox() )
  1603. {
  1604. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[ pBrush->GetBox() ];
  1605. for ( int i = 0; i < 6; i++ )
  1606. {
  1607. csurface_t *surface = pBSPData->GetSurfaceAtIndex( pBox->surfaceIndex[ i ] );
  1608. if ( surface->flags & SURF_NODRAW )
  1609. {
  1610. return true;
  1611. }
  1612. }
  1613. }
  1614. else
  1615. {
  1616. cbrushside_t *side = &pBSPData->map_brushsides[ pBrush->firstbrushside ];
  1617. for ( int i = 0; i < pBrush->numsides; i++, side++ )
  1618. {
  1619. csurface_t *surface = pBSPData->GetSurfaceAtIndex( side->surfaceIndex );
  1620. if ( surface->flags & SURF_NODRAW )
  1621. {
  1622. return true;
  1623. }
  1624. }
  1625. }
  1626. }
  1627. return false;
  1628. }
  1629. FORCEINLINE_TEMPLATE bool CM_BrushListOcclusionPass( COcclusionInfo &oi, const unsigned short *pBrushList, int brushListCount )
  1630. {
  1631. //
  1632. // trace ray/box sweep against all brushes in this leaf
  1633. //
  1634. CRangeValidatedArray<cbrush_t> & map_brushes = oi.m_pBSPData->map_brushes;
  1635. for ( int ndxLeafBrush = 0; ndxLeafBrush < brushListCount; ndxLeafBrush++ )
  1636. {
  1637. // get the current brush
  1638. int ndxBrush = pBrushList[ ndxLeafBrush ];
  1639. cbrush_t * RESTRICT pBrush = &map_brushes[ ndxBrush ];
  1640. const int traceContents = oi.m_contents;
  1641. const int relevantContents = ( pBrush->contents & traceContents );
  1642. // only collide with objects you are interested in
  1643. if ( !relevantContents )
  1644. continue;
  1645. if ( !oi.PrepareCheckBrush( ndxBrush ) )
  1646. continue; // already checked this brush
  1647. if ( IsNoDrawBrush( oi.m_pBSPData, relevantContents, traceContents, pBrush ) )
  1648. continue;
  1649. // trace against the brush and find impact point -- if any?
  1650. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  1651. if( CM_BrushOcclusionPass( oi, pBrush ) )
  1652. return true;
  1653. }
  1654. return false; // nothing fully occludes the path
  1655. }
  1656. template <bool IS_POINT, bool CHECK_COUNTERS>
  1657. FORCEINLINE_TEMPLATE void CM_TraceToBrushList( TraceInfo_t * RESTRICT pTraceInfo, const unsigned short *pBrushList, int brushListCount )
  1658. {
  1659. //
  1660. // trace ray/box sweep against all brushes in this leaf
  1661. //
  1662. CRangeValidatedArray<cbrush_t> & map_brushes = pTraceInfo->m_pBSPData->map_brushes;
  1663. TraceCounter_t * RESTRICT pCounters = NULL;
  1664. TraceCounter_t count = 0;
  1665. if ( CHECK_COUNTERS )
  1666. {
  1667. pCounters = pTraceInfo->GetBrushCounters();
  1668. count = pTraceInfo->GetCount();
  1669. }
  1670. for( int ndxLeafBrush = 0; ndxLeafBrush < brushListCount; ndxLeafBrush++ )
  1671. {
  1672. // get the current brush
  1673. int ndxBrush = pBrushList[ndxLeafBrush];
  1674. cbrush_t * RESTRICT pBrush = &map_brushes[ndxBrush];
  1675. // make sure we only check this brush once per trace/stab
  1676. if ( CHECK_COUNTERS && !pTraceInfo->Visit( pBrush, ndxBrush, count, pCounters ) )
  1677. continue;
  1678. const int traceContents = pTraceInfo->m_contents;
  1679. const int releventContents = ( pBrush->contents & traceContents );
  1680. // only collide with objects you are interested in
  1681. if( !releventContents )
  1682. continue;
  1683. // Many traces rely on CONTENTS_OPAQUE always being hit, even if it is nodraw. AI blocklos brushes
  1684. // need this, for instance. CS and Terror visibility checks don't want this behavior, since
  1685. // blocklight brushes also are CONTENTS_OPAQUE and SURF_NODRAW, and are actually in the playable
  1686. // area in several maps.
  1687. // NOTE: This is no longer true - no traces should rely on hitting CONTENTS_OPAQUE unless they
  1688. // actually want to hit blocklight brushes. No other brushes are marked with those bits
  1689. // so it should be renamed CONTENTS_BLOCKLIGHT. CONTENTS_BLOCKLOS has its own field now
  1690. // so there is no reason to ignore nodraw opaques since you can merely remove CONTENTS_OPAQUE to
  1691. // get that behavior
  1692. if ( releventContents == CONTENTS_OPAQUE && (traceContents & CONTENTS_IGNORE_NODRAW_OPAQUE) )
  1693. {
  1694. // if the only reason we hit this brush is because it is opaque, make sure it isn't nodraw
  1695. bool isNoDraw = false;
  1696. if ( pBrush->IsBox())
  1697. {
  1698. cboxbrush_t *pBox = &pTraceInfo->m_pBSPData->map_boxbrushes[pBrush->GetBox()];
  1699. for (int i=0 ; i<6 && !isNoDraw ;i++)
  1700. {
  1701. csurface_t *surface = pTraceInfo->m_pBSPData->GetSurfaceAtIndex( pBox->surfaceIndex[i] );
  1702. if ( surface->flags & SURF_NODRAW )
  1703. {
  1704. isNoDraw = true;
  1705. break;
  1706. }
  1707. }
  1708. }
  1709. else
  1710. {
  1711. cbrushside_t *side = &pTraceInfo->m_pBSPData->map_brushsides[pBrush->firstbrushside];
  1712. for (int i=0 ; i<pBrush->numsides && !isNoDraw ;i++, side++)
  1713. {
  1714. csurface_t *surface = pTraceInfo->m_pBSPData->GetSurfaceAtIndex( side->surfaceIndex );
  1715. if ( surface->flags & SURF_NODRAW )
  1716. {
  1717. isNoDraw = true;
  1718. break;
  1719. }
  1720. }
  1721. }
  1722. if ( isNoDraw )
  1723. {
  1724. continue;
  1725. }
  1726. }
  1727. // trace against the brush and find impact point -- if any?
  1728. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  1729. CM_ClipBoxToBrush<IS_POINT>( pTraceInfo, pBrush );
  1730. if( !pTraceInfo->m_trace.fraction )
  1731. return;
  1732. }
  1733. }
  1734. bool FASTCALL CM_LeafOcclusionPass( COcclusionInfo &oi, int ndxLeaf, float startFrac, float endFrac )
  1735. {
  1736. // get the leaf
  1737. cleaf_t * RESTRICT pLeaf = &oi.m_pBSPData->map_leafs[ ndxLeaf ];
  1738. if ( pLeaf->numleafbrushes )
  1739. {
  1740. const unsigned short *pBrushList = &oi.m_pBSPData->map_leafbrushes[pLeaf->firstleafbrush];
  1741. return CM_BrushListOcclusionPass( oi, pBrushList, pLeaf->numleafbrushes );
  1742. }
  1743. // ToDo: A pass over displacement surfaces in this leaf.
  1744. return false;
  1745. }
  1746. /*
  1747. ================
  1748. CM_TraceToLeaf
  1749. ================
  1750. */
  1751. template <bool IS_POINT>
  1752. void FASTCALL CM_TraceToLeaf( TraceInfo_t * RESTRICT pTraceInfo, int ndxLeaf, float startFrac, float endFrac )
  1753. {
  1754. VPROF("CM_TraceToLeaf");
  1755. // get the leaf
  1756. cleaf_t * RESTRICT pLeaf = &pTraceInfo->m_pBSPData->map_leafs[ndxLeaf];
  1757. if ( pLeaf->numleafbrushes )
  1758. {
  1759. const unsigned short *pBrushList = &pTraceInfo->m_pBSPData->map_leafbrushes[pLeaf->firstleafbrush];
  1760. CM_TraceToBrushList<IS_POINT, true>( pTraceInfo, pBrushList, pLeaf->numleafbrushes );
  1761. // TODO: this may be redundant
  1762. if( pTraceInfo->m_trace.startsolid )
  1763. return;
  1764. }
  1765. // Collide (test) against displacement surfaces in this leaf.
  1766. if( pLeaf->dispCount )
  1767. {
  1768. unsigned short *pDispList = &pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart];
  1769. CM_TraceToDispList<IS_POINT, true>( pTraceInfo, pDispList, pLeaf->dispCount, startFrac, endFrac );
  1770. }
  1771. }
  1772. void FASTCALL CM_GetTraceDataForLeaf( TraceInfo_t * RESTRICT pTraceInfo, int ndxLeaf, CTraceListData &traceData )
  1773. {
  1774. // get the leaf
  1775. cleaf_t * RESTRICT pLeaf = &pTraceInfo->m_pBSPData->map_leafs[ndxLeaf];
  1776. if ((pLeaf->contents & CONTENTS_SOLID) == 0)
  1777. {
  1778. traceData.m_bFoundNonSolidLeaf = true;
  1779. }
  1780. //
  1781. // add brushes to list
  1782. if ( 1 )
  1783. {
  1784. const int numleafbrushes = pLeaf->numleafbrushes;
  1785. const int lastleafbrush = pLeaf->firstleafbrush + numleafbrushes;
  1786. const CRangeValidatedArray<unsigned short> &map_leafbrushes = pTraceInfo->m_pBSPData->map_leafbrushes;
  1787. CRangeValidatedArray<cbrush_t> & map_brushes = pTraceInfo->m_pBSPData->map_brushes;
  1788. TraceCounter_t * RESTRICT pCounters = pTraceInfo->GetBrushCounters();
  1789. TraceCounter_t count = pTraceInfo->GetCount();
  1790. for( int ndxLeafBrush = pLeaf->firstleafbrush; ndxLeafBrush < lastleafbrush; ndxLeafBrush++ )
  1791. {
  1792. // get the current brush
  1793. int ndxBrush = map_leafbrushes[ndxLeafBrush];
  1794. cbrush_t * RESTRICT pBrush = &map_brushes[ndxBrush];
  1795. // make sure we only add this brush once
  1796. if ( !pTraceInfo->Visit( pBrush, ndxBrush, count, pCounters ) )
  1797. continue;
  1798. traceData.m_brushList.AddToTail(ndxBrush);
  1799. }
  1800. }
  1801. // add displacements to list
  1802. if ( 1 )
  1803. {
  1804. TraceCounter_t *pCounters = pTraceInfo->GetDispCounters();
  1805. TraceCounter_t count = pTraceInfo->GetCount();
  1806. // Collide (test) against displacement surfaces in this leaf.
  1807. for( int i = 0; i < pLeaf->dispCount; i++ )
  1808. {
  1809. int dispIndex = pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart + i];
  1810. alignedbbox_t * RESTRICT pDispBounds = &g_pDispBounds[dispIndex];
  1811. // make sure we only add this disp once
  1812. if ( !pTraceInfo->Visit( pDispBounds->GetCounter(), count, pCounters ) )
  1813. continue;
  1814. if ( !IsBoxIntersectingBox( pDispBounds->mins, pDispBounds->maxs, traceData.m_mins, traceData.m_maxs ) )
  1815. continue;
  1816. traceData.m_dispList.AddToTail(dispIndex);
  1817. }
  1818. }
  1819. }
  1820. void CM_GetTraceDataForBSP( const Vector &mins, const Vector &maxs, CTraceListData &traceData )
  1821. {
  1822. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  1823. Vector center = (mins+maxs)*0.5f;
  1824. Vector extents = maxs - center;
  1825. int nodenum = 0;
  1826. TraceInfo_t *pTraceInfo = BeginTrace();
  1827. const int NODELIST_MAX = 1024;
  1828. int nodeList[NODELIST_MAX];
  1829. int nodeReadIndex = 0;
  1830. int nodeWriteIndex = 0;
  1831. cplane_t *plane;
  1832. cnode_t *node;
  1833. while (1)
  1834. {
  1835. if (nodenum < 0)
  1836. {
  1837. int leafIndex = -1 - nodenum;
  1838. CM_GetTraceDataForLeaf( pTraceInfo, leafIndex, traceData );
  1839. if ( nodeReadIndex == nodeWriteIndex )
  1840. break;
  1841. nodenum = nodeList[nodeReadIndex];
  1842. nodeReadIndex = (nodeReadIndex+1) & (NODELIST_MAX-1);
  1843. }
  1844. else
  1845. {
  1846. node = &pBSPData->map_rootnode[nodenum];
  1847. plane = node->plane;
  1848. // s = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);
  1849. // s = BOX_ON_PLANE_SIDE(*leaf_mins, *leaf_maxs, plane);
  1850. float d0 = DotProduct( plane->normal, center ) - plane->dist;
  1851. float d1 = DotProductAbs( plane->normal, extents );
  1852. if (d0 >= d1)
  1853. nodenum = node->children[0];
  1854. else if (d0 < -d1)
  1855. nodenum = node->children[1];
  1856. else
  1857. { // go down both
  1858. nodeList[nodeWriteIndex] = node->children[0];
  1859. nodeWriteIndex = (nodeWriteIndex+1) & (NODELIST_MAX-1);
  1860. // check for overflow of the ring buffer
  1861. Assert(nodeWriteIndex != nodeReadIndex);
  1862. nodenum = node->children[1];
  1863. }
  1864. }
  1865. }
  1866. EndTrace(pTraceInfo);
  1867. }
  1868. /*
  1869. ================
  1870. CM_TestInLeaf
  1871. ================
  1872. */
  1873. static void FASTCALL CM_TestInLeaf( TraceInfo_t *pTraceInfo, int ndxLeaf )
  1874. {
  1875. // get the leaf
  1876. cleaf_t *pLeaf = &pTraceInfo->m_pBSPData->map_leafs[ndxLeaf];
  1877. //
  1878. // trace ray/box sweep against all brushes in this leaf
  1879. //
  1880. TraceCounter_t *pCounters = pTraceInfo->GetBrushCounters();
  1881. TraceCounter_t count = pTraceInfo->GetCount();
  1882. for( int ndxLeafBrush = 0; ndxLeafBrush < pLeaf->numleafbrushes; ndxLeafBrush++ )
  1883. {
  1884. // get the current brush
  1885. int ndxBrush = pTraceInfo->m_pBSPData->map_leafbrushes[pLeaf->firstleafbrush+ndxLeafBrush];
  1886. cbrush_t *pBrush = &pTraceInfo->m_pBSPData->map_brushes[ndxBrush];
  1887. // make sure we only check this brush once per trace/stab
  1888. if ( !pTraceInfo->Visit( pBrush, ndxBrush, count, pCounters ) )
  1889. continue;
  1890. // only collide with objects you are interested in
  1891. if( !( pBrush->contents & pTraceInfo->m_contents ) )
  1892. continue;
  1893. //
  1894. // test to see if the point/box is inside of any solid
  1895. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  1896. //
  1897. CM_TestBoxInBrush( pTraceInfo, pBrush );
  1898. if( !pTraceInfo->m_trace.fraction )
  1899. return;
  1900. }
  1901. // TODO: this may be redundant
  1902. if( pTraceInfo->m_trace.startsolid )
  1903. return;
  1904. // if there are no displacement surfaces in this leaf -- we are done testing
  1905. if( pLeaf->dispCount )
  1906. {
  1907. // test to see if the point/box is inside of any of the displacement surface
  1908. unsigned short *pDispList = &pTraceInfo->m_pBSPData->map_dispList[pLeaf->dispListStart];
  1909. CM_TestInDispTree( pTraceInfo, pDispList, pLeaf->dispCount, pTraceInfo->m_start, pTraceInfo->m_mins, pTraceInfo->m_maxs, pTraceInfo->m_contents, &pTraceInfo->m_trace );
  1910. }
  1911. }
  1912. //-----------------------------------------------------------------------------
  1913. // Computes the ray endpoints given a trace.
  1914. //-----------------------------------------------------------------------------
  1915. static inline void CM_ComputeTraceEndpoints( const Ray_t& ray, trace_t& tr )
  1916. {
  1917. // The ray start is the center of the extents; compute the actual start
  1918. Vector start;
  1919. VectorAdd( ray.m_Start, ray.m_StartOffset, start );
  1920. if (tr.fraction == 1)
  1921. VectorAdd(start, ray.m_Delta, tr.endpos);
  1922. else
  1923. VectorMA( start, tr.fraction, ray.m_Delta, tr.endpos );
  1924. if (tr.fractionleftsolid == 0)
  1925. {
  1926. VectorCopy (start, tr.startpos);
  1927. }
  1928. else
  1929. {
  1930. if (tr.fractionleftsolid == 1.0f)
  1931. {
  1932. tr.startsolid = tr.allsolid = 1;
  1933. tr.fraction = 0.0f;
  1934. VectorCopy( start, tr.endpos );
  1935. }
  1936. VectorMA( start, tr.fractionleftsolid, ray.m_Delta, tr.startpos );
  1937. }
  1938. }
  1939. //-----------------------------------------------------------------------------
  1940. // Purpose: Get a list of leaves for a trace.
  1941. //-----------------------------------------------------------------------------
  1942. void CM_RayLeafnums_r( const Ray_t &ray, CCollisionBSPData *pBSPData, int iNode,
  1943. float p1f, float p2f, const Vector &vecPoint1, const Vector &vecPoint2,
  1944. int *pLeafList, int nMaxLeafCount, int &nLeafCount )
  1945. {
  1946. cnode_t *pNode = NULL;
  1947. cplane_t *pPlane = NULL;
  1948. float flDist1 = 0.0f, flDist2 = 0.0f;
  1949. float flOffset = 0.0f;
  1950. float flDist;
  1951. float flFrac1, flFrac2;
  1952. int nSide;
  1953. float flMid;
  1954. Vector vecMid;
  1955. // A quick check so we don't flood the message on overflow - or keep testing beyond our means!
  1956. if ( nLeafCount >= nMaxLeafCount )
  1957. return;
  1958. // Find the point distances to the separating plane and the offset for the size of the box.
  1959. // NJS: Hoisted loop invariant comparison to pTraceInfo->m_ispoint
  1960. if( ray.m_IsRay )
  1961. {
  1962. while( iNode >= 0 )
  1963. {
  1964. pNode = pBSPData->map_rootnode + iNode;
  1965. pPlane = pNode->plane;
  1966. if ( pPlane->type < 3 )
  1967. {
  1968. flDist1 = vecPoint1[pPlane->type] - pPlane->dist;
  1969. flDist2 = vecPoint2[pPlane->type] - pPlane->dist;
  1970. flOffset = ray.m_Extents[pPlane->type];
  1971. }
  1972. else
  1973. {
  1974. flDist1 = DotProduct( pPlane->normal, vecPoint1 ) - pPlane->dist;
  1975. flDist2 = DotProduct( pPlane->normal, vecPoint2 ) - pPlane->dist;
  1976. flOffset = 0.0f;
  1977. }
  1978. // See which sides we need to consider
  1979. if ( flDist1 > flOffset && flDist2 > flOffset )
  1980. {
  1981. iNode = pNode->children[0];
  1982. continue;
  1983. }
  1984. if ( flDist1 < -flOffset && flDist2 < -flOffset )
  1985. {
  1986. iNode = pNode->children[1];
  1987. continue;
  1988. }
  1989. break;
  1990. }
  1991. }
  1992. else
  1993. {
  1994. while( iNode >= 0 )
  1995. {
  1996. pNode = pBSPData->map_rootnode + iNode;
  1997. pPlane = pNode->plane;
  1998. if ( pPlane->type < 3 )
  1999. {
  2000. flDist1 = vecPoint1[pPlane->type] - pPlane->dist;
  2001. flDist2 = vecPoint2[pPlane->type] - pPlane->dist;
  2002. flOffset = ray.m_Extents[pPlane->type];
  2003. }
  2004. else
  2005. {
  2006. flDist1 = DotProduct( pPlane->normal, vecPoint1 ) - pPlane->dist;
  2007. flDist2 = DotProduct( pPlane->normal, vecPoint2 ) - pPlane->dist;
  2008. flOffset = fabs( ray.m_Extents[0] * pPlane->normal[0] ) +
  2009. fabs( ray.m_Extents[1] * pPlane->normal[1] ) +
  2010. fabs( ray.m_Extents[2] * pPlane->normal[2] );
  2011. }
  2012. // See which sides we need to consider
  2013. if ( flDist1 > flOffset && flDist2 > flOffset )
  2014. {
  2015. iNode = pNode->children[0];
  2016. continue;
  2017. }
  2018. if ( flDist1 < -flOffset && flDist2 < -flOffset )
  2019. {
  2020. iNode = pNode->children[1];
  2021. continue;
  2022. }
  2023. break;
  2024. }
  2025. }
  2026. // If < 0, we are in a leaf node.
  2027. if ( iNode < 0 )
  2028. {
  2029. if ( nLeafCount < nMaxLeafCount )
  2030. {
  2031. pLeafList[nLeafCount] = -1 - iNode;
  2032. nLeafCount++;
  2033. }
  2034. else
  2035. {
  2036. DevMsg( 1, "CM_RayLeafnums_r: Max leaf count along ray exceeded!\n" );
  2037. }
  2038. return;
  2039. }
  2040. // Put the crosspoint DIST_EPSILON pixels on the near side.
  2041. if ( flDist1 < flDist2 )
  2042. {
  2043. flDist = 1.0 / ( flDist1 - flDist2 );
  2044. nSide = 1;
  2045. flFrac2 = ( flDist1 + flOffset + DIST_EPSILON ) * flDist;
  2046. flFrac1 = ( flDist1 - flOffset - DIST_EPSILON ) * flDist;
  2047. }
  2048. else if ( flDist1 > flDist2 )
  2049. {
  2050. flDist = 1.0 / ( flDist1-flDist2 );
  2051. nSide = 0;
  2052. flFrac2 = ( flDist1 - flOffset - DIST_EPSILON ) * flDist;
  2053. flFrac1 = ( flDist1 + flOffset + DIST_EPSILON ) * flDist;
  2054. }
  2055. else
  2056. {
  2057. nSide = 0;
  2058. flFrac1 = 1.0f;
  2059. flFrac2 = 0.0f;
  2060. }
  2061. // Move up to the node
  2062. flFrac1 = clamp( flFrac1, 0.0f, 1.0f );
  2063. flMid = p1f + ( p2f - p1f ) * flFrac1;
  2064. VectorLerp( vecPoint1, vecPoint2, flFrac1, vecMid );
  2065. CM_RayLeafnums_r( ray, pBSPData, pNode->children[nSide], p1f, flMid, vecPoint1, vecMid, pLeafList, nMaxLeafCount, nLeafCount );
  2066. // Go past the node
  2067. flFrac2 = clamp( flFrac2, 0.0f, 1.0f );
  2068. flMid = p1f + ( p2f - p1f ) * flFrac2;
  2069. VectorLerp( vecPoint1, vecPoint2, flFrac2, vecMid );
  2070. CM_RayLeafnums_r( ray, pBSPData, pNode->children[nSide^1], flMid, p2f, vecMid, vecPoint2, pLeafList, nMaxLeafCount, nLeafCount );
  2071. }
  2072. //-----------------------------------------------------------------------------
  2073. // Purpose:
  2074. //-----------------------------------------------------------------------------
  2075. void CM_RayLeafnums( const Ray_t &ray, int *pLeafList, int nMaxLeafCount, int &nLeafCount )
  2076. {
  2077. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2078. if ( !pBSPData->numnodes )
  2079. return;
  2080. Vector vecEnd;
  2081. VectorAdd( ray.m_Start, ray.m_Delta, vecEnd );
  2082. CM_RayLeafnums_r( ray, pBSPData, 0/*headnode*/, 0.0f, 1.0f, ray.m_Start, vecEnd, pLeafList, nMaxLeafCount, nLeafCount );
  2083. }
  2084. bool FASTCALL CM_RecursiveOcclusionPass( COcclusionInfo &oi, int num, const float p1f, const float p2f, const Vector& p1, const Vector& p2 )
  2085. {
  2086. cnode_t *node = NULL;
  2087. cplane_t *plane;
  2088. float t1 = 0, t2 = 0, offset = 0;
  2089. float frac, frac2;
  2090. float idist;
  2091. Vector mid;
  2092. int side;
  2093. float midf;
  2094. // find the point distances to the separating plane
  2095. // and the offset for the size of the box
  2096. while ( num >= 0 )
  2097. {
  2098. node = oi.m_pBSPData->map_rootnode + num;
  2099. plane = node->plane;
  2100. byte type = plane->type;
  2101. float dist = plane->dist;
  2102. if ( type < 3 )
  2103. {
  2104. t1 = p1[ type ] - dist;
  2105. t2 = p2[ type ] - dist;
  2106. offset = oi.m_extents[ type ];
  2107. }
  2108. else
  2109. {
  2110. t1 = DotProduct( plane->normal, p1 ) - dist;
  2111. t2 = DotProduct( plane->normal, p2 ) - dist;
  2112. offset = fabsf( oi.m_extents[ 0 ] * plane->normal[ 0 ] ) +
  2113. fabsf( oi.m_extents[ 1 ] * plane->normal[ 1 ] ) +
  2114. fabsf( oi.m_extents[ 2 ] * plane->normal[ 2 ] );
  2115. }
  2116. // see which sides we need to consider
  2117. if ( t1 > offset && t2 > offset )
  2118. // if (t1 >= offset && t2 >= offset)
  2119. {
  2120. num = node->children[ 0 ];
  2121. continue;
  2122. }
  2123. if ( t1 < -offset && t2 < -offset )
  2124. {
  2125. num = node->children[ 1 ];
  2126. continue;
  2127. }
  2128. break;
  2129. }
  2130. // if < 0, we are in a leaf node
  2131. if ( num < 0 )
  2132. {
  2133. return CM_LeafOcclusionPass( oi, -1 - num, p1f, p2f );
  2134. }
  2135. // put the crosspoint DIST_EPSILON pixels on the near side
  2136. if ( t1 < t2 )
  2137. {
  2138. idist = 1.0 / ( t1 - t2 );
  2139. side = 1;
  2140. frac2 = ( t1 + offset + DIST_EPSILON )*idist;
  2141. frac = ( t1 - offset - DIST_EPSILON )*idist;
  2142. }
  2143. else if ( t1 > t2 )
  2144. {
  2145. idist = 1.0 / ( t1 - t2 );
  2146. side = 0;
  2147. frac2 = ( t1 - offset - DIST_EPSILON )*idist;
  2148. frac = ( t1 + offset + DIST_EPSILON )*idist;
  2149. }
  2150. else
  2151. {
  2152. side = 0;
  2153. frac = 1;
  2154. frac2 = 0;
  2155. }
  2156. // move up to the node
  2157. frac = clamp( frac, 0, 1 );
  2158. midf = p1f + ( p2f - p1f )*frac;
  2159. VectorLerp( p1, p2, frac, mid );
  2160. if ( CM_RecursiveOcclusionPass( oi, node->children[ side ], p1f, midf, p1, mid ) )
  2161. {
  2162. return true; // found full occlusion
  2163. }
  2164. // go past the node
  2165. frac2 = clamp( frac2, 0, 1 );
  2166. midf = p1f + ( p2f - p1f )*frac2;
  2167. VectorLerp( p1, p2, frac2, mid );
  2168. if ( CM_RecursiveOcclusionPass( oi, node->children[ side ^ 1 ], midf, p2f, mid, p2 ) )
  2169. {
  2170. return true; // found full occlusion
  2171. }
  2172. return false;// didn't find full occlusion yet
  2173. }
  2174. /*
  2175. ==================
  2176. CM_RecursiveHullCheck
  2177. ==================
  2178. Attempt to do whatever is nessecary to get this function to unroll at least once
  2179. */
  2180. template <bool IS_POINT>
  2181. static void FASTCALL CM_RecursiveHullCheckImpl( TraceInfo_t *pTraceInfo, int num, const float p1f, const float p2f, const Vector& p1, const Vector& p2)
  2182. {
  2183. if (pTraceInfo->m_trace.fraction <= p1f)
  2184. return; // already hit something nearer
  2185. cnode_t *node = NULL;
  2186. cplane_t *plane;
  2187. float t1 = 0, t2 = 0, offset = 0;
  2188. float frac, frac2;
  2189. float idist;
  2190. Vector mid;
  2191. int side;
  2192. float midf;
  2193. // find the point distances to the separating plane
  2194. // and the offset for the size of the box
  2195. while( num >= 0 )
  2196. {
  2197. node = pTraceInfo->m_pBSPData->map_rootnode + num;
  2198. plane = node->plane;
  2199. byte type = plane->type;
  2200. float dist = plane->dist;
  2201. if (type < 3)
  2202. {
  2203. t1 = p1[type] - dist;
  2204. t2 = p2[type] - dist;
  2205. offset = pTraceInfo->m_extents[type];
  2206. }
  2207. else
  2208. {
  2209. t1 = DotProduct (plane->normal, p1) - dist;
  2210. t2 = DotProduct (plane->normal, p2) - dist;
  2211. if( IS_POINT )
  2212. {
  2213. offset = 0;
  2214. }
  2215. else
  2216. {
  2217. offset = fabsf(pTraceInfo->m_extents[0]*plane->normal[0]) +
  2218. fabsf(pTraceInfo->m_extents[1]*plane->normal[1]) +
  2219. fabsf(pTraceInfo->m_extents[2]*plane->normal[2]);
  2220. }
  2221. }
  2222. // see which sides we need to consider
  2223. if (t1 > offset && t2 > offset )
  2224. // if (t1 >= offset && t2 >= offset)
  2225. {
  2226. num = node->children[0];
  2227. continue;
  2228. }
  2229. if (t1 < -offset && t2 < -offset)
  2230. {
  2231. num = node->children[1];
  2232. continue;
  2233. }
  2234. break;
  2235. }
  2236. // if < 0, we are in a leaf node
  2237. if (num < 0)
  2238. {
  2239. CM_TraceToLeaf<IS_POINT>(pTraceInfo, -1-num, p1f, p2f);
  2240. return;
  2241. }
  2242. // put the crosspoint DIST_EPSILON pixels on the near side
  2243. if (t1 < t2)
  2244. {
  2245. idist = 1.0/(t1-t2);
  2246. side = 1;
  2247. frac2 = (t1 + offset + DIST_EPSILON)*idist;
  2248. frac = (t1 - offset - DIST_EPSILON)*idist;
  2249. }
  2250. else if (t1 > t2)
  2251. {
  2252. idist = 1.0/(t1-t2);
  2253. side = 0;
  2254. frac2 = (t1 - offset - DIST_EPSILON)*idist;
  2255. frac = (t1 + offset + DIST_EPSILON)*idist;
  2256. }
  2257. else
  2258. {
  2259. side = 0;
  2260. frac = 1;
  2261. frac2 = 0;
  2262. }
  2263. // move up to the node
  2264. frac = clamp( frac, 0, 1 );
  2265. midf = p1f + (p2f - p1f)*frac;
  2266. VectorLerp( p1, p2, frac, mid );
  2267. CM_RecursiveHullCheckImpl<IS_POINT>(pTraceInfo, node->children[side], p1f, midf, p1, mid);
  2268. // go past the node
  2269. frac2 = clamp( frac2, 0, 1 );
  2270. midf = p1f + (p2f - p1f)*frac2;
  2271. VectorLerp( p1, p2, frac2, mid );
  2272. CM_RecursiveHullCheckImpl<IS_POINT>(pTraceInfo, node->children[side^1], midf, p2f, mid, p2);
  2273. }
  2274. void FASTCALL CM_RecursiveHullCheck ( TraceInfo_t *pTraceInfo, int num, const float p1f, const float p2f )
  2275. {
  2276. const Vector& p1 = pTraceInfo->m_start;
  2277. const Vector& p2 = pTraceInfo->m_end;
  2278. if( pTraceInfo->m_ispoint )
  2279. {
  2280. CM_RecursiveHullCheckImpl<true>( pTraceInfo, num, p1f, p2f, p1, p2);
  2281. }
  2282. else
  2283. {
  2284. CM_RecursiveHullCheckImpl<false>( pTraceInfo, num, p1f, p2f, p1, p2);
  2285. }
  2286. }
  2287. void CM_ClearTrace( trace_t *trace )
  2288. {
  2289. memset( trace, 0, sizeof(*trace));
  2290. trace->fraction = 1.f;
  2291. trace->fractionleftsolid = 0;
  2292. trace->surface = CCollisionBSPData::nullsurface;
  2293. }
  2294. //-----------------------------------------------------------------------------
  2295. //
  2296. // The following versions use ray... gradually I'm gonna remove other versions
  2297. //
  2298. //-----------------------------------------------------------------------------
  2299. //-----------------------------------------------------------------------------
  2300. // Test an unswept box
  2301. //-----------------------------------------------------------------------------
  2302. static inline void CM_UnsweptBoxTrace( TraceInfo_t *pTraceInfo, const Ray_t& ray, int headnode, int brushmask )
  2303. {
  2304. int leafs[1024];
  2305. int i, numleafs;
  2306. leafnums_t context;
  2307. context.pLeafList = leafs;
  2308. context.leafTopNode = -1;
  2309. context.leafMaxCount = ARRAYSIZE(leafs);
  2310. context.pBSPData = pTraceInfo->m_pBSPData;
  2311. bool bFoundNonSolidLeaf = false;
  2312. numleafs = CM_BoxLeafnums ( context, ray.m_Start, ray.m_Extents+Vector(1,1,1), headnode);
  2313. for (i=0 ; i<numleafs ; i++)
  2314. {
  2315. if ((pTraceInfo->m_pBSPData->map_leafs[leafs[i]].contents & CONTENTS_SOLID) == 0)
  2316. {
  2317. bFoundNonSolidLeaf = true;
  2318. }
  2319. CM_TestInLeaf ( pTraceInfo, leafs[i] );
  2320. if (pTraceInfo->m_trace.allsolid)
  2321. break;
  2322. }
  2323. if (!bFoundNonSolidLeaf)
  2324. {
  2325. pTraceInfo->m_trace.allsolid = pTraceInfo->m_trace.startsolid = 1;
  2326. pTraceInfo->m_trace.fraction = 0.0f;
  2327. pTraceInfo->m_trace.fractionleftsolid = 1.0f;
  2328. }
  2329. }
  2330. //-----------------------------------------------------------------------------
  2331. // Purpose: Ray/Hull trace against the world without the RecursiveHullTrace
  2332. //-----------------------------------------------------------------------------
  2333. void CM_BoxTraceAgainstLeafList( const Ray_t &ray, const CTraceListData &traceData, int nBrushMask, trace_t &trace )
  2334. {
  2335. VPROF("CM_BoxTraceAgainstLeafList");
  2336. TraceInfo_t *pTraceInfo = BeginTrace();
  2337. CM_ClearTrace(&pTraceInfo->m_trace);
  2338. // Setup global trace data. (This is nasty! I hate this.)
  2339. pTraceInfo->m_bDispHit = false;
  2340. pTraceInfo->m_DispStabDir.Init();
  2341. pTraceInfo->m_contents = nBrushMask;
  2342. VectorCopy( ray.m_Start, pTraceInfo->m_start );
  2343. VectorAdd( ray.m_Start, ray.m_Delta, pTraceInfo->m_end );
  2344. VectorMultiply( ray.m_Extents, -1.0f, pTraceInfo->m_mins );
  2345. VectorCopy( ray.m_Extents, pTraceInfo->m_maxs );
  2346. VectorCopy( ray.m_Extents, pTraceInfo->m_extents );
  2347. pTraceInfo->m_delta = ray.m_Delta;
  2348. pTraceInfo->m_invDelta = ray.InvDelta();
  2349. pTraceInfo->m_ispoint = ray.m_IsRay;
  2350. pTraceInfo->m_isswept = ray.m_IsSwept;
  2351. if ( !ray.m_IsSwept )
  2352. {
  2353. for ( int i = 0; i < traceData.m_brushList.Count(); i++ )
  2354. {
  2355. int brushIndex = traceData.m_brushList[i];
  2356. cbrush_t *pBrush = &pTraceInfo->m_pBSPData->map_brushes[brushIndex];
  2357. // only collide with objects you are interested in
  2358. if( !( pBrush->contents & pTraceInfo->m_contents ) )
  2359. continue;
  2360. //
  2361. // test to see if the point/box is inside of any solid
  2362. // NOTE: pTraceInfo->m_trace.fraction == 0.0f only when trace starts inside of a brush!
  2363. //
  2364. CM_TestBoxInBrush( pTraceInfo, pBrush );
  2365. if ( pTraceInfo->m_trace.allsolid )
  2366. break;
  2367. }
  2368. if( !pTraceInfo->m_trace.startsolid )
  2369. {
  2370. CM_TestInDispTree( pTraceInfo, traceData.m_dispList.Base(), traceData.m_dispList.Count(), pTraceInfo->m_start, pTraceInfo->m_mins, pTraceInfo->m_maxs, pTraceInfo->m_contents, &pTraceInfo->m_trace );
  2371. }
  2372. if (!traceData.m_bFoundNonSolidLeaf)
  2373. {
  2374. pTraceInfo->m_trace.allsolid = pTraceInfo->m_trace.startsolid = 1;
  2375. pTraceInfo->m_trace.fraction = 0.0f;
  2376. pTraceInfo->m_trace.fractionleftsolid = 1.0f;
  2377. }
  2378. }
  2379. else
  2380. {
  2381. if ( ray.m_IsRay )
  2382. {
  2383. CM_TraceToBrushList<true, false>( pTraceInfo, traceData.m_brushList.Base(), traceData.m_brushList.Count() );
  2384. }
  2385. else
  2386. {
  2387. CM_TraceToBrushList<false, false>( pTraceInfo, traceData.m_brushList.Base(), traceData.m_brushList.Count() );
  2388. }
  2389. if ( pTraceInfo->m_trace.fraction > 0 && !pTraceInfo->m_trace.startsolid )
  2390. {
  2391. if ( ray.m_IsRay )
  2392. {
  2393. CM_TraceToDispList<true, false>( pTraceInfo, traceData.m_dispList.Base(), traceData.m_dispList.Count(), 0, 1 );
  2394. }
  2395. else
  2396. {
  2397. CM_TraceToDispList<false, false>( pTraceInfo, traceData.m_dispList.Base(), traceData.m_dispList.Count(), 0, 1 );
  2398. }
  2399. }
  2400. }
  2401. // Compute the trace start and end points.
  2402. CM_ComputeTraceEndpoints( ray, pTraceInfo->m_trace );
  2403. // Copy off the results
  2404. trace = pTraceInfo->m_trace;
  2405. EndTrace( pTraceInfo );
  2406. Assert( !ray.m_IsRay || trace.allsolid || ((trace.fraction + kBoxCheckFloatEpsilon) >= trace.fractionleftsolid) );
  2407. }
  2408. #ifdef _DEBUG
  2409. CON_COMMAND( dump_occlusion_map, "Dump the data used for occlusion testing" )
  2410. {
  2411. CBspDebugLog log("dump_occlusion_map.obj");
  2412. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2413. int nNotBoxes = 0;
  2414. int nRelevantBrushes = 0;
  2415. int traceContents = CONTENTS_SOLID | CONTENTS_MOVEABLE;
  2416. for ( int nBrush = 0; nBrush < pBSPData->numbrushes; ++nBrush )
  2417. {
  2418. const cbrush_t &brush = pBSPData->map_brushes[ nBrush ];
  2419. const int relevantContents = brush.contents & traceContents;
  2420. if ( !relevantContents )
  2421. continue;
  2422. if ( IsNoDrawBrush( pBSPData, relevantContents, traceContents, &brush ) )
  2423. continue;
  2424. ++nRelevantBrushes;
  2425. if ( brush.IsBox() )
  2426. {
  2427. const cboxbrush_t &box = pBSPData->map_boxbrushes[ brush.GetBox() ];
  2428. log.AddBox( CFmtStr( "box%d", brush.GetBox() ).Get(), "relevant", box.mins, box.maxs );
  2429. }
  2430. else
  2431. {
  2432. cbrushside_t * RESTRICT pSidesBegin = &pBSPData->map_brushsides[ brush.firstbrushside ];
  2433. log.AddBrush( CFmtStr( "brush%d_%dside", brush.firstbrushside, brush.numsides ).Get(), "relevant_brush", pSidesBegin, brush.numsides );
  2434. ++nNotBoxes;
  2435. }
  2436. }
  2437. Msg( "%d/%d relevant brushes, %d are not boxes\n", nRelevantBrushes, pBSPData->numbrushes, nNotBoxes );
  2438. }
  2439. #endif
  2440. uint64 COcclusionInfo::s_nAssocArrayCollisions = 0;
  2441. uint64 COcclusionInfo::s_nAssocArrayHits = 0;
  2442. uint64 COcclusionInfo::s_nAssocArrayMisses = 0;
  2443. //uint64 s_nOccluded = 0, s_nUnoccluded = 0;
  2444. const int32 ALIGN16 g_SIMD_0101_signmask[ 4 ] ALIGN16_POST = { 0, 0x80000000, 0, 0x80000000 };
  2445. const int32 ALIGN16 g_SIMD_0011_signmask[ 4 ] ALIGN16_POST = { 0, 0, 0x80000000, 0x80000000 };
  2446. struct OcclusionTestRec_t
  2447. {
  2448. VectorAligned p0;
  2449. VectorAligned vExtents0;
  2450. VectorAligned p1;
  2451. VectorAligned vExtents1;
  2452. };
  2453. ConVar occlusion_test_rays( "occlusion_test_rays", "0", FCVAR_DEVELOPMENTONLY );
  2454. static CUtlVector< OcclusionTestRec_t > s_OcclusionTestRecords;
  2455. static int s_nOcclusionTestsToCollect = 0, s_nOcclusionTestsCollectionsToRun = 0, s_nOcclusionTestCollectionLock = 0;
  2456. CON_COMMAND_F( occlusion_test_record, "dump occlusion tests - useful on server only", FCVAR_CHEAT )
  2457. {
  2458. if ( args.ArgC() < 2 )
  2459. {
  2460. if ( s_nOcclusionTestsCollectionsToRun == 0 )
  2461. Msg( "Not recording occlusion tests now, %d in record array\n", s_OcclusionTestRecords.Count() );
  2462. else
  2463. Msg( "Currently recording %d tests in %d passes, %d records collected in this pass so far\n", s_nOcclusionTestsToCollect, s_nOcclusionTestsCollectionsToRun, s_OcclusionTestRecords.Count() );
  2464. return;
  2465. }
  2466. int nCount = V_atoi( args.Arg( 1 ) );
  2467. if ( nCount > 0 )
  2468. {
  2469. s_nOcclusionTestsToCollect = nCount;
  2470. if ( args.ArgC() >= 3 )
  2471. {
  2472. s_nOcclusionTestsCollectionsToRun = V_atoi( args.Arg( 2 ) );
  2473. if ( s_nOcclusionTestsCollectionsToRun < 1 )
  2474. s_nOcclusionTestsCollectionsToRun = 1;
  2475. }
  2476. else
  2477. {
  2478. s_nOcclusionTestsCollectionsToRun = 1;
  2479. }
  2480. /*
  2481. if ( s_OcclusionTestRecords.Count() > 0 )
  2482. {
  2483. Msg( "Abandoning %d tests already collected. ", s_OcclusionTestRecords.Count() );
  2484. s_OcclusionTestRecords.Purge();
  2485. }
  2486. */
  2487. Msg( "Preparing to collect %d occlusion tests. %d collected so far.\n", nCount, s_OcclusionTestRecords.Count() );
  2488. s_OcclusionTestRecords.EnsureCapacity( nCount );
  2489. }
  2490. else if ( nCount == 0 )
  2491. {
  2492. Msg( "Stopping all occlusion tests: %dx%d, purging %d\n", s_nOcclusionTestsToCollect, s_nOcclusionTestsCollectionsToRun, s_OcclusionTestRecords.Count() );
  2493. s_nOcclusionTestsToCollect = 0;
  2494. s_nOcclusionTestsCollectionsToRun = 0;
  2495. s_OcclusionTestRecords.Purge();
  2496. }
  2497. else
  2498. {
  2499. Msg( "Need to collect positive number of tests\n" );
  2500. }
  2501. }
  2502. const char *GetMapName( void );
  2503. bool DebugCheckOcclusion( const Vector &p0, const Vector &vExtents0, const Vector &p1, const Vector &vExtents1, int nOcclusionRays )
  2504. {
  2505. for ( int i = 0; i < nOcclusionRays; ++i )
  2506. {
  2507. // we shouldn't find a single ray between the two boxes that is not occluded
  2508. Vector rnd0( RandomFloat() * vExtents0.x, RandomFloat() * vExtents0.y, RandomFloat() * vExtents0.z );
  2509. Vector rnd1( RandomFloat() * vExtents1.x, RandomFloat() * vExtents1.y, RandomFloat() * vExtents1.z );
  2510. Ray_t ray;
  2511. ray.Init( p0 + rnd0, p1 + rnd1 );
  2512. trace_t tr;
  2513. V_memset( &tr, 0, sizeof( tr ) );
  2514. CM_BoxTrace( ray, 0, CONTENTS_SOLID | CONTENTS_MOVEABLE, false, tr );
  2515. if ( !tr.DidHit() )
  2516. {
  2517. return false;
  2518. }
  2519. }
  2520. return true;
  2521. }
  2522. bool CM_IsFullyOccluded( const VectorAligned &p0, const VectorAligned &vExtents0, const VectorAligned &p1, const VectorAligned &vExtents1, OcclusionTestResults_t *pResults )
  2523. {
  2524. if ( s_nOcclusionTestsToCollect > 0 && s_nOcclusionTestCollectionLock == 0 )
  2525. {
  2526. OcclusionTestRec_t &rec = s_OcclusionTestRecords[ s_OcclusionTestRecords.AddToTail() ];
  2527. rec.p0 = p0;
  2528. rec.vExtents0 = vExtents0;
  2529. rec.p1 = p1;
  2530. rec.vExtents1 = vExtents1;
  2531. if ( s_OcclusionTestRecords.Count() >= s_nOcclusionTestsToCollect )
  2532. {
  2533. // record the file
  2534. for ( int nAttempt = 0; nAttempt < 10000; ++nAttempt )
  2535. {
  2536. const char *pMapName = GetMapName();
  2537. CFmtStr fileName( "occlusion_records.%s.%04d.ocr", pMapName, nAttempt );
  2538. if ( g_pFullFileSystem->FileExists( fileName.Get() ) )
  2539. continue;
  2540. Msg( "Saving %d occlusion records to %s (#%d)\n", s_OcclusionTestRecords.Count(), fileName.Get(), nAttempt );
  2541. FileHandle_t hDump = g_pFullFileSystem->Open( fileName.Get(), "wb" );
  2542. g_pFullFileSystem->Write( s_OcclusionTestRecords.Base(), s_OcclusionTestRecords.Count() * sizeof( s_OcclusionTestRecords[ 0 ] ), hDump );
  2543. g_pFullFileSystem->Close( hDump );
  2544. s_OcclusionTestRecords.Purge();
  2545. break;
  2546. }
  2547. if ( --s_nOcclusionTestsCollectionsToRun <= 0 )
  2548. {
  2549. s_nOcclusionTestsToCollect = 0;
  2550. s_nOcclusionTestsCollectionsToRun = 0;
  2551. }
  2552. }
  2553. }
  2554. VPROF_BUDGET( "CM_IsFullyOccluded", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED );
  2555. Vector vExtents = VectorMax( vExtents0, vExtents1 );
  2556. COcclusionInfo oi;
  2557. oi.m_pBSPData = GetCollisionBSPData();
  2558. oi.m_pResults = pResults;
  2559. // check if the map is not loaded
  2560. if ( !oi.m_pBSPData->numnodes )
  2561. {
  2562. return false;
  2563. }
  2564. // axis-aligned boxes are a very special case: we can just cast 6 rays and if we don't care about their order (i.e. if we aren't looking for partial occlusions), and have 2x4 or 8 lane SIMD, we can just cast each corner to a corresponding other box'es corner and we'll be just fine
  2565. fltx4 f4ExtentsStart = LoadAlignedSIMD( &vExtents0 ), f4ExtentsEnd = LoadAlignedSIMD( &vExtents1 ), f4Start = LoadAlignedSIMD( &p0 ), f4End = LoadAlignedSIMD( &p1 );
  2566. fltx4 f4Delta = f4End - f4Start;
  2567. fltx4 f4DeltaPos = AbsSIMD( f4Delta );
  2568. if ( !( TestSignSIMD( ( f4ExtentsStart + f4ExtentsEnd ) - f4DeltaPos ) & 7 ) )
  2569. {
  2570. return false; // we can't get full occlusion if the two boxes intersect
  2571. }
  2572. StoreAligned3SIMD( &oi.m_delta, f4Delta );
  2573. StoreAligned3SIMD( &oi.m_deltaPos, f4DeltaPos );
  2574. fltx4 f40101signmask = *( fltx4* )g_SIMD_0101_signmask;
  2575. oi.m_StartXnXYnY = ShuffleXXYY( f4Start ) + XorSIMD( f40101signmask, ShuffleXXYY( f4ExtentsStart ) );
  2576. oi.m_EndXnXYnY = ShuffleXXYY( f4End ) + XorSIMD( f40101signmask, ShuffleXXYY( f4ExtentsEnd ) );
  2577. oi.m_StartEndZnZ = _mm_shuffle_ps( f4Start, f4End, MM_SHUFFLE_REV( 2, 2, 2, 2 ) ) + XorSIMD( f40101signmask, _mm_shuffle_ps( f4ExtentsStart, f4ExtentsEnd, MM_SHUFFLE_REV( 2, 2, 2, 2 ) ) );
  2578. oi.m_start = p0;
  2579. oi.m_end = p1;
  2580. Vector vExtentsScaled = vExtents;
  2581. oi.m_extents = vExtentsScaled;
  2582. oi.m_extents.z = Max( 0.0f, oi.m_extents.z - 0.0625f /*MOVE_HEIGHT_EPSILON*/ );
  2583. oi.m_delta = p1 - p0;
  2584. VectorAbs( oi.m_delta, oi.m_deltaPos );
  2585. oi.m_uvwExtents.Init( oi.m_deltaPos.y * oi.m_extents.z + oi.m_deltaPos.z * oi.m_extents.y, oi.m_deltaPos.z * oi.m_extents.x + oi.m_deltaPos.x * oi.m_extents.z, oi.m_deltaPos.x * oi.m_extents.y + oi.m_deltaPos.y * oi.m_extents.x ); // all extents and their abs projections should be positive or zero
  2586. oi.m_uvwMins = -oi.m_uvwExtents;
  2587. oi.m_uvwMaxs = oi.m_uvwExtents;
  2588. oi.m_deltaSigns.Init( Sign( oi.m_delta.x ), Sign( oi.m_delta.y ), Sign( oi.m_delta.z ) );
  2589. // oi.m_minsPos.Init( oi.m_delta.x >= 0 ? p0.x : -p1.x, oi.m_delta.y >= 0 ? p0.y : -p1.y, oi.m_delta.z >= 0 ? p0.z : -p1.z );
  2590. // oi.m_maxsPos.Init( oi.m_delta.x >= 0 ? p1.x : -p0.x, oi.m_delta.y >= 0 ? p1.y : -p0.y, oi.m_delta.z >= 0 ? p1.z : -p0.z );
  2591. // AssertDbg( oi.m_minsPos.x <= oi.m_maxsPos.x && oi.m_minsPos.y <= oi.m_maxsPos.y && oi.m_minsPos.z <= oi.m_maxsPos.z );
  2592. oi.m_traceMins = VectorMin( p0, p1 );
  2593. oi.m_traceMins.z -= vExtentsScaled.z;
  2594. oi.m_traceMaxs = VectorMax( p0, p1 );
  2595. oi.m_traceMaxs.z += vExtentsScaled.z;
  2596. oi.m_contents = CONTENTS_SOLID | CONTENTS_MOVEABLE; // can solid or moveable be semitransparent?
  2597. oi.m_pDebugLog = NULL;
  2598. #ifdef _DEBUG
  2599. static bool s_bDumpOcclusionPass = false;
  2600. if ( s_bDumpOcclusionPass )
  2601. {
  2602. oi.m_pDebugLog = new CBspDebugLog( "bsp_debug_log.obj");
  2603. oi.m_pDebugLog->AddBox( "start", "start", p0 - vExtents0, p0 + vExtents0 );
  2604. oi.m_pDebugLog->AddBox( "end", "end", p1 - vExtents1, p1 + vExtents1 );
  2605. oi.m_pDebugLog->ResetPrimCount();
  2606. }
  2607. #endif
  2608. return CM_RecursiveOcclusionPass( oi, 0, 0.0f, 1.0f, p0, p1 );
  2609. }
  2610. bool CM_IsFullyOccluded( const AABB_t &aabb1, const AABB_t &aabb2 )
  2611. {
  2612. VectorAligned vCenter1( aabb1.GetCenter() );
  2613. VectorAligned vCenter2( aabb2.GetCenter() );
  2614. VectorAligned vHullExtents1( aabb1.GetSize() * .5f );
  2615. VectorAligned vHullExtents2( aabb2.GetSize() * .5f ); // include both hulls extents
  2616. bool bOccluded = CM_IsFullyOccluded(
  2617. vCenter1,
  2618. vHullExtents1,
  2619. vCenter2,
  2620. vHullExtents2
  2621. );
  2622. return bOccluded;
  2623. }
  2624. CON_COMMAND_F( occlusion_test_run, "run occlusion test", FCVAR_CHEAT )
  2625. {
  2626. if ( s_nOcclusionTestCollectionLock )
  2627. {
  2628. Msg( "Occlusion test collection lock is on (%d). This is unexpected. Resetting.\n", s_nOcclusionTestCollectionLock );
  2629. s_nOcclusionTestCollectionLock = 0;
  2630. }
  2631. if ( args.ArgC() < 2 )
  2632. {
  2633. Msg( "Usage: occlusion_test_run <index> [-cold] [-check [<n_rays>]]\n" );
  2634. return;
  2635. }
  2636. const char *pMapName = GetMapName();
  2637. CFmtStr fileName( "occlusion_records.%s.%04d.ocr", pMapName, V_atoi( args.Arg( 1 ) ) );
  2638. CUtlBuffer buf;
  2639. if ( !g_pFullFileSystem->ReadFile( fileName.Get(), NULL, buf ) )
  2640. {
  2641. Msg( "Cannot read %s\n", fileName.Get() );
  2642. return;
  2643. }
  2644. s_nOcclusionTestCollectionLock++;
  2645. bool bColdCache = false;
  2646. int nDoubleCheck = 0;
  2647. const CPUInformation &cpuInfo = GetCPUInformation();
  2648. uint32 nCacheSizeKb = Max( Max( cpuInfo.m_nL1CacheSizeKb, cpuInfo.m_nL2CacheSizeKb ), cpuInfo.m_nL3CacheSizeKb );
  2649. for ( int i = 2; i < args.ArgC(); ++i )
  2650. {
  2651. const char *pArg = args.Arg( i );
  2652. if ( !V_stricmp( pArg, "-cold" ) )
  2653. {
  2654. bColdCache = true;
  2655. Msg( "Will test with cold cache; flush array size %dkb; cache sizes: L1 %dKb, L2 %dKb, L3 %dKb\n", nCacheSizeKb, cpuInfo.m_nL1CacheSizeKb, cpuInfo.m_nL2CacheSizeKb , cpuInfo.m_nL3CacheSizeKb );
  2656. }
  2657. else if ( !V_stricmp( pArg, "-check" ) )
  2658. {
  2659. nDoubleCheck = 100;
  2660. if ( i + 1 < args.ArgC() )
  2661. {
  2662. nDoubleCheck = V_atoi( args[ i + 1 ] );
  2663. if ( nDoubleCheck > 0 )
  2664. {
  2665. i++;
  2666. }
  2667. else
  2668. {
  2669. nDoubleCheck = 100;
  2670. }
  2671. }
  2672. Msg( "Will double-check occlusion with %d rays\n", nDoubleCheck );
  2673. }
  2674. }
  2675. int nRecCount = buf.TellPut() / sizeof( OcclusionTestRec_t );
  2676. OcclusionTestRec_t *pRec = ( OcclusionTestRec_t * )buf.Base();
  2677. Msg( "Loaded %d occlusion records from %s\n", nRecCount, fileName.Get() );
  2678. int nRecOccluded = 0, nRecFalseOcclusion = 0, nRecFalseNonOcclusion = 0;
  2679. int64 nCpuSpeed = cpuInfo.m_Speed;
  2680. int64 nBestCase = nCpuSpeed, nWorstCase = 0, nTotalTime = 0;
  2681. int nBestIndex = -1, nWorstIndex = -1;
  2682. CUtlVector< int8 > flushCache;
  2683. if ( bColdCache )
  2684. flushCache.SetCount( nCacheSizeKb * 1024 );
  2685. for ( int i = 0; i < nRecCount; ++i )
  2686. {
  2687. if ( bColdCache )
  2688. {
  2689. // flush the cache
  2690. flushCache.FillWithValue( i );
  2691. }
  2692. int64 nTimeStart = GetTimebaseRegister();
  2693. bool bOccluded = CM_IsFullyOccluded( pRec[ i ].p0, pRec[ i ].vExtents0, pRec[ i ].p1, pRec[ i ].vExtents1 );
  2694. int64 nTimeOcclusion = GetTimebaseRegister() - nTimeStart;
  2695. if ( nWorstCase < nTimeOcclusion )
  2696. {
  2697. nWorstCase = nTimeOcclusion;
  2698. nWorstIndex = i;
  2699. }
  2700. if ( nBestCase > nTimeOcclusion )
  2701. {
  2702. nBestCase = nTimeOcclusion ;
  2703. nBestIndex = i;
  2704. }
  2705. nTotalTime += nTimeOcclusion;
  2706. if ( bOccluded )
  2707. ++nRecOccluded;
  2708. if ( nDoubleCheck )
  2709. {
  2710. if ( bOccluded != DebugCheckOcclusion( pRec[ i ].p0, pRec[ i ].vExtents0, pRec[ i ].p1, pRec[ i ].vExtents1, nDoubleCheck ) )
  2711. {
  2712. if ( bOccluded )
  2713. {
  2714. ++nRecFalseOcclusion;
  2715. Warning( "FALSE OCCLUSION FOUND: #%d\n", i );
  2716. }
  2717. else
  2718. ++nRecFalseNonOcclusion;
  2719. }
  2720. }
  2721. }
  2722. double flMicrosecondsPerTick = 1e6 / nCpuSpeed;
  2723. Msg( "%d/%d (%.1f%%) occluded, occlusion tests run %.1f us ave ([%d]=%.1fus, [%d]=%.1fus) on a %.2fGHz CPU\n",
  2724. nRecOccluded, nRecCount, ( nRecOccluded * 100.0f ) / nRecCount,
  2725. ( nTotalTime * flMicrosecondsPerTick ) / nRecCount,
  2726. nBestIndex, nBestCase * flMicrosecondsPerTick,
  2727. nWorstIndex, nWorstCase * flMicrosecondsPerTick,
  2728. nCpuSpeed * 1e-9
  2729. );
  2730. if ( nDoubleCheck )
  2731. {
  2732. Msg( "%d false non-occlusions\n", nRecFalseNonOcclusion );
  2733. }
  2734. s_nOcclusionTestCollectionLock--;
  2735. }
  2736. void CM_BoxTrace( const Ray_t& ray, int headnode, int brushmask, bool computeEndpt, trace_t& tr )
  2737. {
  2738. VPROF("BoxTrace");
  2739. // for multi-check avoidance
  2740. TraceInfo_t *pTraceInfo = BeginTrace();
  2741. #ifdef COUNT_COLLISIONS
  2742. // for statistics, may be zeroed
  2743. g_CollisionCounts.m_Traces++;
  2744. #endif
  2745. // fill in a default trace
  2746. CM_ClearTrace( &pTraceInfo->m_trace );
  2747. // check if the map is not loaded
  2748. if (!pTraceInfo->m_pBSPData->numnodes)
  2749. {
  2750. tr = pTraceInfo->m_trace;
  2751. EndTrace( pTraceInfo );
  2752. return;
  2753. }
  2754. pTraceInfo->m_bDispHit = false;
  2755. pTraceInfo->m_DispStabDir.Init();
  2756. pTraceInfo->m_contents = brushmask;
  2757. VectorCopy (ray.m_Start, pTraceInfo->m_start);
  2758. VectorAdd (ray.m_Start, ray.m_Delta, pTraceInfo->m_end);
  2759. VectorMultiply (ray.m_Extents, -1.0f, pTraceInfo->m_mins);
  2760. VectorCopy (ray.m_Extents, pTraceInfo->m_maxs);
  2761. VectorCopy (ray.m_Extents, pTraceInfo->m_extents);
  2762. pTraceInfo->m_delta = ray.m_Delta;
  2763. pTraceInfo->m_invDelta = ray.InvDelta();
  2764. pTraceInfo->m_ispoint = ray.m_IsRay;
  2765. pTraceInfo->m_isswept = ray.m_IsSwept;
  2766. if (!ray.m_IsSwept)
  2767. {
  2768. // check for position test special case
  2769. CM_UnsweptBoxTrace( pTraceInfo, ray, headnode, brushmask );
  2770. }
  2771. else
  2772. {
  2773. // general sweeping through world
  2774. CM_RecursiveHullCheck( pTraceInfo, headnode, 0, 1 );
  2775. }
  2776. // Compute the trace start + end points
  2777. if (computeEndpt)
  2778. {
  2779. CM_ComputeTraceEndpoints( ray, pTraceInfo->m_trace );
  2780. }
  2781. // Copy off the results
  2782. tr = pTraceInfo->m_trace;
  2783. EndTrace( pTraceInfo );
  2784. Assert( !ray.m_IsRay || tr.allsolid || ((tr.fraction + kBoxCheckFloatEpsilon) >= tr.fractionleftsolid) );
  2785. }
  2786. void CM_TransformedBoxTrace( const Ray_t& ray, int headnode, int brushmask,
  2787. const Vector& origin, QAngle const& angles, trace_t& tr )
  2788. {
  2789. matrix3x4_t localToWorld;
  2790. Ray_t ray_l;
  2791. // subtract origin offset
  2792. VectorCopy( ray.m_StartOffset, ray_l.m_StartOffset );
  2793. VectorCopy( ray.m_Extents, ray_l.m_Extents );
  2794. // Are we rotated?
  2795. bool rotated = (angles[0] || angles[1] || angles[2]);
  2796. // rotate start and end into the models frame of reference
  2797. if (rotated)
  2798. {
  2799. // NOTE: In this case, the bbox is rotated into the space of the BSP as well
  2800. // to insure consistency at all orientations, we must rotate the origin of the ray
  2801. // and reapply the offset to the center of the box. That way all traces with the
  2802. // same box centering will have the same transformation into local space
  2803. Vector worldOrigin = ray.m_Start + ray.m_StartOffset;
  2804. AngleMatrix( angles, origin, localToWorld );
  2805. VectorIRotate( ray.m_Delta, localToWorld, ray_l.m_Delta );
  2806. VectorITransform( worldOrigin, localToWorld, ray_l.m_Start );
  2807. ray_l.m_Start -= ray.m_StartOffset;
  2808. }
  2809. else
  2810. {
  2811. VectorSubtract( ray.m_Start, origin, ray_l.m_Start );
  2812. VectorCopy( ray.m_Delta, ray_l.m_Delta );
  2813. }
  2814. ray_l.m_IsRay = ray.m_IsRay;
  2815. ray_l.m_IsSwept = ray.m_IsSwept;
  2816. // sweep the box through the model, don't compute endpoints
  2817. CM_BoxTrace( ray_l, headnode, brushmask, false, tr );
  2818. // If we hit, gotta fix up the normal...
  2819. if (( tr.fraction != 1 ) && rotated )
  2820. {
  2821. // transform the normal from the local space of this entity to world space
  2822. Vector temp;
  2823. VectorCopy (tr.plane.normal, temp);
  2824. VectorRotate( temp, localToWorld, tr.plane.normal );
  2825. }
  2826. CM_ComputeTraceEndpoints( ray, tr );
  2827. }
  2828. /*
  2829. ===============================================================================
  2830. PVS / PAS
  2831. ===============================================================================
  2832. */
  2833. //-----------------------------------------------------------------------------
  2834. // Purpose:
  2835. // Input : *pBSPData -
  2836. // *out -
  2837. //-----------------------------------------------------------------------------
  2838. void CM_NullVis( CCollisionBSPData *pBSPData, byte *out )
  2839. {
  2840. int numClusterBytes = (pBSPData->numclusters+7)>>3;
  2841. byte *out_p = out;
  2842. while (numClusterBytes)
  2843. {
  2844. *out_p++ = 0xff;
  2845. numClusterBytes--;
  2846. }
  2847. }
  2848. /*
  2849. ===================
  2850. CM_DecompressVis
  2851. ===================
  2852. */
  2853. void CM_DecompressVis( CCollisionBSPData *pBSPData, int cluster, int visType, byte *out )
  2854. {
  2855. int c;
  2856. byte *out_p;
  2857. int numClusterBytes;
  2858. if ( !pBSPData )
  2859. {
  2860. Assert( false ); // Shouldn't ever happen.
  2861. }
  2862. if ( cluster > pBSPData->numclusters || cluster < 0 )
  2863. {
  2864. // This can happen if this is called while the level is loading. See Map_VisCurrentCluster.
  2865. CM_NullVis( pBSPData, out );
  2866. return;
  2867. }
  2868. // no vis info, so make all visible
  2869. if ( !pBSPData->numvisibility || !pBSPData->map_vis )
  2870. {
  2871. CM_NullVis( pBSPData, out );
  2872. return;
  2873. }
  2874. byte *in = ((byte *)pBSPData->map_vis) + pBSPData->map_vis->bitofs[cluster][visType];
  2875. numClusterBytes = (pBSPData->numclusters+7)>>3;
  2876. out_p = out;
  2877. // no vis info, so make all visible
  2878. if ( !in )
  2879. {
  2880. CM_NullVis( pBSPData, out );
  2881. return;
  2882. }
  2883. do
  2884. {
  2885. if (*in)
  2886. {
  2887. *out_p++ = *in++;
  2888. continue;
  2889. }
  2890. c = in[1];
  2891. in += 2;
  2892. if ((out_p - out) + c > numClusterBytes)
  2893. {
  2894. c = numClusterBytes - (out_p - out);
  2895. ConMsg( "warning: Vis decompression overrun\n" );
  2896. }
  2897. while (c)
  2898. {
  2899. *out_p++ = 0;
  2900. c--;
  2901. }
  2902. } while (out_p - out < numClusterBytes);
  2903. }
  2904. //-----------------------------------------------------------------------------
  2905. // Purpose: Decompress the RLE bitstring for PVS or PAS of one cluster
  2906. // Input : *dest - buffer to store the decompressed data
  2907. // cluster - index of cluster of interest
  2908. // visType - DVIS_PAS or DVIS_PAS
  2909. // Output : byte * - pointer to the filled buffer
  2910. //-----------------------------------------------------------------------------
  2911. const byte *CM_Vis( byte *dest, int destlen, int cluster, int visType )
  2912. {
  2913. // get the current collision bsp -- there is only one!
  2914. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2915. if ( !dest || visType > 2 || visType < 0 )
  2916. {
  2917. Sys_Error( "CM_Vis: error");
  2918. return NULL;
  2919. }
  2920. if ( cluster == -1 )
  2921. {
  2922. int len = (pBSPData->numclusters+7)>>3;
  2923. if ( len > destlen )
  2924. {
  2925. Sys_Error( "CM_Vis: buffer not big enough (%i but need %i)\n",
  2926. destlen, len );
  2927. }
  2928. memset( dest, 0, (pBSPData->numclusters+7)>>3 );
  2929. }
  2930. else
  2931. {
  2932. CM_DecompressVis( pBSPData, cluster, visType, dest );
  2933. }
  2934. return dest;
  2935. }
  2936. static byte pvsrow[MAX_MAP_LEAFS/8];
  2937. int CM_ClusterPVSSize()
  2938. {
  2939. return sizeof( pvsrow );
  2940. }
  2941. const byte *CM_ClusterPVS (int cluster)
  2942. {
  2943. return CM_Vis( pvsrow, CM_ClusterPVSSize(), cluster, DVIS_PVS );
  2944. }
  2945. /*
  2946. ===============================================================================
  2947. AREAPORTALS
  2948. ===============================================================================
  2949. */
  2950. void FloodArea_r (CCollisionBSPData *pBSPData, carea_t *area, int floodnum)
  2951. {
  2952. int i;
  2953. dareaportal_t *p;
  2954. if (area->floodvalid == pBSPData->floodvalid)
  2955. {
  2956. if (area->floodnum == floodnum)
  2957. return;
  2958. Sys_Error( "FloodArea_r: reflooded");
  2959. }
  2960. area->floodnum = floodnum;
  2961. area->floodvalid = pBSPData->floodvalid;
  2962. p = &pBSPData->map_areaportals[area->firstareaportal];
  2963. for (i=0 ; i<area->numareaportals ; i++, p++)
  2964. {
  2965. if (pBSPData->portalopen[p->m_PortalKey])
  2966. {
  2967. FloodArea_r (pBSPData, &pBSPData->map_areas[p->otherarea], floodnum);
  2968. }
  2969. }
  2970. }
  2971. /*
  2972. ====================
  2973. FloodAreaConnections
  2974. ====================
  2975. */
  2976. void FloodAreaConnections ( CCollisionBSPData *pBSPData )
  2977. {
  2978. int i;
  2979. carea_t *area;
  2980. int floodnum;
  2981. // all current floods are now invalid
  2982. pBSPData->floodvalid++;
  2983. floodnum = 0;
  2984. // area 0 is not used
  2985. for (i=1 ; i<pBSPData->numareas ; i++)
  2986. {
  2987. area = &pBSPData->map_areas[i];
  2988. if (area->floodvalid == pBSPData->floodvalid)
  2989. continue; // already flooded into
  2990. floodnum++;
  2991. FloodArea_r (pBSPData, area, floodnum);
  2992. }
  2993. }
  2994. void CM_SetAreaPortalState( int portalnum, int isOpen )
  2995. {
  2996. // get the current collision bsp -- there is only one!
  2997. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  2998. // Portalnums in the BSP file are 1-based instead of 0-based
  2999. if (portalnum > pBSPData->numareaportals)
  3000. {
  3001. Sys_Error( "portalnum > numareaportals");
  3002. }
  3003. pBSPData->portalopen[portalnum] = (isOpen != 0);
  3004. FloodAreaConnections (pBSPData);
  3005. }
  3006. void CM_SetAreaPortalStates( const int *portalnums, const int *isOpen, int nPortals )
  3007. {
  3008. if ( nPortals == 0 )
  3009. return;
  3010. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3011. // get the current collision bsp -- there is only one!
  3012. for ( int i=0; i < nPortals; i++ )
  3013. {
  3014. // Portalnums in the BSP file are 1-based instead of 0-based
  3015. if (portalnums[i] > pBSPData->numareaportals)
  3016. Sys_Error( "portalnum > numareaportals");
  3017. pBSPData->portalopen[portalnums[i]] = (isOpen[i] != 0);
  3018. }
  3019. FloodAreaConnections( pBSPData );
  3020. }
  3021. bool CM_AreasConnected( int area1, int area2 )
  3022. {
  3023. // get the current collision bsp -- there is only one!
  3024. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3025. if (map_noareas.GetInt())
  3026. return true;
  3027. if (area1 >= pBSPData->numareas || area2 >= pBSPData->numareas)
  3028. {
  3029. Sys_Error( "area(1==%i, 2==%i) >= numareas (%i): Check if engine->ResetPVS() was called from ClientSetupVisibility", area1, area2, pBSPData->numareas );
  3030. }
  3031. return (pBSPData->map_areas[area1].floodnum == pBSPData->map_areas[area2].floodnum);
  3032. }
  3033. void CM_LeavesConnected( const Vector &vecOrigin, int nCount, const int *pLeaves, bool *pIsConnected )
  3034. {
  3035. // get the current collision bsp -- there is only one!
  3036. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3037. if ( map_noareas.GetInt() )
  3038. {
  3039. memset( pIsConnected, 1, nCount * sizeof(bool) );
  3040. return;
  3041. }
  3042. int nArea = CM_LeafArea( CM_PointLeafnum( vecOrigin ) );
  3043. Assert( nArea < pBSPData->numareas && nArea >= 0 );
  3044. for ( int i = 0; i < nCount; ++i )
  3045. {
  3046. int nLeafArea = pBSPData->map_leafs[ pLeaves[i] ].area;
  3047. Assert( nLeafArea < pBSPData->numareas && nLeafArea >= 0 );
  3048. pIsConnected[i] = ( pBSPData->map_areas[nArea].floodnum == pBSPData->map_areas[ nLeafArea ].floodnum );
  3049. }
  3050. }
  3051. //-----------------------------------------------------------------------------
  3052. // Purpose: CM_WriteAreaBits
  3053. // Writes a bit vector of all the areas in the same flood as the area parameter
  3054. // Input : *buffer -
  3055. // buflen -
  3056. // area -
  3057. // Output : int - number of bytes of collision data (based on area count)
  3058. //-----------------------------------------------------------------------------
  3059. int CM_WriteAreaBits( byte *buffer, int buflen, int area )
  3060. {
  3061. int i;
  3062. int floodnum;
  3063. int bytes;
  3064. if ( buflen < 32 )
  3065. {
  3066. Sys_Error( "CM_WriteAreaBits with buffer size %d < 32\n", buflen );
  3067. }
  3068. // get the current collision bsp -- there is only one!
  3069. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3070. Assert( pBSPData );
  3071. bytes = ( pBSPData->numareas + 7 ) >> 3;
  3072. Assert( buflen >= bytes );
  3073. if ( map_noareas.GetInt() )
  3074. {
  3075. // for debugging, send everything (or nothing if map_noareas == 2 )
  3076. byte fill = ( map_noareas.GetInt() == 2 ) ? 0x00 : 0xff;
  3077. Q_memset( buffer, fill, buflen );
  3078. }
  3079. else
  3080. {
  3081. Q_memset( buffer, 0x00, buflen );
  3082. floodnum = pBSPData->map_areas[area].floodnum;
  3083. for ( i = 0 ; i < pBSPData->numareas; ++i )
  3084. {
  3085. if ( pBSPData->map_areas[i].floodnum == floodnum || !area )
  3086. {
  3087. buffer[ i >> 3 ] |= ( 1 << ( i & 7 ) );
  3088. }
  3089. }
  3090. }
  3091. return bytes;
  3092. }
  3093. bool CM_GetAreaPortalPlane( const Vector &vViewOrigin, int portalKey, VPlane *pPlane )
  3094. {
  3095. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3096. // First, find the leaf and area the viewer is in.
  3097. int iLeaf = CM_PointLeafnum( vViewOrigin );
  3098. if( iLeaf < 0 || iLeaf >= pBSPData->numleafs )
  3099. return false;
  3100. int iArea = pBSPData->map_leafs[iLeaf].area;
  3101. if( iArea < 0 || iArea >= pBSPData->numareas )
  3102. return false;
  3103. carea_t *pArea = &pBSPData->map_areas[iArea];
  3104. for( int i=0; i < pArea->numareaportals; i++ )
  3105. {
  3106. dareaportal_t *pPortal = &pBSPData->map_areaportals[pArea->firstareaportal + i];
  3107. if( pPortal->m_PortalKey == portalKey )
  3108. {
  3109. cplane_t *pMapPlane = &pBSPData->map_planes[pPortal->planenum];
  3110. pPlane->m_Normal = pMapPlane->normal;
  3111. pPlane->m_Dist = pMapPlane->dist;
  3112. return true;
  3113. }
  3114. }
  3115. return false;
  3116. }
  3117. /*
  3118. =============
  3119. CM_HeadnodeVisible
  3120. Returns true if any leaf under headnode has a cluster that
  3121. is potentially visible
  3122. =============
  3123. */
  3124. bool CM_HeadnodeVisible (int nodenum, const byte *visbits, int vissize )
  3125. {
  3126. int leafnum;
  3127. int cluster;
  3128. cnode_t *node;
  3129. // get the current collision bsp -- there is only one!
  3130. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3131. if (nodenum < 0)
  3132. {
  3133. leafnum = -1-nodenum;
  3134. cluster = pBSPData->map_leafs[leafnum].cluster;
  3135. if (cluster == -1)
  3136. return false;
  3137. if (visbits[cluster>>3] & (1<<(cluster&7)))
  3138. return true;
  3139. return false;
  3140. }
  3141. node = &pBSPData->map_rootnode[nodenum];
  3142. if (CM_HeadnodeVisible(node->children[0], visbits, vissize ))
  3143. return true;
  3144. return CM_HeadnodeVisible(node->children[1], visbits, vissize );
  3145. }
  3146. //-----------------------------------------------------------------------------
  3147. // Purpose: returns true if the box is in a cluster that is visible in the visbits
  3148. // Input : mins - box extents
  3149. // maxs -
  3150. // *visbits - pvs or pas of some cluster
  3151. // Output : true if visible, false if not
  3152. //-----------------------------------------------------------------------------
  3153. #define MAX_BOX_LEAVES 256
  3154. int CM_BoxVisible( const Vector& mins, const Vector& maxs, const byte *visbits, int vissize )
  3155. {
  3156. int leafList[MAX_BOX_LEAVES];
  3157. int topnode;
  3158. // FIXME: Could save a loop here by traversing the tree in this routine like the code above
  3159. int count = CM_BoxLeafnums( mins, maxs, leafList, MAX_BOX_LEAVES, &topnode );
  3160. for ( int i = 0; i < count; i++ )
  3161. {
  3162. int cluster = CM_LeafCluster( leafList[i] );
  3163. int offset = cluster>>3;
  3164. if ( offset > vissize )
  3165. {
  3166. Sys_Error( "CM_BoxVisible: cluster %i, offset %i out of bounds %i\n", cluster, offset, vissize );
  3167. }
  3168. if (visbits[cluster>>3] & (1<<(cluster&7)))
  3169. {
  3170. return true;
  3171. }
  3172. }
  3173. return false;
  3174. }
  3175. //-----------------------------------------------------------------------------
  3176. // Returns the world-space center of an entity
  3177. //-----------------------------------------------------------------------------
  3178. void CM_WorldSpaceCenter( ICollideable *pCollideable, Vector *pCenter )
  3179. {
  3180. Vector vecLocalCenter;
  3181. VectorAdd( pCollideable->OBBMins(), pCollideable->OBBMaxs(), vecLocalCenter );
  3182. vecLocalCenter *= 0.5f;
  3183. if ( ( pCollideable->GetCollisionAngles() == vec3_angle ) || ( vecLocalCenter == vec3_origin ) )
  3184. {
  3185. VectorAdd( vecLocalCenter, pCollideable->GetCollisionOrigin(), *pCenter );
  3186. }
  3187. else
  3188. {
  3189. VectorTransform( vecLocalCenter, pCollideable->CollisionToWorldTransform(), *pCenter );
  3190. }
  3191. }
  3192. //-----------------------------------------------------------------------------
  3193. // Returns the world-align bounds of an entity
  3194. //-----------------------------------------------------------------------------
  3195. void CM_WorldAlignBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  3196. {
  3197. if ( pCollideable->GetCollisionAngles() == vec3_angle )
  3198. {
  3199. *pMins = pCollideable->OBBMins();
  3200. *pMaxs = pCollideable->OBBMaxs();
  3201. }
  3202. else
  3203. {
  3204. ITransformAABB( pCollideable->CollisionToWorldTransform(), pCollideable->OBBMins(), pCollideable->OBBMaxs(), *pMins, *pMaxs );
  3205. *pMins -= pCollideable->GetCollisionOrigin();
  3206. *pMaxs -= pCollideable->GetCollisionOrigin();
  3207. }
  3208. }
  3209. //-----------------------------------------------------------------------------
  3210. // Returns the world-space bounds of an entity
  3211. //-----------------------------------------------------------------------------
  3212. void CM_WorldSpaceBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  3213. {
  3214. if ( pCollideable->GetCollisionAngles() == vec3_angle )
  3215. {
  3216. VectorAdd( pCollideable->GetCollisionOrigin(), pCollideable->OBBMins(), *pMins );
  3217. VectorAdd( pCollideable->GetCollisionOrigin(), pCollideable->OBBMaxs(), *pMaxs );
  3218. }
  3219. else
  3220. {
  3221. TransformAABB( pCollideable->CollisionToWorldTransform(), pCollideable->OBBMins(), pCollideable->OBBMaxs(), *pMins, *pMaxs );
  3222. }
  3223. }
  3224. void CM_SetupAreaFloodNums( byte areaFloodNums[MAX_MAP_AREAS], int *pNumAreas )
  3225. {
  3226. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3227. *pNumAreas = pBSPData->numareas;
  3228. if ( pBSPData->numareas > MAX_MAP_AREAS )
  3229. Error( "pBSPData->numareas > MAX_MAP_AREAS" );
  3230. for ( int i=0; i < pBSPData->numareas; i++ )
  3231. {
  3232. Assert( pBSPData->map_areas[i].floodnum < MAX_MAP_AREAS );
  3233. areaFloodNums[i] = (byte)pBSPData->map_areas[i].floodnum;
  3234. }
  3235. }
  3236. // -----------------------------------------------------------------------------
  3237. // CFastLeafAccessor implementation.
  3238. // -----------------------------------------------------------------------------
  3239. CFastPointLeafNum::CFastPointLeafNum()
  3240. {
  3241. Reset();
  3242. }
  3243. int CFastPointLeafNum::GetLeaf( const Vector &vPos )
  3244. {
  3245. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  3246. if ( m_iCachedLeaf < 0 || m_iCachedLeaf >= pBSPData->numleafs ||
  3247. vPos.DistToSqr( m_vCachedPos ) > m_flDistToExitLeafSqr )
  3248. {
  3249. m_vCachedPos = vPos;
  3250. m_flDistToExitLeafSqr = 1e24;
  3251. m_iCachedLeaf = CM_PointLeafnumMinDistSqr_r( pBSPData, vPos, 0, m_flDistToExitLeafSqr );
  3252. }
  3253. Assert( m_iCachedLeaf >= 0 );
  3254. Assert( m_iCachedLeaf < pBSPData->numleafs );
  3255. return m_iCachedLeaf;
  3256. }
  3257. void CFastPointLeafNum::Reset( void )
  3258. {
  3259. m_iCachedLeaf = -1;
  3260. m_flDistToExitLeafSqr = -1;
  3261. m_vCachedPos.Init();
  3262. }
  3263. bool FASTCALL IsBoxIntersectingRayNoLowest( fltx4 boxMin, fltx4 boxMax,
  3264. const fltx4 & origin, const fltx4 & delta, const fltx4 & invDelta, // ray parameters
  3265. const fltx4 & vTolerance ///< eg from ReplicateX4(flTolerance)
  3266. )
  3267. {
  3268. /*
  3269. Assert( boxMin[0] <= boxMax[0] );
  3270. Assert( boxMin[1] <= boxMax[1] );
  3271. Assert( boxMin[2] <= boxMax[2] );
  3272. */
  3273. #if defined(_X360) && defined(DBGFLAG_ASSERT)
  3274. unsigned int r;
  3275. AssertMsg( (XMVectorGreaterOrEqualR(&r, SetWToZeroSIMD(boxMax),SetWToZeroSIMD(boxMin)), XMComparisonAllTrue(r)), "IsBoxIntersectingRay : boxmax < boxmin" );
  3276. #endif
  3277. // test if delta is tiny along any dimension
  3278. bi32x4 bvDeltaTinyComponents = CmpInBoundsSIMD( delta, Four_Epsilons );
  3279. // push box extents out by tolerance (safe to do because pass by copy, not ref)
  3280. boxMin = SubSIMD(boxMin, vTolerance);
  3281. boxMax = AddSIMD(boxMax, vTolerance);
  3282. // for the very short components of the ray, check if the origin is inside the box;
  3283. // if not, then it doesn't intersect.
  3284. bi32x4 bvOriginOutsideBox = OrSIMD( CmpLtSIMD(origin,boxMin), CmpGtSIMD(origin,boxMax) );
  3285. bvDeltaTinyComponents = SetWToZeroSIMD(bvDeltaTinyComponents);
  3286. // work out entry and exit points for the ray. This may produce strange results for
  3287. // very short delta components, but those will be masked out by bvDeltaTinyComponents
  3288. // anyway. We could early-out on bvOriginOutsideBox, but it won't be ready to branch
  3289. // on for fourteen cycles.
  3290. fltx4 vt1 = SubSIMD( boxMin, origin );
  3291. fltx4 vt2 = SubSIMD( boxMax, origin );
  3292. vt1 = MulSIMD( vt1, invDelta );
  3293. vt2 = MulSIMD( vt2, invDelta );
  3294. // ensure that vt1<vt2
  3295. {
  3296. fltx4 temp = MaxSIMD( vt1, vt2 );
  3297. vt1 = MinSIMD( vt1, vt2 );
  3298. vt2 = temp;
  3299. }
  3300. // Non-parallel case
  3301. // Find the t's corresponding to the entry and exit of
  3302. // the ray along x, y, and z. The find the furthest entry
  3303. // point, and the closest exit point. Once that is done,
  3304. // we know we don't collide if the closest exit point
  3305. // is behind the starting location. We also don't collide if
  3306. // the closest exit point is in front of the furthest entry point
  3307. fltx4 closestExit,furthestEntry;
  3308. {
  3309. VectorAligned temp;
  3310. StoreAlignedSIMD(temp.Base(),vt2);
  3311. closestExit = ReplicateX4( MIN( MIN(temp.x,temp.y), temp.z) );
  3312. StoreAlignedSIMD(temp.Base(),vt1);
  3313. furthestEntry = ReplicateX4( MAX( MAX(temp.x,temp.y), temp.z) );
  3314. }
  3315. // now start testing. We bail out if:
  3316. // any component with tiny delta has origin outside the box
  3317. if (!IsAllZeros(AndSIMD(bvOriginOutsideBox, bvDeltaTinyComponents)))
  3318. return false;
  3319. else
  3320. {
  3321. // however if there are tiny components inside the box, we
  3322. // know that they are good. (we didn't really need to run
  3323. // the other computations on them, but it was faster to do
  3324. // so than branching around them).
  3325. // now it's the origin INSIDE box (eg, tiny components & ~outside box)
  3326. bvOriginOutsideBox = AndNotSIMD(bvOriginOutsideBox,bvDeltaTinyComponents);
  3327. }
  3328. // closest exit is in front of furthest entry
  3329. bi32x4 tminOverTmax = CmpGtSIMD( furthestEntry, closestExit );
  3330. // closest exit is behind start, or furthest entry after end
  3331. bi32x4 outOfBounds = OrSIMD( CmpGtSIMD(furthestEntry, LoadOneSIMD()), CmpGtSIMD( LoadZeroSIMD(), closestExit ) );
  3332. bi32x4 failedComponents = OrSIMD(tminOverTmax, outOfBounds); // any 1's here mean return false
  3333. // but, if a component is tiny and has its origin inside the box, ignore the computation against bogus invDelta.
  3334. failedComponents = AndNotSIMD(bvOriginOutsideBox,failedComponents);
  3335. return ( IsAllZeros( SetWToZeroSIMD( failedComponents ) ) );
  3336. }
  3337. #if 0 // example code for testing Quaternion48S
  3338. #include <vectormath/cpp/vectormath_aos.h>
  3339. #include "pixelwriter.h"
  3340. struct inpix_t
  3341. {
  3342. float channels[4];
  3343. };
  3344. CON_COMMAND( ps3_testf16, "test float116" )
  3345. {
  3346. volatile static bool trapper = true;
  3347. const int nPix = atoi(args[1]);
  3348. CUtlMemory<float16> memOld; memOld.EnsureCapacity( nPix * 4 );
  3349. CUtlMemory<float16> memNu; memNu.EnsureCapacity( nPix * 4 );
  3350. // set to be one huge row of pixels
  3351. CPixelWriter vOldWay;
  3352. vOldWay.SetPixelMemory( IMAGE_FORMAT_RGBA16161616F, memOld.Base(), nPix * 4 * sizeof(float16) );
  3353. vOldWay.bIgnorePS3NOCHECKIN = true;
  3354. CPixelWriter vNewWay;
  3355. vNewWay.bIgnorePS3NOCHECKIN = false;
  3356. vNewWay.SetPixelMemory( IMAGE_FORMAT_RGBA16161616F, memOld.Base(), nPix * 4 * sizeof(float16) );
  3357. CUtlVector<inpix_t> indata; indata.EnsureCount( nPix );
  3358. static const float mint = exp2f(-14);
  3359. for ( int i = 0 ; i < nPix ; ++i )
  3360. {
  3361. for ( int j = 0 ; j < 4 ; ++j )
  3362. {
  3363. indata[i].channels[j] = RandomFloat( mint, maxfloat16bits * 2 );
  3364. }
  3365. }
  3366. for ( int i = 0 ; i < nPix ; ++i )
  3367. {
  3368. vOldWay.WritePixelF( indata[i].channels[0], indata[i].channels[1], indata[i].channels[2], indata[i].channels[3] );
  3369. }
  3370. vNewWay.WriteManyPixelTo16BitF( indata[0].channels, nPix );
  3371. const float16 *pOldPixels = (const float16 *)vOldWay.GetPixelMemory();
  3372. const float16 *pNewPixels = (const float16 *)vNewWay.GetPixelMemory();
  3373. for ( int i = 0 ; i < nPix * 4 ; ++i )
  3374. {
  3375. if ( pOldPixels[i] != pNewPixels[i] )
  3376. {
  3377. Msg( "%f -> %f %f\t%x %x\n", indata[i], pOldPixels[i], pNewPixels[i], pOldPixels[i].GetBits(), pNewPixels[i].GetBits() );
  3378. Assert(false);
  3379. }
  3380. }
  3381. #if 0
  3382. static float fins[65536];
  3383. // build an array of every float that can be represented
  3384. // in a float16
  3385. for ( uint i = 0 ; i <= 65535 ; ++i )
  3386. {
  3387. short s = i;
  3388. float16 foo;
  3389. memcpy( &foo, &s, sizeof(short) );
  3390. fins[i] = foo.GetFloat();
  3391. }
  3392. int bottom; int top;
  3393. {
  3394. float16 fbot; fbot.SetFloat( exp2f(-14) );
  3395. float16 ftop; ftop.SetFloat( maxfloat16bits );
  3396. bottom = fbot.GetBits();
  3397. top = ftop.GetBits();
  3398. }
  3399. // now test conversion back
  3400. for ( uint i = bottom ; i <= top ; i+=4 )
  3401. {
  3402. float16 oldway[4];
  3403. float16 neway[4];
  3404. oldway[0].SetFloat( fins[i+0] );
  3405. oldway[1].SetFloat( fins[i+1] );
  3406. oldway[2].SetFloat( fins[i+2] );
  3407. oldway[3].SetFloat( fins[i+3] );
  3408. float16::ConvertFourFloatsTo16BitsAtOnce( neway, fins+i+0, fins+i+1, fins+i+2, fins+i+3 );
  3409. for ( int q = 0 ; q < 4 ; ++q )
  3410. {
  3411. if ( oldway[q] != neway[q] && neway[q].GetBits() > 0 )
  3412. {
  3413. if ( trapper ) DebuggerBreak();
  3414. Msg( "%f -> %f %f\t%x %x\n",
  3415. fins[i+q], oldway[q].GetFloat(), neway[q].GetFloat(), oldway[q].GetBits(), neway[q].GetBits() );
  3416. }
  3417. /*
  3418. if ( neway[q].GetFloat() > 0 )
  3419. {
  3420. if ( trapper ) DebuggerBreak();
  3421. Msg( "%f -> %f %f\t%x %x\n",
  3422. fins[i+q], oldway[q].GetFloat(), neway[q].GetFloat(), oldway[q].GetBits(), neway[q].GetBits() );
  3423. }
  3424. */
  3425. }
  3426. }
  3427. #endif
  3428. #if 0
  3429. float16 a,b;
  3430. a.SetFloat(1.0f);
  3431. b = 1.0f;
  3432. short c = float16::ConvertFloatTo16bits(1.0f);
  3433. short d = float16::ConvertFloatTo16bitsNonDefault<false>(1.0f);
  3434. Msg("%f %f %x %x\n", a,b ,c ,d );
  3435. float foo[128];
  3436. float16 bar[128];
  3437. short quux[128];
  3438. for ( int i = 0 ; i < 128 ; ++i )
  3439. {
  3440. foo[i] = RandomFloat( -65535, 65535 );
  3441. bar[i] = foo[i];
  3442. quux[i] = float16::ConvertFloatTo16bitsNonDefault<false>(foo[i]);
  3443. }
  3444. for ( int i = 0 ; i < 128 ; ++i )
  3445. {
  3446. if ( bar[i].GetBits() != quux[i] )
  3447. {
  3448. Msg( "%f -> %f %x %x \n", foo[i], bar[i].GetFloat(), bar[i].GetBits(), quux[i] );
  3449. }
  3450. }
  3451. #endif
  3452. }
  3453. #endif