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.

2999 lines
96 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "IOcclusionSystem.h"
  9. #include "mathlib/vector.h"
  10. #include "utlsortvector.h"
  11. #include "utllinkedlist.h"
  12. #include "utlvector.h"
  13. #include "collisionutils.h"
  14. #include "filesystem.h"
  15. #include "gl_model_private.h"
  16. #include "gl_matsysiface.h"
  17. #include "client.h"
  18. #include "gl_shader.h"
  19. #include "materialsystem/imesh.h"
  20. #include "tier0/vprof.h"
  21. #include "tier0/icommandline.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. // Uncomment this if you want to get a whole bunch of paranoid error checking
  25. // #define DEBUG_OCCLUSION_SYSTEM 1
  26. //-----------------------------------------------------------------------------
  27. // Used to visualizes what the occlusion system is doing.
  28. //-----------------------------------------------------------------------------
  29. #ifdef _GAMECONSOLE
  30. #define DEFAULT_MIN_OCCLUDER_AREA 70.0f
  31. #else
  32. #define DEFAULT_MIN_OCCLUDER_AREA 5.0f
  33. #endif
  34. #define DEFAULT_MAX_OCCLUDEE_AREA 5.0f
  35. #ifdef _GAMECONSOLE
  36. #define DEFAULT_OCCLUSION_STATE "0"
  37. #else
  38. #define DEFAULT_OCCLUSION_STATE "1"
  39. #endif
  40. // Used by ViewData ring buffer
  41. #define OCCLUSION_SYSTEM_VIEWDATA_MAX 32
  42. ConVar r_visocclusion( "r_visocclusion", "0", FCVAR_CHEAT, "Activate/deactivate wireframe rendering of what the occlusion system is doing." );
  43. ConVar r_occlusion( "r_occlusion", DEFAULT_OCCLUSION_STATE, 0, "Activate/deactivate the occlusion system." );
  44. static ConVar r_occludermincount( "r_occludermincount", "0", 0, "At least this many occluders will be used, no matter how big they are." );
  45. static ConVar r_occlusionspew( "r_occlusionspew", "0", FCVAR_CHEAT, "Activate/deactivates spew about what the occlusion system is doing." );
  46. ConVar r_occluderminarea( "r_occluderminarea", "0", 0, "Prevents this occluder from being used if it takes up less than X% of the screen. 0 means use whatever the level said to use." );
  47. ConVar r_occludeemaxarea( "r_occludeemaxarea", "0", 0, "Prevents occlusion testing for entities that take up more than X% of the screen. 0 means use whatever the level said to use." );
  48. #ifdef DEBUG_OCCLUSION_SYSTEM
  49. static ConVar r_occtest( "r_occtest", "0" );
  50. // Set this in the debugger to activate debugging spew
  51. bool s_bSpew = false;
  52. #endif // DEBUG_OCCLUSION_SYSTEM
  53. //-----------------------------------------------------------------------------
  54. // Visualization
  55. //-----------------------------------------------------------------------------
  56. struct EdgeVisualizationInfo_t
  57. {
  58. Vector m_vecPoint[2];
  59. unsigned char m_pColor[4];
  60. };
  61. //-----------------------------------------------------------------------------
  62. // Queued up rendering
  63. //-----------------------------------------------------------------------------
  64. static CUtlVector<EdgeVisualizationInfo_t> g_EdgeVisualization;
  65. //-----------------------------------------------------------------------------
  66. //
  67. // Edge list that's fast to iterate over, fast to insert into
  68. //
  69. //-----------------------------------------------------------------------------
  70. class CWingedEdgeList
  71. {
  72. public:
  73. struct WingedEdge_t
  74. {
  75. Vector m_vecPosition; // of the upper point in y, measured in screen space
  76. Vector m_vecPositionEnd; // of the lower point in y, measured in screen space
  77. float m_flDxDy; // Change in x per unit in y.
  78. float m_flOODy;
  79. float m_flX;
  80. short m_nLeaveSurfID; // Unique index of the surface this is a part of
  81. short m_nEnterSurfID; // Unique index of the surface this is a part of
  82. WingedEdge_t *m_pPrevActiveEdge;
  83. WingedEdge_t *m_pNextActiveEdge;
  84. };
  85. public:
  86. CWingedEdgeList();
  87. // Clears out the edge list
  88. void Clear();
  89. // Iteration
  90. int EdgeCount() const;
  91. WingedEdge_t &WingedEdge( int i );
  92. // Adds an edge
  93. int AddEdge( );
  94. int AddEdge( const Vector &vecStartVert, const Vector &vecEndVert, int nLeaveSurfID, int nEnterSurfID );
  95. // Adds a surface
  96. int AddSurface( const cplane_t &plane );
  97. // Does this edge list occlude another winged edge list?
  98. bool IsOccludingEdgeList( CWingedEdgeList &testList );
  99. // Queues up stuff to visualize
  100. void QueueVisualization( unsigned char *pColor );
  101. // Renders the winged edge list
  102. void Visualize( unsigned char *pColor );
  103. // Checks consistency of the edge list...
  104. void CheckConsistency();
  105. private:
  106. struct Surface_t
  107. {
  108. cplane_t m_Plane; // measured in projection space
  109. };
  110. private:
  111. // Active edges...
  112. WingedEdge_t *FirstActiveEdge( );
  113. WingedEdge_t *LastActiveEdge( );
  114. bool AtListEnd( const WingedEdge_t* pEdge ) const;
  115. bool AtListStart( const WingedEdge_t* pEdge ) const;
  116. void LinkActiveEdgeAfter( WingedEdge_t *pPrevEdge, WingedEdge_t *pInsertEdge );
  117. void UnlinkActiveEdge( WingedEdge_t *pEdge );
  118. // Used to insert an edge into the active edge list
  119. bool IsEdgeXGreater( const WingedEdge_t *pEdge1, const WingedEdge_t *pEdge2 );
  120. // Clears the active edge list
  121. void ResetActiveEdgeList();
  122. // Spew active edge list
  123. void SpewActiveEdgeList( float y, bool bHex = false );
  124. // Inserts an edge into the active edge list, sorted by X
  125. void InsertActiveEdge( WingedEdge_t *pPrevEdge, WingedEdge_t *pInsertEdge );
  126. // Returns true if this active edge list occludes another active edge list
  127. bool IsOccludingActiveEdgeList( CWingedEdgeList &testList, float y );
  128. // Advances the X values of the active edge list, with no reordering
  129. bool AdvanceActiveEdgeList( float flCurrY );
  130. // Advance the active edge list until a particular X value is reached.
  131. WingedEdge_t *AdvanceActiveEdgeListToX( WingedEdge_t *pEdge, float x );
  132. // Returns the z value of a surface given and x,y coordinate
  133. float ComputeZValue( const Surface_t *pSurface, float x, float y ) const;
  134. // Returns the next time in Y the edge list will undergo a change
  135. float NextDiscontinuity() const;
  136. private:
  137. // Active Edge list...
  138. WingedEdge_t m_StartTerminal;
  139. WingedEdge_t m_EndTerminal;
  140. // Back surface...
  141. Surface_t m_BackSurface;
  142. // Next discontinuity..
  143. float m_flNextDiscontinuity;
  144. int m_nCurrentEdgeIndex;
  145. CUtlVector< WingedEdge_t > m_WingedEdges;
  146. CUtlVector< Surface_t > m_Surfaces;
  147. };
  148. //-----------------------------------------------------------------------------
  149. // Constructor
  150. //-----------------------------------------------------------------------------
  151. CWingedEdgeList::CWingedEdgeList() : m_WingedEdges( 0, 64 )
  152. {
  153. m_StartTerminal.m_vecPosition.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  154. m_StartTerminal.m_vecPositionEnd.Init( -FLT_MAX, FLT_MAX, -FLT_MAX );
  155. m_StartTerminal.m_nLeaveSurfID = -1;
  156. m_StartTerminal.m_nEnterSurfID = -1;
  157. m_StartTerminal.m_pPrevActiveEdge = NULL;
  158. m_StartTerminal.m_pNextActiveEdge = NULL;
  159. m_StartTerminal.m_flDxDy = 0.0f;
  160. m_StartTerminal.m_flOODy = 0.0f;
  161. m_StartTerminal.m_flX = -FLT_MAX;
  162. m_EndTerminal.m_vecPosition.Init( FLT_MAX, -FLT_MAX, -FLT_MAX );
  163. m_EndTerminal.m_vecPositionEnd.Init( FLT_MAX, FLT_MAX, -FLT_MAX );
  164. m_EndTerminal.m_nLeaveSurfID = -1;
  165. m_EndTerminal.m_nEnterSurfID = -1;
  166. m_EndTerminal.m_pPrevActiveEdge = NULL;
  167. m_EndTerminal.m_pNextActiveEdge = NULL;
  168. m_EndTerminal.m_flDxDy = 0.0f;
  169. m_EndTerminal.m_flOODy = 0.0f;
  170. m_EndTerminal.m_flX = FLT_MAX;
  171. m_BackSurface.m_Plane.normal.Init( 0, 0, 1 );
  172. m_BackSurface.m_Plane.dist = FLT_MAX;
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Renders the winged edge list for debugging
  176. //-----------------------------------------------------------------------------
  177. void CWingedEdgeList::Clear()
  178. {
  179. m_WingedEdges.RemoveAll();
  180. m_Surfaces.RemoveAll();
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Iterate over the winged edges
  184. //-----------------------------------------------------------------------------
  185. inline int CWingedEdgeList::EdgeCount() const
  186. {
  187. return m_WingedEdges.Count();
  188. }
  189. inline CWingedEdgeList::WingedEdge_t &CWingedEdgeList::WingedEdge( int i )
  190. {
  191. return m_WingedEdges[i];
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Adds new edges
  195. //-----------------------------------------------------------------------------
  196. inline int CWingedEdgeList::AddEdge( )
  197. {
  198. int i = m_WingedEdges.AddToTail();
  199. WingedEdge_t &newEdge = m_WingedEdges[i];
  200. newEdge.m_pPrevActiveEdge = NULL;
  201. newEdge.m_pNextActiveEdge = NULL;
  202. return i;
  203. }
  204. int CWingedEdgeList::AddEdge( const Vector &vecStartVert, const Vector &vecEndVert, int nLeaveSurfID, int nEnterSurfID )
  205. {
  206. // This is true if we've clipped to the near clip plane
  207. Assert( (vecStartVert.z >= 0.0) && (vecEndVert.z >= 0.0) );
  208. // Don't bother adding edges with dy == 0
  209. float dy;
  210. dy = vecEndVert.y - vecStartVert.y;
  211. if (dy == 0.0f)
  212. return -1;
  213. int i = m_WingedEdges.AddToTail();
  214. WingedEdge_t &newEdge = m_WingedEdges[i];
  215. newEdge.m_flOODy = 1.0f / dy;
  216. newEdge.m_nLeaveSurfID = nLeaveSurfID;
  217. newEdge.m_nEnterSurfID = nEnterSurfID;
  218. newEdge.m_vecPosition = vecStartVert;
  219. newEdge.m_vecPositionEnd = vecEndVert;
  220. newEdge.m_pPrevActiveEdge = NULL;
  221. newEdge.m_pNextActiveEdge = NULL;
  222. newEdge.m_flDxDy = (vecEndVert.x - vecStartVert.x) * newEdge.m_flOODy;
  223. return i;
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Adds new surfaces
  227. //-----------------------------------------------------------------------------
  228. int CWingedEdgeList::AddSurface( const cplane_t &plane )
  229. {
  230. int i = m_Surfaces.AddToTail();
  231. m_Surfaces[i].m_Plane = plane;
  232. return i;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Active edges...
  236. //-----------------------------------------------------------------------------
  237. inline CWingedEdgeList::WingedEdge_t *CWingedEdgeList::FirstActiveEdge( )
  238. {
  239. return m_StartTerminal.m_pNextActiveEdge;
  240. }
  241. inline CWingedEdgeList::WingedEdge_t *CWingedEdgeList::LastActiveEdge( )
  242. {
  243. return m_EndTerminal.m_pPrevActiveEdge;
  244. }
  245. inline bool CWingedEdgeList::AtListEnd( const WingedEdge_t* pEdge ) const
  246. {
  247. return pEdge == &m_EndTerminal;
  248. }
  249. inline bool CWingedEdgeList::AtListStart( const WingedEdge_t* pEdge ) const
  250. {
  251. return pEdge == &m_StartTerminal;
  252. }
  253. inline void CWingedEdgeList::LinkActiveEdgeAfter( WingedEdge_t *pPrevEdge, WingedEdge_t *pInsertEdge )
  254. {
  255. pInsertEdge->m_pNextActiveEdge = pPrevEdge->m_pNextActiveEdge;
  256. pInsertEdge->m_pPrevActiveEdge = pPrevEdge;
  257. pInsertEdge->m_pNextActiveEdge->m_pPrevActiveEdge = pInsertEdge;
  258. pPrevEdge->m_pNextActiveEdge = pInsertEdge;
  259. }
  260. inline void CWingedEdgeList::UnlinkActiveEdge( WingedEdge_t *pEdge )
  261. {
  262. pEdge->m_pPrevActiveEdge->m_pNextActiveEdge = pEdge->m_pNextActiveEdge;
  263. pEdge->m_pNextActiveEdge->m_pPrevActiveEdge = pEdge->m_pPrevActiveEdge;
  264. #ifdef _DEBUG
  265. pEdge->m_pPrevActiveEdge = pEdge->m_pNextActiveEdge = NULL;
  266. #endif
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Checks consistency of the edge list...
  270. //-----------------------------------------------------------------------------
  271. void CWingedEdgeList::CheckConsistency()
  272. {
  273. float flLastY = -FLT_MAX;
  274. float flLastX = -FLT_MAX;
  275. float flLastDxDy = 0;
  276. int nEdgeCount = EdgeCount();
  277. for ( int i = 0; i < nEdgeCount; ++i )
  278. {
  279. WingedEdge_t *pEdge = &WingedEdge(i);
  280. Assert( pEdge->m_vecPosition.y >= flLastY );
  281. if ( pEdge->m_vecPosition.y == flLastY )
  282. {
  283. Assert( pEdge->m_vecPosition.x >= flLastX );
  284. if ( pEdge->m_vecPosition.x == flLastX )
  285. {
  286. Assert( pEdge->m_flDxDy >= flLastDxDy );
  287. }
  288. }
  289. flLastX = pEdge->m_vecPosition.x;
  290. flLastY = pEdge->m_vecPosition.y;
  291. flLastDxDy = pEdge->m_flDxDy;
  292. }
  293. ResetActiveEdgeList();
  294. float flCurrentY = NextDiscontinuity();
  295. AdvanceActiveEdgeList( flCurrentY );
  296. while ( flCurrentY != FLT_MAX )
  297. {
  298. // Make sure all edges have correct Xs + enter + leave surfaces..
  299. int nCurrentSurfID = -1;
  300. float flX = -FLT_MAX;
  301. WingedEdge_t *pCurEdge = FirstActiveEdge();
  302. while ( !AtListEnd( pCurEdge ) )
  303. {
  304. Assert( pCurEdge->m_nLeaveSurfID == nCurrentSurfID );
  305. Assert( pCurEdge->m_flX >= flX );
  306. Assert( pCurEdge->m_nLeaveSurfID != pCurEdge->m_nEnterSurfID );
  307. nCurrentSurfID = pCurEdge->m_nEnterSurfID;
  308. flX = pCurEdge->m_flX;
  309. pCurEdge = pCurEdge->m_pNextActiveEdge;
  310. }
  311. // Assert( nCurrentSurfID == -1 );
  312. flCurrentY = NextDiscontinuity();
  313. AdvanceActiveEdgeList( flCurrentY );
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Returns the z value of a surface given and x,y coordinate
  318. //-----------------------------------------------------------------------------
  319. inline float CWingedEdgeList::ComputeZValue( const Surface_t *pSurface, float x, float y ) const
  320. {
  321. const cplane_t &plane = pSurface->m_Plane;
  322. Assert( plane.normal.z == 1.0f );
  323. return plane.dist - plane.normal.x * x - plane.normal.y * y;
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Used to insert an edge into the active edge list, sorted by X
  327. // If Xs match, sort by Dx/Dy
  328. //-----------------------------------------------------------------------------
  329. inline bool CWingedEdgeList::IsEdgeXGreater( const WingedEdge_t *pEdge1, const WingedEdge_t *pEdge2 )
  330. {
  331. float flDelta = pEdge1->m_flX - pEdge2->m_flX;
  332. if ( flDelta > 0 )
  333. return true;
  334. if ( flDelta < 0 )
  335. return false;
  336. // NOTE: Using > instead of >= means coincident edges won't continually swap places
  337. return pEdge1->m_flDxDy > pEdge2->m_flDxDy;
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Inserts an edge into the active edge list, sorted by X
  341. //-----------------------------------------------------------------------------
  342. inline void CWingedEdgeList::InsertActiveEdge( WingedEdge_t *pPrevEdge, WingedEdge_t *pInsertEdge )
  343. {
  344. while( !AtListStart(pPrevEdge) && IsEdgeXGreater( pPrevEdge, pInsertEdge ) )
  345. {
  346. pPrevEdge = pPrevEdge->m_pPrevActiveEdge;
  347. }
  348. LinkActiveEdgeAfter( pPrevEdge, pInsertEdge );
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Clears the active edge list
  352. //-----------------------------------------------------------------------------
  353. void CWingedEdgeList::ResetActiveEdgeList()
  354. {
  355. // This shouldn't be called unless we're about to do active edge checking
  356. Assert( EdgeCount() );
  357. m_nCurrentEdgeIndex = 0;
  358. // Don't bother with edges below the screen edge
  359. m_flNextDiscontinuity = WingedEdge( 0 ).m_vecPosition.y;
  360. m_flNextDiscontinuity = MAX( m_flNextDiscontinuity, -1.0f );
  361. m_StartTerminal.m_pNextActiveEdge = &m_EndTerminal;
  362. m_EndTerminal.m_pPrevActiveEdge = &m_StartTerminal;
  363. Assert( m_StartTerminal.m_pPrevActiveEdge == NULL );
  364. Assert( m_EndTerminal.m_pNextActiveEdge == NULL );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Spew active edge list
  368. //-----------------------------------------------------------------------------
  369. void CWingedEdgeList::SpewActiveEdgeList( float y, bool bHex )
  370. {
  371. WingedEdge_t *pEdge = FirstActiveEdge();
  372. Msg( "%.3f : ", y );
  373. while ( !AtListEnd( pEdge ) )
  374. {
  375. if (!bHex)
  376. {
  377. Msg( "(%d %.3f [%d/%d]) ", (int)(pEdge - m_WingedEdges.Base()), pEdge->m_flX, pEdge->m_nLeaveSurfID, pEdge->m_nEnterSurfID );
  378. }
  379. else
  380. {
  381. Msg( "(%d %X [%d/%d]) ", (int)(pEdge - m_WingedEdges.Base()), *(int*)&pEdge->m_flX, pEdge->m_nLeaveSurfID, pEdge->m_nEnterSurfID );
  382. }
  383. pEdge = pEdge->m_pNextActiveEdge;
  384. }
  385. Msg( "\n" );
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Returns the next time in Y the edge list will undergo a change
  389. //-----------------------------------------------------------------------------
  390. inline float CWingedEdgeList::NextDiscontinuity() const
  391. {
  392. return m_flNextDiscontinuity;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Advances the X values of the active edge list, with no reordering
  396. //-----------------------------------------------------------------------------
  397. bool CWingedEdgeList::AdvanceActiveEdgeList( float flCurrY )
  398. {
  399. // Reordering is unnecessary because the winged edges are guaranteed to be non-overlapping
  400. m_flNextDiscontinuity = FLT_MAX;
  401. // Advance all edges until the current Y; we don't need to re-order *any* edges.
  402. WingedEdge_t *pCurEdge;
  403. WingedEdge_t *pNextEdge;
  404. for ( pCurEdge = FirstActiveEdge(); !AtListEnd( pCurEdge ); pCurEdge = pNextEdge )
  405. {
  406. pNextEdge = pCurEdge->m_pNextActiveEdge;
  407. if ( pCurEdge->m_vecPositionEnd.y <= flCurrY )
  408. {
  409. UnlinkActiveEdge( pCurEdge );
  410. continue;
  411. }
  412. pCurEdge->m_flX = pCurEdge->m_vecPosition.x + (flCurrY - pCurEdge->m_vecPosition.y) * pCurEdge->m_flDxDy;
  413. if ( pCurEdge->m_vecPositionEnd.y < m_flNextDiscontinuity )
  414. {
  415. m_flNextDiscontinuity = pCurEdge->m_vecPositionEnd.y;
  416. }
  417. }
  418. int nEdgeCount = EdgeCount();
  419. if ( m_nCurrentEdgeIndex == nEdgeCount )
  420. return (m_flNextDiscontinuity != FLT_MAX);
  421. pCurEdge = &WingedEdge( m_nCurrentEdgeIndex );
  422. // Add new edges, computing the x + z coordinates at the requested y value
  423. while ( pCurEdge->m_vecPosition.y <= flCurrY )
  424. {
  425. // This is necessary because of our initial skip up to y == -1.0f
  426. if ( pCurEdge->m_vecPositionEnd.y > flCurrY )
  427. {
  428. float flDy = flCurrY - pCurEdge->m_vecPosition.y;
  429. pCurEdge->m_flX = pCurEdge->m_vecPosition.x + flDy * pCurEdge->m_flDxDy;
  430. if ( pCurEdge->m_vecPositionEnd.y < m_flNextDiscontinuity )
  431. {
  432. m_flNextDiscontinuity = pCurEdge->m_vecPositionEnd.y;
  433. }
  434. // Now re-insert in the list, sorted by X
  435. InsertActiveEdge( LastActiveEdge(), pCurEdge );
  436. }
  437. if ( ++m_nCurrentEdgeIndex == nEdgeCount )
  438. return (m_flNextDiscontinuity != FLT_MAX);
  439. pCurEdge = &WingedEdge( m_nCurrentEdgeIndex );
  440. }
  441. // The next edge in y will also present a discontinuity
  442. if ( pCurEdge->m_vecPosition.y < m_flNextDiscontinuity )
  443. {
  444. m_flNextDiscontinuity = pCurEdge->m_vecPosition.y;
  445. }
  446. return (m_flNextDiscontinuity != FLT_MAX);
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Advance the active edge list until a particular X value is reached.
  450. //-----------------------------------------------------------------------------
  451. inline CWingedEdgeList::WingedEdge_t *CWingedEdgeList::AdvanceActiveEdgeListToX( WingedEdge_t *pEdge, float x )
  452. {
  453. // <= is necessary because we always want to point *after* the edge
  454. while( pEdge->m_flX <= x )
  455. {
  456. pEdge = pEdge->m_pNextActiveEdge;
  457. }
  458. return pEdge;
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Returns true if this active edge list occludes another active edge list
  462. //-----------------------------------------------------------------------------
  463. bool CWingedEdgeList::IsOccludingActiveEdgeList( CWingedEdgeList &testList, float y )
  464. {
  465. WingedEdge_t *pTestEdge = testList.FirstActiveEdge();
  466. // If the occludee is off screen, it's occluded
  467. if ( pTestEdge->m_flX >= 1.0f )
  468. return true;
  469. pTestEdge = AdvanceActiveEdgeListToX( pTestEdge, -1.0f );
  470. // If all occludee edges have x values <= -1.0f, it's occluded
  471. if ( testList.AtListEnd( pTestEdge ) )
  472. return true;
  473. // Start at the first edge whose x value is <= -1.0f
  474. // if the occludee goes off the left side of the screen.
  475. float flNextTestX = pTestEdge->m_flX;
  476. if ( !testList.AtListStart( pTestEdge->m_pPrevActiveEdge ) )
  477. {
  478. // In this case, we should be on a span crossing from x <= -1.0f to x > 1.0f.
  479. // Do the first occlusion test at x = -1.0f.
  480. Assert( pTestEdge->m_flX > -1.0f );
  481. pTestEdge = pTestEdge->m_pPrevActiveEdge;
  482. Assert( pTestEdge->m_flX <= -1.0f );
  483. flNextTestX = -1.0f;
  484. }
  485. WingedEdge_t *pOccluderEdge = FirstActiveEdge();
  486. pOccluderEdge = AdvanceActiveEdgeListToX( pOccluderEdge, flNextTestX );
  487. Surface_t *pTestSurf = (pTestEdge->m_nEnterSurfID >= 0) ? &testList.m_Surfaces[pTestEdge->m_nEnterSurfID] : &m_BackSurface;
  488. // Use the leave surface because we know the occluder has been advanced *beyond* the test surf X.
  489. Surface_t *pOccluderSurf = (pOccluderEdge->m_nLeaveSurfID >= 0) ? &m_Surfaces[pOccluderEdge->m_nLeaveSurfID] : &m_BackSurface;
  490. float flCurrentX = flNextTestX;
  491. float flNextOccluderX = pOccluderEdge->m_flX;
  492. flNextTestX = pTestEdge->m_pNextActiveEdge->m_flX;
  493. while ( true )
  494. {
  495. // Is the occludee in front of the occluder? No dice!
  496. float flTestOOz = ComputeZValue( pTestSurf, flCurrentX, y );
  497. float flOccluderOOz = ComputeZValue( pOccluderSurf, flCurrentX, y );
  498. if ( flTestOOz < flOccluderOOz )
  499. return false;
  500. // We're done if there's no more occludees
  501. if ( flNextTestX == FLT_MAX )
  502. return true;
  503. // We're done if there's no more occluders
  504. if ( flNextOccluderX == FLT_MAX )
  505. return false;
  506. if ( flNextTestX <= flNextOccluderX )
  507. {
  508. flCurrentX = flNextTestX;
  509. pTestEdge = pTestEdge->m_pNextActiveEdge;
  510. if ( pTestEdge->m_nEnterSurfID >= 0 )
  511. {
  512. pTestSurf = &testList.m_Surfaces[pTestEdge->m_nEnterSurfID];
  513. }
  514. else
  515. {
  516. pTestSurf = (pTestEdge->m_nLeaveSurfID >= 0) ? &testList.m_Surfaces[pTestEdge->m_nLeaveSurfID] : &m_BackSurface;
  517. }
  518. flNextTestX = pTestEdge->m_pNextActiveEdge->m_flX;
  519. }
  520. else
  521. {
  522. flCurrentX = flNextOccluderX;
  523. pOccluderEdge = pOccluderEdge->m_pNextActiveEdge;
  524. pOccluderSurf = (pOccluderEdge->m_nLeaveSurfID >= 0) ? &m_Surfaces[pOccluderEdge->m_nLeaveSurfID] : &m_BackSurface;
  525. flNextOccluderX = pOccluderEdge->m_flX;
  526. }
  527. }
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Does this edge list occlude another winged edge list?
  531. //-----------------------------------------------------------------------------
  532. bool CWingedEdgeList::IsOccludingEdgeList( CWingedEdgeList &testList )
  533. {
  534. #ifdef DEBUG_OCCLUSION_SYSTEM
  535. testList.CheckConsistency();
  536. CheckConsistency();
  537. #endif
  538. // Did all the edges get culled for some reason? Then it's occluded
  539. if ( testList.EdgeCount() == 0 )
  540. return true;
  541. testList.ResetActiveEdgeList();
  542. ResetActiveEdgeList();
  543. // What we're going to do is look for the first discontinuities we can find
  544. // in both edge lists. Then, at each discontinuity, we must check the
  545. // active edge lists against each other and see if the occluders always
  546. // block the occludees...
  547. float flCurrentY = testList.NextDiscontinuity();
  548. // The edge list for the occluder must completely obscure the occludee...
  549. // If, then, the first occluder edge starts *below* the first occludee edge, it doesn't occlude.
  550. if ( flCurrentY < NextDiscontinuity() )
  551. return false;
  552. // If we start outside the screen bounds, then it's occluded!
  553. if ( flCurrentY >= 1.0f )
  554. return true;
  555. testList.AdvanceActiveEdgeList( flCurrentY );
  556. AdvanceActiveEdgeList( flCurrentY );
  557. while ( true )
  558. {
  559. if ( !IsOccludingActiveEdgeList( testList, flCurrentY ) )
  560. return false;
  561. // If we got outside the screen bounds, then it's occluded!
  562. if ( flCurrentY >= 1.0f )
  563. return true;
  564. float flTestY = testList.NextDiscontinuity();
  565. float flOccluderY = NextDiscontinuity();
  566. flCurrentY = MIN( flTestY, flOccluderY );
  567. // NOTE: This check here is to help occlusion @ the top of the screen
  568. // We cut the occluders off at y = 1.0 + epsilon, which means there's
  569. // not necessarily a discontinuity at y == 1.0. We need to create a discontinuity
  570. // there so that the occluder edges are still being used.
  571. if ( flCurrentY > 1.0f )
  572. {
  573. flCurrentY = 1.0f;
  574. }
  575. // If the occludee list is empty, then it's occluded!
  576. if ( !testList.AdvanceActiveEdgeList( flCurrentY ) )
  577. return true;
  578. // If the occluder list is empty, then the occludee is not occluded!
  579. if ( !AdvanceActiveEdgeList( flCurrentY ) )
  580. return false;
  581. }
  582. }
  583. //-----------------------------------------------------------------------------
  584. // Queues up stuff to visualize
  585. //-----------------------------------------------------------------------------
  586. void CWingedEdgeList::QueueVisualization( unsigned char *pColor )
  587. {
  588. #ifndef DEDICATED
  589. if ( !r_visocclusion.GetInt() )
  590. return;
  591. int nFirst = g_EdgeVisualization.AddMultipleToTail( m_WingedEdges.Count() );
  592. for ( int i = m_WingedEdges.Count(); --i >= 0; )
  593. {
  594. WingedEdge_t *pEdge = &m_WingedEdges[i];
  595. EdgeVisualizationInfo_t &info = g_EdgeVisualization[nFirst + i];
  596. info.m_vecPoint[0] = pEdge->m_vecPosition;
  597. info.m_vecPoint[1] = pEdge->m_vecPositionEnd;
  598. *(int*)(info.m_pColor) = *(int*)pColor;
  599. }
  600. #endif
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Renders the winged edge list for debugging
  604. //-----------------------------------------------------------------------------
  605. void CWingedEdgeList::Visualize( unsigned char *pColor )
  606. {
  607. #ifndef DEDICATED
  608. if ( !r_visocclusion.GetInt() )
  609. return;
  610. CMatRenderContextPtr pRenderContext( materials );
  611. pRenderContext->MatrixMode( MATERIAL_MODEL );
  612. pRenderContext->PushMatrix();
  613. pRenderContext->LoadIdentity();
  614. pRenderContext->MatrixMode( MATERIAL_VIEW );
  615. pRenderContext->PushMatrix();
  616. pRenderContext->LoadIdentity();
  617. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  618. pRenderContext->PushMatrix();
  619. pRenderContext->LoadIdentity();
  620. pRenderContext->Bind( g_pMaterialWireframeVertexColorIgnoreZ );
  621. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  622. CMeshBuilder meshBuilder;
  623. meshBuilder.Begin( pMesh, MATERIAL_LINES, m_WingedEdges.Count() );
  624. int i;
  625. int nCount = m_WingedEdges.Count();
  626. for ( i = nCount; --i >= 0; )
  627. {
  628. WingedEdge_t *pEdge = &m_WingedEdges[i];
  629. meshBuilder.Position3fv( pEdge->m_vecPosition.Base() );
  630. meshBuilder.Color4ubv( pColor );
  631. meshBuilder.AdvanceVertex();
  632. meshBuilder.Position3fv( pEdge->m_vecPositionEnd.Base() );
  633. #ifdef DEBUG_OCCLUSION_SYSTEM
  634. meshBuilder.Color4ub( 0, 0, 255, 255 );
  635. #else
  636. meshBuilder.Color4ubv( pColor );
  637. #endif
  638. meshBuilder.AdvanceVertex();
  639. }
  640. meshBuilder.End();
  641. pMesh->Draw();
  642. pRenderContext->MatrixMode( MATERIAL_MODEL );
  643. pRenderContext->PopMatrix();
  644. pRenderContext->MatrixMode( MATERIAL_VIEW );
  645. pRenderContext->PopMatrix();
  646. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  647. pRenderContext->PopMatrix();
  648. #endif
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Edge list that's fast to iterate over, fast to insert into
  652. //-----------------------------------------------------------------------------
  653. class CEdgeList
  654. {
  655. public:
  656. struct Edge_t
  657. {
  658. Vector m_vecPosition; // of the upper point in y, measured in screen space
  659. Vector m_vecPositionEnd; // of the lower point in y, measured in screen space
  660. float m_flDxDy; // Change in x per unit in y.
  661. float m_flOODy;
  662. float m_flX;
  663. int m_nSurfID; // Unique index of the surface this is a part of
  664. // Active edge list
  665. Edge_t *m_pPrevActiveEdge;
  666. Edge_t *m_pNextActiveEdge;
  667. };
  668. public:
  669. CEdgeList();
  670. // Insertion
  671. void AddEdge( Vector **ppEdgeVertices, int nSurfID );
  672. // Surface ID management
  673. int AddSurface( const cplane_t &plane );
  674. void SetSurfaceArea( int nSurfID, float flArea );
  675. // Removal
  676. void RemoveAll();
  677. // Visualization
  678. void QueueVisualization( unsigned char *pColor );
  679. void Visualize( unsigned char *pColor );
  680. // Access
  681. int EdgeCount() const;
  682. int ActualEdgeCount() const;
  683. const Edge_t &EdgeFromSortIndex( int nSortIndex ) const;
  684. Edge_t &EdgeFromSortIndex( int nSortIndex );
  685. // Is the test edge list occluded by this edge list
  686. bool IsOccludingEdgeList( CEdgeList &testList );
  687. // Reduces the active occlusion edge list to the bare minimum set of edges
  688. void ReduceActiveList( CWingedEdgeList &newEdgeList );
  689. // Removal of small occluders
  690. void CullSmallOccluders();
  691. private:
  692. struct Surface_t
  693. {
  694. cplane_t m_Plane; // measured in projection space
  695. float m_flOOz;
  696. Surface_t *m_pPrevSurface;
  697. Surface_t *m_pNextSurface;
  698. int m_nSurfID;
  699. float m_flArea; // Area in screen space
  700. };
  701. struct ReduceInfo_t
  702. {
  703. short m_hEdge;
  704. short m_nWingedEdge;
  705. const Edge_t *m_pEdge;
  706. };
  707. enum
  708. {
  709. MAX_EDGE_CROSSINGS = 64
  710. };
  711. typedef CUtlVector<Edge_t> EdgeList_t;
  712. private:
  713. // Gets an edge
  714. const Edge_t &Edge( int nIndex ) const;
  715. // Active edges...
  716. const Edge_t *FirstActiveEdge( ) const;
  717. Edge_t *FirstActiveEdge( );
  718. const Edge_t *LastActiveEdge( ) const;
  719. Edge_t *LastActiveEdge( );
  720. bool AtListEnd( const Edge_t* pEdge ) const;
  721. bool AtListStart( const Edge_t* pEdge ) const;
  722. void LinkActiveEdgeAfter( Edge_t *pPrevEdge, Edge_t *pInsertEdge );
  723. void UnlinkActiveEdge( Edge_t *pEdge );
  724. // Surface list
  725. Surface_t* TopSurface();
  726. bool AtSurfListEnd( const Surface_t* pSurface ) const;
  727. void CleanupCurrentSurfaceList();
  728. // Active edge list
  729. void ResetActiveEdgeList();
  730. float NextDiscontinuity() const;
  731. // Clears the current scan line
  732. float ClearCurrentSurfaceList();
  733. // Returns the z value of a surface given and x,y coordinate
  734. float ComputeZValue( const Surface_t *pSurface, float x, float y ) const;
  735. // Computes a point at a specified y value along an edge
  736. void ComputePointAlongEdge( const Edge_t *pEdge, int nSurfID, float y, Vector *pPoint ) const;
  737. // Inserts an edge into the active edge list, sorted by X
  738. void InsertActiveEdge( Edge_t *pPrevEdge, Edge_t *pInsertEdge );
  739. // Used to insert an edge into the active edge list
  740. bool IsEdgeXGreater( const Edge_t *pEdge1, const Edge_t *pEdge2 );
  741. // Reduces the active edge list into a subset of ones we truly care about
  742. void ReduceActiveEdgeList( CWingedEdgeList &newEdgeList, float flMinY, float flMaxY );
  743. // Discovers the first edge crossing discontinuity
  744. float LocateEdgeCrossingDiscontinuity( float flNextY, float flPrevY, int &nCount, Edge_t **pInfo );
  745. // Generates a list of surfaces on the current scan line
  746. void UpdateCurrentSurfaceZValues( float x, float y );
  747. // Intoruces a single new edge
  748. void IntroduceSingleActiveEdge( const Edge_t *pEdge, float flCurrY );
  749. // Returns true if pTestSurf is closer (lower z value)
  750. bool IsSurfaceBehind( Surface_t *pTestSurf, Surface_t *pSurf );
  751. // Advances the X values of the active edge list, with no reordering
  752. void AdvanceActiveEdgeList( float flNextY );
  753. void IntroduceNewActiveEdges( float y );
  754. void ReorderActiveEdgeList( int nCount, Edge_t **ppInfo );
  755. // Debugging spew
  756. void SpewActiveEdgeList( float y, bool bHex = false );
  757. // Checks consistency of the edge list...
  758. void CheckConsistency();
  759. class EdgeLess
  760. {
  761. public:
  762. bool Less( const unsigned short& src1, const unsigned short& src2, void *pCtx );
  763. };
  764. static int __cdecl SurfCompare( const void *elem1, const void *elem2 );
  765. private:
  766. // Used to sort surfaces by screen area
  767. static Surface_t *s_pSortSurfaces;
  768. // List of all edges
  769. EdgeList_t m_Edges;
  770. CUtlSortVector<unsigned short, EdgeLess > m_OrigSortIndices;
  771. CUtlVector<unsigned short> m_SortIndices;
  772. Edge_t m_StartTerminal;
  773. Edge_t m_EndTerminal;
  774. // Surfaces
  775. CUtlVector< Surface_t > m_Surfaces;
  776. CUtlVector< int > m_SurfaceSort;
  777. Surface_t m_StartSurfTerminal;
  778. Surface_t m_EndSurfTerminal;
  779. // Active edges
  780. int m_nCurrentEdgeIndex;
  781. float m_flNextDiscontinuity;
  782. // List of edges on the current Y scan-line
  783. Edge_t *m_pCurrentActiveEdge;
  784. // Last X on the current scan line
  785. float m_flLastX;
  786. // Reduce list
  787. ReduceInfo_t *m_pNewReduceInfo;
  788. ReduceInfo_t *m_pPrevReduceInfo;
  789. int m_nNewReduceCount;
  790. int m_nPrevReduceCount;
  791. };
  792. //-----------------------------------------------------------------------------
  793. // Used to sort the edge list
  794. //-----------------------------------------------------------------------------
  795. bool CEdgeList::EdgeLess::Less( const unsigned short& src1, const unsigned short& src2, void *pCtx )
  796. {
  797. EdgeList_t *pEdgeList = (EdgeList_t*)pCtx;
  798. const Edge_t &e1 = pEdgeList->Element(src1);
  799. const Edge_t &e2 = pEdgeList->Element(src2);
  800. if ( e1.m_vecPosition.y < e2.m_vecPosition.y )
  801. return true;
  802. if ( e1.m_vecPosition.y > e2.m_vecPosition.y )
  803. return false;
  804. if ( e1.m_vecPosition.x < e2.m_vecPosition.x )
  805. return true;
  806. if ( e1.m_vecPosition.x > e2.m_vecPosition.x )
  807. return false;
  808. // This makes it so that if two edges start on the same point,
  809. // the leftmost edge is always selected
  810. return ( e1.m_flDxDy <= e2.m_flDxDy );
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Constructor
  814. //-----------------------------------------------------------------------------
  815. CEdgeList::CEdgeList() : m_Edges( 0, 32 ), m_OrigSortIndices( 0, 32 )
  816. {
  817. m_OrigSortIndices.SetLessContext( &m_Edges );
  818. m_StartTerminal.m_vecPosition.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  819. m_StartTerminal.m_vecPositionEnd.Init( -FLT_MAX, FLT_MAX, -FLT_MAX );
  820. m_StartTerminal.m_nSurfID = -1;
  821. m_StartTerminal.m_pPrevActiveEdge = NULL;
  822. m_StartTerminal.m_pNextActiveEdge = NULL;
  823. m_StartTerminal.m_flDxDy = 0.0f;
  824. m_StartTerminal.m_flOODy = 0.0f;
  825. m_StartTerminal.m_flX = -FLT_MAX;
  826. m_EndTerminal.m_vecPosition.Init( FLT_MAX, -FLT_MAX, -FLT_MAX );
  827. m_EndTerminal.m_vecPositionEnd.Init( FLT_MAX, FLT_MAX, -FLT_MAX );
  828. m_EndTerminal.m_nSurfID = -1;
  829. m_EndTerminal.m_pPrevActiveEdge = NULL;
  830. m_EndTerminal.m_pNextActiveEdge = NULL;
  831. m_EndTerminal.m_flDxDy = 0.0f;
  832. m_EndTerminal.m_flOODy = 0.0f;
  833. m_EndTerminal.m_flX = FLT_MAX;
  834. m_StartSurfTerminal.m_flOOz = -FLT_MAX;
  835. m_StartSurfTerminal.m_Plane.normal.Init( 0, 0, 1 );
  836. m_StartSurfTerminal.m_Plane.dist = -FLT_MAX;
  837. m_StartSurfTerminal.m_nSurfID = -1;
  838. m_StartSurfTerminal.m_pNextSurface = NULL;
  839. m_StartSurfTerminal.m_pPrevSurface = NULL;
  840. m_EndSurfTerminal.m_flOOz = FLT_MAX;
  841. m_EndSurfTerminal.m_Plane.normal.Init( 0, 0, 1 );
  842. m_EndSurfTerminal.m_Plane.dist = FLT_MAX;
  843. m_EndSurfTerminal.m_nSurfID = -1;
  844. m_EndSurfTerminal.m_pNextSurface = NULL;
  845. m_EndSurfTerminal.m_pPrevSurface = NULL;
  846. }
  847. //-----------------------------------------------------------------------------
  848. // iteration
  849. //-----------------------------------------------------------------------------
  850. inline int CEdgeList::EdgeCount() const
  851. {
  852. return m_Edges.Count();
  853. }
  854. inline int CEdgeList::ActualEdgeCount() const
  855. {
  856. return m_SortIndices.Count();
  857. }
  858. inline const CEdgeList::Edge_t &CEdgeList::EdgeFromSortIndex( int nSortIndex ) const
  859. {
  860. return m_Edges[ m_SortIndices[nSortIndex] ];
  861. }
  862. inline CEdgeList::Edge_t &CEdgeList::EdgeFromSortIndex( int nSortIndex )
  863. {
  864. return m_Edges[ m_SortIndices[nSortIndex] ];
  865. }
  866. inline const CEdgeList::Edge_t &CEdgeList::Edge( int nIndex ) const
  867. {
  868. return m_Edges[ nIndex ];
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Active edges...
  872. //-----------------------------------------------------------------------------
  873. inline const CEdgeList::Edge_t *CEdgeList::FirstActiveEdge( ) const
  874. {
  875. return m_StartTerminal.m_pNextActiveEdge;
  876. }
  877. inline CEdgeList::Edge_t *CEdgeList::FirstActiveEdge( )
  878. {
  879. return m_StartTerminal.m_pNextActiveEdge;
  880. }
  881. inline const CEdgeList::Edge_t *CEdgeList::LastActiveEdge( ) const
  882. {
  883. return m_EndTerminal.m_pPrevActiveEdge;
  884. }
  885. inline CEdgeList::Edge_t *CEdgeList::LastActiveEdge( )
  886. {
  887. return m_EndTerminal.m_pPrevActiveEdge;
  888. }
  889. inline bool CEdgeList::AtListEnd( const Edge_t* pEdge ) const
  890. {
  891. return pEdge == &m_EndTerminal;
  892. }
  893. inline bool CEdgeList::AtListStart( const Edge_t* pEdge ) const
  894. {
  895. return pEdge == &m_StartTerminal;
  896. }
  897. inline void CEdgeList::LinkActiveEdgeAfter( Edge_t *pPrevEdge, Edge_t *pInsertEdge )
  898. {
  899. pInsertEdge->m_pNextActiveEdge = pPrevEdge->m_pNextActiveEdge;
  900. pInsertEdge->m_pPrevActiveEdge = pPrevEdge;
  901. pInsertEdge->m_pNextActiveEdge->m_pPrevActiveEdge = pInsertEdge;
  902. pPrevEdge->m_pNextActiveEdge = pInsertEdge;
  903. }
  904. inline void CEdgeList::UnlinkActiveEdge( Edge_t *pEdge )
  905. {
  906. pEdge->m_pPrevActiveEdge->m_pNextActiveEdge = pEdge->m_pNextActiveEdge;
  907. pEdge->m_pNextActiveEdge->m_pPrevActiveEdge = pEdge->m_pPrevActiveEdge;
  908. #ifdef _DEBUG
  909. pEdge->m_pPrevActiveEdge = pEdge->m_pNextActiveEdge = NULL;
  910. #endif
  911. }
  912. //-----------------------------------------------------------------------------
  913. // Surface list
  914. //-----------------------------------------------------------------------------
  915. inline CEdgeList::Surface_t* CEdgeList::TopSurface()
  916. {
  917. return m_StartSurfTerminal.m_pNextSurface;
  918. }
  919. inline bool CEdgeList::AtSurfListEnd( const Surface_t* pSurface ) const
  920. {
  921. return pSurface == &m_EndSurfTerminal;
  922. }
  923. void CEdgeList::CleanupCurrentSurfaceList()
  924. {
  925. Surface_t *pSurf = TopSurface();
  926. while ( !AtSurfListEnd(pSurf) )
  927. {
  928. Surface_t *pNext = pSurf->m_pNextSurface;
  929. pSurf->m_pPrevSurface = pSurf->m_pNextSurface = NULL;
  930. pSurf = pNext;
  931. }
  932. }
  933. inline void CEdgeList::SetSurfaceArea( int nSurfID, float flArea )
  934. {
  935. m_Surfaces[nSurfID].m_flArea = flArea;
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Returns the z value of a surface given and x,y coordinate
  939. //-----------------------------------------------------------------------------
  940. inline float CEdgeList::ComputeZValue( const Surface_t *pSurface, float x, float y ) const
  941. {
  942. const cplane_t &plane = pSurface->m_Plane;
  943. Assert( plane.normal.z == 1.0f );
  944. return plane.dist - plane.normal.x * x - plane.normal.y * y;
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Computes a point at a specified y value along an edge
  948. //-----------------------------------------------------------------------------
  949. inline void CEdgeList::ComputePointAlongEdge( const Edge_t *pEdge, int nSurfID, float y, Vector *pPoint ) const
  950. {
  951. Assert( (y >= pEdge->m_vecPosition.y) && (y <= pEdge->m_vecPositionEnd.y) );
  952. float t;
  953. t = (y - pEdge->m_vecPosition.y) * pEdge->m_flOODy;
  954. pPoint->x = pEdge->m_vecPosition.x + ( pEdge->m_vecPositionEnd.x - pEdge->m_vecPosition.x ) * t;
  955. pPoint->y = y;
  956. pPoint->z = ComputeZValue( &m_Surfaces[nSurfID], pPoint->x, y );
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Surface ID management
  960. //-----------------------------------------------------------------------------
  961. int CEdgeList::AddSurface( const cplane_t &plane )
  962. {
  963. int nIndex = m_Surfaces.AddToTail();
  964. Surface_t &surf = m_Surfaces[nIndex];
  965. surf.m_flOOz = 0.0f;
  966. surf.m_Plane = plane;
  967. surf.m_pNextSurface = NULL;
  968. surf.m_pPrevSurface = NULL;
  969. surf.m_nSurfID = nIndex;
  970. m_SurfaceSort.AddToTail(nIndex);
  971. return nIndex;
  972. }
  973. //-----------------------------------------------------------------------------
  974. // Insertion
  975. //-----------------------------------------------------------------------------
  976. void CEdgeList::AddEdge( Vector **ppEdgeVertices, int nSurfID )
  977. {
  978. int nMinIndex = ( ppEdgeVertices[0]->y >= ppEdgeVertices[1]->y );
  979. const Vector &vecStartVert = *(ppEdgeVertices[ nMinIndex ]);
  980. const Vector &vecEndVert = *(ppEdgeVertices[ 1 - nMinIndex ]);
  981. // This is true if we've clipped to the near clip plane
  982. Assert( (vecStartVert.z >= 0.0f) && (vecEndVert.z >= 0.0f) );
  983. // Don't bother adding edges with dy == 0
  984. float dy = vecEndVert.y - vecStartVert.y;
  985. if (dy == 0.0f)
  986. return;
  987. int i = m_Edges.AddToTail();
  988. Edge_t &newEdge = m_Edges[i];
  989. newEdge.m_flOODy = 1.0f / dy;
  990. newEdge.m_vecPosition = vecStartVert;
  991. newEdge.m_vecPositionEnd = vecEndVert;
  992. newEdge.m_nSurfID = nSurfID;
  993. newEdge.m_flDxDy = (vecEndVert.x - vecStartVert.x) * newEdge.m_flOODy;
  994. newEdge.m_pPrevActiveEdge = NULL;
  995. newEdge.m_pNextActiveEdge = NULL;
  996. // Insert it into the sorted list
  997. m_OrigSortIndices.Insert( i );
  998. }
  999. //-----------------------------------------------------------------------------
  1000. // Used to sort the surfaces
  1001. //-----------------------------------------------------------------------------
  1002. CEdgeList::Surface_t *CEdgeList::s_pSortSurfaces = NULL;
  1003. int __cdecl CEdgeList::SurfCompare( const void *elem1, const void *elem2 )
  1004. {
  1005. int nSurfID1 = *(int*)elem1;
  1006. float flArea1 = s_pSortSurfaces[nSurfID1].m_flArea;
  1007. int nSurfID2 = *(int*)elem2;
  1008. float flArea2 = s_pSortSurfaces[nSurfID2].m_flArea;
  1009. if (flArea1 > flArea2)
  1010. return -1;
  1011. if (flArea1 < flArea2)
  1012. return 1;
  1013. return 0;
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Removal of small occluders
  1017. //-----------------------------------------------------------------------------
  1018. void CEdgeList::CullSmallOccluders()
  1019. {
  1020. // Cull out all surfaces with too small of a screen area...
  1021. // Sort the surfaces by screen area, in descending order
  1022. int nSurfCount = m_Surfaces.Count();
  1023. s_pSortSurfaces = m_Surfaces.Base();
  1024. qsort( m_SurfaceSort.Base(), nSurfCount, sizeof(int), SurfCompare );
  1025. // We're going to keep the greater of r_occludermin + All surfaces with a screen area >= r_occluderarea
  1026. int nMinSurfaces = r_occludermincount.GetInt();
  1027. // The *2 here is because surf areas are 2x bigger than actual
  1028. float flMinScreenArea = r_occluderminarea.GetFloat() * 0.02f;
  1029. if ( flMinScreenArea == 0.0f )
  1030. {
  1031. flMinScreenArea = OcclusionSystem()->MinOccluderArea() * 0.02f;
  1032. }
  1033. bool *bUseSurface = (bool*)stackalloc( nSurfCount * sizeof(bool) );
  1034. memset( bUseSurface, 0, nSurfCount * sizeof(bool) );
  1035. int i;
  1036. for ( i = 0; i < nSurfCount; ++i )
  1037. {
  1038. int nSurfID = m_SurfaceSort[i];
  1039. if (( m_Surfaces[ nSurfID ].m_flArea < flMinScreenArea ) && (i >= nMinSurfaces ))
  1040. break;
  1041. bUseSurface[nSurfID] = true;
  1042. }
  1043. MEM_ALLOC_CREDIT();
  1044. int nEdgeCount = m_OrigSortIndices.Count();
  1045. m_SortIndices.RemoveAll();
  1046. m_SortIndices.EnsureCapacity( nEdgeCount );
  1047. for( i = 0; i < nEdgeCount; ++i )
  1048. {
  1049. int nEdgeIndex = m_OrigSortIndices[i];
  1050. if ( bUseSurface[ m_Edges[ nEdgeIndex ].m_nSurfID ] )
  1051. {
  1052. m_SortIndices.AddToTail( nEdgeIndex );
  1053. }
  1054. }
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Removal
  1058. //-----------------------------------------------------------------------------
  1059. void CEdgeList::RemoveAll()
  1060. {
  1061. m_Edges.RemoveAll();
  1062. m_SortIndices.RemoveAll();
  1063. m_OrigSortIndices.RemoveAll();
  1064. m_Surfaces.RemoveAll();
  1065. m_SurfaceSort.RemoveAll();
  1066. }
  1067. //-----------------------------------------------------------------------------
  1068. // Active edge list
  1069. //-----------------------------------------------------------------------------
  1070. void CEdgeList::ResetActiveEdgeList()
  1071. {
  1072. // This shouldn't be called unless we're about to do active edge checking
  1073. Assert( ActualEdgeCount() );
  1074. m_nCurrentEdgeIndex = 0;
  1075. m_flNextDiscontinuity = EdgeFromSortIndex( 0 ).m_vecPosition.y;
  1076. m_StartTerminal.m_pNextActiveEdge = &m_EndTerminal;
  1077. m_EndTerminal.m_pPrevActiveEdge = &m_StartTerminal;
  1078. Assert( m_StartTerminal.m_pPrevActiveEdge == NULL );
  1079. Assert( m_EndTerminal.m_pNextActiveEdge == NULL );
  1080. }
  1081. //-----------------------------------------------------------------------------
  1082. // Returns the next time in Y the edge list will undergo a change
  1083. //-----------------------------------------------------------------------------
  1084. inline float CEdgeList::NextDiscontinuity() const
  1085. {
  1086. return m_flNextDiscontinuity;
  1087. }
  1088. //-----------------------------------------------------------------------------
  1089. // Used to insert an edge into the active edge list, sorted by X
  1090. // If Xs match, sort by Dx/Dy
  1091. //-----------------------------------------------------------------------------
  1092. inline bool CEdgeList::IsEdgeXGreater( const Edge_t *pEdge1, const Edge_t *pEdge2 )
  1093. {
  1094. float flDelta = pEdge1->m_flX - pEdge2->m_flX;
  1095. if ( flDelta > 0 )
  1096. return true;
  1097. if ( flDelta < 0 )
  1098. return false;
  1099. // NOTE: Using > instead of >= means coincident edges won't continually swap places
  1100. return pEdge1->m_flDxDy > pEdge2->m_flDxDy;
  1101. }
  1102. //-----------------------------------------------------------------------------
  1103. // Inserts an edge into the active edge list, sorted by X
  1104. //-----------------------------------------------------------------------------
  1105. inline void CEdgeList::InsertActiveEdge( Edge_t *pPrevEdge, Edge_t *pInsertEdge )
  1106. {
  1107. while( !AtListStart(pPrevEdge) && IsEdgeXGreater( pPrevEdge, pInsertEdge ) )
  1108. {
  1109. pPrevEdge = pPrevEdge->m_pPrevActiveEdge;
  1110. }
  1111. LinkActiveEdgeAfter( pPrevEdge, pInsertEdge );
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Clears the current scan line
  1115. //-----------------------------------------------------------------------------
  1116. float CEdgeList::ClearCurrentSurfaceList()
  1117. {
  1118. m_pCurrentActiveEdge = FirstActiveEdge();
  1119. m_flLastX = m_pCurrentActiveEdge->m_flX;
  1120. m_StartSurfTerminal.m_pNextSurface = &m_EndSurfTerminal;
  1121. m_EndSurfTerminal.m_pPrevSurface = &m_StartSurfTerminal;
  1122. return m_flLastX;
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. // Generates a list of surfaces on the current scan line
  1126. //-----------------------------------------------------------------------------
  1127. inline void CEdgeList::UpdateCurrentSurfaceZValues( float x, float y )
  1128. {
  1129. // Update the z values of all active surfaces
  1130. for ( Surface_t *pSurf = TopSurface(); !AtSurfListEnd( pSurf ); pSurf = pSurf->m_pNextSurface )
  1131. {
  1132. // NOTE: As long as we assume no interpenetrating surfaces,
  1133. // we don't need to re-sort by ooz here.
  1134. pSurf->m_flOOz = ComputeZValue( pSurf, x, y );
  1135. }
  1136. }
  1137. //-----------------------------------------------------------------------------
  1138. // Returns true if pTestSurf is closer (lower z value)
  1139. //-----------------------------------------------------------------------------
  1140. inline bool CEdgeList::IsSurfaceBehind( Surface_t *pTestSurf, Surface_t *pSurf )
  1141. {
  1142. if ( pTestSurf->m_flOOz - pSurf->m_flOOz <= -1e-6 )
  1143. return true;
  1144. if ( pTestSurf->m_flOOz - pSurf->m_flOOz >= 1e-6 )
  1145. return false;
  1146. // If they're nearly equal, then the thing that's approaching the screen
  1147. // more quickly as we ascend in y is closer
  1148. return ( pTestSurf->m_Plane.normal.y >= pSurf->m_Plane.normal.y );
  1149. }
  1150. //-----------------------------------------------------------------------------
  1151. // Introduces a single new edge
  1152. //-----------------------------------------------------------------------------
  1153. void CEdgeList::IntroduceSingleActiveEdge( const Edge_t *pEdge, float flCurrY )
  1154. {
  1155. Surface_t *pCurrentSurf = &m_Surfaces[ pEdge->m_nSurfID ];
  1156. if ( !pCurrentSurf->m_pNextSurface )
  1157. {
  1158. pCurrentSurf->m_flOOz = ComputeZValue( pCurrentSurf, pEdge->m_flX, flCurrY );
  1159. // Determine where to insert the surface into the surface list...
  1160. // Insert it so that the surface list is sorted by OOz
  1161. Surface_t *pNextSurface = TopSurface();
  1162. while( IsSurfaceBehind( pNextSurface, pCurrentSurf ) )
  1163. {
  1164. pNextSurface = pNextSurface->m_pNextSurface;
  1165. }
  1166. pCurrentSurf->m_pNextSurface = pNextSurface;
  1167. pCurrentSurf->m_pPrevSurface = pNextSurface->m_pPrevSurface;
  1168. pNextSurface->m_pPrevSurface = pCurrentSurf;
  1169. pCurrentSurf->m_pPrevSurface->m_pNextSurface = pCurrentSurf;
  1170. }
  1171. else
  1172. {
  1173. // This means this edge is associated with a surface
  1174. // already in the current surface list
  1175. // In this case, simply remove the surface from the surface list
  1176. pCurrentSurf->m_pNextSurface->m_pPrevSurface = pCurrentSurf->m_pPrevSurface;
  1177. pCurrentSurf->m_pPrevSurface->m_pNextSurface = pCurrentSurf->m_pNextSurface;
  1178. pCurrentSurf->m_pPrevSurface = pCurrentSurf->m_pNextSurface = NULL;
  1179. }
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Reduces the active occlusion edge list to the bare minimum set of edges
  1183. //-----------------------------------------------------------------------------
  1184. void CEdgeList::IntroduceNewActiveEdges( float y )
  1185. {
  1186. int nEdgeCount = ActualEdgeCount();
  1187. if ( m_nCurrentEdgeIndex == nEdgeCount )
  1188. return;
  1189. Edge_t *pCurEdge = &EdgeFromSortIndex( m_nCurrentEdgeIndex );
  1190. // Add new edges, computing the x + z coordinates at the requested y value
  1191. while ( pCurEdge->m_vecPosition.y <= y )
  1192. {
  1193. // This is necessary because of our initial skip up to y == -1.0f
  1194. if (pCurEdge->m_vecPositionEnd.y > y)
  1195. {
  1196. float flDy = y - pCurEdge->m_vecPosition.y;
  1197. pCurEdge->m_flX = pCurEdge->m_vecPosition.x + flDy * pCurEdge->m_flDxDy;
  1198. if ( pCurEdge->m_vecPositionEnd.y < m_flNextDiscontinuity )
  1199. {
  1200. m_flNextDiscontinuity = pCurEdge->m_vecPositionEnd.y;
  1201. }
  1202. // Now re-insert in the list, sorted by X
  1203. InsertActiveEdge( LastActiveEdge(), pCurEdge );
  1204. }
  1205. if ( ++m_nCurrentEdgeIndex == nEdgeCount )
  1206. return;
  1207. pCurEdge = &EdgeFromSortIndex( m_nCurrentEdgeIndex );
  1208. }
  1209. // The next edge in y will also present a discontinuity
  1210. if ( pCurEdge->m_vecPosition.y < m_flNextDiscontinuity )
  1211. {
  1212. m_flNextDiscontinuity = pCurEdge->m_vecPosition.y;
  1213. }
  1214. }
  1215. //-----------------------------------------------------------------------------
  1216. // Reduces the active edge list into a subset of ones we truly care about
  1217. //-----------------------------------------------------------------------------
  1218. void CEdgeList::ReduceActiveEdgeList( CWingedEdgeList &wingedEdgeList, float flMinY, float flMaxY )
  1219. {
  1220. // Surface lists should be empty
  1221. int i;
  1222. #ifdef DEBUG_OCCLUSION_SYSTEM
  1223. for ( i = m_Surfaces.Count(); --i >= 0; )
  1224. {
  1225. Assert( m_Surfaces[i].m_pNextSurface == NULL );
  1226. }
  1227. #endif
  1228. int nLeaveSurfID = -1;
  1229. const Edge_t *pCurEdge = FirstActiveEdge();
  1230. const Edge_t *pNextEdge;
  1231. // NOTE: This algorithm depends on the fact that the active edge
  1232. // list is not only sorted by ascending X, but also because edges
  1233. // that land on the same X value are sorted by ascending dy/dx
  1234. float flPrevX = pCurEdge->m_flX;
  1235. for ( ; !AtListEnd( pCurEdge ); pCurEdge = pNextEdge )
  1236. {
  1237. if ( pCurEdge->m_flX != flPrevX )
  1238. {
  1239. UpdateCurrentSurfaceZValues( pCurEdge->m_flX, flMinY );
  1240. }
  1241. IntroduceSingleActiveEdge( pCurEdge, flMinY );
  1242. flPrevX = pCurEdge->m_flX;
  1243. // If we have coincident edges, we have to introduce them at the same time...
  1244. pNextEdge = pCurEdge->m_pNextActiveEdge;
  1245. if ( (flPrevX == pNextEdge->m_flX) && (pCurEdge->m_flDxDy == pNextEdge->m_flDxDy) )
  1246. continue;
  1247. // If there's more than one overlapping surface at this point,
  1248. // we can eliminate some edges.
  1249. int nEnterSurfID = TopSurface()->m_nSurfID;
  1250. // No change in the top surface? No edges needed...
  1251. if ( nLeaveSurfID == nEnterSurfID )
  1252. continue;
  1253. Assert( ( nLeaveSurfID != -1 ) || ( nEnterSurfID != -1 ) );
  1254. int nEdgeSurfID = ( nEnterSurfID != -1 ) ? nEnterSurfID : nLeaveSurfID;
  1255. // Seam up edges...
  1256. for ( i = m_nPrevReduceCount; --i >= 0; )
  1257. {
  1258. CWingedEdgeList::WingedEdge_t &testEdge = wingedEdgeList.WingedEdge( m_pPrevReduceInfo[i].m_nWingedEdge );
  1259. if (( testEdge.m_nLeaveSurfID != nLeaveSurfID ) || ( testEdge.m_nEnterSurfID != nEnterSurfID ))
  1260. continue;
  1261. if ( ( testEdge.m_flDxDy != pCurEdge->m_flDxDy) || ( fabs( testEdge.m_vecPositionEnd.x - pCurEdge->m_flX ) >= 1e-3 ) )
  1262. continue;
  1263. ComputePointAlongEdge( m_pPrevReduceInfo[i].m_pEdge, nEdgeSurfID, flMaxY, &testEdge.m_vecPositionEnd );
  1264. // Don't try to seam up edges that end on this line...
  1265. if ( pCurEdge->m_vecPositionEnd.y > flMaxY )
  1266. {
  1267. ReduceInfo_t *pNewEdge = &m_pNewReduceInfo[ m_nNewReduceCount ];
  1268. ++m_nNewReduceCount;
  1269. pNewEdge->m_pEdge = m_pPrevReduceInfo[i].m_pEdge;
  1270. pNewEdge->m_nWingedEdge = m_pPrevReduceInfo[i].m_nWingedEdge;
  1271. }
  1272. break;
  1273. }
  1274. // This edge didn't exist on the previous y discontinuity line
  1275. // We'll need to make a new one
  1276. if ( i < 0 )
  1277. {
  1278. i = wingedEdgeList.AddEdge();
  1279. CWingedEdgeList::WingedEdge_t &newWingedEdge = wingedEdgeList.WingedEdge(i);
  1280. newWingedEdge.m_nLeaveSurfID = nLeaveSurfID;
  1281. newWingedEdge.m_nEnterSurfID = nEnterSurfID;
  1282. newWingedEdge.m_flDxDy = pCurEdge->m_flDxDy;
  1283. ComputePointAlongEdge( pCurEdge, nEdgeSurfID, flMinY, &newWingedEdge.m_vecPosition );
  1284. ComputePointAlongEdge( pCurEdge, nEdgeSurfID, flMaxY, &newWingedEdge.m_vecPositionEnd );
  1285. // Enforce sort order...
  1286. // Required because we're computing the x position here, which can introduce error.
  1287. if ( i != 0 )
  1288. {
  1289. CWingedEdgeList::WingedEdge_t &prevWingedEdge = wingedEdgeList.WingedEdge(i - 1);
  1290. if ( newWingedEdge.m_vecPosition.y == prevWingedEdge.m_vecPosition.y )
  1291. {
  1292. if ( newWingedEdge.m_vecPosition.x < prevWingedEdge.m_vecPosition.x )
  1293. {
  1294. newWingedEdge.m_vecPosition.x = prevWingedEdge.m_vecPosition.x;
  1295. }
  1296. }
  1297. }
  1298. // Don't try to seam up edges that end on this line...
  1299. if ( pCurEdge->m_vecPositionEnd.y > flMaxY )
  1300. {
  1301. ReduceInfo_t *pNewEdge = &m_pNewReduceInfo[ m_nNewReduceCount ];
  1302. ++m_nNewReduceCount;
  1303. pNewEdge->m_pEdge = pCurEdge;
  1304. pNewEdge->m_nWingedEdge = i;
  1305. }
  1306. #ifdef DEBUG_OCCLUSION_SYSTEM
  1307. wingedEdgeList.CheckConsistency();
  1308. #endif
  1309. }
  1310. nLeaveSurfID = nEnterSurfID;
  1311. }
  1312. Assert( nLeaveSurfID == -1 );
  1313. // Msg("\n");
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. // Discovers the first edge crossing discontinuity
  1317. //-----------------------------------------------------------------------------
  1318. float CEdgeList::LocateEdgeCrossingDiscontinuity( float flNextY, float flPrevY, int &nCount, Edge_t **ppInfo )
  1319. {
  1320. nCount = 0;
  1321. float flCurrX = -FLT_MAX;
  1322. float flNextX = -FLT_MAX;
  1323. float flCurrY = flNextY;
  1324. Vector2D vecDelta, vecIntersection;
  1325. Edge_t *pCurEdge;
  1326. for ( pCurEdge = FirstActiveEdge(); !AtListEnd(pCurEdge); flCurrX = flNextX, pCurEdge = pCurEdge->m_pNextActiveEdge )
  1327. {
  1328. // Don't take into account edges that end on the current line
  1329. Assert( pCurEdge->m_vecPositionEnd.y >= flCurrY );
  1330. flNextX = pCurEdge->m_vecPosition.x + (flCurrY - pCurEdge->m_vecPosition.y) * pCurEdge->m_flDxDy;
  1331. // Look for an X-crossing... This check helps for nearly co-linear lines
  1332. // NOTE: You might think this would crash since it could dereference a NULL
  1333. // pointer the first time through the loop, but it never hits that check since the
  1334. // first X test is guaranteed to pass
  1335. Edge_t *pPrevEdge = pCurEdge->m_pPrevActiveEdge;
  1336. if ( ( flNextX > flCurrX ) || ( pPrevEdge->m_flDxDy <= pCurEdge->m_flDxDy ) )
  1337. continue;
  1338. // This test is necessary to not capture edges that meet at a point...
  1339. if ( pPrevEdge->m_vecPositionEnd == pCurEdge->m_vecPositionEnd )
  1340. continue;
  1341. Assert( pPrevEdge->m_flDxDy != pCurEdge->m_flDxDy );
  1342. // Found one! Let's find the intersection of these two
  1343. // edges and up the Y discontinuity to that point.
  1344. // We'll solve this by doing an intersection of point + plane in 2D...
  1345. // For the line, we'll use the previous line where
  1346. // P = Pop + D * t, Pop = prevEdge.m_vecPosition, D = [dx dy] = [(dx/dy) 1]
  1347. // For the plane, we'll use the current line where
  1348. // N * P = d
  1349. // Normal is perpendicular to the line, therefore N = [-dy dx] = [-1 (dx/dy)]
  1350. // d = DotProduct( N, edge.m_vecPosition ) = N dot Pon
  1351. // So, the t that solve the equation is given by t = (d - N dot Pop) / (N dot D)
  1352. // Or, t = (N dot Pon - N dot Pop) / (N dot D)
  1353. // t = (N dot (Pon - Pop)) / (N dot D)
  1354. float flDenominator = 1.0f / (-pPrevEdge->m_flDxDy + pCurEdge->m_flDxDy);
  1355. Vector2DSubtract( pCurEdge->m_vecPosition.AsVector2D(), pPrevEdge->m_vecPosition.AsVector2D(), vecDelta );
  1356. float flNumerator = - vecDelta.x + pCurEdge->m_flDxDy * vecDelta.y;
  1357. float t = flNumerator * flDenominator;
  1358. float flYCrossing = pPrevEdge->m_vecPosition.y + t;
  1359. // Precision errors...
  1360. // NOTE: The optimizer unfortunately causes this test to not return ==
  1361. // if the bitpattern of flYCrossing and flNextY are the exact same, because it's
  1362. // doing the test with the 80bit fp registers. flYCrossing is still sitting in the register
  1363. // from the computation on the line above, but flNextY isn't. Therefore it returns not equal.
  1364. // That's why I have to do the explicit bitpattern check.
  1365. if ( ( flYCrossing >= flNextY ) || ( *(int*)&flYCrossing == *(int*)&flNextY ) )
  1366. continue;
  1367. if ( flYCrossing < flPrevY )
  1368. {
  1369. flYCrossing = flPrevY;
  1370. }
  1371. // If we advanced in Y, then reset the edge crossings
  1372. if ( flCurrY != flYCrossing )
  1373. {
  1374. flCurrY = flYCrossing;
  1375. nCount = 0;
  1376. }
  1377. Assert( nCount < MAX_EDGE_CROSSINGS );
  1378. flNextX = pPrevEdge->m_vecPosition.x + t * pPrevEdge->m_flDxDy;
  1379. ppInfo[nCount++] = pCurEdge;
  1380. }
  1381. return flCurrY;
  1382. }
  1383. //-----------------------------------------------------------------------------
  1384. // Advances the X values of the active edge list, with no reordering
  1385. //-----------------------------------------------------------------------------
  1386. void CEdgeList::AdvanceActiveEdgeList( float flCurrY )
  1387. {
  1388. m_flNextDiscontinuity = FLT_MAX;
  1389. // Advance all edges until the current Y; we don't need to re-order *any* edges.
  1390. Edge_t *pCurEdge;
  1391. Edge_t *pNextEdge;
  1392. float flPrevX = -FLT_MAX;
  1393. for ( pCurEdge = FirstActiveEdge(); !AtListEnd( pCurEdge ); pCurEdge = pNextEdge )
  1394. {
  1395. pNextEdge = pCurEdge->m_pNextActiveEdge;
  1396. if ( pCurEdge->m_vecPositionEnd.y <= flCurrY )
  1397. {
  1398. UnlinkActiveEdge( pCurEdge );
  1399. continue;
  1400. }
  1401. pCurEdge->m_flX = pCurEdge->m_vecPosition.x + (flCurrY - pCurEdge->m_vecPosition.y) * pCurEdge->m_flDxDy;
  1402. // Eliminate precision errors by guaranteeing sort ordering...
  1403. if ( pCurEdge->m_flX < flPrevX )
  1404. {
  1405. pCurEdge->m_flX = flPrevX;
  1406. }
  1407. else
  1408. {
  1409. flPrevX = pCurEdge->m_flX;
  1410. }
  1411. if ( pCurEdge->m_vecPositionEnd.y < m_flNextDiscontinuity )
  1412. {
  1413. m_flNextDiscontinuity = pCurEdge->m_vecPositionEnd.y;
  1414. }
  1415. }
  1416. }
  1417. //-----------------------------------------------------------------------------
  1418. // Reorders the active edge list based on where edge crossings occur
  1419. //-----------------------------------------------------------------------------
  1420. void CEdgeList::ReorderActiveEdgeList( int nCount, Edge_t **ppCrossings )
  1421. {
  1422. int nCurCrossing = 0;
  1423. while ( nCurCrossing < nCount )
  1424. {
  1425. // Re-order the list where the edge crossing occurred.
  1426. // For all edges that passed through the exact same point, we need only
  1427. // reverse the order of those edges. At the same time, slam the X value of each
  1428. // crossing edge to reduce precision errors
  1429. Edge_t *pCurCrossing = ppCrossings[nCurCrossing++];
  1430. Edge_t *pFirstCrossing = pCurCrossing->m_pPrevActiveEdge;
  1431. // First, bring shared (or nearly shared) edges into the crossing list...
  1432. while ( pFirstCrossing->m_pPrevActiveEdge->m_flX == pFirstCrossing->m_flX )
  1433. {
  1434. pFirstCrossing = pFirstCrossing->m_pPrevActiveEdge;
  1435. }
  1436. // Find the last crossing...
  1437. Edge_t *pLastCrossing = pCurCrossing->m_pNextActiveEdge;
  1438. Edge_t *pPrevCrossing = pCurCrossing;
  1439. while ( true )
  1440. {
  1441. if ( (nCurCrossing < nCount) && (pLastCrossing == ppCrossings[nCurCrossing]) )
  1442. {
  1443. pPrevCrossing = pLastCrossing;
  1444. pLastCrossing = pLastCrossing->m_pNextActiveEdge;
  1445. ++nCurCrossing;
  1446. continue;
  1447. }
  1448. if ( pPrevCrossing->m_flX != pLastCrossing->m_flX )
  1449. break;
  1450. pLastCrossing = pLastCrossing->m_pNextActiveEdge;
  1451. }
  1452. // This should always be true, since there's always an edge at FLT_MAX.
  1453. Assert( pLastCrossing );
  1454. // Slam all x values to be the same to avoid precision errors...
  1455. // This guarantees that this crossing at least will occur
  1456. float flXCrossing = pFirstCrossing->m_flX;
  1457. for ( Edge_t *pCrossing = pFirstCrossing->m_pNextActiveEdge; pCrossing != pLastCrossing; pCrossing = pCrossing->m_pNextActiveEdge )
  1458. {
  1459. pCrossing->m_flX = flXCrossing;
  1460. }
  1461. }
  1462. // Now re-insert everything to take into account other edges which may well have
  1463. // crossed on this line
  1464. Edge_t *pEdge;
  1465. Edge_t *pNextEdge;
  1466. for( pEdge = FirstActiveEdge(); !AtListEnd(pEdge); pEdge = pNextEdge )
  1467. {
  1468. pNextEdge = pEdge->m_pNextActiveEdge;
  1469. Edge_t *pPrevEdge = pEdge->m_pPrevActiveEdge;
  1470. if ( pPrevEdge->m_flX == pEdge->m_flX )
  1471. {
  1472. UnlinkActiveEdge( pEdge );
  1473. InsertActiveEdge( pPrevEdge, pEdge );
  1474. }
  1475. }
  1476. }
  1477. //-----------------------------------------------------------------------------
  1478. // Reduces the active occlusion edge list to the bare minimum set of edges
  1479. //-----------------------------------------------------------------------------
  1480. void CEdgeList::SpewActiveEdgeList( float y, bool bHex)
  1481. {
  1482. Edge_t *pEdge = FirstActiveEdge();
  1483. Msg( "%.3f : ", y );
  1484. while ( !AtListEnd( pEdge ) )
  1485. {
  1486. if (!bHex)
  1487. {
  1488. Msg( "(%d %.3f [%d]) ", (int)(pEdge - m_Edges.Base()), pEdge->m_flX, pEdge->m_nSurfID );
  1489. }
  1490. else
  1491. {
  1492. Msg( "(%d %X [%d]) ", (int)(pEdge - m_Edges.Base()), *(int*)&pEdge->m_flX, pEdge->m_nSurfID );
  1493. }
  1494. pEdge = pEdge->m_pNextActiveEdge;
  1495. }
  1496. Msg( "\n" );
  1497. }
  1498. //-----------------------------------------------------------------------------
  1499. // Checks consistency of the edge list...
  1500. //-----------------------------------------------------------------------------
  1501. void CEdgeList::CheckConsistency()
  1502. {
  1503. Edge_t *pEdge = FirstActiveEdge();
  1504. while( !AtListEnd( pEdge ) )
  1505. {
  1506. Edge_t *pPrevEdge = pEdge->m_pPrevActiveEdge;
  1507. Assert( pEdge->m_flX >= pPrevEdge->m_flX );
  1508. if ( pEdge->m_flX == pPrevEdge->m_flX )
  1509. {
  1510. // End point check necessary because of precision errors
  1511. Assert( (pEdge->m_flDxDy >= pPrevEdge->m_flDxDy) || (pEdge->m_vecPositionEnd == pPrevEdge->m_vecPositionEnd) );
  1512. }
  1513. pEdge = pEdge->m_pNextActiveEdge;
  1514. }
  1515. }
  1516. //-----------------------------------------------------------------------------
  1517. // Reduces the active occlusion edge list to the bare minimum set of edges
  1518. //-----------------------------------------------------------------------------
  1519. void CEdgeList::ReduceActiveList( CWingedEdgeList &newEdgeList )
  1520. {
  1521. int nEdgeCount = ActualEdgeCount();
  1522. if ( nEdgeCount == 0 )
  1523. return;
  1524. // Copy the surfaces over
  1525. int nCount = m_Surfaces.Count();
  1526. // newEdgeList.m_Surfaces.EnsureCapacity( nCount );
  1527. for ( int i = 0; i < nCount; ++i )
  1528. {
  1529. newEdgeList.AddSurface( m_Surfaces[i].m_Plane );
  1530. }
  1531. Edge_t *pEdgeCrossings[MAX_EDGE_CROSSINGS];
  1532. ReduceInfo_t *pBuf[2];
  1533. pBuf[0] = (ReduceInfo_t*)stackalloc( nEdgeCount * sizeof(ReduceInfo_t) );
  1534. pBuf[1] = (ReduceInfo_t*)stackalloc( nEdgeCount * sizeof(ReduceInfo_t) );
  1535. m_nPrevReduceCount = m_nNewReduceCount = 0;
  1536. int nIndex = 0;
  1537. ResetActiveEdgeList();
  1538. ClearCurrentSurfaceList();
  1539. // We can skip everything up to y = -1.0f; since that's offscreen
  1540. float flPrevY = NextDiscontinuity();
  1541. flPrevY = fpmax( -1.0f, flPrevY );
  1542. m_flNextDiscontinuity = FLT_MAX;
  1543. IntroduceNewActiveEdges( flPrevY );
  1544. int nEdgeCrossingCount = 0;
  1545. bool bDone = false;
  1546. while( !bDone )
  1547. {
  1548. // Don't immediately progress to the next discontinuity if there are edge crossings.
  1549. float flNextY = LocateEdgeCrossingDiscontinuity( NextDiscontinuity(), flPrevY, nEdgeCrossingCount, pEdgeCrossings );
  1550. #ifdef DEBUG_OCCLUSION_SYSTEM
  1551. if ( s_bSpew )
  1552. {
  1553. // Debugging spew
  1554. SpewActiveEdgeList( flPrevY );
  1555. }
  1556. #endif
  1557. // Reduce the active edge list
  1558. m_pNewReduceInfo = pBuf[1 - nIndex];
  1559. m_pPrevReduceInfo = pBuf[nIndex];
  1560. m_nPrevReduceCount = m_nNewReduceCount;
  1561. m_nNewReduceCount = 0;
  1562. // Add a small epsilon so we occlude things on the top edge at y = 1.0
  1563. if (flNextY >= 1.001f)
  1564. {
  1565. flNextY = 1.001f;
  1566. bDone = true;
  1567. }
  1568. ReduceActiveEdgeList( newEdgeList, flPrevY, flNextY );
  1569. flPrevY = flNextY;
  1570. // Advance the active edge list, with no resorting necessary!!
  1571. AdvanceActiveEdgeList( flNextY );
  1572. // If we had an edge crossing, re-order the edges. Otherwise introduce new active edges
  1573. if ( !nEdgeCrossingCount )
  1574. {
  1575. IntroduceNewActiveEdges( flNextY );
  1576. // Keep advancing the active edge list until it's got no more discontinuities
  1577. if ( NextDiscontinuity() == FLT_MAX )
  1578. return;
  1579. }
  1580. else
  1581. {
  1582. ReorderActiveEdgeList( nEdgeCrossingCount, pEdgeCrossings );
  1583. // The next edge in y will also present a discontinuity
  1584. if ( m_nCurrentEdgeIndex < nEdgeCount )
  1585. {
  1586. float flNextEdgeY = EdgeFromSortIndex( m_nCurrentEdgeIndex ).m_vecPosition.y;
  1587. if ( flNextEdgeY < m_flNextDiscontinuity )
  1588. {
  1589. m_flNextDiscontinuity = flNextEdgeY;
  1590. }
  1591. }
  1592. }
  1593. #ifdef DEBUG_OCCLUSION_SYSTEM
  1594. CheckConsistency();
  1595. #endif
  1596. nIndex = 1 - nIndex;
  1597. }
  1598. }
  1599. //-----------------------------------------------------------------------------
  1600. // Used to debug the occlusion system
  1601. //-----------------------------------------------------------------------------
  1602. void CEdgeList::QueueVisualization( unsigned char *pColor )
  1603. {
  1604. #ifndef DEDICATED
  1605. if ( !r_visocclusion.GetInt() )
  1606. return;
  1607. int nFirst = g_EdgeVisualization.AddMultipleToTail( m_Edges.Count() );
  1608. for ( int i = m_Edges.Count(); --i >= 0; )
  1609. {
  1610. EdgeVisualizationInfo_t &info = g_EdgeVisualization[nFirst + i];
  1611. info.m_vecPoint[0] = m_Edges[i].m_vecPosition;
  1612. info.m_vecPoint[1] = m_Edges[i].m_vecPositionEnd;
  1613. *(int*)(info.m_pColor) = *(int*)pColor;
  1614. }
  1615. #endif
  1616. }
  1617. void CEdgeList::Visualize( unsigned char *pColor )
  1618. {
  1619. #ifndef DEDICATED
  1620. if ( !r_visocclusion.GetInt() )
  1621. return;
  1622. CMatRenderContextPtr pRenderContext( materials );
  1623. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1624. pRenderContext->PushMatrix();
  1625. pRenderContext->LoadIdentity();
  1626. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1627. pRenderContext->PushMatrix();
  1628. pRenderContext->LoadIdentity();
  1629. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1630. pRenderContext->PushMatrix();
  1631. pRenderContext->LoadIdentity();
  1632. pRenderContext->Bind( g_pMaterialWireframeVertexColorIgnoreZ );
  1633. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  1634. CMeshBuilder meshBuilder;
  1635. meshBuilder.Begin( pMesh, MATERIAL_LINES, m_Edges.Count() );
  1636. int i;
  1637. for ( i = m_Edges.Count(); --i >= 0; )
  1638. {
  1639. meshBuilder.Position3fv( m_Edges[i].m_vecPosition.Base() );
  1640. meshBuilder.Color4ubv( pColor );
  1641. meshBuilder.AdvanceVertex();
  1642. meshBuilder.Position3fv( m_Edges[i].m_vecPositionEnd.Base() );
  1643. #ifdef DEBUG_OCCLUSION_SYSTEM
  1644. meshBuilder.Color4ub( 0, 0, 255, 255 );
  1645. #else
  1646. meshBuilder.Color4ubv( pColor );
  1647. #endif
  1648. meshBuilder.AdvanceVertex();
  1649. }
  1650. meshBuilder.End();
  1651. pMesh->Draw();
  1652. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1653. pRenderContext->PopMatrix();
  1654. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1655. pRenderContext->PopMatrix();
  1656. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1657. pRenderContext->PopMatrix();
  1658. #endif
  1659. }
  1660. //-----------------------------------------------------------------------------
  1661. // Implementation of IOcclusionSystem
  1662. //-----------------------------------------------------------------------------
  1663. class COcclusionSystem : public IOcclusionSystem
  1664. {
  1665. public:
  1666. COcclusionSystem();
  1667. ~COcclusionSystem();
  1668. // Inherited from IOcclusionSystem
  1669. virtual void ActivateOccluder( int nOccluderIndex, bool bActive );
  1670. virtual void SetView( const Vector &vecCameraPos, float flFOV, const VMatrix &worldToCamera, const VMatrix &cameraToProjection, const VPlane &nearClipPlane );
  1671. virtual int GetViewId() const;
  1672. virtual bool IsOccluded( int occlusionViewId, const Vector &vecAbsMins, const Vector &vecAbsMaxs );
  1673. virtual void SetOcclusionParameters( float flMaxOccludeeArea, float flMinOccluderArea );
  1674. virtual float MinOccluderArea() const;
  1675. virtual void DrawDebugOverlays();
  1676. private:
  1677. struct AxisAlignedPlane_t
  1678. {
  1679. int m_nAxis;
  1680. float m_flSign;
  1681. float m_flDist;
  1682. };
  1683. struct ViewData_t
  1684. {
  1685. bool m_bEdgeListDirty;
  1686. VMatrix m_WorldToProjection;
  1687. VMatrix m_WorldToCamera;
  1688. float m_flXProjScale;
  1689. float m_flYProjScale;
  1690. float m_flProjDistScale;
  1691. float m_flProjDistOffset;
  1692. Vector m_vecCameraPosition; // in world space
  1693. cplane_t m_NearClipPlane;
  1694. float m_flNearPlaneDist;
  1695. float m_flFOVFactor;
  1696. };
  1697. // Recomputes the edge list for occluders
  1698. void RecomputeOccluderEdgeList( ViewData_t& viewData );
  1699. // Is the point inside the near plane?
  1700. bool IsPointInsideNearPlane( const ViewData_t& viewData, const Vector &vecPos ) const;
  1701. void IntersectWithNearPlane( const ViewData_t& viewData, const Vector &vecStart, const Vector &vecEnd, Vector &outPos ) const;
  1702. // Clips a polygon to the near clip plane
  1703. int ClipPolygonToNearPlane( const ViewData_t& viewData, Vector **ppVertices, int nVertexCount, Vector **ppOutVerts, bool *pClipped ) const;
  1704. // Project world-space verts + add into the edge list
  1705. void AddPolygonToEdgeList( const ViewData_t& viewData, CEdgeList &edgeList, Vector **ppPolygon, int nCount, int nSurfID, bool bClipped );
  1706. // Computes the plane equation of a polygon in screen space from a camera-space plane
  1707. void ComputeScreenSpacePlane( const ViewData_t& viewData, const cplane_t &cameraSpacePlane, cplane_t *pScreenSpacePlane );
  1708. // Used to clip the screen space polygons to the screen
  1709. void ResetClipTempVerts();
  1710. int ClipPolygonToAxisAlignedPlane( Vector **ppVertices, int nVertexCount,
  1711. const AxisAlignedPlane_t &plane, Vector **ppOutVerts ) const;
  1712. // Is the point within an axis-aligned plane?
  1713. bool IsPointInsideAAPlane( const Vector &vecPos, const AxisAlignedPlane_t &plane ) const;
  1714. void IntersectWithAAPlane( const Vector &vecStart, const Vector &vecEnd, const AxisAlignedPlane_t &plane, Vector &outPos ) const;
  1715. // Stitches up clipped vertices
  1716. void StitchClippedVertices( Vector *pVertices, int nCount );
  1717. private:
  1718. // Per-frame information
  1719. int m_nCurrentViewId;
  1720. // ring buffer
  1721. ViewData_t m_viewData[OCCLUSION_SYSTEM_VIEWDATA_MAX];
  1722. CEdgeList m_EdgeList;
  1723. CWingedEdgeList m_WingedEdgeList;
  1724. CUtlVector< Vector > m_ClippedVerts;
  1725. float m_flMaxOccludeeArea;
  1726. float m_flMinOccluderArea;
  1727. // Stats
  1728. int m_nTests;
  1729. int m_nOccluded;
  1730. };
  1731. static COcclusionSystem g_OcclusionSystem;
  1732. //-----------------------------------------------------------------------------
  1733. // Singleton accessor
  1734. //-----------------------------------------------------------------------------
  1735. IOcclusionSystem *OcclusionSystem()
  1736. {
  1737. return &g_OcclusionSystem;
  1738. }
  1739. //-----------------------------------------------------------------------------
  1740. // Constructor, destructor
  1741. //-----------------------------------------------------------------------------
  1742. COcclusionSystem::COcclusionSystem() : m_ClippedVerts( 0, 64 )
  1743. {
  1744. m_nCurrentViewId = 0;
  1745. for (int i = 0; i < OCCLUSION_SYSTEM_VIEWDATA_MAX; ++i)
  1746. {
  1747. m_viewData[i].m_bEdgeListDirty = false;
  1748. }
  1749. m_nTests = 0;
  1750. m_nOccluded = 0;
  1751. m_flMinOccluderArea = DEFAULT_MIN_OCCLUDER_AREA;
  1752. m_flMaxOccludeeArea = DEFAULT_MAX_OCCLUDEE_AREA;
  1753. }
  1754. COcclusionSystem::~COcclusionSystem()
  1755. {
  1756. }
  1757. //-----------------------------------------------------------------------------
  1758. // Occlusion parameters?
  1759. //-----------------------------------------------------------------------------
  1760. void COcclusionSystem::SetOcclusionParameters( float flMaxOccludeeArea, float flMinOccluderArea )
  1761. {
  1762. m_flMaxOccludeeArea = (flMaxOccludeeArea ? flMaxOccludeeArea : DEFAULT_MAX_OCCLUDEE_AREA) * 0.01f;
  1763. m_flMinOccluderArea = (flMinOccluderArea ? flMinOccluderArea : DEFAULT_MIN_OCCLUDER_AREA);
  1764. }
  1765. float COcclusionSystem::MinOccluderArea() const
  1766. {
  1767. return m_flMinOccluderArea;
  1768. }
  1769. //-----------------------------------------------------------------------------
  1770. // Is the point within the near plane?
  1771. //-----------------------------------------------------------------------------
  1772. inline bool COcclusionSystem::IsPointInsideNearPlane( const ViewData_t& viewData, const Vector &vecPos ) const
  1773. {
  1774. return DotProduct( vecPos, viewData.m_NearClipPlane.normal ) >= viewData.m_NearClipPlane.dist;
  1775. }
  1776. inline void COcclusionSystem::IntersectWithNearPlane( const ViewData_t& viewData, const Vector &vecStart, const Vector &vecEnd, Vector &outPos ) const
  1777. {
  1778. Vector vecDir;
  1779. VectorSubtract( vecEnd, vecStart, vecDir );
  1780. float t = IntersectRayWithPlane( vecStart, vecDir, viewData.m_NearClipPlane.normal, viewData.m_NearClipPlane.dist );
  1781. VectorLerp( vecStart, vecEnd, t, outPos );
  1782. }
  1783. //-----------------------------------------------------------------------------
  1784. // Clips a surface to the near clip plane
  1785. // FIXME: This blows: a *third* S-H clipper in the engine! All because the
  1786. // vertex formats are different owing to different goals of the 3 clippers
  1787. //-----------------------------------------------------------------------------
  1788. static Vector s_TempVertMemory[256];
  1789. int COcclusionSystem::ClipPolygonToNearPlane( const ViewData_t& viewData, Vector **ppVertices, int nVertexCount, Vector **ppOutVerts, bool *pClipped ) const
  1790. {
  1791. *pClipped = false;
  1792. if ( nVertexCount < 3 )
  1793. return 0;
  1794. // Ye Olde Sutherland-Hodgman clipping algorithm
  1795. int nOutVertCount = 0;
  1796. int nNewVertCount = 0;
  1797. Vector* pStart = ppVertices[ nVertexCount - 1 ];
  1798. bool bStartInside = IsPointInsideNearPlane( viewData, *pStart );
  1799. for ( int i = 0; i < nVertexCount; ++i )
  1800. {
  1801. Vector* pEnd = ppVertices[ i ];
  1802. bool bEndInside = IsPointInsideNearPlane( viewData, *pEnd );
  1803. if (bEndInside)
  1804. {
  1805. if (!bStartInside)
  1806. {
  1807. // Started outside, ended inside, need to clip the edge
  1808. ppOutVerts[nOutVertCount] = &s_TempVertMemory[ nNewVertCount++ ];
  1809. IntersectWithNearPlane( viewData, *pStart, *pEnd, *ppOutVerts[nOutVertCount] );
  1810. ++nOutVertCount;
  1811. *pClipped = true;
  1812. }
  1813. ppOutVerts[nOutVertCount++] = pEnd;
  1814. }
  1815. else
  1816. {
  1817. if (bStartInside)
  1818. {
  1819. // Started inside, ended outside, need to clip the edge
  1820. ppOutVerts[nOutVertCount] = &s_TempVertMemory[ nNewVertCount++ ];
  1821. IntersectWithNearPlane( viewData, *pStart, *pEnd, *ppOutVerts[nOutVertCount] );
  1822. ++nOutVertCount;
  1823. *pClipped = true;
  1824. }
  1825. }
  1826. pStart = pEnd;
  1827. bStartInside = bEndInside;
  1828. }
  1829. return nOutVertCount;
  1830. }
  1831. //-----------------------------------------------------------------------------
  1832. // Is the point within an axis-aligned plane?
  1833. //-----------------------------------------------------------------------------
  1834. inline bool COcclusionSystem::IsPointInsideAAPlane( const Vector &vecPos, const AxisAlignedPlane_t &plane ) const
  1835. {
  1836. return vecPos[plane.m_nAxis] * plane.m_flSign >= plane.m_flDist;
  1837. }
  1838. inline void COcclusionSystem::IntersectWithAAPlane( const Vector &vecStart, const Vector &vecEnd, const AxisAlignedPlane_t &plane, Vector &outPos ) const
  1839. {
  1840. float t = IntersectRayWithAAPlane( vecStart, vecEnd, plane.m_nAxis, plane.m_flSign, plane.m_flDist );
  1841. VectorLerp( vecStart, vecEnd, t, outPos );
  1842. }
  1843. //-----------------------------------------------------------------------------
  1844. // Clips a surface to the edges of the screen (axis-aligned planes)
  1845. //-----------------------------------------------------------------------------
  1846. static int s_nTempVertCount = 0;
  1847. void COcclusionSystem::ResetClipTempVerts()
  1848. {
  1849. s_nTempVertCount = 0;
  1850. }
  1851. int COcclusionSystem::ClipPolygonToAxisAlignedPlane( Vector **ppVertices, int nVertexCount,
  1852. const AxisAlignedPlane_t &plane, Vector **ppOutVerts ) const
  1853. {
  1854. // Ye Olde Sutherland-Hodgman clipping algorithm
  1855. int nOutVertCount = 0;
  1856. Vector* pStart = ppVertices[ nVertexCount - 1 ];
  1857. bool bStartInside = IsPointInsideAAPlane( *pStart, plane );
  1858. for ( int i = 0; i < nVertexCount; ++i )
  1859. {
  1860. Vector* pEnd = ppVertices[ i ];
  1861. bool bEndInside = IsPointInsideAAPlane( *pEnd, plane );
  1862. if (bEndInside)
  1863. {
  1864. if (!bStartInside)
  1865. {
  1866. // Started outside, ended inside, need to clip the edge
  1867. ppOutVerts[nOutVertCount] = &s_TempVertMemory[ s_nTempVertCount++ ];
  1868. IntersectWithAAPlane( *pStart, *pEnd, plane, *ppOutVerts[nOutVertCount] );
  1869. ++nOutVertCount;
  1870. }
  1871. ppOutVerts[nOutVertCount++] = pEnd;
  1872. }
  1873. else
  1874. {
  1875. if (bStartInside)
  1876. {
  1877. // Started inside, ended outside, need to clip the edge
  1878. ppOutVerts[nOutVertCount] = &s_TempVertMemory[ s_nTempVertCount++ ];
  1879. IntersectWithAAPlane( *pStart, *pEnd, plane, *ppOutVerts[nOutVertCount] );
  1880. ++nOutVertCount;
  1881. }
  1882. }
  1883. pStart = pEnd;
  1884. bStartInside = bEndInside;
  1885. }
  1886. return nOutVertCount;
  1887. }
  1888. //-----------------------------------------------------------------------------
  1889. // Computes the plane equation of a polygon in screen space from a world-space plane
  1890. //-----------------------------------------------------------------------------
  1891. void COcclusionSystem::ComputeScreenSpacePlane( const ViewData_t& viewData, const cplane_t &cameraSpacePlane, cplane_t *pScreenSpacePlane )
  1892. {
  1893. // Here's how this is computed:
  1894. // If the *camera* space plane is Ax+By+Cz = D,
  1895. // and xs = -(xf) * x/z, ys = -(yf) y/z, zs = - (zc + zf * ooz)
  1896. // Then x = -xs * z / xf, y = -ys * z / yf, ooz = -(zs + zc) / zf
  1897. // So - A * xs * z / xf - B * ys * z / yf + C * z = D
  1898. // - A xs / xf - B ys / yf + C = D * ooz
  1899. // (A/D) xs/xf + (B/D) ys/yf + ooz = (C/D)
  1900. // (A/D) xs/xf + (B/D) ys/yf - (zs + zc) / zf = (C/D)
  1901. // -(A/D) xs/xf - (B/D) ys/yf + (zs + zc) / zf = -(C/D)
  1902. // -zf * (A/D) xs/xf - zf * (B/D) ys/yf + zs = -zf * (C/D) - zc
  1903. // Let A' = -zf/xf*(A/D), B' = -zf/yf*(B/D), D' = -zf * (C/D) - zc
  1904. // A' xs + B' ys + zs = D' is the screen space plane equation
  1905. float ooD = (cameraSpacePlane.dist != 0) ? (1.0f / cameraSpacePlane.dist) : 0.0f;
  1906. pScreenSpacePlane->normal.x = cameraSpacePlane.normal.x * ooD * viewData.m_flXProjScale;
  1907. pScreenSpacePlane->normal.y = cameraSpacePlane.normal.y * ooD * viewData.m_flYProjScale;
  1908. pScreenSpacePlane->normal.z = 1;
  1909. pScreenSpacePlane->dist = cameraSpacePlane.normal.z * ooD * viewData.m_flProjDistScale + viewData.m_flProjDistOffset;
  1910. }
  1911. //-----------------------------------------------------------------------------
  1912. // Stitches up clipped vertices
  1913. //-----------------------------------------------------------------------------
  1914. void COcclusionSystem::StitchClippedVertices( Vector *pVertices, int nCount )
  1915. {
  1916. for ( int i = 0; i < nCount; ++i )
  1917. {
  1918. // Only stitch ones that have been clipped by the near clip plane
  1919. if ( fabs( pVertices[i].z ) > 1e-3 )
  1920. continue;
  1921. int j;
  1922. for ( j = m_ClippedVerts.Count(); --j >= 0; )
  1923. {
  1924. if ( VectorsAreEqual( pVertices[i], m_ClippedVerts[j], 1e-3 ) )
  1925. {
  1926. pVertices[i] = m_ClippedVerts[j];
  1927. break;
  1928. }
  1929. }
  1930. if ( j < 0 )
  1931. {
  1932. MEM_ALLOC_CREDIT();
  1933. // No match found...
  1934. m_ClippedVerts.AddToTail( pVertices[i] );
  1935. }
  1936. }
  1937. }
  1938. //-----------------------------------------------------------------------------
  1939. // Project world-space verts + add into the edge list
  1940. //-----------------------------------------------------------------------------
  1941. void COcclusionSystem::AddPolygonToEdgeList( const ViewData_t& viewData, CEdgeList &edgeList, Vector **ppPolygon, int nCount, int nSurfID, bool bClipped )
  1942. {
  1943. // Transform the verts into projection space
  1944. // Transform into projection space (extra logic here is to simply guarantee that we project each vert exactly once)
  1945. int nMaxClipVerts = (nCount * 4);
  1946. int nClipCount, nClipCount1;
  1947. Vector **ppClipVertex = (Vector**)stackalloc( nMaxClipVerts * sizeof(Vector*) );
  1948. Vector **ppClipVertex1 = (Vector**)stackalloc( nMaxClipVerts * sizeof(Vector*) );
  1949. Vector *pVecProjectedVertex = (Vector*)stackalloc( nCount * sizeof(Vector) );
  1950. int k;
  1951. for ( k = 0; k < nCount; ++k )
  1952. {
  1953. Vector3DMultiplyPositionProjective( viewData.m_WorldToProjection, *(ppPolygon[k]), pVecProjectedVertex[k] );
  1954. // Clamp needed to avoid precision problems.
  1955. // if ( pVecProjectedVertex[k].z < 0.0f )
  1956. // pVecProjectedVertex[k].z = 0.0f;
  1957. pVecProjectedVertex[k].z *= (pVecProjectedVertex[k].z > 0.0f);
  1958. ppClipVertex[k] = &pVecProjectedVertex[k];
  1959. }
  1960. // Clip vertices to the screen in x,y...
  1961. AxisAlignedPlane_t aaPlane;
  1962. aaPlane.m_nAxis = 0;
  1963. aaPlane.m_flDist = -1;
  1964. aaPlane.m_flSign = -1;
  1965. nClipCount = nCount;
  1966. ResetClipTempVerts();
  1967. nClipCount1 = ClipPolygonToAxisAlignedPlane( ppClipVertex, nClipCount, aaPlane, ppClipVertex1 );
  1968. if ( nClipCount1 < 3 )
  1969. return;
  1970. Assert( nClipCount1 < nMaxClipVerts );
  1971. aaPlane.m_flSign = 1;
  1972. nClipCount = ClipPolygonToAxisAlignedPlane( ppClipVertex1, nClipCount1, aaPlane, ppClipVertex );
  1973. if ( nClipCount < 3 )
  1974. return;
  1975. Assert( nClipCount < nMaxClipVerts );
  1976. aaPlane.m_nAxis = 1;
  1977. nClipCount1 = ClipPolygonToAxisAlignedPlane( ppClipVertex, nClipCount, aaPlane, ppClipVertex1 );
  1978. if ( nClipCount1 < 3 )
  1979. return;
  1980. Assert( nClipCount1 < nMaxClipVerts );
  1981. aaPlane.m_flSign = -1;
  1982. nClipCount = ClipPolygonToAxisAlignedPlane( ppClipVertex1, nClipCount1, aaPlane, ppClipVertex );
  1983. if ( nClipCount < 3 )
  1984. return;
  1985. Assert( nClipCount < nMaxClipVerts );
  1986. // Compute the screen area...
  1987. float flScreenArea = 0.0f;
  1988. int nLastClipVert = nClipCount - 1;
  1989. for ( k = 1; k < nLastClipVert; ++k )
  1990. {
  1991. // Using area times two simply because it's faster...
  1992. float flTriArea = TriArea2DTimesTwo( (*ppClipVertex[0]), (*ppClipVertex[k]), (*ppClipVertex[k+1]) );
  1993. Assert( flTriArea <= 1e-3 );
  1994. if ( flTriArea < 0 )
  1995. {
  1996. flScreenArea += -flTriArea;
  1997. }
  1998. }
  1999. edgeList.SetSurfaceArea( nSurfID, flScreenArea );
  2000. // If there's a clipped vertex, attempt to seam up with other edges...
  2001. if ( bClipped )
  2002. {
  2003. StitchClippedVertices( pVecProjectedVertex, nCount );
  2004. }
  2005. // Add in the edges of the *unclipped* polygon: to avoid precision errors
  2006. Vector *ppEdgeVertices[2];
  2007. int nLastVert = nCount - 1;
  2008. ppEdgeVertices[ 1 ] = &pVecProjectedVertex[ nLastVert ];
  2009. for ( k = 0; k < nLastVert; ++k )
  2010. {
  2011. ppEdgeVertices[ k & 0x1 ] = &pVecProjectedVertex[ k ];
  2012. edgeList.AddEdge( ppEdgeVertices, nSurfID );
  2013. }
  2014. ppEdgeVertices[ nLastVert & 0x1 ] = &pVecProjectedVertex[ nLastVert ];
  2015. edgeList.AddEdge( ppEdgeVertices, nSurfID );
  2016. }
  2017. //-----------------------------------------------------------------------------
  2018. // Recomputes the occluder edge list
  2019. //-----------------------------------------------------------------------------
  2020. void COcclusionSystem::RecomputeOccluderEdgeList( ViewData_t& viewData )
  2021. {
  2022. #ifndef DEDICATED
  2023. if ( !viewData.m_bEdgeListDirty )
  2024. return;
  2025. // Tracker 17772: If building cubemaps can end up calling into here w/o GetBaseLocalClient().pAreaBits setup yet, oh well.
  2026. if ( !GetBaseLocalClient().m_bAreaBitsValid && ( CommandLine()->FindParm( "-buildcubemaps" ) || CommandLine()->FindParm( "-buildmodelforworld" ) ) )
  2027. return;
  2028. viewData.m_bEdgeListDirty = false;
  2029. m_EdgeList.RemoveAll();
  2030. m_WingedEdgeList.Clear();
  2031. m_ClippedVerts.RemoveAll();
  2032. mvertex_t *pVertices = host_state.worldbrush->vertexes;
  2033. int *pIndices = host_state.worldbrush->occludervertindices;
  2034. doccluderdata_t *pOccluders = host_state.worldbrush->occluders;
  2035. int i, j, k;
  2036. for ( i = host_state.worldbrush->numoccluders ; --i >= 0; )
  2037. {
  2038. if ( pOccluders[i].flags & OCCLUDER_FLAGS_INACTIVE )
  2039. continue;
  2040. // Skip the occluder if it's in a disconnected area
  2041. if ( GetBaseLocalClient().m_chAreaBits &&
  2042. (GetBaseLocalClient().m_chAreaBits[pOccluders[i].area >> 3] & (1 << ( pOccluders[i].area & 0x7 )) ) == 0 )
  2043. continue;
  2044. int nSurfID = pOccluders[i].firstpoly;
  2045. int nSurfCount = pOccluders[i].polycount;
  2046. for ( j = 0; j < nSurfCount; ++j, ++nSurfID )
  2047. {
  2048. doccluderpolydata_t *pSurf = &host_state.worldbrush->occluderpolys[nSurfID];
  2049. int nFirstVertexIndex = pSurf->firstvertexindex;
  2050. int nVertexCount = pSurf->vertexcount;
  2051. // If the surface is backfacing, blow it off...
  2052. const cplane_t &surfPlane = host_state.worldbrush->planes[ pSurf->planenum ];
  2053. if ( DotProduct( surfPlane.normal, viewData.m_vecCameraPosition ) <= surfPlane.dist )
  2054. continue;
  2055. // Clip to the near plane (has to be done in world space)
  2056. Vector **ppSurfVerts = (Vector**)stackalloc( ( nVertexCount ) * sizeof(Vector*) );
  2057. Vector **ppClipVerts = (Vector**)stackalloc( ( nVertexCount * 2 ) * sizeof(Vector*) );
  2058. for ( k = 0; k < nVertexCount; ++k )
  2059. {
  2060. int nVertIndex = pIndices[nFirstVertexIndex + k];
  2061. ppSurfVerts[k] = &( pVertices[nVertIndex].position );
  2062. }
  2063. bool bClipped;
  2064. int nClipCount = ClipPolygonToNearPlane( viewData, ppSurfVerts, nVertexCount, ppClipVerts, &bClipped );
  2065. Assert( nClipCount <= ( nVertexCount * 2 ) );
  2066. if ( nClipCount < 3 )
  2067. continue;
  2068. cplane_t projectionSpacePlane;
  2069. cplane_t cameraSpacePlane;
  2070. MatrixTransformPlane( viewData.m_WorldToCamera, surfPlane, cameraSpacePlane );
  2071. ComputeScreenSpacePlane( viewData, cameraSpacePlane, &projectionSpacePlane );
  2072. int nEdgeSurfID = m_EdgeList.AddSurface( projectionSpacePlane );
  2073. // Transform into projection space (extra logic here is to simply guarantee that we project each vert exactly once)
  2074. AddPolygonToEdgeList( viewData, m_EdgeList, ppClipVerts, nClipCount, nEdgeSurfID, bClipped );
  2075. }
  2076. }
  2077. m_EdgeList.CullSmallOccluders();
  2078. m_EdgeList.ReduceActiveList( m_WingedEdgeList );
  2079. // Msg("Edge count %d -> %d\n", m_EdgeList.EdgeCount(), m_WingedEdgeList.EdgeCount() );
  2080. // Draw the occluders
  2081. unsigned char color[4] = { 255, 255, 255, 255 };
  2082. m_WingedEdgeList.QueueVisualization( color );
  2083. #endif
  2084. }
  2085. //-----------------------------------------------------------------------------
  2086. // Occluder list management
  2087. //-----------------------------------------------------------------------------
  2088. void COcclusionSystem::ActivateOccluder( int nOccluderIndex, bool bActive )
  2089. {
  2090. if ( ( nOccluderIndex >= host_state.worldbrush->numoccluders ) || ( nOccluderIndex < 0 ) )
  2091. return;
  2092. if ( bActive )
  2093. {
  2094. host_state.worldbrush->occluders[nOccluderIndex].flags &= ~OCCLUDER_FLAGS_INACTIVE;
  2095. }
  2096. else
  2097. {
  2098. host_state.worldbrush->occluders[nOccluderIndex].flags |= OCCLUDER_FLAGS_INACTIVE;
  2099. }
  2100. for ( int i = 0; i < OCCLUSION_SYSTEM_VIEWDATA_MAX; ++i )
  2101. {
  2102. m_viewData[i].m_bEdgeListDirty = true;
  2103. }
  2104. }
  2105. void COcclusionSystem::SetView( const Vector &vecCameraPos, float flFOV, const VMatrix &worldToCamera,
  2106. const VMatrix &cameraToProjection, const VPlane &nearClipPlane )
  2107. {
  2108. m_nCurrentViewId = ( m_nCurrentViewId + 1 ) % OCCLUSION_SYSTEM_VIEWDATA_MAX;
  2109. ViewData_t* pActiveView = &m_viewData[m_nCurrentViewId];
  2110. pActiveView->m_vecCameraPosition = vecCameraPos;
  2111. pActiveView->m_WorldToCamera = worldToCamera;
  2112. // See ComputeScreenSpacePlane() for the use of these constants
  2113. pActiveView->m_flXProjScale = -cameraToProjection[2][3] / cameraToProjection[0][0];
  2114. pActiveView->m_flYProjScale = -cameraToProjection[2][3] / cameraToProjection[1][1];
  2115. pActiveView->m_flProjDistScale = -cameraToProjection[2][3];
  2116. pActiveView->m_flProjDistOffset = -cameraToProjection[2][2];
  2117. MatrixMultiply( cameraToProjection, worldToCamera, pActiveView->m_WorldToProjection );
  2118. pActiveView->m_NearClipPlane.normal = nearClipPlane.m_Normal;
  2119. pActiveView->m_NearClipPlane.dist = nearClipPlane.m_Dist;
  2120. pActiveView->m_NearClipPlane.type = 3;
  2121. pActiveView->m_bEdgeListDirty = true;
  2122. pActiveView->m_flNearPlaneDist = -( DotProduct( vecCameraPos, pActiveView->m_NearClipPlane.normal ) - pActiveView->m_NearClipPlane.dist );
  2123. // Due to FP precision issues this value can sometimes drop slightly below 0.0f (during CSM shadow rendering).
  2124. Assert( pActiveView->m_flNearPlaneDist > -0.125f );
  2125. pActiveView->m_flNearPlaneDist = MAX( pActiveView->m_flNearPlaneDist, 0.0f );
  2126. pActiveView->m_flFOVFactor = pActiveView->m_flNearPlaneDist * tan( flFOV * 0.5f * M_PI / 180.0f );
  2127. pActiveView->m_flFOVFactor = pActiveView->m_flNearPlaneDist / pActiveView->m_flFOVFactor;
  2128. pActiveView->m_flFOVFactor *= pActiveView->m_flFOVFactor;
  2129. if ( r_occlusionspew.GetInt() )
  2130. {
  2131. if ( m_nTests )
  2132. {
  2133. float flPercent = 100.0f * ((float)m_nOccluded / (float)m_nTests);
  2134. Msg("Occl %.2f (%d/%d)\n", flPercent, m_nOccluded, m_nTests );
  2135. m_nTests = 0;
  2136. m_nOccluded = 0;
  2137. }
  2138. }
  2139. }
  2140. int COcclusionSystem::GetViewId() const
  2141. {
  2142. return m_nCurrentViewId;
  2143. }
  2144. //-----------------------------------------------------------------------------
  2145. // Used to build the quads to test for occlusion
  2146. //-----------------------------------------------------------------------------
  2147. static int s_pFaceIndices[6][4] =
  2148. {
  2149. { 0, 4, 6, 2 }, // -x
  2150. { 1, 3, 7, 5 }, // +x
  2151. { 0, 1, 5, 4 }, // -y
  2152. { 2, 6, 7, 3 }, // +y
  2153. { 0, 2, 3, 1 }, // -z
  2154. { 4, 5, 7, 6 }, // +z
  2155. };
  2156. static int s_pSourceIndices[8] =
  2157. {
  2158. -1, 0, 0, 1, 0, 1, 2, 3
  2159. };
  2160. static int s_pDeltaIndices[8] =
  2161. {
  2162. -1, 0, 1, 1, 2, 2, 2, 2
  2163. };
  2164. static unsigned char s_VisualizationColor[2][4] =
  2165. {
  2166. { 255, 0, 0, 255 },
  2167. { 0, 255, 0, 255 }
  2168. };
  2169. struct EdgeInfo_t
  2170. {
  2171. unsigned char m_nVert[2];
  2172. unsigned char m_nFace[2];
  2173. int m_nTestCount;
  2174. int m_nMinVert;
  2175. };
  2176. // NOTE: The face indices here have to very carefully ordered for the algorithm
  2177. // to work. They must be ordered so that vert0 -> vert1 is clockwise
  2178. // for the first face listed and vert1 -> vert0 is clockwise for the 2nd face listed
  2179. static EdgeInfo_t s_pEdges[12] =
  2180. {
  2181. { 0, 1, 2, 4, 0, 0 }, // 0: Edge between -y + -z
  2182. { 2, 0, 0, 4, 0, 0 }, // 1: Edge between -x + -z
  2183. { 1, 3, 1, 4, 0, 0 }, // 2: Edge between +x + -z
  2184. { 3, 2, 3, 4, 0, 0 }, // 3: Edge between +y + -z
  2185. { 0, 4, 0, 2, 0, 0 }, // 4: Edge between -x + -y
  2186. { 5, 1, 1, 2, 0, 0 }, // 5: Edge between +x + -y
  2187. { 6, 2, 0, 3, 0, 0 }, // 6: Edge between -x + +y
  2188. { 3, 7, 1, 3, 0, 0 }, // 7: Edge between +x + +y
  2189. { 5, 4, 2, 5, 0, 0 }, // 8: Edge between -y + +z
  2190. { 4, 6, 0, 5, 0, 0 }, // 9: Edge between -x + +z
  2191. { 7, 5, 1, 5, 0, 0 }, // 10:Edge between +x + +z
  2192. { 6, 7, 3, 5, 0, 0 }, // 11:Edge between +y + +z
  2193. };
  2194. static int s_pFaceEdges[6][4] =
  2195. {
  2196. { 4, 9, 6, 1 },
  2197. { 2, 7, 10, 5 },
  2198. { 0, 5, 8, 4 },
  2199. { 6, 11, 7, 3 },
  2200. { 1, 3, 2, 0 },
  2201. { 8, 10, 11, 9 },
  2202. };
  2203. //-----------------------------------------------------------------------------
  2204. // Occlusion checks
  2205. //-----------------------------------------------------------------------------
  2206. static CWingedEdgeList s_WingedTestEdgeList;
  2207. class WingedEdgeLessFunc
  2208. {
  2209. public:
  2210. bool Less( const int& src1, const int& src2, void *pCtx )
  2211. {
  2212. Vector *pVertices = (Vector*)pCtx;
  2213. EdgeInfo_t *pEdge1 = &s_pEdges[ src1 ];
  2214. EdgeInfo_t *pEdge2 = &s_pEdges[ src2 ];
  2215. Vector *pV1 = &pVertices[ pEdge1->m_nVert[ pEdge1->m_nMinVert ] ];
  2216. Vector *pV2 = &pVertices[ pEdge2->m_nVert[ pEdge2->m_nMinVert ] ];
  2217. if (pV1->y < pV2->y)
  2218. return true;
  2219. if (pV1->y > pV2->y)
  2220. return false;
  2221. if (pV1->x < pV2->x)
  2222. return true;
  2223. if (pV1->x > pV2->x)
  2224. return false;
  2225. // This is the same as the following line:
  2226. // return (pEdge1->m_flDxDy <= pEdge2->m_flDxDy);
  2227. Vector2D dEdge1, dEdge2;
  2228. Vector2DSubtract( pVertices[ pEdge1->m_nVert[ 1 - pEdge1->m_nMinVert ] ].AsVector2D(), pV1->AsVector2D(), dEdge1 );
  2229. Vector2DSubtract( pVertices[ pEdge2->m_nVert[ 1 - pEdge2->m_nMinVert ] ].AsVector2D(), pV2->AsVector2D(), dEdge2 );
  2230. Assert( dEdge1.y >= 0.0f );
  2231. Assert( dEdge2.y >= 0.0f );
  2232. return dEdge1.x * dEdge2.y <= dEdge1.y * dEdge2.x;
  2233. }
  2234. };
  2235. bool COcclusionSystem::IsOccluded( int occlusionViewId, const Vector &vecAbsMins, const Vector &vecAbsMaxs )
  2236. {
  2237. if ( r_occlusion.GetInt() == 0 )
  2238. return false;
  2239. if ( occlusionViewId < 0 || occlusionViewId >= OCCLUSION_SYSTEM_VIEWDATA_MAX )
  2240. return false;
  2241. ViewData_t* pActiveView = &m_viewData[occlusionViewId];
  2242. VPROF_BUDGET( "COcclusionSystem::IsOccluded", VPROF_BUDGETGROUP_OCCLUSION );
  2243. // @MULTICORE (toml 9/11/2006): need to eliminate this mutex
  2244. static CThreadFastMutex mutex;
  2245. AUTO_LOCK( mutex );
  2246. RecomputeOccluderEdgeList( *pActiveView );
  2247. // No occluders? Then the edge list isn't occluded
  2248. if ( m_WingedEdgeList.EdgeCount() == 0 )
  2249. return false;
  2250. // Don't occlude things that have large screen area
  2251. // Use a super cheap but inaccurate screen area computation
  2252. Vector vecCenter;
  2253. VectorAdd( vecAbsMaxs, vecAbsMins, vecCenter );
  2254. vecCenter *= 0.5f;
  2255. vecCenter -= pActiveView->m_vecCameraPosition;
  2256. float flDist = DotProduct( pActiveView->m_NearClipPlane.normal, vecCenter );
  2257. if (flDist <= 0.0f)
  2258. return false;
  2259. flDist += pActiveView->m_flNearPlaneDist;
  2260. Vector vecSize;
  2261. VectorSubtract( vecAbsMaxs, vecAbsMins, vecSize );
  2262. float flRadiusSq = DotProduct( vecSize, vecSize ) * 0.25f;
  2263. float flScreenArea = pActiveView->m_flFOVFactor * flRadiusSq / (flDist * flDist);
  2264. float flMaxSize = r_occludeemaxarea.GetFloat() * 0.01f;
  2265. if ( flMaxSize == 0.0f )
  2266. {
  2267. flMaxSize = m_flMaxOccludeeArea;
  2268. }
  2269. if (flScreenArea >= flMaxSize)
  2270. return false;
  2271. // Clear out its state
  2272. s_WingedTestEdgeList.Clear();
  2273. // NOTE: This assumes that frustum culling has already occurred on this object
  2274. // If that were not the case, we'd need to add a little extra into this
  2275. // (probably a single plane test, which tests if the box is wholly behind the camera )
  2276. // Convert the bbox into a max of 3 quads...
  2277. const Vector *pCornerVert[2] = { &vecAbsMins, &vecAbsMaxs };
  2278. // Compute the 8 box verts, and transform them into projective space...
  2279. // NOTE: We'd want to project them *after* the plane test if there were
  2280. // no frustum culling.
  2281. int i;
  2282. Vector pVecProjectedVertex[8];
  2283. // NOTE: The code immediately below is an optimized version of this loop
  2284. // The optimization takes advantage of the fact that the verts are all
  2285. // axis aligned.
  2286. // Vector vecBoxVertex;
  2287. // for ( i = 0; i < 8; ++i )
  2288. // {
  2289. // vecBoxVertex.x = pCornerVert[ (i & 0x1) ]->x;
  2290. // vecBoxVertex.y = pCornerVert[ (i & 0x2) >> 1 ]->y;
  2291. // vecBoxVertex.z = pCornerVert[ (i & 0x4) >> 2 ]->z;
  2292. // Vector3DMultiplyPositionProjective( m_WorldToProjection, vecBoxVertex, pVecProjectedVertex[ i ] );
  2293. // if ( pVecProjectedVertex[ i ].z <= 0.0f )
  2294. // return false;
  2295. // }
  2296. Vector4D vecProjVert[8];
  2297. Vector4D vecDeltaProj[3];
  2298. Vector4D vecAbsMins4D( vecAbsMins.x, vecAbsMins.y, vecAbsMins.z, 1.0f );
  2299. Vector4DMultiply( pActiveView->m_WorldToProjection, vecAbsMins4D, vecProjVert[0] );
  2300. if ( vecProjVert[0].w <= 0.0f )
  2301. return false;
  2302. float flOOW = 1.0f / vecProjVert[0].w;
  2303. vecDeltaProj[0].Init( vecSize.x * pActiveView->m_WorldToProjection[0][0], vecSize.x * pActiveView->m_WorldToProjection[1][0], vecSize.x * pActiveView->m_WorldToProjection[2][0], vecSize.x * pActiveView->m_WorldToProjection[3][0] );
  2304. vecDeltaProj[1].Init( vecSize.y * pActiveView->m_WorldToProjection[0][1], vecSize.y * pActiveView->m_WorldToProjection[1][1], vecSize.y * pActiveView->m_WorldToProjection[2][1], vecSize.y * pActiveView->m_WorldToProjection[3][1] );
  2305. vecDeltaProj[2].Init( vecSize.z * pActiveView->m_WorldToProjection[0][2], vecSize.z * pActiveView->m_WorldToProjection[1][2], vecSize.z * pActiveView->m_WorldToProjection[2][2], vecSize.z * pActiveView->m_WorldToProjection[3][2] );
  2306. pVecProjectedVertex[0].Init( vecProjVert[0].x * flOOW, vecProjVert[0].y * flOOW, vecProjVert[0].z * flOOW );
  2307. if ( pVecProjectedVertex[0].z <= 0.0f )
  2308. return false;
  2309. for ( i = 1; i < 8; ++i )
  2310. {
  2311. int nIndex = s_pSourceIndices[i];
  2312. int nDelta = s_pDeltaIndices[i];
  2313. Vector4DAdd( vecProjVert[nIndex], vecDeltaProj[nDelta], vecProjVert[i] );
  2314. if ( vecProjVert[ i ].w <= 0.0f )
  2315. return false;
  2316. flOOW = 1.0f / vecProjVert[i].w;
  2317. pVecProjectedVertex[ i ].Init( vecProjVert[i].x * flOOW, vecProjVert[i].y * flOOW, vecProjVert[i].z * flOOW );
  2318. if ( pVecProjectedVertex[ i ].z <= 0.0f )
  2319. return false;
  2320. }
  2321. // Precompute stuff needed by the loop over faces below
  2322. float pSign[2] = { -1, 1 };
  2323. Vector vecDelta[2];
  2324. VectorSubtract( *pCornerVert[0], pActiveView->m_vecCameraPosition, vecDelta[0] );
  2325. VectorSubtract( pActiveView->m_vecCameraPosition, *pCornerVert[1], vecDelta[1] );
  2326. // Determine which faces + edges are visible...
  2327. ++m_nTests;
  2328. int pSurfInd[6];
  2329. for ( i = 0; i < 6; ++i )
  2330. {
  2331. int nDim = ( i >> 1 );
  2332. int nInd = i & 0x1;
  2333. // Try to backface cull each of the 6 box faces
  2334. if ( vecDelta[nInd][nDim] <= 0.0f )
  2335. {
  2336. pSurfInd[i] = -1;
  2337. continue;
  2338. }
  2339. cplane_t cameraSpacePlane, projectionSpacePlane;
  2340. float flSign = pSign[nInd];
  2341. float flPlaneDist = (*pCornerVert[nInd])[ nDim ] * flSign;
  2342. MatrixTransformAxisAlignedPlane( pActiveView->m_WorldToCamera, nDim, flSign, flPlaneDist, cameraSpacePlane );
  2343. ComputeScreenSpacePlane( *pActiveView, cameraSpacePlane, &projectionSpacePlane );
  2344. int nSurfID = s_WingedTestEdgeList.AddSurface( projectionSpacePlane );
  2345. pSurfInd[i] = nSurfID;
  2346. // Mark edges as being used...
  2347. int *pFaceEdges = s_pFaceEdges[i];
  2348. s_pEdges[ pFaceEdges[0] ].m_nTestCount = m_nTests;
  2349. s_pEdges[ pFaceEdges[1] ].m_nTestCount = m_nTests;
  2350. s_pEdges[ pFaceEdges[2] ].m_nTestCount = m_nTests;
  2351. s_pEdges[ pFaceEdges[3] ].m_nTestCount = m_nTests;
  2352. }
  2353. // Sort edges by minimum Y + dx/dy...
  2354. int pEdgeSort[12];
  2355. CUtlSortVector< int, WingedEdgeLessFunc > edgeSort( pEdgeSort, 12 );
  2356. edgeSort.SetLessContext( pVecProjectedVertex );
  2357. for ( i = 0; i < 12; ++i )
  2358. {
  2359. // Skip non-visible edges
  2360. EdgeInfo_t *pEdge = &s_pEdges[i];
  2361. if ( pEdge->m_nTestCount != m_nTests )
  2362. continue;
  2363. pEdge->m_nMinVert = ( pVecProjectedVertex[ pEdge->m_nVert[0] ].y >= pVecProjectedVertex[ pEdge->m_nVert[1] ].y );
  2364. edgeSort.Insert( i );
  2365. }
  2366. // Now add them into the winged edge list, in sorted order...
  2367. int nEdgeCount = edgeSort.Count();
  2368. for ( i = 0; i < nEdgeCount; ++i )
  2369. {
  2370. EdgeInfo_t *pEdge = &s_pEdges[edgeSort[i]];
  2371. // The enter + leave ids depend entirely on which edge is further up
  2372. // This works because the edges listed in s_pEdges show the edges as they
  2373. // would be visited in *clockwise* order
  2374. const Vector &startVert = pVecProjectedVertex[pEdge->m_nVert[pEdge->m_nMinVert]];
  2375. const Vector &endVert = pVecProjectedVertex[pEdge->m_nVert[1 - pEdge->m_nMinVert]];
  2376. int nLeaveSurfID = pSurfInd[ pEdge->m_nFace[pEdge->m_nMinVert] ];
  2377. int nEnterSurfID = pSurfInd[ pEdge->m_nFace[1 - pEdge->m_nMinVert] ];
  2378. s_WingedTestEdgeList.AddEdge( startVert, endVert, nLeaveSurfID, nEnterSurfID );
  2379. }
  2380. #ifdef DEBUG_OCCLUSION_SYSTEM
  2381. s_WingedTestEdgeList.CheckConsistency();
  2382. #endif
  2383. // Now let's see if this edge list is occluded or not..
  2384. bool bOccluded = m_WingedEdgeList.IsOccludingEdgeList( s_WingedTestEdgeList );
  2385. if (bOccluded)
  2386. {
  2387. ++m_nOccluded;
  2388. }
  2389. s_WingedTestEdgeList.QueueVisualization( s_VisualizationColor[bOccluded] );
  2390. return bOccluded;
  2391. }
  2392. //-----------------------------------------------------------------------------
  2393. // Used to debug the occlusion system
  2394. //-----------------------------------------------------------------------------
  2395. void VisualizeQueuedEdges( )
  2396. {
  2397. #ifndef DEDICATED
  2398. if ( !g_EdgeVisualization.Count() )
  2399. return;
  2400. CMatRenderContextPtr pRenderContext( materials );
  2401. pRenderContext->MatrixMode( MATERIAL_MODEL );
  2402. pRenderContext->PushMatrix();
  2403. pRenderContext->LoadIdentity();
  2404. pRenderContext->MatrixMode( MATERIAL_VIEW );
  2405. pRenderContext->PushMatrix();
  2406. pRenderContext->LoadIdentity();
  2407. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  2408. pRenderContext->PushMatrix();
  2409. pRenderContext->LoadIdentity();
  2410. pRenderContext->Bind( g_pMaterialWireframeVertexColorIgnoreZ );
  2411. IMesh *pMesh = pRenderContext->GetDynamicMesh( );
  2412. CMeshBuilder meshBuilder;
  2413. meshBuilder.Begin( pMesh, MATERIAL_LINES, g_EdgeVisualization.Count() );
  2414. int i;
  2415. for ( i = g_EdgeVisualization.Count(); --i >= 0; )
  2416. {
  2417. EdgeVisualizationInfo_t &info = g_EdgeVisualization[i];
  2418. meshBuilder.Position3fv( info.m_vecPoint[0].Base() );
  2419. meshBuilder.Color4ubv( info.m_pColor );
  2420. meshBuilder.AdvanceVertex();
  2421. meshBuilder.Position3fv( info.m_vecPoint[1].Base() );
  2422. #ifdef DEBUG_OCCLUSION_SYSTEM
  2423. meshBuilder.Color4ub( 0, 0, 255, 255 );
  2424. #else
  2425. meshBuilder.Color4ubv( info.m_pColor );
  2426. #endif
  2427. meshBuilder.AdvanceVertex();
  2428. }
  2429. meshBuilder.End();
  2430. pMesh->Draw();
  2431. pRenderContext->MatrixMode( MATERIAL_MODEL );
  2432. pRenderContext->PopMatrix();
  2433. pRenderContext->MatrixMode( MATERIAL_VIEW );
  2434. pRenderContext->PopMatrix();
  2435. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  2436. pRenderContext->PopMatrix();
  2437. g_EdgeVisualization.RemoveAll();
  2438. #endif
  2439. }
  2440. //-----------------------------------------------------------------------------
  2441. // Render debugging overlay
  2442. //-----------------------------------------------------------------------------
  2443. void COcclusionSystem::DrawDebugOverlays()
  2444. {
  2445. // Draw the occludees
  2446. VisualizeQueuedEdges();
  2447. }