source-engine/game/client/glow_overlay.cpp
2022-03-01 23:00:42 +03:00

580 lines
15 KiB
C++

//========= Copyright 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 "clienteffectprecachesystem.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"
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectGlow )
CLIENTEFFECT_MATERIAL( "sun/overlay" )
CLIENTEFFECT_MATERIAL( "sprites/light_glow02_add_noz" )
CLIENTEFFECT_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;
}
ConVar building_cubemaps( "building_cubemaps", "0" );
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.999f;
// 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