846 lines
32 KiB
C++
846 lines
32 KiB
C++
//============ Copyright (c) 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 "view_shared.h"
|
|
#include "c_cs_player.h"
|
|
#include "tier2/renderutils.h"
|
|
|
|
#define FULL_FRAME_TEXTURE "_rt_FullFrameFB"
|
|
|
|
#define GLOWBOX_PASS_COLOR 0
|
|
#define GLOWBOX_PASS_STENCIL 1
|
|
#define GLOW_PULSE_DURATION 0.2f
|
|
|
|
ConVar glow_outline_effect_enable( "glow_outline_effect_enable", "1", FCVAR_CHEAT, "Enable entity outline glow effects." );
|
|
ConVar glow_outline_effect_width( "glow_outline_width", "6.0f", FCVAR_CHEAT, "Width of glow outline effect in screen space." );
|
|
|
|
ConVar glow_muzzle_debug( "glow_muzzle_debug", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show muzzle glow shapes outside of the glow pass." );
|
|
|
|
|
|
CGlowObjectManager &GlowObjectManager()
|
|
{
|
|
static CGlowObjectManager s_GlowObjectManager;
|
|
return s_GlowObjectManager;
|
|
}
|
|
|
|
void CGlowObjectManager::RenderGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot )
|
|
{
|
|
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::RenderGlowBoxes( int iPass, CMatRenderContextPtr &pRenderContext )
|
|
{
|
|
for ( int n = m_GlowBoxDefinitions.Count() - 1; n >= 0 ; n-- )
|
|
{
|
|
if ( m_GlowBoxDefinitions[n].m_flTerminationTimeIndex < gpGlobals->curtime )
|
|
{
|
|
m_GlowBoxDefinitions.FastRemove(n);
|
|
}
|
|
else
|
|
{
|
|
float flLifeLeft = (m_GlowBoxDefinitions[n].m_flTerminationTimeIndex - gpGlobals->curtime) / (m_GlowBoxDefinitions[n].m_flTerminationTimeIndex - m_GlowBoxDefinitions[n].m_flBirthTimeIndex);
|
|
if ( flLifeLeft > 0.95 )
|
|
flLifeLeft = (0.05f - ( flLifeLeft - 0.95f )) / 0.05f; //fade in the first 5% of lifetime
|
|
else
|
|
flLifeLeft = MIN( flLifeLeft * 4.0f, 1.0f ); //fade out the last 25% of lifetime
|
|
|
|
m_GlowBoxDefinitions[n].m_colColor[3] = flLifeLeft * 255;
|
|
|
|
if ( iPass == GLOWBOX_PASS_COLOR )
|
|
{
|
|
Vector vecForward;
|
|
AngleVectors( m_GlowBoxDefinitions[n].m_angOrientation, &vecForward );
|
|
Vector vecLineEnd = m_GlowBoxDefinitions[n].m_vPosition + ( vecForward * m_GlowBoxDefinitions[n].m_vMaxs.x );
|
|
|
|
RenderLine( m_GlowBoxDefinitions[n].m_vPosition, vecLineEnd, m_GlowBoxDefinitions[n].m_colColor, false );
|
|
}
|
|
else if ( iPass == GLOWBOX_PASS_STENCIL )
|
|
{
|
|
|
|
ShaderStencilState_t stencilState;
|
|
stencilState.m_bEnable = true;
|
|
stencilState.m_nReferenceValue = 1;
|
|
stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
RenderBox( m_GlowBoxDefinitions[n].m_vPosition, m_GlowBoxDefinitions[n].m_angOrientation, m_GlowBoxDefinitions[n].m_vMins, m_GlowBoxDefinitions[n].m_vMaxs, m_GlowBoxDefinitions[n].m_colColor, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// *** Keep in sync with matsys_interface.cpp, where the texture is declared ***
|
|
// Resolution for glow target chosen to be the largest that we can fit in EDRAM after 720p color/depth textures.
|
|
#define GLOW_360_RT_WIDTH ( MIN( 1120, pSetup->width ) )
|
|
#define GLOW_360_RT_HEIGHT ( MIN( 624, pSetup->height ) )
|
|
|
|
void CGlowObjectManager::RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, CUtlVector<GlowObjectDefinition_t> &vecGlowObjects )
|
|
{
|
|
//==========================================================================================//
|
|
// 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();
|
|
|
|
ITexture *pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET );
|
|
|
|
if ( IsX360() )
|
|
{
|
|
ITexture *pRtGlowTexture360 = materials->FindTexture( "_rt_Glows360", TEXTURE_GROUP_RENDER_TARGET );
|
|
|
|
SetRenderTargetAndViewPort( pRtGlowTexture360, GLOW_360_RT_WIDTH, GLOW_360_RT_HEIGHT );
|
|
}
|
|
else
|
|
{
|
|
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 );
|
|
|
|
//==================//
|
|
// Draw the objects //
|
|
//==================//
|
|
for ( int i = 0; i < vecGlowObjects.Count(); ++ i )
|
|
{
|
|
if ( vecGlowObjects[i].IsUnused() || !vecGlowObjects[i].ShouldDraw( nSplitScreenSlot ) || vecGlowObjects[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
|
|
continue;
|
|
|
|
g_pStudioRender->ForcedMaterialOverride( pMatGlowColor );
|
|
|
|
if ( vecGlowObjects[i].m_bFullBloomRender )
|
|
{
|
|
|
|
// Disabled because stencil test on off-screen buffers doesn't work with MSAA on.
|
|
// Also, the normal model render does not seem to work on the off-screen buffer
|
|
|
|
//g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
|
|
// ShaderStencilState_t stencilState;
|
|
// stencilState.m_bEnable = true;
|
|
// stencilState.m_nReferenceValue = vecGlowObjects[i].m_nFullBloomStencilTestValue;
|
|
// stencilState.m_nTestMask = 0xFF;
|
|
// stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
|
|
// stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
// stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
// stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|
//
|
|
// pRenderContext->SetStencilState( stencilState );
|
|
}
|
|
else
|
|
{
|
|
|
|
// Disabled because stencil test on off-screen buffers doesn't work with MSAA on
|
|
// Most features still work, but some (e.g. partial occlusion) don't
|
|
// ShaderStencilState_t stencilState;
|
|
// stencilState.m_bEnable = true;
|
|
// stencilState.m_nReferenceValue = 1;
|
|
// stencilState.m_nTestMask = 0x1;
|
|
// stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
|
|
// stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
// stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
// stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|
//
|
|
// pRenderContext->SetStencilState( stencilState );
|
|
}
|
|
|
|
render->SetBlend( vecGlowObjects[i].m_flGlowAlpha );
|
|
Vector vGlowColor = vecGlowObjects[i].m_vGlowColor * vecGlowObjects[i].m_flGlowAlpha;
|
|
|
|
// if pulse overdrive is non-zero, add its contribution to render alpha
|
|
if ( vecGlowObjects[i].m_flGlowPulseOverdrive > 0 )
|
|
{
|
|
render->SetBlend( vecGlowObjects[i].m_flGlowAlpha + vecGlowObjects[i].m_flGlowPulseOverdrive );
|
|
}
|
|
|
|
// if set, cap glow alpha according to alpha of the non-glowing entity
|
|
float flRenderAlpha = (float)vecGlowObjects[i].m_pEntity->GetRenderAlpha() * 0.00392;
|
|
if ( vecGlowObjects[i].m_bGlowAlphaCappedByRenderAlpha && vecGlowObjects[i].m_flGlowAlpha > flRenderAlpha )
|
|
{
|
|
render->SetBlend( flRenderAlpha );
|
|
vGlowColor *= flRenderAlpha;
|
|
}
|
|
|
|
// if set, alpha is multiplied by the ratio of entity velocity over the given maximum (e.g. faster = more opaque)
|
|
if ( vecGlowObjects[i].m_flGlowAlphaFunctionOfMaxVelocity > 0.0f )
|
|
{
|
|
float flVelocityToAlpha = (vecGlowObjects[i].m_pEntity->GetAbsVelocity().Length() / vecGlowObjects[i].m_flGlowAlphaFunctionOfMaxVelocity);
|
|
render->SetBlend( flVelocityToAlpha );
|
|
vGlowColor *= flVelocityToAlpha;
|
|
}
|
|
|
|
// if set, cap cumulative glow alpha to a maximum value
|
|
if ( render->GetBlend() > 0 && vecGlowObjects[i].m_flGlowAlphaMax < 1.0f )
|
|
{
|
|
float flCappedAlpha = MIN( render->GetBlend(), vecGlowObjects[i].m_flGlowAlphaMax );
|
|
render->SetBlend( flCappedAlpha );
|
|
vGlowColor *= flCappedAlpha;
|
|
}
|
|
|
|
render->SetColorModulation( &vGlowColor[0] ); // This only sets rgb, not alpha
|
|
|
|
vecGlowObjects[i].DrawModel();
|
|
|
|
// align and render the glow-only muzzle flash model for glowing weapon fire
|
|
if ( vecGlowObjects[i].m_flGlowPulseOverdrive >= 0.25f )
|
|
{
|
|
C_CSPlayer* localPlayer = GetLocalOrInEyeCSPlayer();
|
|
C_CSPlayer* tempPlayer = ToCSPlayer( vecGlowObjects[i].m_pEntity );
|
|
if ( tempPlayer && localPlayer && (localPlayer->GetAbsOrigin() - tempPlayer->GetAbsOrigin()).Length() > 20 )
|
|
{
|
|
CWeaponCSBase* tempWeapon = tempPlayer->GetActiveCSWeapon();
|
|
if ( tempWeapon )
|
|
{
|
|
//move muzzle flash shape to muzzle location
|
|
if ( tempPlayer->m_hMuzzleFlashShape && !(tempWeapon->HasSilencer() && tempWeapon->IsSilenced()) )
|
|
{
|
|
tempPlayer->m_hMuzzleFlashShape->SetAbsOrigin( tempPlayer->m_vecLastMuzzleFlashPos );
|
|
tempPlayer->m_hMuzzleFlashShape->SetAbsAngles( tempPlayer->m_angLastMuzzleFlashAngle );
|
|
|
|
//pick a random flash shape
|
|
//tempPlayer->m_hMuzzleFlashShape->SetBodygroup(0, RandomInt( 0, tempPlayer->m_hMuzzleFlashShape->GetNumBodyGroups()-1 ));
|
|
|
|
//unhide and render the muzzle flash shape
|
|
tempPlayer->m_hMuzzleFlashShape->RemoveEffects( EF_NODRAW );
|
|
RenderableInstance_t instance;
|
|
instance.m_nAlpha = (uint8)( vecGlowObjects[i].m_flGlowAlpha * 255.0f );
|
|
tempPlayer->m_hMuzzleFlashShape->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
|
|
if ( glow_muzzle_debug.GetInt() == 0 )
|
|
tempPlayer->m_hMuzzleFlashShape->AddEffects( EF_NODRAW );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// dampen overdrive here. Do this at the end, otherwise our framerate may be low enough that we don't see the effect for even one frame
|
|
if ( vecGlowObjects[i].m_flGlowPulseOverdrive > 0 )
|
|
{
|
|
vecGlowObjects[i].m_flGlowPulseOverdrive -= MAX(0, ( vecGlowObjects[i].m_flGlowPulseOverdrive * ( gpGlobals->frametime / GLOW_PULSE_DURATION ) ) ); //return to default over 1/5th a second
|
|
}
|
|
|
|
}
|
|
|
|
RenderGlowBoxes(GLOWBOX_PASS_COLOR, pRenderContext);
|
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
render->SetColorModulation( vOrigColor.Base() );
|
|
render->SetBlend( flOrigBlend );
|
|
|
|
ShaderStencilState_t stencilStateDisable;
|
|
stencilStateDisable.m_bEnable = false;
|
|
pRenderContext->SetStencilState( stencilStateDisable );
|
|
|
|
if ( IsX360() )
|
|
{
|
|
Rect_t rect;
|
|
rect.x = rect.y = 0;
|
|
rect.width = GLOW_360_RT_WIDTH;
|
|
rect.height = GLOW_360_RT_HEIGHT;
|
|
|
|
pRenderContext->CopyRenderTargetToTextureEx( pRtFullFrame, 0, &rect, &rect );
|
|
}
|
|
|
|
pRenderContext->PopRenderTargetAndViewport();
|
|
}
|
|
|
|
void CGlowObjectManager::DownSampleAndBlurRT( const CViewSetup *pSetup, CMatRenderContextPtr &pRenderContext, float flBloomScale, ITexture *pRtFullFrame, ITexture *pRtQuarterSize0, ITexture *pRtQuarterSize1 )
|
|
{
|
|
static bool s_bFirstPass = true;
|
|
|
|
//===================================
|
|
// Setup state for downsample/bloom
|
|
//===================================
|
|
|
|
#if defined( _X360 )
|
|
pRenderContext->PushVertexShaderGPRAllocation( 16 ); // Max out pixel shader threads
|
|
#endif
|
|
|
|
pRenderContext->PushRenderTargetAndViewport();
|
|
|
|
// 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
|
|
IMaterial *pMatDownsample = materials->FindMaterial( "dev/glow_downsample", TEXTURE_GROUP_OTHER, true);
|
|
IMaterial *pMatBlurX = materials->FindMaterial( "dev/glow_blur_x", TEXTURE_GROUP_OTHER, true );
|
|
IMaterial *pMatBlurY = materials->FindMaterial( "dev/glow_blur_y", TEXTURE_GROUP_OTHER, true );
|
|
|
|
|
|
|
|
//============================================
|
|
// Downsample _rt_FullFrameFB to _rt_SmallFB0
|
|
//============================================
|
|
|
|
// First clear the full target to black if we're not going to touch every pixel
|
|
if ( ( pRtQuarterSize0->GetActualWidth() != ( pSetup->width / 4 ) ) || ( pRtQuarterSize0->GetActualHeight() != ( pSetup->height / 4 ) ) )
|
|
{
|
|
SetRenderTargetAndViewPort( pRtQuarterSize0, pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
|
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|
pRenderContext->ClearBuffers( true, false, false );
|
|
}
|
|
|
|
// Set the viewport
|
|
SetRenderTargetAndViewPort( pRtQuarterSize0, pSetup->width / 4, pSetup->height / 4 );
|
|
|
|
IMaterialVar *pbloomexpvar = pMatDownsample->FindVar( "$bloomexp", 0 );
|
|
if ( pbloomexpvar != NULL )
|
|
{
|
|
pbloomexpvar->SetFloatValue( 2.5f );
|
|
}
|
|
|
|
IMaterialVar *pbloomsaturationvar = pMatDownsample->FindVar( "$bloomsaturation", 0 );
|
|
if ( pbloomsaturationvar != NULL )
|
|
{
|
|
pbloomsaturationvar->SetFloatValue( 1.0f );
|
|
}
|
|
|
|
// note the -2's below. Thats because we are downsampling on each axis and the shader
|
|
// accesses pixels on both sides of the source coord
|
|
int nFullFbWidth = nSrcWidth;
|
|
int nFullFbHeight = nSrcHeight;
|
|
if ( IsX360() )
|
|
{
|
|
nFullFbWidth = GLOW_360_RT_WIDTH;
|
|
nFullFbHeight = GLOW_360_RT_HEIGHT;
|
|
}
|
|
pRenderContext->DrawScreenSpaceRectangle( pMatDownsample, 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|
0, 0, nFullFbWidth - 4, nFullFbHeight - 4,
|
|
pRtFullFrame->GetActualWidth(), pRtFullFrame->GetActualHeight() );
|
|
|
|
if ( IsX360() )
|
|
{
|
|
// Need to reset viewport to full size so we can also copy the cleared black pixels around the border
|
|
SetRenderTargetAndViewPort( pRtQuarterSize0, pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
|
|
pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize0, 0, NULL, NULL );
|
|
}
|
|
|
|
//============================//
|
|
// Guassian blur x rt0 to rt1 //
|
|
//============================//
|
|
|
|
// First clear the full target to black if we're not going to touch every pixel
|
|
if ( s_bFirstPass || ( pRtQuarterSize1->GetActualWidth() != ( pSetup->width / 4 ) ) || ( pRtQuarterSize1->GetActualHeight() != ( pSetup->height / 4 ) ) )
|
|
{
|
|
// On the first render, this viewport may require clearing
|
|
s_bFirstPass = false;
|
|
SetRenderTargetAndViewPort( pRtQuarterSize1, pRtQuarterSize1->GetActualWidth(), pRtQuarterSize1->GetActualHeight() );
|
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|
pRenderContext->ClearBuffers( true, false, false );
|
|
}
|
|
|
|
// Set the viewport
|
|
SetRenderTargetAndViewPort( pRtQuarterSize1, pSetup->width / 4, pSetup->height / 4 );
|
|
|
|
pRenderContext->DrawScreenSpaceRectangle( pMatBlurX, 0, 0, nSrcWidth/4, nSrcHeight/4,
|
|
0, 0, nSrcWidth/4-1, nSrcHeight/4-1,
|
|
pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
|
|
|
|
if ( IsX360() )
|
|
{
|
|
pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize1, 0, NULL, NULL );
|
|
}
|
|
|
|
//============================//
|
|
// Gaussian blur y rt1 to rt0 //
|
|
//============================//
|
|
SetRenderTargetAndViewPort( pRtQuarterSize0, pSetup->width / 4, pSetup->height / 4 );
|
|
IMaterialVar *pBloomAmountVar = pMatBlurY->FindVar( "$bloomamount", NULL );
|
|
pBloomAmountVar->SetFloatValue( flBloomScale );
|
|
pRenderContext->DrawScreenSpaceRectangle( pMatBlurY, 0, 0, nSrcWidth / 4, nSrcHeight / 4,
|
|
0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
|
|
pRtQuarterSize1->GetActualWidth(), pRtQuarterSize1->GetActualHeight() );
|
|
|
|
if ( IsX360() )
|
|
{
|
|
pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize1, 0, NULL, NULL ); // copy to rt1 instead of rt0 because rt1 has linear reads enabled and works more easily with screenspace_general to fix 360 bloom issues
|
|
}
|
|
|
|
// Pop RT
|
|
pRenderContext->PopRenderTargetAndViewport();
|
|
}
|
|
|
|
void CGlowObjectManager::ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h )
|
|
{
|
|
|
|
|
|
// gather up special glow styles
|
|
|
|
CUtlVector<GlowObjectDefinition_t> vecGlowObjectsRimGlow3DStyle;
|
|
vecGlowObjectsRimGlow3DStyle.RemoveAll();
|
|
|
|
CUtlVector<GlowObjectDefinition_t> vecGlowObjectsEdgeHighlightStyle;
|
|
vecGlowObjectsEdgeHighlightStyle.RemoveAll();
|
|
|
|
FOR_EACH_VEC_BACK( m_GlowObjectDefinitions, i )
|
|
{
|
|
if ( !m_GlowObjectDefinitions[i].IsUnused() && m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) )
|
|
{
|
|
if ( m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_RIMGLOW3D )
|
|
{
|
|
vecGlowObjectsRimGlow3DStyle.AddToTail( m_GlowObjectDefinitions[i] );
|
|
}
|
|
else if ( m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT || m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT_PULSE )
|
|
{
|
|
vecGlowObjectsEdgeHighlightStyle.AddToTail( m_GlowObjectDefinitions[i] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ( vecGlowObjectsRimGlow3DStyle.Count() )
|
|
{
|
|
//todo: expose pulse width and frequency parameters
|
|
float flPulse = 0.5f + 0.5f * sin( gpGlobals->curtime * 12.0f );
|
|
|
|
IMaterial *pMatRim = materials->FindMaterial( "dev/glow_rim3d", TEXTURE_GROUP_OTHER, true );
|
|
g_pStudioRender->ForcedMaterialOverride( pMatRim );
|
|
|
|
for ( int i = 0; i < vecGlowObjectsRimGlow3DStyle.Count(); ++ i )
|
|
{
|
|
if ( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha <= 0 )
|
|
continue;
|
|
|
|
IMaterialVar *pMatVar = pMatRim->FindVar( "$envmaptint", 0 );
|
|
if ( pMatVar != NULL )
|
|
{
|
|
pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.x, 0, 1 ), 0 );
|
|
pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.y, 0, 1 ), 1 );
|
|
pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.z, 0, 1 ), 2 );
|
|
}
|
|
|
|
pMatVar = pMatRim->FindVar( "$envmapfresnelminmaxexp", 0 );
|
|
if ( pMatVar != NULL )
|
|
{
|
|
pMatVar->SetVecComponentValue( 0, 0 );
|
|
pMatVar->SetVecComponentValue( 1.5f, 1 );
|
|
pMatVar->SetVecComponentValue( 3.0f + flPulse * 2.0f, 2 );
|
|
}
|
|
|
|
vecGlowObjectsRimGlow3DStyle[i].DrawModel();
|
|
}
|
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
}
|
|
|
|
|
|
|
|
if ( vecGlowObjectsEdgeHighlightStyle.Count() )
|
|
{
|
|
// render players into the fullscreen rt all white
|
|
// render in-world 3d imposter players into scene using screenspace UV lookup from fullscreen RT, use glow object alpha and colorize here.
|
|
// in the above step, multisample the rt to edge detect.
|
|
|
|
// push the rt
|
|
pRenderContext->PushRenderTargetAndViewport();
|
|
|
|
// remember the color modulation so we can put it back again at the end
|
|
Vector vOrigColor;
|
|
render->GetColorModulation( vOrigColor.Base() );
|
|
|
|
ITexture *pRtOutput = materials->FindTexture( "_rt_FullScreen", TEXTURE_GROUP_RENDER_TARGET );
|
|
SetRenderTargetAndViewPort( pRtOutput, pSetup->width, pSetup->height );
|
|
|
|
pRenderContext->ClearColor3ub( 0, 0, 0 );
|
|
pRenderContext->ClearBuffers( true, true, true );
|
|
|
|
// Set override material for drawing the shapes
|
|
IMaterial *pMatTeamIdShape = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true );
|
|
g_pStudioRender->ForcedMaterialOverride( pMatTeamIdShape );
|
|
|
|
// we want to render fully opaque, so bash blend to 1
|
|
render->SetBlend( 1 );
|
|
|
|
// set override color
|
|
Vector vecOverrideColor = Vector( 1, 1, 1 );
|
|
render->SetColorModulation( vecOverrideColor.Base() );
|
|
|
|
// draw players
|
|
FOR_EACH_VEC( vecGlowObjectsEdgeHighlightStyle, i )
|
|
{
|
|
vecGlowObjectsEdgeHighlightStyle[i].DrawModel();
|
|
}
|
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
|
|
pRenderContext->PopRenderTargetAndViewport();
|
|
|
|
|
|
IMaterial *pMatTemp = materials->FindMaterial( "dev/glow_edge_highlight", TEXTURE_GROUP_OTHER, false );
|
|
g_pStudioRender->ForcedMaterialOverride( pMatTemp );
|
|
|
|
// draw players
|
|
FOR_EACH_VEC( vecGlowObjectsEdgeHighlightStyle, i )
|
|
{
|
|
Vector vecTempColor = vecGlowObjectsEdgeHighlightStyle[i].m_vGlowColor * clamp( vecGlowObjectsEdgeHighlightStyle[i].m_flGlowAlpha, 0.0f, 1.0f ) * 1.4f; // boost a bit
|
|
|
|
if ( vecGlowObjectsEdgeHighlightStyle[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT_PULSE )
|
|
{
|
|
float flPulse = 1.5f + 0.5f * sin( gpGlobals->curtime * 16.0f );
|
|
vecTempColor *= (flPulse * 0.5f);
|
|
}
|
|
|
|
render->SetColorModulation( vecTempColor.Base() );
|
|
vecGlowObjectsEdgeHighlightStyle[i].DrawModel();
|
|
}
|
|
|
|
// restore the modulation from before
|
|
render->SetColorModulation( vOrigColor.Base() );
|
|
|
|
}
|
|
|
|
|
|
//=======================================================//
|
|
// 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 );
|
|
|
|
RenderableInstance_t instance;
|
|
instance.m_nAlpha = 255;
|
|
|
|
int iNumGlowObjects = 0;
|
|
|
|
for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i )
|
|
{
|
|
if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
|
|
continue;
|
|
|
|
// Full bloom rendered objects should not be stenciled out here
|
|
if ( m_GlowObjectDefinitions[i].m_bFullBloomRender )
|
|
{
|
|
++ iNumGlowObjects;
|
|
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 = SHADER_STENCILFUNC_ALWAYS;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
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 = SHADER_STENCILFUNC_ALWAYS;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
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 = SHADER_STENCILFUNC_EQUAL;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_INCREMENT_CLAMP;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
m_GlowObjectDefinitions[i].DrawModel();
|
|
}
|
|
}
|
|
|
|
iNumGlowObjects++;
|
|
}
|
|
|
|
int iTempHealthBarRenderMaskIndex = 4;
|
|
|
|
// 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 ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
|
|
continue;
|
|
|
|
// Full bloom rendered objects should not be stenciled out here
|
|
if ( m_GlowObjectDefinitions[i].m_bFullBloomRender )
|
|
continue;
|
|
|
|
if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && !m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded )
|
|
{
|
|
ShaderStencilState_t stencilState;
|
|
stencilState.m_bEnable = true;
|
|
stencilState.m_nReferenceValue = 2;
|
|
|
|
C_CSPlayer* pPlayer = ToCSPlayer( m_GlowObjectDefinitions[i].m_pEntity );
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->m_iHealthBarRenderMaskIndex = iTempHealthBarRenderMaskIndex;
|
|
stencilState.m_nReferenceValue = iTempHealthBarRenderMaskIndex;
|
|
iTempHealthBarRenderMaskIndex ++;
|
|
}
|
|
|
|
stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
m_GlowObjectDefinitions[i].DrawModel();
|
|
}
|
|
}
|
|
|
|
RenderGlowBoxes(GLOWBOX_PASS_STENCIL, pRenderContext);
|
|
iNumGlowObjects += m_GlowBoxDefinitions.Count();
|
|
|
|
g_pStudioRender->ForcedMaterialOverride( NULL );
|
|
render->SetBlend( 0.0f );
|
|
|
|
pRenderContext->OverrideDepthEnable( true, false, false ); // health bars render over everything
|
|
|
|
IMaterial *pMatGlowHealthColor = NULL;
|
|
pMatGlowHealthColor = materials->FindMaterial( "dev/glow_health_color", TEXTURE_GROUP_OTHER, true );
|
|
|
|
for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i )
|
|
{
|
|
|
|
if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
|
|
continue;
|
|
|
|
C_CSPlayer* pPlayer = ToCSPlayer( m_GlowObjectDefinitions[i].m_pEntity );
|
|
if ( pPlayer && pPlayer != GetLocalOrInEyeCSPlayer() && pPlayer->IsAlive() )
|
|
{
|
|
|
|
ShaderStencilState_t stencilState;
|
|
stencilState.m_bEnable = true;
|
|
stencilState.m_nWriteMask = 0x0;
|
|
stencilState.m_nTestMask = 0xFFFFFF;
|
|
stencilState.m_nReferenceValue = pPlayer->m_iHealthBarRenderMaskIndex;
|
|
stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
if ( pPlayer->m_flHealthFadeAlpha > 0 )
|
|
pPlayer->m_flHealthFadeAlpha -= gpGlobals->frametime * 0.4f;
|
|
|
|
if ( pPlayer->m_flHealthFadeValue != pPlayer->GetHealth() )
|
|
pPlayer->m_flHealthFadeAlpha = 1.0f;
|
|
|
|
pPlayer->m_flHealthFadeValue = (float)pPlayer->GetHealth();
|
|
|
|
if ( pPlayer->m_flHealthFadeAlpha > 0 ) //only need to update the effect if we can see it
|
|
{
|
|
float flGlowPulseSpeed = Lerp( pPlayer->m_flHealthFadeValue/100.0f, 30.0f, 10.0f );
|
|
pMatGlowHealthColor->AlphaModulate( pPlayer->m_flHealthFadeAlpha * (0.4*(sin(flGlowPulseSpeed*gpGlobals->curtime)+1.4)) );
|
|
|
|
Vector vecPlayerScreenSpaceOrigin;
|
|
if ( ScreenTransform( pPlayer->GetAbsOrigin(), vecPlayerScreenSpaceOrigin ) == 0 )
|
|
{
|
|
|
|
ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceOrigin);
|
|
|
|
float flHealthLeft = (100.0f-pPlayer->m_flHealthFadeValue)/100.0f;
|
|
float flHealthHeightOffset = 72.0f;
|
|
|
|
if ( pPlayer->GetFlags() & FL_DUCKING )
|
|
flHealthHeightOffset = 55.0f;
|
|
|
|
Vector vecPlayerScreenSpaceHealthPos;
|
|
if ( ScreenTransform( pPlayer->GetAbsOrigin() + Vector (0,0,flHealthLeft*flHealthHeightOffset), vecPlayerScreenSpaceHealthPos ) == 0 )
|
|
{
|
|
|
|
ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceHealthPos);
|
|
|
|
Vector vecPlayerScreenSpaceSizeA;
|
|
ScreenTransform( pPlayer->GetAbsOrigin() + Vector(0,0,100), vecPlayerScreenSpaceSizeA );
|
|
ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceSizeA);
|
|
|
|
Vector vecPlayerScreenSpaceSizeB;
|
|
ScreenTransform( pPlayer->GetAbsOrigin() + Vector(100,0,0), vecPlayerScreenSpaceSizeB );
|
|
ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceSizeB);
|
|
|
|
Vector vecPlayerScreenSpaceSizeAPixels;
|
|
VectorSubtract( vecPlayerScreenSpaceOrigin, vecPlayerScreenSpaceSizeA, vecPlayerScreenSpaceSizeAPixels );
|
|
Vector vecPlayerScreenSpaceSizeBPixels;
|
|
VectorSubtract( vecPlayerScreenSpaceOrigin, vecPlayerScreenSpaceSizeB, vecPlayerScreenSpaceSizeBPixels );
|
|
|
|
float flPlayerScreenSpaceCoverage = Max( vecPlayerScreenSpaceSizeAPixels.Length(), vecPlayerScreenSpaceSizeBPixels.Length() );
|
|
|
|
if ( flPlayerScreenSpaceCoverage < ScreenHeight() * 2 )
|
|
{
|
|
float flHealthWidth = flPlayerScreenSpaceCoverage;
|
|
float flHealthHeight = flPlayerScreenSpaceCoverage;
|
|
|
|
float flHealthPosX = vecPlayerScreenSpaceHealthPos.x - (flPlayerScreenSpaceCoverage * 0.5);
|
|
float flHealthPosY = vecPlayerScreenSpaceHealthPos.y;
|
|
|
|
pRenderContext->DrawScreenSpaceRectangle( pMatGlowHealthColor,
|
|
flHealthPosX, flHealthPosY,
|
|
flHealthWidth, flHealthHeight,
|
|
0, 0, 0, 0, 1, 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//clear out custom render settings
|
|
pRenderContext->OverrideDepthEnable( false, false );
|
|
render->SetBlend( flSavedBlend );
|
|
pRenderContext->SetStencilState( stencilStateDisable );
|
|
|
|
// 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, m_GlowObjectDefinitions );
|
|
}
|
|
|
|
ITexture *pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET );
|
|
ITexture *pRtQuarterSize0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
|
|
ITexture *pRtQuarterSize1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
|
|
|
|
DownSampleAndBlurRT( pSetup, pRenderContext, flBloomScale, pRtFullFrame, pRtQuarterSize0, pRtQuarterSize1 );
|
|
|
|
|
|
{
|
|
//=======================================================================================================//
|
|
// 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 );
|
|
|
|
ShaderStencilState_t stencilState;
|
|
stencilState.m_bEnable = true;
|
|
stencilState.m_nWriteMask = 0x0; // We're not changing stencil
|
|
//stencilState.m_nTestMask = 0x3;
|
|
stencilState.m_nReferenceValue = 0x0;
|
|
stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
|
|
stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
|
|
stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
|
|
pRenderContext->SetStencilState( stencilState );
|
|
|
|
// Get viewport
|
|
int nSrcWidth = pSetup->width;
|
|
int nSrcHeight = pSetup->height;
|
|
int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
|
|
pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
|
|
|
|
// Draw quad
|
|
pRenderContext->DrawScreenSpaceRectangle( pMatHaloAddToScreen, 0, 0, nViewportWidth, nViewportHeight,
|
|
0.0f, -0.5f, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
|
|
pRtQuarterSize1->GetActualWidth(),
|
|
pRtQuarterSize1->GetActualHeight() );
|
|
|
|
// Disable stencil
|
|
pRenderContext->SetStencilState( stencilStateDisable );
|
|
}
|
|
|
|
#if defined( _X360 )
|
|
pRenderContext->PopVertexShaderGPRAllocation();
|
|
#endif
|
|
}
|
|
|
|
void CGlowObjectManager::GlowObjectDefinition_t::DrawModel()
|
|
{
|
|
RenderableInstance_t instance;
|
|
instance.m_nAlpha = (uint8)( m_flGlowAlpha * 255.0f );
|
|
|
|
m_pEntity->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
|
|
C_BaseEntity *pAttachment = m_pEntity->FirstMoveChild();
|
|
|
|
while ( pAttachment != NULL )
|
|
{
|
|
if ( pAttachment->ShouldDraw() )
|
|
{
|
|
pAttachment->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
|
|
}
|
|
pAttachment = pAttachment->NextMovePeer();
|
|
}
|
|
}
|