From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/client/c_pixel_visibility.cpp | 1762 ++++++++++++++--------------- 1 file changed, 881 insertions(+), 881 deletions(-) (limited to 'mp/src/game/client/c_pixel_visibility.cpp') diff --git a/mp/src/game/client/c_pixel_visibility.cpp b/mp/src/game/client/c_pixel_visibility.cpp index 294b862a..e379c856 100644 --- a/mp/src/game/client/c_pixel_visibility.cpp +++ b/mp/src/game/client/c_pixel_visibility.cpp @@ -1,881 +1,881 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//===========================================================================// - -#include "cbase.h" -#include "c_pixel_visibility.h" -#include "materialsystem/imesh.h" -#include "materialsystem/imaterial.h" -#include "clienteffectprecachesystem.h" -#include "view.h" -#include "viewrender.h" -#include "utlmultilist.h" -#include "vprof.h" -#include "icommandline.h" -#include "headtrack/isourcevirtualreality.h" - -static void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue ); - -ConVar r_pixelvisibility_partial( "r_pixelvisibility_partial", "1" ); -ConVar r_dopixelvisibility( "r_dopixelvisibility", "1" ); -ConVar r_drawpixelvisibility( "r_drawpixelvisibility", "0", 0, "Show the occlusion proxies", PixelvisDrawChanged ); -ConVar r_pixelvisibility_spew( "r_pixelvisibility_spew", "0" ); - -#ifdef OSX - // GLMgr will set this one to "1" if it senses the new post-10.6.4 driver (m_hasPerfPackage1) - ConVar gl_can_query_fast( "gl_can_query_fast", "0" ); - - static bool HasFastQueries( void ) - { - return gl_can_query_fast.GetBool(); - } -#else - // non OSX path - static bool HasFastQueries( void ) - { - return true; - } -#endif - -extern ConVar building_cubemaps; - -#ifndef _X360 -const float MIN_PROXY_PIXELS = 5.0f; -#else -const float MIN_PROXY_PIXELS = 25.0f; -#endif - -float PixelVisibility_DrawProxy( IMatRenderContext *pRenderContext, OcclusionQueryObjectHandle_t queryHandle, Vector origin, float scale, float proxyAspect, IMaterial *pMaterial, bool screenspace ) -{ - Vector point; - - // don't expand this with distance to fit pixels or the sprite will poke through - // only expand the parts perpendicular to the view - float forwardScale = scale; - // draw a pyramid of points touching a sphere of radius "scale" at origin - float pixelsPerUnit = pRenderContext->ComputePixelDiameterOfSphere( origin, 1.0f ); - pixelsPerUnit = MAX( pixelsPerUnit, 1e-4f ); - if ( screenspace ) - { - // Force this to be the size of a sphere of diameter "scale" at some reference distance (1.0 unit) - float pixelsPerUnit2 = pRenderContext->ComputePixelDiameterOfSphere( CurrentViewOrigin() + CurrentViewForward()*1.0f, scale*0.5f ); - // force drawing of "scale" pixels - scale = pixelsPerUnit2 / pixelsPerUnit; - } - else - { - float pixels = scale * pixelsPerUnit; - - // make the radius larger to ensure a minimum screen space size of the proxy geometry - if ( pixels < MIN_PROXY_PIXELS ) - { - scale = MIN_PROXY_PIXELS / pixelsPerUnit; - } - } - - // collapses the pyramid to a plane - so this could be a quad instead - Vector dir = origin - CurrentViewOrigin(); - VectorNormalize(dir); - origin -= dir * forwardScale; - forwardScale = 0.0f; - // - - Vector verts[5]; - const float sqrt2 = 0.707106781f; // sqrt(2) - keeps all vectors the same length from origin - scale *= sqrt2; - float scale45x = scale; - float scale45y = scale / proxyAspect; - verts[0] = origin - CurrentViewForward() * forwardScale; // the apex of the pyramid - verts[1] = origin + CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // these four form the base - verts[2] = origin + CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // the pyramid is a sprite with a point that - verts[3] = origin - CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // pokes back toward the camera through any nearby - verts[4] = origin - CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // geometry - - // get screen coords of edges - Vector screen[4]; - for ( int i = 0; i < 4; i++ ) - { - extern int ScreenTransform( const Vector& point, Vector& screen ); - if ( ScreenTransform( verts[i+1], screen[i] ) ) - return -1; - } - - // compute area and screen-clipped area - float w = screen[1].x - screen[0].x; - float h = screen[0].y - screen[3].y; - float ws = MIN(1.0f, screen[1].x) - MAX(-1.0f, screen[0].x); - float hs = MIN(1.0f, screen[0].y) - MAX(-1.0f, screen[3].y); - float area = w*h; // area can be zero when we ALT-TAB - float areaClipped = ws*hs; - float ratio = 0.0f; - if ( area != 0 ) - { - // compute the ratio of the area not clipped by the frustum to total area - ratio = areaClipped / area; - ratio = clamp(ratio, 0.0f, 1.0f); - } - - pRenderContext->BeginOcclusionQueryDrawing( queryHandle ); - CMeshBuilder meshBuilder; - IMesh* pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, pMaterial ); - meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 4 ); - // draw a pyramid - for ( int i = 0; i < 4; i++ ) - { - int a = i+1; - int b = (a%4)+1; - meshBuilder.Position3fv( verts[0].Base() ); - meshBuilder.AdvanceVertex(); - meshBuilder.Position3fv( verts[a].Base() ); - meshBuilder.AdvanceVertex(); - meshBuilder.Position3fv( verts[b].Base() ); - meshBuilder.AdvanceVertex(); - } - meshBuilder.End(); - pMesh->Draw(); - - // sprite/quad proxy -#if 0 - meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); - - VectorMA (origin, -scale, CurrentViewUp(), point); - VectorMA (point, -scale, CurrentViewRight(), point); - meshBuilder.Position3fv (point.Base()); - meshBuilder.AdvanceVertex(); - - VectorMA (origin, scale, CurrentViewUp(), point); - VectorMA (point, -scale, CurrentViewRight(), point); - meshBuilder.Position3fv (point.Base()); - meshBuilder.AdvanceVertex(); - - VectorMA (origin, scale, CurrentViewUp(), point); - VectorMA (point, scale, CurrentViewRight(), point); - meshBuilder.Position3fv (point.Base()); - meshBuilder.AdvanceVertex(); - - VectorMA (origin, -scale, CurrentViewUp(), point); - VectorMA (point, scale, CurrentViewRight(), point); - meshBuilder.Position3fv (point.Base()); - meshBuilder.AdvanceVertex(); - - meshBuilder.End(); - pMesh->Draw(); -#endif - pRenderContext->EndOcclusionQueryDrawing( queryHandle ); - - // fraction clipped by frustum - return ratio; -} - -class CPixelVisSet -{ -public: - void Init( const pixelvis_queryparams_t ¶ms ); - void MarkActive(); - bool IsActive(); - CPixelVisSet() - { - frameIssued = 0; - serial = 0; - queryList = 0xFFFF; - sizeIsScreenSpace = false; - } - -public: - float proxySize; - float proxyAspect; - float fadeTimeInv; - unsigned short queryList; - unsigned short serial; - bool sizeIsScreenSpace; -private: - int frameIssued; -}; - - -void CPixelVisSet::Init( const pixelvis_queryparams_t ¶ms ) -{ - Assert( params.bSetup ); - proxySize = params.proxySize; - proxyAspect = params.proxyAspect; - if ( params.fadeTime > 0.0f ) - { - fadeTimeInv = 1.0f / params.fadeTime; - } - else - { - // fade in over 0.125 seconds - fadeTimeInv = 1.0f / 0.125f; - } - frameIssued = 0; - sizeIsScreenSpace = params.bSizeInScreenspace; -} - -void CPixelVisSet::MarkActive() -{ - frameIssued = gpGlobals->framecount; -} - -bool CPixelVisSet::IsActive() -{ - return (gpGlobals->framecount - frameIssued) > 1 ? false : true; -} - -class CPixelVisibilityQuery -{ -public: - CPixelVisibilityQuery(); - ~CPixelVisibilityQuery(); - bool IsValid(); - bool IsForView( int viewID ); - bool IsActive(); - float GetFractionVisible( float fadeTimeInv ); - void IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ); - void IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ); - void ResetOcclusionQueries(); - void SetView( int viewID ) - { - m_viewID = viewID; - m_brightnessTarget = 0.0f; - m_clipFraction = 1.0f; - m_frameIssued = -1; - m_failed = false; - m_wasQueriedThisFrame = false; - m_hasValidQueryResults = false; - } - -public: - Vector m_origin; - int m_frameIssued; -private: - float m_brightnessTarget; - float m_clipFraction; - OcclusionQueryObjectHandle_t m_queryHandle; - OcclusionQueryObjectHandle_t m_queryHandleCount; - unsigned short m_wasQueriedThisFrame : 1; - unsigned short m_failed : 1; - unsigned short m_hasValidQueryResults : 1; - unsigned short m_pad : 13; - unsigned short m_viewID; - - friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth -}; - -CPixelVisibilityQuery::CPixelVisibilityQuery() -{ - CMatRenderContextPtr pRenderContext( materials ); - SetView( 0xFFFF ); - m_queryHandle = pRenderContext->CreateOcclusionQueryObject(); - m_queryHandleCount = pRenderContext->CreateOcclusionQueryObject(); -} - -CPixelVisibilityQuery::~CPixelVisibilityQuery() -{ - CMatRenderContextPtr pRenderContext( materials ); - if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) - { - pRenderContext->DestroyOcclusionQueryObject( m_queryHandle ); - } - if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) - { - pRenderContext->DestroyOcclusionQueryObject( m_queryHandleCount ); - } -} - -void CPixelVisibilityQuery::ResetOcclusionQueries() -{ - // NOTE: Since we're keeping the CPixelVisibilityQuery objects around in a pool - // and not actually deleting them, this means that our material system occlusion queries are - // not being deleted either. Which means that if a CPixelVisibilityQuery is - // put into the free list and then immediately re-used, then we have an opportunity for - // a bug: What can happen on the first frame of the material system query - // is that if the query isn't done yet, it will use the last queried value - // which will happen to be set to the value of the last query done - // for the previous CPixelVisSet the CPixelVisibilityQuery happened to be associated with - // which makes queries have an invalid value for the first frame - - // This will mark the occlusion query objects as not ever having been read from before - CMatRenderContextPtr pRenderContext( materials ); - if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) - { - pRenderContext->ResetOcclusionQueryObject( m_queryHandle ); - } - if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) - { - pRenderContext->ResetOcclusionQueryObject( m_queryHandleCount ); - } -} - -bool CPixelVisibilityQuery::IsValid() -{ - return (m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE) ? true : false; -} -bool CPixelVisibilityQuery::IsForView( int viewID ) -{ - return m_viewID == viewID ? true : false; -} - -bool CPixelVisibilityQuery::IsActive() -{ - return (gpGlobals->framecount - m_frameIssued) > 1 ? false : true; -} - -float CPixelVisibilityQuery::GetFractionVisible( float fadeTimeInv ) -{ - if ( !IsValid() ) - return 0.0f; - - if ( !m_wasQueriedThisFrame ) - { - CMatRenderContextPtr pRenderContext( materials ); - m_wasQueriedThisFrame = true; - int pixels = -1; - int pixelsPossible = -1; - if ( r_pixelvisibility_partial.GetBool() ) - { - if ( m_frameIssued != -1 ) - { - pixelsPossible = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandleCount ); - pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle ); - } - - if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) - { - 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 ); - } - - if ( pixels < 0 || pixelsPossible < 0 ) - { - m_failed = ( m_frameIssued >= 0 ) ? true : false; - return m_brightnessTarget * m_clipFraction; - } - m_hasValidQueryResults = true; - - if ( pixelsPossible > 0 ) - { - float target = (float)pixels / (float)pixelsPossible; - target = (target >= 0.95f) ? 1.0f : (target < 0.0f) ? 0.0f : target; - float rate = gpGlobals->frametime * fadeTimeInv; - m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out - } - else - { - m_brightnessTarget = 0.0f; - } - } - else - { - if ( m_frameIssued != -1 ) - { - pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle ); - } - - if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) - { - DevMsg( 1, "Pixels visible: %d (qh:%d) (frame:%d)\n", pixels, (int)m_queryHandle, gpGlobals->framecount ); - } - - if ( pixels < 0 ) - { - m_failed = ( m_frameIssued >= 0 ) ? true : false; - return m_brightnessTarget * m_clipFraction; - } - m_hasValidQueryResults = true; - if ( m_frameIssued == gpGlobals->framecount-1 ) - { - float rate = gpGlobals->frametime * fadeTimeInv; - float target = 0.0f; - if ( pixels > 0 ) - { - // fade in slower than you fade out - rate *= 0.5f; - target = 1.0f; - } - m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out - } - else - { - m_brightnessTarget = 0.0f; - } - } - } - - return m_brightnessTarget * m_clipFraction; -} - -void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ) -{ - if ( !m_failed ) - { - Assert( IsValid() ); - - if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) - { - 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 ); - } - - m_clipFraction = PixelVisibility_DrawProxy( pRenderContext, m_queryHandle, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace ); - if ( m_clipFraction < 0 ) - { - // NOTE: In this case, the proxy wasn't issued cause it was offscreen - // can't set the m_frameissued field since that would cause it to get marked as failed - m_clipFraction = 0; - m_wasQueriedThisFrame = false; - m_failed = false; - return; - } - } -#ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders! - Assert ( ( m_frameIssued != gpGlobals->framecount ) || UseVR() ); -#endif - - m_frameIssued = gpGlobals->framecount; - m_wasQueriedThisFrame = false; - m_failed = false; -} - -void CPixelVisibilityQuery::IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ) -{ - if ( !m_failed ) - { - Assert( IsValid() ); -#if 0 - // this centers it on the screen. - // This is nice because it makes the glows fade as they get partially clipped by the view frustum - // But it introduces sub-pixel errors (off by one row/column of pixels) so the glows shimmer - // UNDONE: Compute an offset center coord that matches sub-pixel coords with the real glow position - // UNDONE: Or frustum clip the sphere/geometry and fade based on proxy size - Vector origin = m_origin - CurrentViewOrigin(); - float dot = DotProduct(CurrentViewForward(), origin); - origin = CurrentViewOrigin() + dot * CurrentViewForward(); -#endif - PixelVisibility_DrawProxy( pRenderContext, m_queryHandleCount, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace ); - } -} - -//Precache the effects -CLIENTEFFECT_REGISTER_BEGIN( PrecacheOcclusionProxy ) -CLIENTEFFECT_MATERIAL( "engine/occlusionproxy" ) -CLIENTEFFECT_MATERIAL( "engine/occlusionproxy_countdraw" ) -CLIENTEFFECT_REGISTER_END() - -class CPixelVisibilitySystem : public CAutoGameSystem -{ -public: - - // GameSystem: Level init, shutdown - virtual void LevelInitPreEntity(); - virtual void LevelShutdownPostEntity(); - - // locals - CPixelVisibilitySystem(); - float GetFractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ); - void EndView(); - void EndScene(); - unsigned short FindQueryForView( CPixelVisSet *pSet, int viewID ); - unsigned short FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID ); - - void DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll ); - void DeleteUnusedSets( bool bDeleteAll ); - void ShowQueries( bool show ); - unsigned short AllocQuery(); - unsigned short AllocSet(); - void FreeSet( unsigned short node ); - CPixelVisSet *FindOrCreatePixelVisSet( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ); - bool SupportsOcclusion() { return m_hwCanTestGlows; } - void DebugInfo() - { - Msg("Pixel vis system using %d sets total (%d in free list), %d queries total (%d in free list)\n", - m_setList.TotalCount(), m_setList.Count(m_freeSetsList), m_queryList.TotalCount(), m_queryList.Count( m_freeQueriesList ) ); - } - -private: - CUtlMultiList< CPixelVisSet, unsigned short > m_setList; - CUtlMultiList m_queryList; - unsigned short m_freeQueriesList; - unsigned short m_activeSetsList; - unsigned short m_freeSetsList; - unsigned short m_pad0; - - IMaterial *m_pProxyMaterial; - IMaterial *m_pDrawMaterial; - bool m_hwCanTestGlows; - bool m_drawQueries; - - - friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth -}; - -static CPixelVisibilitySystem g_PixelVisibilitySystem; - -CPixelVisibilitySystem::CPixelVisibilitySystem() : CAutoGameSystem( "CPixelVisibilitySystem" ) -{ - m_hwCanTestGlows = true; - m_drawQueries = false; -} -// Level init, shutdown -void CPixelVisibilitySystem::LevelInitPreEntity() -{ - bool fastqueries = HasFastQueries(); - // printf("\n ** fast queries: %s **", fastqueries?"true":"false" ); - - m_hwCanTestGlows = r_dopixelvisibility.GetBool() && fastqueries && engine->GetDXSupportLevel() >= 80; - if ( m_hwCanTestGlows ) - { - CMatRenderContextPtr pRenderContext( materials ); - - OcclusionQueryObjectHandle_t query = pRenderContext->CreateOcclusionQueryObject(); - if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) - { - pRenderContext->DestroyOcclusionQueryObject( query ); - } - else - { - m_hwCanTestGlows = false; - } - } - - m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS); - m_pProxyMaterial->IncrementReferenceCount(); - m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS); - m_pDrawMaterial->IncrementReferenceCount(); - m_freeQueriesList = m_queryList.CreateList(); - m_activeSetsList = m_setList.CreateList(); - m_freeSetsList = m_setList.CreateList(); -} - -void CPixelVisibilitySystem::LevelShutdownPostEntity() -{ - m_pProxyMaterial->DecrementReferenceCount(); - m_pProxyMaterial = NULL; - m_pDrawMaterial->DecrementReferenceCount(); - m_pDrawMaterial = NULL; - DeleteUnusedSets(true); - m_setList.Purge(); - m_queryList.Purge(); - m_freeQueriesList = m_queryList.InvalidIndex(); - m_activeSetsList = m_setList.InvalidIndex(); - m_freeSetsList = m_setList.InvalidIndex(); -} - -float CPixelVisibilitySystem::GetFractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) -{ - if ( !m_hwCanTestGlows || building_cubemaps.GetBool() ) - { - return GlowSightDistance( params.position, true ) > 0 ? 1.0f : 0.0f; - } - if ( CurrentViewID() < 0 ) - return 0.0f; - - CPixelVisSet *pSet = FindOrCreatePixelVisSet( params, queryHandle ); - Assert( pSet ); - unsigned short node = FindOrCreateQueryForView( pSet, CurrentViewID() ); - m_queryList[node].m_origin = params.position; - float fraction = m_queryList[node].GetFractionVisible( pSet->fadeTimeInv ); - pSet->MarkActive(); - return fraction; -} - -void CPixelVisibilitySystem::EndView() -{ - if ( !PixelVisibility_IsAvailable() && CurrentViewID() >= 0 ) - return; - - if ( m_setList.Head( m_activeSetsList ) == m_setList.InvalidIndex() ) - return; - - CMatRenderContextPtr pRenderContext( materials ); - - IMaterial *pProxy = m_drawQueries ? m_pDrawMaterial : m_pProxyMaterial; - pRenderContext->Bind( pProxy ); - - // BUGBUG: If you draw both queries, the measure query fails for some reason. - if ( r_pixelvisibility_partial.GetBool() && !m_drawQueries ) - { - pRenderContext->DepthRange( 0.0f, 0.01f ); - unsigned short node = m_setList.Head( m_activeSetsList ); - while( node != m_setList.InvalidIndex() ) - { - CPixelVisSet *pSet = &m_setList[node]; - unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() ); - if ( queryNode != m_queryList.InvalidIndex() ) - { - m_queryList[queryNode].IssueCountingQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace ); - } - node = m_setList.Next( node ); - } - pRenderContext->DepthRange( 0.0f, 1.0f ); - } - - { - unsigned short node = m_setList.Head( m_activeSetsList ); - while( node != m_setList.InvalidIndex() ) - { - CPixelVisSet *pSet = &m_setList[node]; - unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() ); - if ( queryNode != m_queryList.InvalidIndex() ) - { - m_queryList[queryNode].IssueQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace ); - } - node = m_setList.Next( node ); - } - } -} - -void CPixelVisibilitySystem::EndScene() -{ - DeleteUnusedSets(false); -} - -unsigned short CPixelVisibilitySystem::FindQueryForView( CPixelVisSet *pSet, int viewID ) -{ - unsigned short node = m_queryList.Head( pSet->queryList ); - while ( node != m_queryList.InvalidIndex() ) - { - if ( m_queryList[node].IsForView( viewID ) ) - return node; - node = m_queryList.Next( node ); - } - return m_queryList.InvalidIndex(); -} -unsigned short CPixelVisibilitySystem::FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID ) -{ - unsigned short node = FindQueryForView( pSet, viewID ); - if ( node != m_queryList.InvalidIndex() ) - return node; - - node = AllocQuery(); - m_queryList.LinkToHead( pSet->queryList, node ); - m_queryList[node].SetView( viewID ); - return node; -} - - -void CPixelVisibilitySystem::DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll ) -{ - unsigned short node = m_queryList.Head( pSet->queryList ); - while ( node != m_queryList.InvalidIndex() ) - { - unsigned short next = m_queryList.Next( node ); - if ( bDeleteAll || !m_queryList[node].IsActive() ) - { - m_queryList.Unlink( pSet->queryList, node); - m_queryList.LinkToHead( m_freeQueriesList, node ); - } - node = next; - } -} -void CPixelVisibilitySystem::DeleteUnusedSets( bool bDeleteAll ) -{ - unsigned short node = m_setList.Head( m_activeSetsList ); - while ( node != m_setList.InvalidIndex() ) - { - unsigned short next = m_setList.Next( node ); - CPixelVisSet *pSet = &m_setList[node]; - if ( bDeleteAll || !m_setList[node].IsActive() ) - { - DeleteUnusedQueries( pSet, true ); - } - else - { - DeleteUnusedQueries( pSet, false ); - } - if ( m_queryList.Head(pSet->queryList) == m_queryList.InvalidIndex() ) - { - FreeSet( node ); - } - node = next; - } -} - -void CPixelVisibilitySystem::ShowQueries( bool show ) -{ - m_drawQueries = show; -} - -unsigned short CPixelVisibilitySystem::AllocQuery() -{ - unsigned short node = m_queryList.Head(m_freeQueriesList); - if ( node != m_queryList.InvalidIndex() ) - { - m_queryList.Unlink( m_freeQueriesList, node ); - m_queryList[node].ResetOcclusionQueries(); - } - else - { - node = m_queryList.Alloc(); - } - return node; -} - -unsigned short CPixelVisibilitySystem::AllocSet() -{ - unsigned short node = m_setList.Head(m_freeSetsList); - if ( node != m_setList.InvalidIndex() ) - { - m_setList.Unlink( m_freeSetsList, node ); - } - else - { - node = m_setList.Alloc(); - m_setList[node].queryList = m_queryList.CreateList(); - } - m_setList.LinkToHead( m_activeSetsList, node ); - return node; -} - -void CPixelVisibilitySystem::FreeSet( unsigned short node ) -{ - m_setList.Unlink( m_activeSetsList, node ); - m_setList.LinkToHead( m_freeSetsList, node ); - m_setList[node].serial++; -} - -CPixelVisSet *CPixelVisibilitySystem::FindOrCreatePixelVisSet( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) -{ - if ( queryHandle[0] ) - { - unsigned short handle = queryHandle[0] & 0xFFFF; - handle--; - unsigned short serial = queryHandle[0] >> 16; - if ( m_setList.IsValidIndex(handle) && m_setList[handle].serial == serial ) - { - return &m_setList[handle]; - } - } - - unsigned short node = AllocSet(); - m_setList[node].Init( params ); - unsigned int out = m_setList[node].serial; - unsigned short nodeHandle = node + 1; - out <<= 16; - out |= nodeHandle; - queryHandle[0] = out; - return &m_setList[node]; -} - - -void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue ) -{ - ConVarRef var( pPixelvisVar ); - g_PixelVisibilitySystem.ShowQueries( var.GetBool() ); -} - -class CTraceFilterGlow : public CTraceFilterSimple -{ -public: - DECLARE_CLASS( CTraceFilterGlow, CTraceFilterSimple ); - - CTraceFilterGlow( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple(passentity, collisionGroup) {} - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity; - ICollideable *pCollide = pUnk->GetCollideable(); - if ( pCollide->GetSolid() != SOLID_VPHYSICS && pCollide->GetSolid() != SOLID_BSP ) - return false; - return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); - } -}; -float GlowSightDistance( const Vector &glowOrigin, bool bShouldTrace ) -{ - float dist = (glowOrigin - CurrentViewOrigin()).Length(); - C_BasePlayer *local = C_BasePlayer::GetLocalPlayer(); - if ( local ) - { - dist *= local->GetFOVDistanceAdjustFactor(); - } - - if ( bShouldTrace ) - { - Vector end = glowOrigin; - // HACKHACK: trace 4" from destination in case the glow is inside some parent object - // allow a little error... - if ( dist > 4 ) - { - end -= CurrentViewForward()*4; - } - int traceFlags = MASK_OPAQUE|CONTENTS_MONSTER|CONTENTS_DEBRIS; - - CTraceFilterGlow filter(NULL, COLLISION_GROUP_NONE); - trace_t tr; - UTIL_TraceLine( CurrentViewOrigin(), end, traceFlags, &filter, &tr ); - if ( tr.fraction != 1.0f ) - return -1; - } - - return dist; -} - -void PixelVisibility_EndCurrentView() -{ - g_PixelVisibilitySystem.EndView(); -} - -void PixelVisibility_EndScene() -{ - tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); - - g_PixelVisibilitySystem.EndScene(); -} - -float PixelVisibility_FractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) -{ - if ( !queryHandle ) - { - return GlowSightDistance( params.position, true ) > 0.0f ? 1.0f : 0.0f; - } - else - { - return g_PixelVisibilitySystem.GetFractionVisible( params, queryHandle ); - } -} - -bool PixelVisibility_IsAvailable() -{ - bool fastqueries = HasFastQueries(); - return r_dopixelvisibility.GetBool() && fastqueries && g_PixelVisibilitySystem.SupportsOcclusion(); -} - -//this originally called a class function of CPixelVisibiltySystem to keep the work clean, but that function needed friend access to CPixelVisibilityQuery -//and I didn't want to make the whole class a friend or shift all the functions and class declarations around in this file -void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ) -{ - unsigned short node = g_PixelVisibilitySystem.m_setList.Head( g_PixelVisibilitySystem.m_activeSetsList ); - while ( node != g_PixelVisibilitySystem.m_setList.InvalidIndex() ) - { - unsigned short next = g_PixelVisibilitySystem.m_setList.Next( node ); - CPixelVisSet *pSet = &g_PixelVisibilitySystem.m_setList[node]; - - unsigned short iSourceQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iSourceViewID ); - unsigned short iDestQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iDestViewID ); - - if( iDestQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) - { - //delete the destination if found - g_PixelVisibilitySystem.m_queryList.Unlink( pSet->queryList, iDestQueryNode ); - g_PixelVisibilitySystem.m_queryList.LinkToHead( g_PixelVisibilitySystem.m_freeQueriesList, iDestQueryNode ); - - if ( g_PixelVisibilitySystem.m_queryList.Head(pSet->queryList) == g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) - { - g_PixelVisibilitySystem.FreeSet( node ); - } - } - - if( iSourceQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) - { - //make the source believe it's the destination - g_PixelVisibilitySystem.m_queryList[iSourceQueryNode].m_viewID = iDestViewID; - } - - node = next; - } -} - -CON_COMMAND( pixelvis_debug, "Dump debug info" ) -{ - g_PixelVisibilitySystem.DebugInfo(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "cbase.h" +#include "c_pixel_visibility.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterial.h" +#include "clienteffectprecachesystem.h" +#include "view.h" +#include "viewrender.h" +#include "utlmultilist.h" +#include "vprof.h" +#include "icommandline.h" +#include "headtrack/isourcevirtualreality.h" + +static void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue ); + +ConVar r_pixelvisibility_partial( "r_pixelvisibility_partial", "1" ); +ConVar r_dopixelvisibility( "r_dopixelvisibility", "1" ); +ConVar r_drawpixelvisibility( "r_drawpixelvisibility", "0", 0, "Show the occlusion proxies", PixelvisDrawChanged ); +ConVar r_pixelvisibility_spew( "r_pixelvisibility_spew", "0" ); + +#ifdef OSX + // GLMgr will set this one to "1" if it senses the new post-10.6.4 driver (m_hasPerfPackage1) + ConVar gl_can_query_fast( "gl_can_query_fast", "0" ); + + static bool HasFastQueries( void ) + { + return gl_can_query_fast.GetBool(); + } +#else + // non OSX path + static bool HasFastQueries( void ) + { + return true; + } +#endif + +extern ConVar building_cubemaps; + +#ifndef _X360 +const float MIN_PROXY_PIXELS = 5.0f; +#else +const float MIN_PROXY_PIXELS = 25.0f; +#endif + +float PixelVisibility_DrawProxy( IMatRenderContext *pRenderContext, OcclusionQueryObjectHandle_t queryHandle, Vector origin, float scale, float proxyAspect, IMaterial *pMaterial, bool screenspace ) +{ + Vector point; + + // don't expand this with distance to fit pixels or the sprite will poke through + // only expand the parts perpendicular to the view + float forwardScale = scale; + // draw a pyramid of points touching a sphere of radius "scale" at origin + float pixelsPerUnit = pRenderContext->ComputePixelDiameterOfSphere( origin, 1.0f ); + pixelsPerUnit = MAX( pixelsPerUnit, 1e-4f ); + if ( screenspace ) + { + // Force this to be the size of a sphere of diameter "scale" at some reference distance (1.0 unit) + float pixelsPerUnit2 = pRenderContext->ComputePixelDiameterOfSphere( CurrentViewOrigin() + CurrentViewForward()*1.0f, scale*0.5f ); + // force drawing of "scale" pixels + scale = pixelsPerUnit2 / pixelsPerUnit; + } + else + { + float pixels = scale * pixelsPerUnit; + + // make the radius larger to ensure a minimum screen space size of the proxy geometry + if ( pixels < MIN_PROXY_PIXELS ) + { + scale = MIN_PROXY_PIXELS / pixelsPerUnit; + } + } + + // collapses the pyramid to a plane - so this could be a quad instead + Vector dir = origin - CurrentViewOrigin(); + VectorNormalize(dir); + origin -= dir * forwardScale; + forwardScale = 0.0f; + // + + Vector verts[5]; + const float sqrt2 = 0.707106781f; // sqrt(2) - keeps all vectors the same length from origin + scale *= sqrt2; + float scale45x = scale; + float scale45y = scale / proxyAspect; + verts[0] = origin - CurrentViewForward() * forwardScale; // the apex of the pyramid + verts[1] = origin + CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // these four form the base + verts[2] = origin + CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // the pyramid is a sprite with a point that + verts[3] = origin - CurrentViewUp() * scale45y + CurrentViewRight() * scale45x; // pokes back toward the camera through any nearby + verts[4] = origin - CurrentViewUp() * scale45y - CurrentViewRight() * scale45x; // geometry + + // get screen coords of edges + Vector screen[4]; + for ( int i = 0; i < 4; i++ ) + { + extern int ScreenTransform( const Vector& point, Vector& screen ); + if ( ScreenTransform( verts[i+1], screen[i] ) ) + return -1; + } + + // compute area and screen-clipped area + float w = screen[1].x - screen[0].x; + float h = screen[0].y - screen[3].y; + float ws = MIN(1.0f, screen[1].x) - MAX(-1.0f, screen[0].x); + float hs = MIN(1.0f, screen[0].y) - MAX(-1.0f, screen[3].y); + float area = w*h; // area can be zero when we ALT-TAB + float areaClipped = ws*hs; + float ratio = 0.0f; + if ( area != 0 ) + { + // compute the ratio of the area not clipped by the frustum to total area + ratio = areaClipped / area; + ratio = clamp(ratio, 0.0f, 1.0f); + } + + pRenderContext->BeginOcclusionQueryDrawing( queryHandle ); + CMeshBuilder meshBuilder; + IMesh* pMesh = pRenderContext->GetDynamicMesh( false, NULL, NULL, pMaterial ); + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 4 ); + // draw a pyramid + for ( int i = 0; i < 4; i++ ) + { + int a = i+1; + int b = (a%4)+1; + meshBuilder.Position3fv( verts[0].Base() ); + meshBuilder.AdvanceVertex(); + meshBuilder.Position3fv( verts[a].Base() ); + meshBuilder.AdvanceVertex(); + meshBuilder.Position3fv( verts[b].Base() ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); + + // sprite/quad proxy +#if 0 + meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); + + VectorMA (origin, -scale, CurrentViewUp(), point); + VectorMA (point, -scale, CurrentViewRight(), point); + meshBuilder.Position3fv (point.Base()); + meshBuilder.AdvanceVertex(); + + VectorMA (origin, scale, CurrentViewUp(), point); + VectorMA (point, -scale, CurrentViewRight(), point); + meshBuilder.Position3fv (point.Base()); + meshBuilder.AdvanceVertex(); + + VectorMA (origin, scale, CurrentViewUp(), point); + VectorMA (point, scale, CurrentViewRight(), point); + meshBuilder.Position3fv (point.Base()); + meshBuilder.AdvanceVertex(); + + VectorMA (origin, -scale, CurrentViewUp(), point); + VectorMA (point, scale, CurrentViewRight(), point); + meshBuilder.Position3fv (point.Base()); + meshBuilder.AdvanceVertex(); + + meshBuilder.End(); + pMesh->Draw(); +#endif + pRenderContext->EndOcclusionQueryDrawing( queryHandle ); + + // fraction clipped by frustum + return ratio; +} + +class CPixelVisSet +{ +public: + void Init( const pixelvis_queryparams_t ¶ms ); + void MarkActive(); + bool IsActive(); + CPixelVisSet() + { + frameIssued = 0; + serial = 0; + queryList = 0xFFFF; + sizeIsScreenSpace = false; + } + +public: + float proxySize; + float proxyAspect; + float fadeTimeInv; + unsigned short queryList; + unsigned short serial; + bool sizeIsScreenSpace; +private: + int frameIssued; +}; + + +void CPixelVisSet::Init( const pixelvis_queryparams_t ¶ms ) +{ + Assert( params.bSetup ); + proxySize = params.proxySize; + proxyAspect = params.proxyAspect; + if ( params.fadeTime > 0.0f ) + { + fadeTimeInv = 1.0f / params.fadeTime; + } + else + { + // fade in over 0.125 seconds + fadeTimeInv = 1.0f / 0.125f; + } + frameIssued = 0; + sizeIsScreenSpace = params.bSizeInScreenspace; +} + +void CPixelVisSet::MarkActive() +{ + frameIssued = gpGlobals->framecount; +} + +bool CPixelVisSet::IsActive() +{ + return (gpGlobals->framecount - frameIssued) > 1 ? false : true; +} + +class CPixelVisibilityQuery +{ +public: + CPixelVisibilityQuery(); + ~CPixelVisibilityQuery(); + bool IsValid(); + bool IsForView( int viewID ); + bool IsActive(); + float GetFractionVisible( float fadeTimeInv ); + void IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ); + void IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ); + void ResetOcclusionQueries(); + void SetView( int viewID ) + { + m_viewID = viewID; + m_brightnessTarget = 0.0f; + m_clipFraction = 1.0f; + m_frameIssued = -1; + m_failed = false; + m_wasQueriedThisFrame = false; + m_hasValidQueryResults = false; + } + +public: + Vector m_origin; + int m_frameIssued; +private: + float m_brightnessTarget; + float m_clipFraction; + OcclusionQueryObjectHandle_t m_queryHandle; + OcclusionQueryObjectHandle_t m_queryHandleCount; + unsigned short m_wasQueriedThisFrame : 1; + unsigned short m_failed : 1; + unsigned short m_hasValidQueryResults : 1; + unsigned short m_pad : 13; + unsigned short m_viewID; + + friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth +}; + +CPixelVisibilityQuery::CPixelVisibilityQuery() +{ + CMatRenderContextPtr pRenderContext( materials ); + SetView( 0xFFFF ); + m_queryHandle = pRenderContext->CreateOcclusionQueryObject(); + m_queryHandleCount = pRenderContext->CreateOcclusionQueryObject(); +} + +CPixelVisibilityQuery::~CPixelVisibilityQuery() +{ + CMatRenderContextPtr pRenderContext( materials ); + if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) + { + pRenderContext->DestroyOcclusionQueryObject( m_queryHandle ); + } + if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) + { + pRenderContext->DestroyOcclusionQueryObject( m_queryHandleCount ); + } +} + +void CPixelVisibilityQuery::ResetOcclusionQueries() +{ + // NOTE: Since we're keeping the CPixelVisibilityQuery objects around in a pool + // and not actually deleting them, this means that our material system occlusion queries are + // not being deleted either. Which means that if a CPixelVisibilityQuery is + // put into the free list and then immediately re-used, then we have an opportunity for + // a bug: What can happen on the first frame of the material system query + // is that if the query isn't done yet, it will use the last queried value + // which will happen to be set to the value of the last query done + // for the previous CPixelVisSet the CPixelVisibilityQuery happened to be associated with + // which makes queries have an invalid value for the first frame + + // This will mark the occlusion query objects as not ever having been read from before + CMatRenderContextPtr pRenderContext( materials ); + if ( m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) + { + pRenderContext->ResetOcclusionQueryObject( m_queryHandle ); + } + if ( m_queryHandleCount != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) + { + pRenderContext->ResetOcclusionQueryObject( m_queryHandleCount ); + } +} + +bool CPixelVisibilityQuery::IsValid() +{ + return (m_queryHandle != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE) ? true : false; +} +bool CPixelVisibilityQuery::IsForView( int viewID ) +{ + return m_viewID == viewID ? true : false; +} + +bool CPixelVisibilityQuery::IsActive() +{ + return (gpGlobals->framecount - m_frameIssued) > 1 ? false : true; +} + +float CPixelVisibilityQuery::GetFractionVisible( float fadeTimeInv ) +{ + if ( !IsValid() ) + return 0.0f; + + if ( !m_wasQueriedThisFrame ) + { + CMatRenderContextPtr pRenderContext( materials ); + m_wasQueriedThisFrame = true; + int pixels = -1; + int pixelsPossible = -1; + if ( r_pixelvisibility_partial.GetBool() ) + { + if ( m_frameIssued != -1 ) + { + pixelsPossible = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandleCount ); + pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle ); + } + + if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) + { + 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 ); + } + + if ( pixels < 0 || pixelsPossible < 0 ) + { + m_failed = ( m_frameIssued >= 0 ) ? true : false; + return m_brightnessTarget * m_clipFraction; + } + m_hasValidQueryResults = true; + + if ( pixelsPossible > 0 ) + { + float target = (float)pixels / (float)pixelsPossible; + target = (target >= 0.95f) ? 1.0f : (target < 0.0f) ? 0.0f : target; + float rate = gpGlobals->frametime * fadeTimeInv; + m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out + } + else + { + m_brightnessTarget = 0.0f; + } + } + else + { + if ( m_frameIssued != -1 ) + { + pixels = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_queryHandle ); + } + + if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) + { + DevMsg( 1, "Pixels visible: %d (qh:%d) (frame:%d)\n", pixels, (int)m_queryHandle, gpGlobals->framecount ); + } + + if ( pixels < 0 ) + { + m_failed = ( m_frameIssued >= 0 ) ? true : false; + return m_brightnessTarget * m_clipFraction; + } + m_hasValidQueryResults = true; + if ( m_frameIssued == gpGlobals->framecount-1 ) + { + float rate = gpGlobals->frametime * fadeTimeInv; + float target = 0.0f; + if ( pixels > 0 ) + { + // fade in slower than you fade out + rate *= 0.5f; + target = 1.0f; + } + m_brightnessTarget = Approach( target, m_brightnessTarget, rate ); // fade in / out + } + else + { + m_brightnessTarget = 0.0f; + } + } + } + + return m_brightnessTarget * m_clipFraction; +} + +void CPixelVisibilityQuery::IssueQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ) +{ + if ( !m_failed ) + { + Assert( IsValid() ); + + if ( r_pixelvisibility_spew.GetBool() && CurrentViewID() == 0 ) + { + 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 ); + } + + m_clipFraction = PixelVisibility_DrawProxy( pRenderContext, m_queryHandle, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace ); + if ( m_clipFraction < 0 ) + { + // NOTE: In this case, the proxy wasn't issued cause it was offscreen + // can't set the m_frameissued field since that would cause it to get marked as failed + m_clipFraction = 0; + m_wasQueriedThisFrame = false; + m_failed = false; + return; + } + } +#ifndef PORTAL // FIXME: In portal we query visibility multiple times per frame because of portal renders! + Assert ( ( m_frameIssued != gpGlobals->framecount ) || UseVR() ); +#endif + + m_frameIssued = gpGlobals->framecount; + m_wasQueriedThisFrame = false; + m_failed = false; +} + +void CPixelVisibilityQuery::IssueCountingQuery( IMatRenderContext *pRenderContext, float proxySize, float proxyAspect, IMaterial *pMaterial, bool sizeIsScreenSpace ) +{ + if ( !m_failed ) + { + Assert( IsValid() ); +#if 0 + // this centers it on the screen. + // This is nice because it makes the glows fade as they get partially clipped by the view frustum + // But it introduces sub-pixel errors (off by one row/column of pixels) so the glows shimmer + // UNDONE: Compute an offset center coord that matches sub-pixel coords with the real glow position + // UNDONE: Or frustum clip the sphere/geometry and fade based on proxy size + Vector origin = m_origin - CurrentViewOrigin(); + float dot = DotProduct(CurrentViewForward(), origin); + origin = CurrentViewOrigin() + dot * CurrentViewForward(); +#endif + PixelVisibility_DrawProxy( pRenderContext, m_queryHandleCount, m_origin, proxySize, proxyAspect, pMaterial, sizeIsScreenSpace ); + } +} + +//Precache the effects +CLIENTEFFECT_REGISTER_BEGIN( PrecacheOcclusionProxy ) +CLIENTEFFECT_MATERIAL( "engine/occlusionproxy" ) +CLIENTEFFECT_MATERIAL( "engine/occlusionproxy_countdraw" ) +CLIENTEFFECT_REGISTER_END() + +class CPixelVisibilitySystem : public CAutoGameSystem +{ +public: + + // GameSystem: Level init, shutdown + virtual void LevelInitPreEntity(); + virtual void LevelShutdownPostEntity(); + + // locals + CPixelVisibilitySystem(); + float GetFractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ); + void EndView(); + void EndScene(); + unsigned short FindQueryForView( CPixelVisSet *pSet, int viewID ); + unsigned short FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID ); + + void DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll ); + void DeleteUnusedSets( bool bDeleteAll ); + void ShowQueries( bool show ); + unsigned short AllocQuery(); + unsigned short AllocSet(); + void FreeSet( unsigned short node ); + CPixelVisSet *FindOrCreatePixelVisSet( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ); + bool SupportsOcclusion() { return m_hwCanTestGlows; } + void DebugInfo() + { + Msg("Pixel vis system using %d sets total (%d in free list), %d queries total (%d in free list)\n", + m_setList.TotalCount(), m_setList.Count(m_freeSetsList), m_queryList.TotalCount(), m_queryList.Count( m_freeQueriesList ) ); + } + +private: + CUtlMultiList< CPixelVisSet, unsigned short > m_setList; + CUtlMultiList m_queryList; + unsigned short m_freeQueriesList; + unsigned short m_activeSetsList; + unsigned short m_freeSetsList; + unsigned short m_pad0; + + IMaterial *m_pProxyMaterial; + IMaterial *m_pDrawMaterial; + bool m_hwCanTestGlows; + bool m_drawQueries; + + + friend void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ); //need direct access to private data to make shifting smooth +}; + +static CPixelVisibilitySystem g_PixelVisibilitySystem; + +CPixelVisibilitySystem::CPixelVisibilitySystem() : CAutoGameSystem( "CPixelVisibilitySystem" ) +{ + m_hwCanTestGlows = true; + m_drawQueries = false; +} +// Level init, shutdown +void CPixelVisibilitySystem::LevelInitPreEntity() +{ + bool fastqueries = HasFastQueries(); + // printf("\n ** fast queries: %s **", fastqueries?"true":"false" ); + + m_hwCanTestGlows = r_dopixelvisibility.GetBool() && fastqueries && engine->GetDXSupportLevel() >= 80; + if ( m_hwCanTestGlows ) + { + CMatRenderContextPtr pRenderContext( materials ); + + OcclusionQueryObjectHandle_t query = pRenderContext->CreateOcclusionQueryObject(); + if ( query != INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ) + { + pRenderContext->DestroyOcclusionQueryObject( query ); + } + else + { + m_hwCanTestGlows = false; + } + } + + m_pProxyMaterial = materials->FindMaterial("engine/occlusionproxy", TEXTURE_GROUP_CLIENT_EFFECTS); + m_pProxyMaterial->IncrementReferenceCount(); + m_pDrawMaterial = materials->FindMaterial("engine/occlusionproxy_countdraw", TEXTURE_GROUP_CLIENT_EFFECTS); + m_pDrawMaterial->IncrementReferenceCount(); + m_freeQueriesList = m_queryList.CreateList(); + m_activeSetsList = m_setList.CreateList(); + m_freeSetsList = m_setList.CreateList(); +} + +void CPixelVisibilitySystem::LevelShutdownPostEntity() +{ + m_pProxyMaterial->DecrementReferenceCount(); + m_pProxyMaterial = NULL; + m_pDrawMaterial->DecrementReferenceCount(); + m_pDrawMaterial = NULL; + DeleteUnusedSets(true); + m_setList.Purge(); + m_queryList.Purge(); + m_freeQueriesList = m_queryList.InvalidIndex(); + m_activeSetsList = m_setList.InvalidIndex(); + m_freeSetsList = m_setList.InvalidIndex(); +} + +float CPixelVisibilitySystem::GetFractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) +{ + if ( !m_hwCanTestGlows || building_cubemaps.GetBool() ) + { + return GlowSightDistance( params.position, true ) > 0 ? 1.0f : 0.0f; + } + if ( CurrentViewID() < 0 ) + return 0.0f; + + CPixelVisSet *pSet = FindOrCreatePixelVisSet( params, queryHandle ); + Assert( pSet ); + unsigned short node = FindOrCreateQueryForView( pSet, CurrentViewID() ); + m_queryList[node].m_origin = params.position; + float fraction = m_queryList[node].GetFractionVisible( pSet->fadeTimeInv ); + pSet->MarkActive(); + return fraction; +} + +void CPixelVisibilitySystem::EndView() +{ + if ( !PixelVisibility_IsAvailable() && CurrentViewID() >= 0 ) + return; + + if ( m_setList.Head( m_activeSetsList ) == m_setList.InvalidIndex() ) + return; + + CMatRenderContextPtr pRenderContext( materials ); + + IMaterial *pProxy = m_drawQueries ? m_pDrawMaterial : m_pProxyMaterial; + pRenderContext->Bind( pProxy ); + + // BUGBUG: If you draw both queries, the measure query fails for some reason. + if ( r_pixelvisibility_partial.GetBool() && !m_drawQueries ) + { + pRenderContext->DepthRange( 0.0f, 0.01f ); + unsigned short node = m_setList.Head( m_activeSetsList ); + while( node != m_setList.InvalidIndex() ) + { + CPixelVisSet *pSet = &m_setList[node]; + unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() ); + if ( queryNode != m_queryList.InvalidIndex() ) + { + m_queryList[queryNode].IssueCountingQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace ); + } + node = m_setList.Next( node ); + } + pRenderContext->DepthRange( 0.0f, 1.0f ); + } + + { + unsigned short node = m_setList.Head( m_activeSetsList ); + while( node != m_setList.InvalidIndex() ) + { + CPixelVisSet *pSet = &m_setList[node]; + unsigned short queryNode = FindQueryForView( pSet, CurrentViewID() ); + if ( queryNode != m_queryList.InvalidIndex() ) + { + m_queryList[queryNode].IssueQuery( pRenderContext, pSet->proxySize, pSet->proxyAspect, pProxy, pSet->sizeIsScreenSpace ); + } + node = m_setList.Next( node ); + } + } +} + +void CPixelVisibilitySystem::EndScene() +{ + DeleteUnusedSets(false); +} + +unsigned short CPixelVisibilitySystem::FindQueryForView( CPixelVisSet *pSet, int viewID ) +{ + unsigned short node = m_queryList.Head( pSet->queryList ); + while ( node != m_queryList.InvalidIndex() ) + { + if ( m_queryList[node].IsForView( viewID ) ) + return node; + node = m_queryList.Next( node ); + } + return m_queryList.InvalidIndex(); +} +unsigned short CPixelVisibilitySystem::FindOrCreateQueryForView( CPixelVisSet *pSet, int viewID ) +{ + unsigned short node = FindQueryForView( pSet, viewID ); + if ( node != m_queryList.InvalidIndex() ) + return node; + + node = AllocQuery(); + m_queryList.LinkToHead( pSet->queryList, node ); + m_queryList[node].SetView( viewID ); + return node; +} + + +void CPixelVisibilitySystem::DeleteUnusedQueries( CPixelVisSet *pSet, bool bDeleteAll ) +{ + unsigned short node = m_queryList.Head( pSet->queryList ); + while ( node != m_queryList.InvalidIndex() ) + { + unsigned short next = m_queryList.Next( node ); + if ( bDeleteAll || !m_queryList[node].IsActive() ) + { + m_queryList.Unlink( pSet->queryList, node); + m_queryList.LinkToHead( m_freeQueriesList, node ); + } + node = next; + } +} +void CPixelVisibilitySystem::DeleteUnusedSets( bool bDeleteAll ) +{ + unsigned short node = m_setList.Head( m_activeSetsList ); + while ( node != m_setList.InvalidIndex() ) + { + unsigned short next = m_setList.Next( node ); + CPixelVisSet *pSet = &m_setList[node]; + if ( bDeleteAll || !m_setList[node].IsActive() ) + { + DeleteUnusedQueries( pSet, true ); + } + else + { + DeleteUnusedQueries( pSet, false ); + } + if ( m_queryList.Head(pSet->queryList) == m_queryList.InvalidIndex() ) + { + FreeSet( node ); + } + node = next; + } +} + +void CPixelVisibilitySystem::ShowQueries( bool show ) +{ + m_drawQueries = show; +} + +unsigned short CPixelVisibilitySystem::AllocQuery() +{ + unsigned short node = m_queryList.Head(m_freeQueriesList); + if ( node != m_queryList.InvalidIndex() ) + { + m_queryList.Unlink( m_freeQueriesList, node ); + m_queryList[node].ResetOcclusionQueries(); + } + else + { + node = m_queryList.Alloc(); + } + return node; +} + +unsigned short CPixelVisibilitySystem::AllocSet() +{ + unsigned short node = m_setList.Head(m_freeSetsList); + if ( node != m_setList.InvalidIndex() ) + { + m_setList.Unlink( m_freeSetsList, node ); + } + else + { + node = m_setList.Alloc(); + m_setList[node].queryList = m_queryList.CreateList(); + } + m_setList.LinkToHead( m_activeSetsList, node ); + return node; +} + +void CPixelVisibilitySystem::FreeSet( unsigned short node ) +{ + m_setList.Unlink( m_activeSetsList, node ); + m_setList.LinkToHead( m_freeSetsList, node ); + m_setList[node].serial++; +} + +CPixelVisSet *CPixelVisibilitySystem::FindOrCreatePixelVisSet( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) +{ + if ( queryHandle[0] ) + { + unsigned short handle = queryHandle[0] & 0xFFFF; + handle--; + unsigned short serial = queryHandle[0] >> 16; + if ( m_setList.IsValidIndex(handle) && m_setList[handle].serial == serial ) + { + return &m_setList[handle]; + } + } + + unsigned short node = AllocSet(); + m_setList[node].Init( params ); + unsigned int out = m_setList[node].serial; + unsigned short nodeHandle = node + 1; + out <<= 16; + out |= nodeHandle; + queryHandle[0] = out; + return &m_setList[node]; +} + + +void PixelvisDrawChanged( IConVar *pPixelvisVar, const char *pOld, float flOldValue ) +{ + ConVarRef var( pPixelvisVar ); + g_PixelVisibilitySystem.ShowQueries( var.GetBool() ); +} + +class CTraceFilterGlow : public CTraceFilterSimple +{ +public: + DECLARE_CLASS( CTraceFilterGlow, CTraceFilterSimple ); + + CTraceFilterGlow( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple(passentity, collisionGroup) {} + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity; + ICollideable *pCollide = pUnk->GetCollideable(); + if ( pCollide->GetSolid() != SOLID_VPHYSICS && pCollide->GetSolid() != SOLID_BSP ) + return false; + return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); + } +}; +float GlowSightDistance( const Vector &glowOrigin, bool bShouldTrace ) +{ + float dist = (glowOrigin - CurrentViewOrigin()).Length(); + C_BasePlayer *local = C_BasePlayer::GetLocalPlayer(); + if ( local ) + { + dist *= local->GetFOVDistanceAdjustFactor(); + } + + if ( bShouldTrace ) + { + Vector end = glowOrigin; + // HACKHACK: trace 4" from destination in case the glow is inside some parent object + // allow a little error... + if ( dist > 4 ) + { + end -= CurrentViewForward()*4; + } + int traceFlags = MASK_OPAQUE|CONTENTS_MONSTER|CONTENTS_DEBRIS; + + CTraceFilterGlow filter(NULL, COLLISION_GROUP_NONE); + trace_t tr; + UTIL_TraceLine( CurrentViewOrigin(), end, traceFlags, &filter, &tr ); + if ( tr.fraction != 1.0f ) + return -1; + } + + return dist; +} + +void PixelVisibility_EndCurrentView() +{ + g_PixelVisibilitySystem.EndView(); +} + +void PixelVisibility_EndScene() +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + g_PixelVisibilitySystem.EndScene(); +} + +float PixelVisibility_FractionVisible( const pixelvis_queryparams_t ¶ms, pixelvis_handle_t *queryHandle ) +{ + if ( !queryHandle ) + { + return GlowSightDistance( params.position, true ) > 0.0f ? 1.0f : 0.0f; + } + else + { + return g_PixelVisibilitySystem.GetFractionVisible( params, queryHandle ); + } +} + +bool PixelVisibility_IsAvailable() +{ + bool fastqueries = HasFastQueries(); + return r_dopixelvisibility.GetBool() && fastqueries && g_PixelVisibilitySystem.SupportsOcclusion(); +} + +//this originally called a class function of CPixelVisibiltySystem to keep the work clean, but that function needed friend access to CPixelVisibilityQuery +//and I didn't want to make the whole class a friend or shift all the functions and class declarations around in this file +void PixelVisibility_ShiftVisibilityViews( int iSourceViewID, int iDestViewID ) +{ + unsigned short node = g_PixelVisibilitySystem.m_setList.Head( g_PixelVisibilitySystem.m_activeSetsList ); + while ( node != g_PixelVisibilitySystem.m_setList.InvalidIndex() ) + { + unsigned short next = g_PixelVisibilitySystem.m_setList.Next( node ); + CPixelVisSet *pSet = &g_PixelVisibilitySystem.m_setList[node]; + + unsigned short iSourceQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iSourceViewID ); + unsigned short iDestQueryNode = g_PixelVisibilitySystem.FindQueryForView( pSet, iDestViewID ); + + if( iDestQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) + { + //delete the destination if found + g_PixelVisibilitySystem.m_queryList.Unlink( pSet->queryList, iDestQueryNode ); + g_PixelVisibilitySystem.m_queryList.LinkToHead( g_PixelVisibilitySystem.m_freeQueriesList, iDestQueryNode ); + + if ( g_PixelVisibilitySystem.m_queryList.Head(pSet->queryList) == g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) + { + g_PixelVisibilitySystem.FreeSet( node ); + } + } + + if( iSourceQueryNode != g_PixelVisibilitySystem.m_queryList.InvalidIndex() ) + { + //make the source believe it's the destination + g_PixelVisibilitySystem.m_queryList[iSourceQueryNode].m_viewID = iDestViewID; + } + + node = next; + } +} + +CON_COMMAND( pixelvis_debug, "Dump debug info" ) +{ + g_PixelVisibilitySystem.DebugInfo(); +} -- cgit v1.2.3