|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "view.h"
#include "iviewrender.h"
#include "c_sun.h"
#include "particles_simple.h"
#include "precache_register.h"
#include "c_pixel_visibility.h"
#include "glow_overlay.h"
#include "utllinkedlist.h"
#include "view_shared.h"
#include "tier0/vprof.h"
#include "materialsystem/imaterialvar.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheEffectGlow ) PRECACHE( MATERIAL, "sun/overlay" ) PRECACHE( MATERIAL, "sprites/light_glow02_add_noz" ) PRECACHE_REGISTER_END()
class CGlowOverlaySystem : public CAutoGameSystem { public: CGlowOverlaySystem() : CAutoGameSystem( "CGlowOverlaySystem" ) { } // Level init, shutdown
virtual void LevelInitPreEntity() {}
virtual void LevelShutdownPostEntity() { m_GlowOverlays.PurgeAndDeleteElements(); }
unsigned short AddToOverlayList( CGlowOverlay *pGlow ) { return m_GlowOverlays.AddToTail( pGlow ); } void RemoveFromOverlayList( unsigned short handle ) { if( handle != m_GlowOverlays.InvalidIndex() ) { m_GlowOverlays.Remove( handle ); } } CUtlLinkedList<CGlowOverlay*, unsigned short> m_GlowOverlays; }; CGlowOverlaySystem g_GlowOverlaySystem;
ConVar cl_ShowSunVectors( "cl_ShowSunVectors", "0", 0 );
ConVar cl_sun_decay_rate( "cl_sun_decay_rate", "0.05", FCVAR_CHEAT );
// Dot product space the overlays are drawn in.
// Here it's setup to allow you to see it if you're looking within 40 degrees of the source.
float g_flOverlayRange = cos( DEG2RAD( 40 ) );
// ----------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------- //
void Do2DRotation( Vector vIn, Vector &vOut, float flDegrees, int i1, int i2, int i3 ) { float c, s; SinCos( DEG2RAD( flDegrees ), &s, &c );
vOut[i1] = vIn[i1]*c - vIn[i2]*s; vOut[i2] = vIn[i1]*s + vIn[i2]*c; vOut[i3] = vIn[i3]; }
// ----------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------- //
CGlowOverlay::CGlowOverlay() { m_ListIndex = 0xFFFF; m_nSprites = 0; m_flGlowObstructionScale = 0.0f; m_bDirectional = false; m_bInSky = false; m_skyObstructionScale = 1.0f; m_bActivated = false;
m_flProxyRadius = 2.0f;
m_queryHandle = 0;
m_flHDRColorScale = 1.0f;
//Init our sprites
for ( int i = 0; i < MAX_SUN_LAYERS; i++ ) { m_Sprites[i].m_vColor.Init(); m_Sprites[i].m_flHorzSize = 1.0f; m_Sprites[i].m_flVertSize = 1.0f; m_Sprites[i].m_pMaterial = NULL; }
#ifdef PORTAL
for( int i = 0; i != MAX_PORTAL_RECURSIVE_VIEWS; ++i ) { m_skyObstructionScaleBackups[i] = 1.0f; } #endif
}
CGlowOverlay::~CGlowOverlay() { g_GlowOverlaySystem.RemoveFromOverlayList( m_ListIndex ); }
bool CGlowOverlay::Update() { return true; }
void BuildingCubemapsChanged( IConVar *var, const char *pOldValue, float flOldValue ) { C_BaseEntity::UpdateVisibilityAllEntities(); }
ConVar building_cubemaps( "building_cubemaps", "0", FCVAR_CHEAT, "Indicates we're building cubemaps", BuildingCubemapsChanged );
float CGlowOverlay::CalcGlowAspect() { if ( m_nSprites ) { if ( m_Sprites[0].m_flHorzSize != 0 && m_Sprites[0].m_flVertSize != 0 ) return m_Sprites[0].m_flHorzSize / m_Sprites[0].m_flVertSize; } return 1.0f; }
void CGlowOverlay::UpdateSkyGlowObstruction( float zFar, bool bCacheFullSceneState ) { Assert( m_bInSky );
// If we already cached the sky obstruction and are still using that, early-out
if ( bCacheFullSceneState && m_bCacheSkyObstruction ) return;
// Turning on sky obstruction caching mode
if ( bCacheFullSceneState && !m_bCacheSkyObstruction ) { m_bCacheSkyObstruction = true; }
// Turning off sky obstruction caching mode
if ( !bCacheFullSceneState && m_bCacheSkyObstruction ) { m_bCacheSkyObstruction = false; }
if ( PixelVisibility_IsAvailable() ) { // Trace a ray at the object.
Vector pos = CurrentViewOrigin() + m_vDirection * zFar * 0.99f;
// UNDONE: Can probably do only the pixelvis query in this case if you can figure out where
// to put it - or save the position of this trace
pixelvis_queryparams_t params; params.Init( pos, m_flProxyRadius ); params.bSizeInScreenspace = true; m_skyObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle ); return; } // Trace a ray at the object.
trace_t trace; UTIL_TraceLine( CurrentViewOrigin(), CurrentViewOrigin() + (m_vDirection*MAX_TRACE_LENGTH), CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace ); // back the trace with a pixel query to occlude with models
if ( trace.surface.flags & SURF_SKY ) { m_skyObstructionScale = 1.0f; } else { m_skyObstructionScale = 0.0f; } }
void CGlowOverlay::UpdateGlowObstruction( const Vector &vToGlow, bool bCacheFullSceneState ) { // If we already cached the glow obstruction and are still using that, early-out
if ( bCacheFullSceneState && m_bCacheGlowObstruction ) return; if ( bCacheFullSceneState && !m_bCacheGlowObstruction ) // If turning on sky obstruction caching mode
{ m_bCacheGlowObstruction = true; }
if ( !bCacheFullSceneState && m_bCacheGlowObstruction ) { m_bCacheGlowObstruction = false; }
if ( PixelVisibility_IsAvailable() ) { if ( m_bInSky ) { const CViewSetup *pViewSetup = view->GetViewSetup(); Vector pos = CurrentViewOrigin() + m_vDirection * (pViewSetup->zFar * 0.999f); pixelvis_queryparams_t params; params.Init( pos, m_flProxyRadius, CalcGlowAspect() ); params.bSizeInScreenspace = true; // use a pixel query to occlude with models
m_flGlowObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle ) * m_skyObstructionScale; } else { // If it's not in the sky, then we need a valid position or else we don't
// know what's in front of it.
Assert( !m_bDirectional );
pixelvis_queryparams_t params; params.Init( m_vPos, m_flProxyRadius, CalcGlowAspect() );
m_flGlowObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle ); } return; }
bool bFade = false; if ( m_bInSky ) { // Trace a ray at the object.
trace_t trace; UTIL_TraceLine( CurrentViewOrigin(), CurrentViewOrigin() + (vToGlow*MAX_TRACE_LENGTH), CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace ); bFade = (trace.fraction < 1 && !(trace.surface.flags & SURF_SKY)); } else { // If it's not in the sky, then we need a valid position or else we don't
// know what's in front of it.
Assert( !m_bDirectional );
pixelvis_queryparams_t params; params.Init( m_vPos, m_flProxyRadius );
bFade = PixelVisibility_FractionVisible( params, &m_queryHandle ) < 1.0f ? true : false;
}
if ( bFade ) { if ( building_cubemaps.GetBool() ) { m_flGlowObstructionScale = 0.0f; } else { m_flGlowObstructionScale -= gpGlobals->frametime / cl_sun_decay_rate.GetFloat(); m_flGlowObstructionScale = MAX( m_flGlowObstructionScale, 0.0f ); } } else { if ( building_cubemaps.GetBool() ) { m_flGlowObstructionScale = 1.0f; } else { m_flGlowObstructionScale += gpGlobals->frametime / cl_sun_decay_rate.GetFloat(); m_flGlowObstructionScale = MIN( m_flGlowObstructionScale, 1.0f ); } } }
void CGlowOverlay::CalcSpriteColorAndSize( float flDot, CGlowSprite *pSprite, float *flHorzSize, float *flVertSize, Vector *vColor ) { // The overlay is largest and completely translucent at g_flOverlayRange.
// When the dot product is 1, then it's smaller and more opaque.
const float flSizeAtOverlayRangeMul = 150; const float flSizeAtOneMul = 70; const float flOpacityAtOverlayRange = 0; const float flOpacityAtOne = 1;
// Figure out how big and how opaque it will be.
*flHorzSize = RemapValClamped( flDot, g_flOverlayRange, 1, flSizeAtOverlayRangeMul * pSprite->m_flHorzSize, flSizeAtOneMul * pSprite->m_flHorzSize );
*flVertSize = RemapValClamped( flDot, g_flOverlayRange, 1, flSizeAtOverlayRangeMul * pSprite->m_flVertSize, flSizeAtOneMul * pSprite->m_flVertSize ); float flOpacity = RemapValClamped( flDot, g_flOverlayRange, 1, flOpacityAtOverlayRange, flOpacityAtOne );
flOpacity = flOpacity * m_flGlowObstructionScale; *vColor = pSprite->m_vColor * flOpacity; }
void CGlowOverlay::CalcBasis( const Vector &vToGlow, float flHorzSize, float flVertSize, Vector &vBasePt, Vector &vUp, Vector &vRight ) { const float flOverlayDist = 100; vBasePt = CurrentViewOrigin() + vToGlow * flOverlayDist; vUp.Init( 0, 0, 1 ); vRight = vToGlow.Cross( vUp ); VectorNormalize( vRight );
vUp = vRight.Cross( vToGlow ); VectorNormalize( vUp );
vRight *= flHorzSize; vUp *= flVertSize; }
void CGlowOverlay::Draw( bool bCacheFullSceneState ) { extern ConVar r_drawsprites; if( !r_drawsprites.GetBool() ) return; // Get the vector to the sun.
Vector vToGlow; if( m_bDirectional ) vToGlow = m_vDirection; else vToGlow = m_vPos - CurrentViewOrigin();
VectorNormalize( vToGlow );
float flDot = vToGlow.Dot( CurrentViewForward() );
UpdateGlowObstruction( vToGlow, bCacheFullSceneState ); if( m_flGlowObstructionScale == 0 ) return; bool bWireframe = ShouldDrawInWireFrameMode() || (r_drawsprites.GetInt() == 2); CMatRenderContextPtr pRenderContext( materials );
for( int iSprite=0; iSprite < m_nSprites; iSprite++ ) { CGlowSprite *pSprite = &m_Sprites[iSprite]; // Figure out the color and size to draw it.
float flHorzSize, flVertSize; Vector vColor; CalcSpriteColorAndSize( flDot, pSprite, &flHorzSize, &flVertSize, &vColor ); // If we're alpha'd out, then don't bother
if ( vColor.LengthSqr() < 0.00001f ) continue; // Setup the basis to draw the sprite.
Vector vBasePt, vUp, vRight; CalcBasis( vToGlow, flHorzSize, flVertSize, vBasePt, vUp, vRight );
//Get our diagonal radius
float radius = (vRight+vUp).Length(); if ( R_CullSphere( view->GetFrustum(), 5, &vBasePt, radius ) ) continue;
// Get our material (deferred default load)
if ( m_Sprites[iSprite].m_pMaterial == NULL ) { m_Sprites[iSprite].m_pMaterial = materials->FindMaterial( "sprites/light_glow02_add_noz", TEXTURE_GROUP_CLIENT_EFFECTS ); }
Assert( m_Sprites[iSprite].m_pMaterial ); static unsigned int nHDRColorScaleCache = 0; IMaterialVar *pHDRColorScaleVar = m_Sprites[iSprite].m_pMaterial->FindVarFast( "$hdrcolorscale", &nHDRColorScaleCache ); if( pHDRColorScaleVar ) { pHDRColorScaleVar->SetFloatValue( m_flHDRColorScale ); }
// Draw the sprite.
IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, m_Sprites[iSprite].m_pMaterial );
CMeshBuilder builder; builder.Begin( pMesh, MATERIAL_QUADS, 1 ); Vector vPt; vPt = vBasePt - vRight + vUp; builder.Position3fv( vPt.Base() ); builder.Color4f( VectorExpand(vColor), 1 ); builder.TexCoord2f( 0, 0, 1 ); builder.AdvanceVertex(); vPt = vBasePt + vRight + vUp; builder.Position3fv( vPt.Base() ); builder.Color4f( VectorExpand(vColor), 1 ); builder.TexCoord2f( 0, 1, 1 ); builder.AdvanceVertex(); vPt = vBasePt + vRight - vUp; builder.Position3fv( vPt.Base() ); builder.Color4f( VectorExpand(vColor), 1 ); builder.TexCoord2f( 0, 1, 0 ); builder.AdvanceVertex(); vPt = vBasePt - vRight - vUp; builder.Position3fv( vPt.Base() ); builder.Color4f( VectorExpand(vColor), 1 ); builder.TexCoord2f( 0, 0, 0 ); builder.AdvanceVertex(); builder.End( false, true );
if( bWireframe ) { IMaterial *pWireframeMaterial = materials->FindMaterial( "debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER ); pRenderContext->Bind( pWireframeMaterial ); // Draw the sprite.
IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, pWireframeMaterial ); CMeshBuilder builder; builder.Begin( pMesh, MATERIAL_QUADS, 1 ); Vector vPt; vPt = vBasePt - vRight + vUp; builder.Position3fv( vPt.Base() ); builder.Color3f( 1.0f, 0.0f, 0.0f ); builder.AdvanceVertex(); vPt = vBasePt + vRight + vUp; builder.Position3fv( vPt.Base() ); builder.Color3f( 1.0f, 0.0f, 0.0f ); builder.AdvanceVertex(); vPt = vBasePt + vRight - vUp; builder.Position3fv( vPt.Base() ); builder.Color3f( 1.0f, 0.0f, 0.0f ); builder.AdvanceVertex(); vPt = vBasePt - vRight - vUp; builder.Position3fv( vPt.Base() ); builder.Color3f( 1.0f, 0.0f, 0.0f ); builder.AdvanceVertex(); builder.End( false, true ); } } }
void CGlowOverlay::Activate() { m_bActivated = true; if( m_ListIndex == 0xFFFF ) { m_ListIndex = g_GlowOverlaySystem.AddToOverlayList( this ); } }
void CGlowOverlay::Deactivate() { m_bActivated = false; }
void CGlowOverlay::DrawOverlays( bool bCacheFullSceneState ) { VPROF("CGlowOverlay::DrawOverlays()");
CMatRenderContextPtr pRenderContext( materials );
bool bClippingEnabled = pRenderContext->EnableClipping( true );
unsigned short iNext; for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext ) { iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i ); CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i]; if( !pOverlay->m_bActivated ) continue;
if( pOverlay->Update() ) { pRenderContext->EnableClipping( ((pOverlay->m_bInSky) ? (false):(bClippingEnabled)) ); //disable clipping in skybox, restore clipping to pre-existing state when not in skybox (it may be off as well)
pOverlay->Draw( bCacheFullSceneState ); } else { delete pOverlay; } }
pRenderContext->EnableClipping( bClippingEnabled ); //restore clipping to original state
}
void CGlowOverlay::UpdateSkyOverlays( float zFar, bool bCacheFullSceneState ) { unsigned short iNext; for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext ) { iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i ); CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i]; if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky ) continue;
pOverlay->UpdateSkyGlowObstruction( zFar, bCacheFullSceneState ); } }
#ifdef PORTAL
void CGlowOverlay::BackupSkyOverlayData( int iBackupToSlot ) { unsigned short iNext; for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext ) { iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i ); CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky ) continue;
pOverlay->m_skyObstructionScaleBackups[iBackupToSlot] = pOverlay->m_skyObstructionScale; } }
void CGlowOverlay::RestoreSkyOverlayData( int iRestoreFromSlot ) { unsigned short iNext; for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext ) { iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i ); CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky ) continue;
pOverlay->m_skyObstructionScale = pOverlay->m_skyObstructionScaleBackups[iRestoreFromSlot]; } }
#endif //#ifdef PORTAL
|