Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2957 lines
93 KiB

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