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.

881 lines
27 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "c_pixel_visibility.h"
  10. #include "materialsystem/imesh.h"
  11. #include "materialsystem/imaterial.h"
  12. #include "clienteffectprecachesystem.h"
  13. #include "view.h"
  14. #include "viewrender.h"
  15. #include "utlmultilist.h"
  16. #include "vprof.h"
  17. #include "icommandline.h"
  18. #include "sourcevr/isourcevirtualreality.h"
  19. static void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue );
  20. ConVar r_pixelvisibility_partial( "r_pixelvisibility_partial", "1" );
  21. ConVar r_dopixelvisibility( "r_dopixelvisibility", "1", FCVAR_ALLOWED_IN_COMPETITIVE );
  22. ConVar r_drawpixelvisibility( "r_drawpixelvisibility", "0", 0, "Show the occlusion proxies", PixelvisDrawChanged );
  23. ConVar r_pixelvisibility_spew( "r_pixelvisibility_spew", "0" );
  24. #ifdef OSX
  25. // GLMgr will set this one to "1" if it senses the new post-10.6.4 driver (m_hasPerfPackage1)
  26. ConVar gl_can_query_fast( "gl_can_query_fast", "0" );
  27. static bool HasFastQueries( void )
  28. {
  29. return gl_can_query_fast.GetBool();
  30. }
  31. #else
  32. // non OSX path
  33. static bool HasFastQueries( void )
  34. {
  35. return true;
  36. }
  37. #endif
  38. extern ConVar building_cubemaps;
  39. #ifndef _X360
  40. const float MIN_PROXY_PIXELS = 5.0f;
  41. #else
  42. const float MIN_PROXY_PIXELS = 25.0f;
  43. #endif
  44. float PixelVisibility_DrawProxy( IMatRenderContext *pRenderContext, OcclusionQueryObjectHandle_t queryHandle, Vector origin, float scale, float proxyAspect, IMaterial *pMaterial, bool screenspace )
  45. {
  46. Vector point;
  47. // don't expand this with distance to fit pixels or the sprite will poke through
  48. // only expand the parts perpendicular to the view
  49. float forwardScale = scale;
  50. // draw a pyramid of points touching a sphere of radius "scale" at origin
  51. float pixelsPerUnit = pRenderContext->ComputePixelDiameterOfSphere( origin, 1.0f );
  52. pixelsPerUnit = MAX( pixelsPerUnit, 1e-4f );
  53. if ( screenspace )
  54. {
  55. // Force this to be the size of a sphere of diameter "scale" at some reference distance (1.0 unit)
  56. float pixelsPerUnit2 = pRenderContext->ComputePixelDiameterOfSphere( CurrentViewOrigin() + CurrentViewForward()*1.0f, scale*0.5f );
  57. // force drawing of "scale" pixels
  58. scale = pixelsPerUnit2 / pixelsPerUnit;
  59. }
  60. else
  61. {
  62. float pixels = scale * pixelsPerUnit;
  63. // make the radius larger to ensure a minimum screen space size of the proxy geometry
  64. if ( pixels < MIN_PROXY_PIXELS )
  65. {
  66. scale = MIN_PROXY_PIXELS / pixelsPerUnit;
  67. }
  68. }
  69. // collapses the pyramid to a plane - so this could be a quad instead
  70. Vector dir = origin - CurrentViewOrigin();
  71. VectorNormalize(dir);
  72. origin -= dir * forwardScale;
  73. forwardScale = 0.0f;
  74. //
  75. Vector verts[5];
  76. const float sqrt2 = 0.707106781f; // sqrt(2) - keeps all vectors the same length from origin
  77. scale *= sqrt2;
  78. float scale45x = scale;
  79. float scale45y = scale / proxyAspect;
  80. verts[0] = origin - CurrentViewForward() * forwardScale; // the apex of the pyramid
  81. verts[1] = origin + CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // these four form the base
  82. verts[2] = origin + CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // the pyramid is a sprite with a point that
  83. verts[3] = origin - CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // pokes back toward the camera through any nearby
  84. verts[4] = origin - CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // geometry
  85. // get screen coords of edges
  86. Vector screen[4];
  87. for ( int i = 0; i < 4; i++ )
  88. {
  89. extern int ScreenTransform( const Vector& point, Vector& screen );
  90. if ( ScreenTransform( verts[i+1], screen[i] ) )
  91. return -1;
  92. }
  93. // compute area and screen-clipped area
  94. float w = screen[1].x - screen[0].x;
  95. float h = screen[0].y - screen[3].y;
  96. float ws = MIN(1.0f, screen[1].x) - MAX(-1.0f, screen[0].x);
  97. float hs = MIN(1.0f, screen[0].y) - MAX(-1.0f, screen[3].y);
  98. float area = w*h; // area can be zero when we ALT-TAB
  99. float areaClipped = ws*hs;
  100. float ratio = 0.0f;
  101. if ( area != 0 )
  102. {
  103. // compute the ratio of the area not clipped by the frustum to total area
  104. ratio = areaClipped / area;
  105. ratio = clamp(ratio, 0.0f, 1.0f);
  106. }
  107. pRenderContext->BeginOcclusionQueryDrawing( queryHandle );
  108. CMeshBuilder meshBuilder;
  109. IMesh* pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, pMaterial );
  110. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 4 );
  111. // draw a pyramid
  112. for ( int i = 0; i < 4; i++ )
  113. {
  114. int a = i+1;
  115. int b = (a%4)+1;
  116. meshBuilder.Position3fv( verts[0].Base() );
  117. meshBuilder.AdvanceVertex();
  118. meshBuilder.Position3fv( verts[a].Base() );
  119. meshBuilder.AdvanceVertex();
  120. meshBuilder.Position3fv( verts[b].Base() );
  121. meshBuilder.AdvanceVertex();
  122. }
  123. meshBuilder.End();
  124. pMesh->Draw();
  125. // sprite/quad proxy
  126. #if 0
  127. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  128. VectorMA (origin, -scale, CurrentViewUp(), point);
  129. VectorMA (point, -scale, CurrentViewRight(), point);
  130. meshBuilder.Position3fv (point.Base());
  131. meshBuilder.AdvanceVertex();
  132. VectorMA (origin, scale, CurrentViewUp(), point);
  133. VectorMA (point, -scale, CurrentViewRight(), point);
  134. meshBuilder.Position3fv (point.Base());
  135. meshBuilder.AdvanceVertex();
  136. VectorMA (origin, scale, CurrentViewUp(), point);
  137. VectorMA (point, scale, CurrentViewRight(), point);
  138. meshBuilder.Position3fv (point.Base());
  139. meshBuilder.AdvanceVertex();
  140. VectorMA (origin, -scale, CurrentViewUp(), point);
  141. VectorMA (point, scale, CurrentViewRight(), point);
  142. meshBuilder.Position3fv (point.Base());
  143. meshBuilder.AdvanceVertex();
  144. meshBuilder.End();
  145. pMesh->Draw();
  146. #endif
  147. pRenderContext->EndOcclusionQueryDrawing( queryHandle );
  148. // fraction clipped by frustum
  149. return ratio;
  150. }
  151. class CPixelVisSet
  152. {
  153. public:
  154. void Init( const pixelvis_queryparams_t &params );
  155. void MarkActive();
  156. bool IsActive();
  157. CPixelVisSet()
  158. {
  159. frameIssued = 0;
  160. serial = 0;
  161. queryList = 0xFFFF;
  162. sizeIsScreenSpace = false;
  163. }
  164. public:
  165. float proxySize;
  166. float proxyAspect;
  167. float fadeTimeInv;
  168. unsigned short queryList;
  169. unsigned short serial;
  170. bool sizeIsScreenSpace;
  171. private:
  172. int frameIssued;
  173. };
  174. void CPixelVisSet::Init( const pixelvis_queryparams_t &params )
  175. {
  176. Assert( params.bSetup );
  177. proxySize = params.proxySize;
  178. proxyAspect = params.proxyAspect;
  179. if ( params.fadeTime > 0.0f )
  180. {
  181. fadeTimeInv = 1.0f / params.fadeTime;
  182. }
  183. else
  184. {
  185. // fade in over 0.125 seconds
  186. fadeTimeInv = 1.0f / 0.125f;
  187. }
  188. frameIssued = 0;
  189. sizeIsScreenSpace = params.bSizeInScreenspace;
  190. }
  191. void CPixelVisSet::MarkActive()
  192. {
  193. frameIssued = gpGlobals->framecount;
  194. }
  195. bool CPixelVisSet::IsActive()
  196. {
  197. return (gpGlobals->framecount - frameIssued) > 1 ? false : true;
  198. }
  199. class CPixelVisibilityQuery
  200. {
  201. public:
  202. CPixelVisibilityQuery();
  203. ~CPixelVisibilityQuery();
  204. bool IsValid();
  205. bool IsForView( int viewID );
  206. bool IsActive();
  207. float GetFractionVisible( float fadeTimeInv );
  208. void IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace );
  209. void IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace );
  210. void ResetOcclusionQueries();
  211. void SetView( int viewID )
  212. {
  213. m_viewID = viewID;
  214. m_brightnessTarget = 0.0f;
  215. m_clipFraction = 1.0f;
  216. m_frameIssued = -1;
  217. m_failed = false;
  218. m_wasQueriedThisFrame = false;
  219. m_hasValidQueryResults = false;
  220. }
  221. public:
  222. Vector m_origin;
  223. int m_frameIssued;
  224. private:
  225. float m_brightnessTarget;
  226. float m_clipFraction;
  227. OcclusionQueryObjectHandle_t m_queryHandle;
  228. OcclusionQueryObjectHandle_t m_queryHandleCount;
  229. unsigned short m_wasQueriedThisFrame : 1;
  230. unsigned short m_failed : 1;
  231. unsigned short m_hasValidQueryResults : 1;
  232. unsigned short m_pad : 13;
  233. unsigned short m_viewID;
  234. friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth
  235. };
  236. CPixelVisibilityQuery::CPixelVisibilityQuery()
  237. {
  238. CMatRenderContextPtr pRenderContext( materials );
  239. SetView( 0xFFFF );
  240. m_queryHandle = pRenderContext->CreateOcclusionQueryObject();
  241. m_queryHandleCount = pRenderContext->CreateOcclusionQueryObject();
  242. }
  243. CPixelVisibilityQuery::~CPixelVisibilityQuery()
  244. {
  245. CMatRenderContextPtr pRenderContext( materials );
  246. if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  247. {
  248. pRenderContext->DestroyOcclusionQueryObject( m_queryHandle );
  249. }
  250. if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  251. {
  252. pRenderContext->DestroyOcclusionQueryObject( m_queryHandleCount );
  253. }
  254. }
  255. void CPixelVisibilityQuery::ResetOcclusionQueries()
  256. {
  257. // NOTE: Since we're keeping the CPixelVisibilityQuery objects around in a pool
  258. // and not actually deleting them, this means that our material system occlusion queries are
  259. // not being deleted either. Which means that if a CPixelVisibilityQuery is
  260. // put into the free list and then immediately re-used, then we have an opportunity for
  261. // a bug: What can happen on the first frame of the material system query
  262. // is that if the query isn't done yet, it will use the last queried value
  263. // which will happen to be set to the value of the last query done
  264. // for the previous CPixelVisSet the CPixelVisibilityQuery happened to be associated with
  265. // which makes queries have an invalid value for the first frame
  266. // This will mark the occlusion query objects as not ever having been read from before
  267. CMatRenderContextPtr pRenderContext( materials );
  268. if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  269. {
  270. pRenderContext->ResetOcclusionQueryObject( m_queryHandle );
  271. }
  272. if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  273. {
  274. pRenderContext->ResetOcclusionQueryObject( m_queryHandleCount );
  275. }
  276. }
  277. bool CPixelVisibilityQuery::IsValid()
  278. {
  279. return (m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE) ? true : false;
  280. }
  281. bool CPixelVisibilityQuery::IsForView( int viewID )
  282. {
  283. return m_viewID == viewID ? true : false;
  284. }
  285. bool CPixelVisibilityQuery::IsActive()
  286. {
  287. return (gpGlobals->framecount - m_frameIssued) > 1 ? false : true;
  288. }
  289. float CPixelVisibilityQuery::GetFractionVisible( float fadeTimeInv )
  290. {
  291. if ( !IsValid() )
  292. return 0.0f;
  293. if ( !m_wasQueriedThisFrame )
  294. {
  295. CMatRenderContextPtr pRenderContext( materials );
  296. m_wasQueriedThisFrame = true;
  297. int pixels = -1;
  298. int pixelsPossible = -1;
  299. if ( r_pixelvisibility_partial.GetBool() )
  300. {
  301. if ( m_frameIssued != -1 )
  302. {
  303. pixelsPossible = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandleCount );
  304. pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle );
  305. }
  306. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  307. {
  308. DevMsg( 1, "Pixels visible: %d (qh:%d) Pixels possible: %d (qh:%d) (frame:%d)\n", pixels, (int)m_queryHandle, pixelsPossible, (int)m_queryHandleCount, gpGlobals->framecount );
  309. }
  310. if ( pixels < 0 || pixelsPossible < 0 )
  311. {
  312. m_failed = ( m_frameIssued >= 0 ) ? true : false;
  313. return m_brightnessTarget * m_clipFraction;
  314. }
  315. m_hasValidQueryResults = true;
  316. if ( pixelsPossible > 0 )
  317. {
  318. float target = (float)pixels / (float)pixelsPossible;
  319. target = (target >= 0.95f) ? 1.0f : (target < 0.0f) ? 0.0f : target;
  320. float rate = gpGlobals->frametime * fadeTimeInv;
  321. m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out
  322. }
  323. else
  324. {
  325. m_brightnessTarget = 0.0f;
  326. }
  327. }
  328. else
  329. {
  330. if ( m_frameIssued != -1 )
  331. {
  332. pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle );
  333. }
  334. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  335. {
  336. DevMsg( 1, "Pixels visible: %d (qh:%d) (frame:%d)\n", pixels, (int)m_queryHandle, gpGlobals->framecount );
  337. }
  338. if ( pixels < 0 )
  339. {
  340. m_failed = ( m_frameIssued >= 0 ) ? true : false;
  341. return m_brightnessTarget * m_clipFraction;
  342. }
  343. m_hasValidQueryResults = true;
  344. if ( m_frameIssued == gpGlobals->framecount-1 )
  345. {
  346. float rate = gpGlobals->frametime * fadeTimeInv;
  347. float target = 0.0f;
  348. if ( pixels > 0 )
  349. {
  350. // fade in slower than you fade out
  351. rate *= 0.5f;
  352. target = 1.0f;
  353. }
  354. m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out
  355. }
  356. else
  357. {
  358. m_brightnessTarget = 0.0f;
  359. }
  360. }
  361. }
  362. return m_brightnessTarget * m_clipFraction;
  363. }
  364. void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace )
  365. {
  366. if ( !m_failed )
  367. {
  368. Assert( IsValid() );
  369. if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 )
  370. {
  371. DevMsg( 1, "Draw Proxy: qh:%d org:<%d,%d,%d> (frame:%d)\n", (int)m_queryHandle, (int)m_origin[0], (int)m_origin[1], (int)m_origin[2], gpGlobals->framecount );
  372. }
  373. m_clipFraction = PixelVisibility_DrawProxy( pRenderContext, m_queryHandle, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace );
  374. if ( m_clipFraction < 0 )
  375. {
  376. // NOTE: In this case, the proxy wasn't issued cause it was offscreen
  377. // can't set the m_frameissued field since that would cause it to get marked as failed
  378. m_clipFraction = 0;
  379. m_wasQueriedThisFrame = false;
  380. m_failed = false;
  381. return;
  382. }
  383. }
  384. #ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders!
  385. Assert ( ( m_frameIssued != gpGlobals->framecount ) || UseVR() );
  386. #endif
  387. m_frameIssued = gpGlobals->framecount;
  388. m_wasQueriedThisFrame = false;
  389. m_failed = false;
  390. }
  391. void CPixelVisibilityQuery::IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace )
  392. {
  393. if ( !m_failed )
  394. {
  395. Assert( IsValid() );
  396. #if 0
  397. // this centers it on the screen.
  398. // This is nice because it makes the glows fade as they get partially clipped by the view frustum
  399. // But it introduces sub-pixel errors (off by one row/column of pixels) so the glows shimmer
  400. // UNDONE: Compute an offset center coord that matches sub-pixel coords with the real glow position
  401. // UNDONE: Or frustum clip the sphere/geometry and fade based on proxy size
  402. Vector origin = m_origin - CurrentViewOrigin();
  403. float dot = DotProduct(CurrentViewForward(), origin);
  404. origin = CurrentViewOrigin() + dot * CurrentViewForward();
  405. #endif
  406. PixelVisibility_DrawProxy( pRenderContext, m_queryHandleCount, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace );
  407. }
  408. }
  409. //Precache the effects
  410. CLIENTEFFECT_REGISTER_BEGIN( PrecacheOcclusionProxy )
  411. CLIENTEFFECT_MATERIAL( "engine/occlusionproxy" )
  412. CLIENTEFFECT_MATERIAL( "engine/occlusionproxy_countdraw" )
  413. CLIENTEFFECT_REGISTER_END()
  414. class CPixelVisibilitySystem : public CAutoGameSystem
  415. {
  416. public:
  417. // GameSystem: Level init, shutdown
  418. virtual void LevelInitPreEntity();
  419. virtual void LevelShutdownPostEntity();
  420. // locals
  421. CPixelVisibilitySystem();
  422. float GetFractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle );
  423. void EndView();
  424. void EndScene();
  425. unsigned short FindQueryForView( CPixelVisSet *pSet, int viewID );
  426. unsigned short FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID );
  427. void DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll );
  428. void DeleteUnusedSets( bool bDeleteAll );
  429. void ShowQueries( bool show );
  430. unsigned short AllocQuery();
  431. unsigned short AllocSet();
  432. void FreeSet( unsigned short node );
  433. CPixelVisSet *FindOrCreatePixelVisSet( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle );
  434. bool SupportsOcclusion() { return m_hwCanTestGlows; }
  435. void DebugInfo()
  436. {
  437. Msg("Pixel vis system using %d sets total (%d in free list), %d queries total (%d in free list)\n",
  438. m_setList.TotalCount(), m_setList.Count(m_freeSetsList), m_queryList.TotalCount(), m_queryList.Count( m_freeQueriesList ) );
  439. }
  440. private:
  441. CUtlMultiList< CPixelVisSet, unsigned short > m_setList;
  442. CUtlMultiList<CPixelVisibilityQuery, unsigned short> m_queryList;
  443. unsigned short m_freeQueriesList;
  444. unsigned short m_activeSetsList;
  445. unsigned short m_freeSetsList;
  446. unsigned short m_pad0;
  447. IMaterial *m_pProxyMaterial;
  448. IMaterial *m_pDrawMaterial;
  449. bool m_hwCanTestGlows;
  450. bool m_drawQueries;
  451. friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth
  452. };
  453. static CPixelVisibilitySystem g_PixelVisibilitySystem;
  454. CPixelVisibilitySystem::CPixelVisibilitySystem() : CAutoGameSystem( "CPixelVisibilitySystem" )
  455. {
  456. m_hwCanTestGlows = true;
  457. m_drawQueries = false;
  458. }
  459. // Level init, shutdown
  460. void CPixelVisibilitySystem::LevelInitPreEntity()
  461. {
  462. bool fastqueries = HasFastQueries();
  463. // printf("\n ** fast queries: %s **", fastqueries?"true":"false" );
  464. m_hwCanTestGlows = r_dopixelvisibility.GetBool() && fastqueries && engine->GetDXSupportLevel() >= 80;
  465. if ( m_hwCanTestGlows )
  466. {
  467. CMatRenderContextPtr pRenderContext( materials );
  468. OcclusionQueryObjectHandle_t query = pRenderContext->CreateOcclusionQueryObject();
  469. if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE )
  470. {
  471. pRenderContext->DestroyOcclusionQueryObject( query );
  472. }
  473. else
  474. {
  475. m_hwCanTestGlows = false;
  476. }
  477. }
  478. m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS);
  479. m_pProxyMaterial->IncrementReferenceCount();
  480. m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS);
  481. m_pDrawMaterial->IncrementReferenceCount();
  482. m_freeQueriesList = m_queryList.CreateList();
  483. m_activeSetsList = m_setList.CreateList();
  484. m_freeSetsList = m_setList.CreateList();
  485. }
  486. void CPixelVisibilitySystem::LevelShutdownPostEntity()
  487. {
  488. m_pProxyMaterial->DecrementReferenceCount();
  489. m_pProxyMaterial = NULL;
  490. m_pDrawMaterial->DecrementReferenceCount();
  491. m_pDrawMaterial = NULL;
  492. DeleteUnusedSets(true);
  493. m_setList.Purge();
  494. m_queryList.Purge();
  495. m_freeQueriesList = m_queryList.InvalidIndex();
  496. m_activeSetsList = m_setList.InvalidIndex();
  497. m_freeSetsList = m_setList.InvalidIndex();
  498. }
  499. float CPixelVisibilitySystem::GetFractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  500. {
  501. if ( !m_hwCanTestGlows || building_cubemaps.GetBool() )
  502. {
  503. return GlowSightDistance( params.position, true ) > 0 ? 1.0f : 0.0f;
  504. }
  505. if ( CurrentViewID() < 0 )
  506. return 0.0f;
  507. CPixelVisSet *pSet = FindOrCreatePixelVisSet( params, queryHandle );
  508. Assert( pSet );
  509. unsigned short node = FindOrCreateQueryForView( pSet, CurrentViewID() );
  510. m_queryList[node].m_origin = params.position;
  511. float fraction = m_queryList[node].GetFractionVisible( pSet->fadeTimeInv );
  512. pSet->MarkActive();
  513. return fraction;
  514. }
  515. void CPixelVisibilitySystem::EndView()
  516. {
  517. if ( !PixelVisibility_IsAvailable() && CurrentViewID() >= 0 )
  518. return;
  519. if ( m_setList.Head( m_activeSetsList ) == m_setList.InvalidIndex() )
  520. return;
  521. CMatRenderContextPtr pRenderContext( materials );
  522. IMaterial *pProxy = m_drawQueries ? m_pDrawMaterial : m_pProxyMaterial;
  523. pRenderContext->Bind( pProxy );
  524. // BUGBUG: If you draw both queries, the measure query fails for some reason.
  525. if ( r_pixelvisibility_partial.GetBool() && !m_drawQueries )
  526. {
  527. pRenderContext->DepthRange( 0.0f, 0.01f );
  528. unsigned short node = m_setList.Head( m_activeSetsList );
  529. while( node != m_setList.InvalidIndex() )
  530. {
  531. CPixelVisSet *pSet = &m_setList[node];
  532. unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() );
  533. if ( queryNode != m_queryList.InvalidIndex() )
  534. {
  535. m_queryList[queryNode].IssueCountingQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
  536. }
  537. node = m_setList.Next( node );
  538. }
  539. pRenderContext->DepthRange( 0.0f, 1.0f );
  540. }
  541. {
  542. unsigned short node = m_setList.Head( m_activeSetsList );
  543. while( node != m_setList.InvalidIndex() )
  544. {
  545. CPixelVisSet *pSet = &m_setList[node];
  546. unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() );
  547. if ( queryNode != m_queryList.InvalidIndex() )
  548. {
  549. m_queryList[queryNode].IssueQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace );
  550. }
  551. node = m_setList.Next( node );
  552. }
  553. }
  554. }
  555. void CPixelVisibilitySystem::EndScene()
  556. {
  557. DeleteUnusedSets(false);
  558. }
  559. unsigned short CPixelVisibilitySystem::FindQueryForView( CPixelVisSet *pSet, int viewID )
  560. {
  561. unsigned short node = m_queryList.Head( pSet->queryList );
  562. while ( node != m_queryList.InvalidIndex() )
  563. {
  564. if ( m_queryList[node].IsForView( viewID ) )
  565. return node;
  566. node = m_queryList.Next( node );
  567. }
  568. return m_queryList.InvalidIndex();
  569. }
  570. unsigned short CPixelVisibilitySystem::FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID )
  571. {
  572. unsigned short node = FindQueryForView( pSet, viewID );
  573. if ( node != m_queryList.InvalidIndex() )
  574. return node;
  575. node = AllocQuery();
  576. m_queryList.LinkToHead( pSet->queryList, node );
  577. m_queryList[node].SetView( viewID );
  578. return node;
  579. }
  580. void CPixelVisibilitySystem::DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll )
  581. {
  582. unsigned short node = m_queryList.Head( pSet->queryList );
  583. while ( node != m_queryList.InvalidIndex() )
  584. {
  585. unsigned short next = m_queryList.Next( node );
  586. if ( bDeleteAll || !m_queryList[node].IsActive() )
  587. {
  588. m_queryList.Unlink( pSet->queryList, node);
  589. m_queryList.LinkToHead( m_freeQueriesList, node );
  590. }
  591. node = next;
  592. }
  593. }
  594. void CPixelVisibilitySystem::DeleteUnusedSets( bool bDeleteAll )
  595. {
  596. unsigned short node = m_setList.Head( m_activeSetsList );
  597. while ( node != m_setList.InvalidIndex() )
  598. {
  599. unsigned short next = m_setList.Next( node );
  600. CPixelVisSet *pSet = &m_setList[node];
  601. if ( bDeleteAll || !m_setList[node].IsActive() )
  602. {
  603. DeleteUnusedQueries( pSet, true );
  604. }
  605. else
  606. {
  607. DeleteUnusedQueries( pSet, false );
  608. }
  609. if ( m_queryList.Head(pSet->queryList) == m_queryList.InvalidIndex() )
  610. {
  611. FreeSet( node );
  612. }
  613. node = next;
  614. }
  615. }
  616. void CPixelVisibilitySystem::ShowQueries( bool show )
  617. {
  618. m_drawQueries = show;
  619. }
  620. unsigned short CPixelVisibilitySystem::AllocQuery()
  621. {
  622. unsigned short node = m_queryList.Head(m_freeQueriesList);
  623. if ( node != m_queryList.InvalidIndex() )
  624. {
  625. m_queryList.Unlink( m_freeQueriesList, node );
  626. m_queryList[node].ResetOcclusionQueries();
  627. }
  628. else
  629. {
  630. node = m_queryList.Alloc();
  631. }
  632. return node;
  633. }
  634. unsigned short CPixelVisibilitySystem::AllocSet()
  635. {
  636. unsigned short node = m_setList.Head(m_freeSetsList);
  637. if ( node != m_setList.InvalidIndex() )
  638. {
  639. m_setList.Unlink( m_freeSetsList, node );
  640. }
  641. else
  642. {
  643. node = m_setList.Alloc();
  644. m_setList[node].queryList = m_queryList.CreateList();
  645. }
  646. m_setList.LinkToHead( m_activeSetsList, node );
  647. return node;
  648. }
  649. void CPixelVisibilitySystem::FreeSet( unsigned short node )
  650. {
  651. m_setList.Unlink( m_activeSetsList, node );
  652. m_setList.LinkToHead( m_freeSetsList, node );
  653. m_setList[node].serial++;
  654. }
  655. CPixelVisSet *CPixelVisibilitySystem::FindOrCreatePixelVisSet( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  656. {
  657. if ( queryHandle[0] )
  658. {
  659. unsigned short handle = queryHandle[0] & 0xFFFF;
  660. handle--;
  661. unsigned short serial = queryHandle[0] >> 16;
  662. if ( m_setList.IsValidIndex(handle) && m_setList[handle].serial == serial )
  663. {
  664. return &m_setList[handle];
  665. }
  666. }
  667. unsigned short node = AllocSet();
  668. m_setList[node].Init( params );
  669. unsigned int out = m_setList[node].serial;
  670. unsigned short nodeHandle = node + 1;
  671. out <<= 16;
  672. out |= nodeHandle;
  673. queryHandle[0] = out;
  674. return &m_setList[node];
  675. }
  676. void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue )
  677. {
  678. ConVarRef var( pPixelvisVar );
  679. g_PixelVisibilitySystem.ShowQueries( var.GetBool() );
  680. }
  681. class CTraceFilterGlow : public CTraceFilterSimple
  682. {
  683. public:
  684. DECLARE_CLASS( CTraceFilterGlow, CTraceFilterSimple );
  685. CTraceFilterGlow( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple(passentity, collisionGroup) {}
  686. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  687. {
  688. IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity;
  689. ICollideable *pCollide = pUnk->GetCollideable();
  690. if ( pCollide->GetSolid() != SOLID_VPHYSICS && pCollide->GetSolid() != SOLID_BSP )
  691. return false;
  692. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  693. }
  694. };
  695. float GlowSightDistance( const Vector &glowOrigin, bool bShouldTrace )
  696. {
  697. float dist = (glowOrigin - CurrentViewOrigin()).Length();
  698. C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
  699. if ( local )
  700. {
  701. dist *= local->GetFOVDistanceAdjustFactor();
  702. }
  703. if ( bShouldTrace )
  704. {
  705. Vector end = glowOrigin;
  706. // HACKHACK: trace 4" from destination in case the glow is inside some parent object
  707. // allow a little error...
  708. if ( dist > 4 )
  709. {
  710. end -= CurrentViewForward()*4;
  711. }
  712. int traceFlags = MASK_OPAQUE|CONTENTS_MONSTER|CONTENTS_DEBRIS;
  713. CTraceFilterGlow filter(NULL, COLLISION_GROUP_NONE);
  714. trace_t tr;
  715. UTIL_TraceLine( CurrentViewOrigin(), end, traceFlags, &filter, &tr );
  716. if ( tr.fraction != 1.0f )
  717. return -1;
  718. }
  719. return dist;
  720. }
  721. void PixelVisibility_EndCurrentView()
  722. {
  723. g_PixelVisibilitySystem.EndView();
  724. }
  725. void PixelVisibility_EndScene()
  726. {
  727. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  728. g_PixelVisibilitySystem.EndScene();
  729. }
  730. float PixelVisibility_FractionVisible( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle )
  731. {
  732. if ( !queryHandle )
  733. {
  734. return GlowSightDistance( params.position, true ) > 0.0f ? 1.0f : 0.0f;
  735. }
  736. else
  737. {
  738. return g_PixelVisibilitySystem.GetFractionVisible( params, queryHandle );
  739. }
  740. }
  741. bool PixelVisibility_IsAvailable()
  742. {
  743. bool fastqueries = HasFastQueries();
  744. return r_dopixelvisibility.GetBool() && fastqueries && g_PixelVisibilitySystem.SupportsOcclusion();
  745. }
  746. //this originally called a class function of CPixelVisibiltySystem to keep the work clean, but that function needed friend access to CPixelVisibilityQuery
  747. //and I didn't want to make the whole class a friend or shift all the functions and class declarations around in this file
  748. void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID )
  749. {
  750. unsigned short node = g_PixelVisibilitySystem.m_setList.Head( g_PixelVisibilitySystem.m_activeSetsList );
  751. while ( node != g_PixelVisibilitySystem.m_setList.InvalidIndex() )
  752. {
  753. unsigned short next = g_PixelVisibilitySystem.m_setList.Next( node );
  754. CPixelVisSet *pSet = &g_PixelVisibilitySystem.m_setList[node];
  755. unsigned short iSourceQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iSourceViewID );
  756. unsigned short iDestQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iDestViewID );
  757. if( iDestQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  758. {
  759. //delete the destination if found
  760. g_PixelVisibilitySystem.m_queryList.Unlink( pSet->queryList, iDestQueryNode );
  761. g_PixelVisibilitySystem.m_queryList.LinkToHead( g_PixelVisibilitySystem.m_freeQueriesList, iDestQueryNode );
  762. if ( g_PixelVisibilitySystem.m_queryList.Head(pSet->queryList) == g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  763. {
  764. g_PixelVisibilitySystem.FreeSet( node );
  765. }
  766. }
  767. if( iSourceQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() )
  768. {
  769. //make the source believe it's the destination
  770. g_PixelVisibilitySystem.m_queryList[iSourceQueryNode].m_viewID = iDestViewID;
  771. }
  772. node = next;
  773. }
  774. }
  775. CON_COMMAND( pixelvis_debug, "Dump debug info" )
  776. {
  777. g_PixelVisibilitySystem.DebugInfo();
  778. }