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.
331 lines
12 KiB
331 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Functionality to render a glowing outline around client renderable objects. |
|
// |
|
//=============================================================================== |
|
|
|
#include "cbase.h" |
|
#include "glow_outline_effect.h" |
|
#include "model_types.h" |
|
#include "shaderapi/ishaderapi.h" |
|
#include "materialsystem/imaterialvar.h" |
|
#include "materialsystem/itexture.h" |
|
#include "view_shared.h" |
|
#include "viewpostprocess.h" |
|
|
|
#define FULL_FRAME_TEXTURE "_rt_FullFrameFB" |
|
|
|
#ifdef GLOWS_ENABLE |
|
|
|
ConVar glow_outline_effect_enable( "glow_outline_effect_enable", "1", FCVAR_ARCHIVE, "Enable entity outline glow effects." ); |
|
ConVar glow_outline_effect_width( "glow_outline_width", "10.0f", FCVAR_CHEAT, "Width of glow outline effect in screen space." ); |
|
|
|
extern bool g_bDumpRenderTargets; // in viewpostprocess.cpp |
|
|
|
CGlowObjectManager g_GlowObjectManager; |
|
|
|
struct ShaderStencilState_t |
|
{ |
|
bool m_bEnable; |
|
StencilOperation_t m_FailOp; |
|
StencilOperation_t m_ZFailOp; |
|
StencilOperation_t m_PassOp; |
|
StencilComparisonFunction_t m_CompareFunc; |
|
int m_nReferenceValue; |
|
uint32 m_nTestMask; |
|
uint32 m_nWriteMask; |
|
|
|
ShaderStencilState_t() |
|
{ |
|
m_bEnable = false; |
|
m_PassOp = m_FailOp = m_ZFailOp = STENCILOPERATION_KEEP; |
|
m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; |
|
m_nReferenceValue = 0; |
|
m_nTestMask = m_nWriteMask = 0xFFFFFFFF; |
|
} |
|
|
|
void SetStencilState( CMatRenderContextPtr &pRenderContext ) |
|
{ |
|
pRenderContext->SetStencilEnable( m_bEnable ); |
|
pRenderContext->SetStencilFailOperation( m_FailOp ); |
|
pRenderContext->SetStencilZFailOperation( m_ZFailOp ); |
|
pRenderContext->SetStencilPassOperation( m_PassOp ); |
|
pRenderContext->SetStencilCompareFunction( m_CompareFunc ); |
|
pRenderContext->SetStencilReferenceValue( m_nReferenceValue ); |
|
pRenderContext->SetStencilTestMask( m_nTestMask ); |
|
pRenderContext->SetStencilWriteMask( m_nWriteMask ); |
|
} |
|
}; |
|
|
|
void CGlowObjectManager::RenderGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot ) |
|
{ |
|
if ( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() ) |
|
{ |
|
if ( glow_outline_effect_enable.GetBool() ) |
|
{ |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
int nX, nY, nWidth, nHeight; |
|
pRenderContext->GetViewport( nX, nY, nWidth, nHeight ); |
|
|
|
PIXEvent _pixEvent( pRenderContext, "EntityGlowEffects" ); |
|
ApplyEntityGlowEffects( pSetup, nSplitScreenSlot, pRenderContext, glow_outline_effect_width.GetFloat(), nX, nY, nWidth, nHeight ); |
|
} |
|
} |
|
} |
|
|
|
static void SetRenderTargetAndViewPort( ITexture *rt, int w, int h ) |
|
{ |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
pRenderContext->SetRenderTarget(rt); |
|
pRenderContext->Viewport(0,0,w,h); |
|
} |
|
|
|
void CGlowObjectManager::RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext ) |
|
{ |
|
//==========================================================================================// |
|
// This renders solid pixels with the correct coloring for each object that needs the glow. // |
|
// After this function returns, this image will then be blurred and added into the frame // |
|
// buffer with the objects stenciled out. // |
|
//==========================================================================================// |
|
pRenderContext->PushRenderTargetAndViewport(); |
|
|
|
// Save modulation color and blend |
|
Vector vOrigColor; |
|
render->GetColorModulation( vOrigColor.Base() ); |
|
float flOrigBlend = render->GetBlend(); |
|
|
|
// Get pointer to FullFrameFB |
|
ITexture *pRtFullFrame = NULL; |
|
pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET ); |
|
|
|
SetRenderTargetAndViewPort( pRtFullFrame, pSetup->width, pSetup->height ); |
|
|
|
pRenderContext->ClearColor3ub( 0, 0, 0 ); |
|
pRenderContext->ClearBuffers( true, false, false ); |
|
|
|
// Set override material for glow color |
|
IMaterial *pMatGlowColor = NULL; |
|
|
|
pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); |
|
g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); |
|
|
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = false; |
|
stencilState.m_nReferenceValue = 0; |
|
stencilState.m_nTestMask = 0xFF; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; |
|
stencilState.m_PassOp = STENCILOPERATION_KEEP; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_KEEP; |
|
|
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
//==================// |
|
// Draw the objects // |
|
//==================// |
|
for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) |
|
{ |
|
if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) |
|
continue; |
|
|
|
render->SetBlend( m_GlowObjectDefinitions[i].m_flGlowAlpha ); |
|
Vector vGlowColor = m_GlowObjectDefinitions[i].m_vGlowColor * m_GlowObjectDefinitions[i].m_flGlowAlpha; |
|
render->SetColorModulation( &vGlowColor[0] ); // This only sets rgb, not alpha |
|
|
|
m_GlowObjectDefinitions[i].DrawModel(); |
|
} |
|
|
|
if ( g_bDumpRenderTargets ) |
|
{ |
|
DumpTGAofRenderTarget( pSetup->width, pSetup->height, "GlowModels" ); |
|
} |
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
render->SetColorModulation( vOrigColor.Base() ); |
|
render->SetBlend( flOrigBlend ); |
|
|
|
ShaderStencilState_t stencilStateDisable; |
|
stencilStateDisable.m_bEnable = false; |
|
stencilStateDisable.SetStencilState( pRenderContext ); |
|
|
|
pRenderContext->PopRenderTargetAndViewport(); |
|
} |
|
|
|
void CGlowObjectManager::ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h ) |
|
{ |
|
//=======================================================// |
|
// Render objects into stencil buffer // |
|
//=======================================================// |
|
// Set override shader to the same simple shader we use to render the glow models |
|
IMaterial *pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true ); |
|
g_pStudioRender->ForcedMaterialOverride( pMatGlowColor ); |
|
|
|
ShaderStencilState_t stencilStateDisable; |
|
stencilStateDisable.m_bEnable = false; |
|
float flSavedBlend = render->GetBlend(); |
|
|
|
// Set alpha to 0 so we don't touch any color pixels |
|
render->SetBlend( 0.0f ); |
|
pRenderContext->OverrideDepthEnable( true, false ); |
|
|
|
int iNumGlowObjects = 0; |
|
|
|
for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) |
|
{ |
|
if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) |
|
continue; |
|
|
|
if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded || m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) |
|
{ |
|
if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) |
|
{ |
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = true; |
|
stencilState.m_nReferenceValue = 1; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; |
|
stencilState.m_PassOp = STENCILOPERATION_REPLACE; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; |
|
|
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
m_GlowObjectDefinitions[i].DrawModel(); |
|
} |
|
else if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded ) |
|
{ |
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = true; |
|
stencilState.m_nReferenceValue = 1; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; |
|
stencilState.m_PassOp = STENCILOPERATION_KEEP; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; |
|
|
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
m_GlowObjectDefinitions[i].DrawModel(); |
|
} |
|
else if ( m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) |
|
{ |
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = true; |
|
stencilState.m_nReferenceValue = 2; |
|
stencilState.m_nTestMask = 0x1; |
|
stencilState.m_nWriteMask = 0x3; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; |
|
stencilState.m_PassOp = STENCILOPERATION_INCRSAT; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_REPLACE; |
|
|
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
m_GlowObjectDefinitions[i].DrawModel(); |
|
} |
|
} |
|
|
|
iNumGlowObjects++; |
|
} |
|
|
|
// Need to do a 2nd pass to warm stencil for objects which are rendered only when occluded |
|
for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i ) |
|
{ |
|
if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) ) |
|
continue; |
|
|
|
if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && !m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded ) |
|
{ |
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = true; |
|
stencilState.m_nReferenceValue = 2; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_ALWAYS; |
|
stencilState.m_PassOp = STENCILOPERATION_REPLACE; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_KEEP; |
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
m_GlowObjectDefinitions[i].DrawModel(); |
|
} |
|
} |
|
|
|
pRenderContext->OverrideDepthEnable( false, false ); |
|
render->SetBlend( flSavedBlend ); |
|
stencilStateDisable.SetStencilState( pRenderContext ); |
|
g_pStudioRender->ForcedMaterialOverride( NULL ); |
|
|
|
// If there aren't any objects to glow, don't do all this other stuff |
|
// this fixes a bug where if there are glow objects in the list, but none of them are glowing, |
|
// the whole screen blooms. |
|
if ( iNumGlowObjects <= 0 ) |
|
return; |
|
|
|
//============================================= |
|
// Render the glow colors to _rt_FullFrameFB |
|
//============================================= |
|
{ |
|
PIXEvent pixEvent( pRenderContext, "RenderGlowModels" ); |
|
RenderGlowModels( pSetup, nSplitScreenSlot, pRenderContext ); |
|
} |
|
|
|
// Get viewport |
|
int nSrcWidth = pSetup->width; |
|
int nSrcHeight = pSetup->height; |
|
int nViewportX, nViewportY, nViewportWidth, nViewportHeight; |
|
pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight ); |
|
|
|
// Get material and texture pointers |
|
ITexture *pRtQuarterSize1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); |
|
|
|
{ |
|
//=======================================================================================================// |
|
// At this point, pRtQuarterSize0 is filled with the fully colored glow around everything as solid glowy // |
|
// blobs. Now we need to stencil out the original objects by only writing pixels that have no // |
|
// stencil bits set in the range we care about. // |
|
//=======================================================================================================// |
|
IMaterial *pMatHaloAddToScreen = materials->FindMaterial( "dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true ); |
|
|
|
// Do not fade the glows out at all (weight = 1.0) |
|
IMaterialVar *pDimVar = pMatHaloAddToScreen->FindVar( "$C0_X", NULL ); |
|
pDimVar->SetFloatValue( 1.0f ); |
|
|
|
// Set stencil state |
|
ShaderStencilState_t stencilState; |
|
stencilState.m_bEnable = true; |
|
stencilState.m_nWriteMask = 0x0; // We're not changing stencil |
|
stencilState.m_nTestMask = 0xFF; |
|
stencilState.m_nReferenceValue = 0x0; |
|
stencilState.m_CompareFunc = STENCILCOMPARISONFUNCTION_EQUAL; |
|
stencilState.m_PassOp = STENCILOPERATION_KEEP; |
|
stencilState.m_FailOp = STENCILOPERATION_KEEP; |
|
stencilState.m_ZFailOp = STENCILOPERATION_KEEP; |
|
stencilState.SetStencilState( pRenderContext ); |
|
|
|
// Draw quad |
|
pRenderContext->DrawScreenSpaceRectangle( pMatHaloAddToScreen, 0, 0, nViewportWidth, nViewportHeight, |
|
0.0f, -0.5f, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, |
|
pRtQuarterSize1->GetActualWidth(), |
|
pRtQuarterSize1->GetActualHeight() ); |
|
|
|
stencilStateDisable.SetStencilState( pRenderContext ); |
|
} |
|
} |
|
|
|
void CGlowObjectManager::GlowObjectDefinition_t::DrawModel() |
|
{ |
|
if ( m_hEntity.Get() ) |
|
{ |
|
m_hEntity->DrawModel( STUDIO_RENDER ); |
|
C_BaseEntity *pAttachment = m_hEntity->FirstMoveChild(); |
|
|
|
while ( pAttachment != NULL ) |
|
{ |
|
if ( !g_GlowObjectManager.HasGlowEffect( pAttachment ) && pAttachment->ShouldDraw() ) |
|
{ |
|
pAttachment->DrawModel( STUDIO_RENDER ); |
|
} |
|
pAttachment = pAttachment->NextMovePeer(); |
|
} |
|
} |
|
} |
|
|
|
#endif // GLOWS_ENABLE
|