|
|
//========== Copyright (c) 2008, Valve Corporation, All rights reserved. ==========
//
// Purpose:
//
//=================================================================================
#include "cbase.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/itexture.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/materialsystem_config.h"
#include "tier1/callqueue.h"
#include "colorcorrectionmgr.h"
#include "view_scene.h"
#include "c_world.h"
#include "renderparm.h"
#include "shaderapi/ishaderapi.h"
#include "proxyentity.h"
#include "imaterialproxydict.h"
#include "model_types.h"
#include "bitmap/tgawriter.h"
#include "filesystem.h"
#ifdef PORTAL2
#include "c_portal_player.h"
#endif
#ifdef CSTRIKE15
#include "weapon_csbase.h"
#include "c_cs_player.h"
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
// mapmaker controlled autoexposure
bool g_bUseCustomAutoExposureMin = false; bool g_bUseCustomAutoExposureMax = false; bool g_bUseCustomBloomScale = false; float g_flCustomAutoExposureMin = 0; float g_flCustomAutoExposureMax = 0; float g_flCustomBloomScale = 0.0f; float g_flCustomBloomScaleMinimum = 0.0f;
float g_flBloomExponent = 2.5f; float g_flBloomSaturation = 1.0f; float g_flTonemapPercentBrightPixels = 2.0f; float g_flTonemapMinAvgLum = 3.0f; float g_flTonemapRate = 1.0f;
#if defined( _X360 )
#if defined( CSTRIKE15 )
float g_flTonemapPercentTarget = 60.0f; #else
// Move "up" the percent target to make X360 a bit brighter than it's been to compensate for our bad 8-bit histogram utilization and to also compensate for the non-PWL texture change.
float g_flTonemapPercentTarget = 80.0f #endif
#else
float g_flTonemapPercentTarget = 60.0f; #endif
extern void GetTonemapSettingsFromEnvTonemapController( void );
// mapmaker controlled depth of field
bool g_bDOFEnabled = false; float g_flDOFNearBlurDepth = 50.0f; float g_flDOFNearFocusDepth = 200.0f; float g_flDOFFarFocusDepth = 250.0f; float g_flDOFFarBlurDepth = 1000.0f; float g_flDOFNearBlurRadius = 0.0f; float g_flDOFFarBlurRadius = 5.0f;
bool g_bFlashlightIsOn = false;
// hdr parameters
ConVar mat_bloomscale( "mat_bloomscale", "1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
ConVar mat_hdr_level( "mat_hdr_level", "2", FCVAR_DEVELOPMENTONLY ); ConVar mat_bloomamount_rate( "mat_bloomamount_rate", "0.05f", FCVAR_CHEAT ); static ConVar debug_postproc( "mat_debug_postprocessing_effects", "0", FCVAR_CHEAT, "0 = off, 1 = show post-processing passes in quadrants of the screen, 2 = only apply post-processing to the centre of the screen" ); static ConVar mat_dynamic_tonemapping( "mat_dynamic_tonemapping", "1", FCVAR_CHEAT ); static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "0", FCVAR_DEVELOPMENTONLY );
static ConVar mat_autoexposure_max( "mat_autoexposure_max", "2", FCVAR_CHEAT ); #if defined( _X360 )
#if defined( CSTRIKE15 )
static ConVar mat_autoexposure_max_multiplier("mat_autoexposure_max_multiplier","1.0", FCVAR_CHEAT); #else
// Allow the max. pixel shader multiplier to be 50% higher to better utilize the available 8-bit output range, and to help compensate for the gamma ramp adjustments we've made. (At some point we should also adjust the PS3.)
static ConVar mat_autoexposure_max_multiplier("mat_autoexposure_max_multiplier","1.5", FCVAR_CHEAT ); #endif
#else
static ConVar mat_autoexposure_max_multiplier("mat_autoexposure_max_multiplier","1.0", FCVAR_CHEAT ); #endif
static ConVar mat_autoexposure_min( "mat_autoexposure_min", "0.5", FCVAR_CHEAT ); static ConVar mat_show_histogram( "mat_show_histogram", "0", FCVAR_CHEAT ); ConVar mat_hdr_uncapexposure( "mat_hdr_uncapexposure", "0", FCVAR_CHEAT ); ConVar mat_force_bloom("mat_force_bloom","0", FCVAR_CHEAT );
ConVar mat_disable_bloom("mat_disable_bloom","0", FCVAR_CHEAT ); ConVar mat_debug_bloom("mat_debug_bloom","0", FCVAR_CHEAT); ConVar mat_colorcorrection( "mat_colorcorrection", "1", FCVAR_CHEAT );
ConVar mat_accelerate_adjust_exposure_down( "mat_accelerate_adjust_exposure_down", "40.0", FCVAR_CHEAT );
// fudge factor to make non-hdr bloom more closely match hdr bloom. Because of auto-exposure, high
// bloomscales don't blow out as much in hdr. this factor was derived by comparing images in a
// reference scene.
ConVar mat_non_hdr_bloom_scalefactor("mat_non_hdr_bloom_scalefactor",".3", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
// Apply addition scale to the final bloom scale
static ConVar mat_bloom_scalefactor_scalar( "mat_bloom_scalefactor_scalar", "1.0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
//ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.75", FCVAR_CHEAT );
//ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.80", FCVAR_CHEAT );
ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.9", FCVAR_CHEAT ); ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.85", FCVAR_CHEAT );
ConVar mat_tonemap_algorithm( "mat_tonemap_algorithm", "1", FCVAR_CHEAT, "0 = Original Algorithm 1 = New Algorithm" ); ConVar mat_force_tonemap_percent_target( "mat_force_tonemap_percent_target", "-1", FCVAR_CHEAT, "Override. Old default was 60." ); ConVar mat_force_tonemap_percent_bright_pixels( "mat_force_tonemap_percent_bright_pixels", "-1", FCVAR_CHEAT, "Override. Old value was 2.0" ); ConVar mat_force_tonemap_min_avglum( "mat_force_tonemap_min_avglum", "-1", FCVAR_CHEAT, "Override. Old default was 3.0" ); ConVar mat_force_tonemap_scale( "mat_force_tonemap_scale", "0.0", FCVAR_CHEAT ); ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT );
ConVar mat_grain_enable( "mat_grain_enable", "1" ); //ConVar mat_vignette_enable( "mat_vignette_enable", "1", FCVAR_RELEASE | FCVAR_REPLICATED );
ConVar mat_local_contrast_enable( "mat_local_contrast_enable", "1", FCVAR_DEVELOPMENTONLY );
ConVar mat_blur_r( "mat_blur_r", "0.7" ); ConVar mat_blur_g( "mat_blur_g", "0.7" ); ConVar mat_blur_b( "mat_blur_b", "0.7" );
#if defined(_PS3)
ConVar mat_PS3_findpostvarsfast( "mat_PS3_findpostvarsfast", "1" ); #endif
// These material proxies + the GetXXXMaterial calls within have been added for PS3 for perf reasons (to avoid the expensive PS3 mutexes on Find_Var, Find_Material)
// gives us almost 0.5ms saving from main thread, could be added to other platforms - need to test
// FIXME: also need to test in and out of levels, but that is not currently working without this change on the current PS3 CS:GO build anyway
//=====================================================================================================================
// LumCompare material proxy ============================================================================================
//=====================================================================================================================
class CLumCompareMaterialProxy : public CEntityMaterialProxy { public: CLumCompareMaterialProxy(); virtual ~CLumCompareMaterialProxy() {}; virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
static IMaterial *GetLumCompareMaterial( IMaterialSystem * materials ); static void SetupLumCompareMaterial( float flTestRangeMin, float flTestRangeMax );
private: static IMaterialVar *s_pMaterialParam_C0_X; static IMaterialVar *s_pMaterialParam_C0_Y;
static float s_C0_X; static float s_C0_Y;
static IMaterial *s_pLumCompareMaterial; };
IMaterialVar *CLumCompareMaterialProxy::s_pMaterialParam_C0_X = NULL; IMaterialVar *CLumCompareMaterialProxy::s_pMaterialParam_C0_Y = NULL; float CLumCompareMaterialProxy::s_C0_X = -1e20f; float CLumCompareMaterialProxy::s_C0_Y = 1e20f; IMaterial *CLumCompareMaterialProxy::s_pLumCompareMaterial = NULL;
CLumCompareMaterialProxy::CLumCompareMaterialProxy() { s_pMaterialParam_C0_X = NULL; s_pMaterialParam_C0_Y = NULL;
s_pLumCompareMaterial = NULL; }
bool CLumCompareMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false;
s_pLumCompareMaterial = materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER, true );
s_pMaterialParam_C0_X = pMaterial->FindVar( "$C0_X", &bFoundVar, false ); s_pMaterialParam_C0_Y = pMaterial->FindVar( "$C0_Y", &bFoundVar, false );
return true; }
void CLumCompareMaterialProxy::OnBind( C_BaseEntity *pEnt ) { }
IMaterial *CLumCompareMaterialProxy::GetMaterial() { if ( s_pMaterialParam_C0_X == NULL) return NULL;
return s_pMaterialParam_C0_X->GetOwningMaterial(); }
IMaterial *CLumCompareMaterialProxy::GetLumCompareMaterial( IMaterialSystem * materials ) { if( s_pLumCompareMaterial == NULL) { s_pLumCompareMaterial = materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER, true ); }
return s_pLumCompareMaterial; }
void CLumCompareMaterialProxy::SetupLumCompareMaterial( float flTestRangeMin, float flTestRangeMax ) { s_C0_X = flTestRangeMin; s_C0_Y = flTestRangeMax;
s_pMaterialParam_C0_X->SetFloatValue( s_C0_X ); s_pMaterialParam_C0_Y->SetFloatValue( s_C0_Y ); }
EXPOSE_MATERIAL_PROXY( CLumCompareMaterialProxy, LumCompare );
//=====================================================================================================================
// LumCompareStencil material proxy ============================================================================================
//=====================================================================================================================
class CLumCompareStencilMaterialProxy : public CEntityMaterialProxy { public: CLumCompareStencilMaterialProxy(); virtual ~CLumCompareStencilMaterialProxy() {}; virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
private:
public: static IMaterial *GetLumCompareStencilMaterial( IMaterialSystem * materials );
private:
static IMaterial *s_pLumCompareStencilMaterial; };
IMaterial *CLumCompareStencilMaterialProxy::s_pLumCompareStencilMaterial = NULL;
CLumCompareStencilMaterialProxy::CLumCompareStencilMaterialProxy() { s_pLumCompareStencilMaterial = NULL; }
bool CLumCompareStencilMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { return true; }
void CLumCompareStencilMaterialProxy::OnBind( C_BaseEntity *pEnt ) { }
IMaterial *CLumCompareStencilMaterialProxy::GetMaterial() { if( s_pLumCompareStencilMaterial == NULL) { s_pLumCompareStencilMaterial = materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER, true ); }
return s_pLumCompareStencilMaterial; }
IMaterial *CLumCompareStencilMaterialProxy::GetLumCompareStencilMaterial( IMaterialSystem * materials ) { if( s_pLumCompareStencilMaterial == NULL) { s_pLumCompareStencilMaterial = materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER, true ); }
return s_pLumCompareStencilMaterial; }
EXPOSE_MATERIAL_PROXY( CLumCompareStencilMaterialProxy, no_pixel_write );
//=====================================================================================================================
// Downsample material proxy ============================================================================================
//=====================================================================================================================
class CDownsampleMaterialProxy : public CEntityMaterialProxy { public: CDownsampleMaterialProxy(); virtual ~CDownsampleMaterialProxy() {}; virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
public: static IMaterial *GetDownsampleMaterial( IMaterialSystem * materials ); static void SetupDownsampleMaterial( float flBloomExponent, float flBloomSaturation );
private: static IMaterialVar *s_pMaterialParam_bloomexp; static IMaterialVar *s_pMaterialParam_bloomsaturation;
static float s_bloomexp; static float s_bloomsaturation;
static IMaterial *s_pDownsampleMaterial; };
IMaterialVar *CDownsampleMaterialProxy::s_pMaterialParam_bloomexp = NULL; IMaterialVar *CDownsampleMaterialProxy::s_pMaterialParam_bloomsaturation = NULL; float CDownsampleMaterialProxy::s_bloomexp = 2.5f; float CDownsampleMaterialProxy::s_bloomsaturation = 1.0f; IMaterial *CDownsampleMaterialProxy::s_pDownsampleMaterial = NULL;
CDownsampleMaterialProxy::CDownsampleMaterialProxy() { s_pMaterialParam_bloomexp = NULL; s_pMaterialParam_bloomsaturation = NULL;
s_pDownsampleMaterial = NULL; }
bool CDownsampleMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false;
s_pMaterialParam_bloomexp = pMaterial->FindVar( "$bloomexp", &bFoundVar, false ); s_pMaterialParam_bloomsaturation = pMaterial->FindVar( "$bloomsaturation", &bFoundVar, false );
s_bloomexp = 2.5f; s_bloomsaturation = 1.0f;
return true; }
void CDownsampleMaterialProxy::OnBind( C_BaseEntity *pEnt ) { }
IMaterial *CDownsampleMaterialProxy::GetMaterial() { if ( s_pMaterialParam_bloomexp == NULL) return NULL;
return s_pMaterialParam_bloomexp->GetOwningMaterial(); }
IMaterial *CDownsampleMaterialProxy::GetDownsampleMaterial( IMaterialSystem * materials ) { if( s_pDownsampleMaterial == NULL) { // FIXME: doesn't support dev/downsample (bFloathdr == true) path on PS3 here yet...
s_pDownsampleMaterial = materials->FindMaterial( "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true ); }
return s_pDownsampleMaterial; }
void CDownsampleMaterialProxy::SetupDownsampleMaterial( float flBloomExponent, float flBloomSaturation ) { s_bloomexp = flBloomExponent; s_bloomsaturation = flBloomSaturation;
s_pMaterialParam_bloomexp->SetFloatValue( s_bloomexp ); s_pMaterialParam_bloomsaturation->SetFloatValue( s_bloomsaturation ); }
EXPOSE_MATERIAL_PROXY( CDownsampleMaterialProxy, downsample_non_hdr );
//=====================================================================================================================
// XBlur material proxy ============================================================================================
//=====================================================================================================================
class CXBlurMaterialProxy : public CEntityMaterialProxy { public: CXBlurMaterialProxy(); virtual ~CXBlurMaterialProxy() {}; virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
private:
public: static IMaterial *GetXBlurMaterial( IMaterialSystem * materials );
private:
static IMaterial *s_pXBlurMaterial; };
IMaterial *CXBlurMaterialProxy::s_pXBlurMaterial = NULL;
CXBlurMaterialProxy::CXBlurMaterialProxy() { s_pXBlurMaterial = NULL; }
bool CXBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { return true; }
void CXBlurMaterialProxy::OnBind( C_BaseEntity *pEnt ) { }
IMaterial *CXBlurMaterialProxy::GetMaterial() { if( s_pXBlurMaterial == NULL) { s_pXBlurMaterial = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true ); }
return s_pXBlurMaterial; }
IMaterial *CXBlurMaterialProxy::GetXBlurMaterial( IMaterialSystem * materials ) { if( s_pXBlurMaterial == NULL) { s_pXBlurMaterial = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true ); }
return s_pXBlurMaterial; }
EXPOSE_MATERIAL_PROXY( CXBlurMaterialProxy, blurfilterx );
//=====================================================================================================================
// YBlur material proxy ============================================================================================
//=====================================================================================================================
class CYBlurMaterialProxy : public CEntityMaterialProxy { public: CYBlurMaterialProxy(); virtual ~CYBlurMaterialProxy() {}; virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
private:
public: static IMaterial *GetYBlurMaterial( IMaterialSystem * materials );
private:
static IMaterial *s_pYBlurMaterial; };
IMaterial *CYBlurMaterialProxy::s_pYBlurMaterial = NULL;
CYBlurMaterialProxy::CYBlurMaterialProxy() { s_pYBlurMaterial = NULL; }
bool CYBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { return true; }
void CYBlurMaterialProxy::OnBind( C_BaseEntity *pEnt ) { }
IMaterial *CYBlurMaterialProxy::GetMaterial() { if( s_pYBlurMaterial == NULL) { s_pYBlurMaterial = materials->FindMaterial( "dev/blurfiltery_nohdr", TEXTURE_GROUP_OTHER, true ); }
return s_pYBlurMaterial; }
IMaterial *CYBlurMaterialProxy::GetYBlurMaterial( IMaterialSystem * materials ) { if( s_pYBlurMaterial == NULL) { s_pYBlurMaterial = materials->FindMaterial( "dev/blurfiltery_nohdr", TEXTURE_GROUP_OTHER, true ); }
return s_pYBlurMaterial; }
EXPOSE_MATERIAL_PROXY( CYBlurMaterialProxy, blurfiltery );
static void SetRenderTargetAndViewPort(ITexture *rt) { CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetRenderTarget(rt); if ( rt ) { pRenderContext->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight()); } }
enum HistogramEntryState_t { HESTATE_INITIAL = 0, HESTATE_FIRST_QUERY_IN_FLIGHT, HESTATE_QUERY_IN_FLIGHT, HESTATE_QUERY_DONE, };
#define NUM_HISTOGRAM_BUCKETS 31
#define NUM_HISTOGRAM_BUCKETS_NEW 17
#define MAX_QUERIES_PER_FRAME 1
class CHistogramBucket { public: HistogramEntryState_t m_state; OcclusionQueryObjectHandle_t m_hOcclusionQueryHandle; int m_nFrameQueued; // when this query was last queued
int m_nPixels; // # of pixels this histogram represents
int m_nPixelsInRange; float m_flMinLuminance, m_flMaxLuminance; // the luminance range this entry was queried with
float m_flScreenMinX, m_flScreenMinY, m_flScreenMaxX, m_flScreenMaxY; // range is 0..1 in fractions of the screen
bool ContainsValidData( void ) { return ( m_state == HESTATE_QUERY_DONE ) || ( m_state == HESTATE_QUERY_IN_FLIGHT ); }
void IssueQuery( int nFrameNum ); };
void CHistogramBucket::IssueQuery( int nFrameNum ) { CMatRenderContextPtr pRenderContext( materials ); if ( !m_hOcclusionQueryHandle ) { m_hOcclusionQueryHandle = pRenderContext->CreateOcclusionQueryObject(); }
int nViewportX, nViewportY, nViewportWidth, nViewportHeight; pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
// Find min and max gamma-space text range
float flTestRangeMin = ( m_flMinLuminance == 0.0f ) ? -1e20f : m_flMinLuminance; // Count all pixels < 0.0 as 0.0 (for float HDR buffers)
float flTestRangeMax = ( m_flMaxLuminance == 1.0f ) ? 1e20f : m_flMaxLuminance; // Count all pixels >1.0 as 1.0
// Set stencil bits where the colors match
IMaterial *pLumCompareMaterial;
#if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) { pLumCompareMaterial = CLumCompareMaterialProxy::GetLumCompareMaterial( materials ); CLumCompareMaterialProxy::SetupLumCompareMaterial( flTestRangeMin, flTestRangeMax ); } else #endif
{ pLumCompareMaterial = materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER, true );
IMaterialVar *pMinVar = pLumCompareMaterial->FindVar( "$C0_X", NULL ); pMinVar->SetFloatValue( flTestRangeMin );
IMaterialVar *pMaxVar = pLumCompareMaterial->FindVar( "$C0_Y", NULL ); pMaxVar->SetFloatValue( flTestRangeMax ); }
int nScreenMinX = FLerp( nViewportX, ( nViewportX + nViewportWidth - 1 ), 0, 1, m_flScreenMinX ); int nScreenMaxX = FLerp( nViewportX, ( nViewportX + nViewportWidth - 1 ), 0, 1, m_flScreenMaxX ); int nScreenMinY = FLerp( nViewportY, ( nViewportY + nViewportHeight - 1 ), 0, 1, m_flScreenMinY ); int nScreenMaxY = FLerp( nViewportY, ( nViewportY + nViewportHeight - 1 ), 0, 1, m_flScreenMaxY );
float flExposureWidthScale, flExposureHeightScale;
// Shrink region of interest if the flashlight is on
flExposureWidthScale = ( 0.5f * ( 1.0f - mat_exposure_center_region_x.GetFloat() ) ); flExposureHeightScale = ( 0.5f * ( 1.0f - mat_exposure_center_region_y.GetFloat() ) );
int nBorderWidth = ( nScreenMaxX - nScreenMinX + 1 ) * flExposureWidthScale; int nBorderHeight = ( nScreenMaxY - nScreenMinY + 1 ) * flExposureHeightScale;
// Do luminance compare
m_nPixels = ( 1 + nScreenMaxX - nScreenMinX ) * ( 1 + nScreenMaxY - nScreenMinY );
ShaderStencilState_t state; if ( mat_tonemapping_occlusion_use_stencil.GetInt() ) { state.m_nWriteMask = 1; state.m_bEnable = true; state.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE; state.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS; state.m_FailOp = SHADER_STENCILOP_KEEP; state.m_ZFailOp = SHADER_STENCILOP_KEEP; state.m_nReferenceValue = 0x80; state.m_nWriteMask = 0x80; pRenderContext->SetStencilState( state ); } else { pRenderContext->BeginOcclusionQueryDrawing( m_hOcclusionQueryHandle ); }
int nWindowWidth = 0; int nWindowHeight = 0; pRenderContext->GetWindowSize( nWindowWidth, nWindowHeight );
nScreenMinX += nBorderWidth; nScreenMinY += nBorderHeight; nScreenMaxX -= nBorderWidth; nScreenMaxY -= nBorderHeight; pRenderContext->DrawScreenSpaceRectangle( pLumCompareMaterial, nScreenMinX - nViewportX, nScreenMinY - nViewportY, 1 + nScreenMaxX - nScreenMinX, 1 + nScreenMaxY - nScreenMinY, nScreenMinX, nScreenMinY, nScreenMaxX, nScreenMaxY, nWindowWidth, nWindowHeight );
if ( mat_tonemapping_occlusion_use_stencil.GetInt() ) { // Start counting how many pixels had their stencil bit set via an occlusion query
pRenderContext->BeginOcclusionQueryDrawing( m_hOcclusionQueryHandle );
// Issue an occlusion query using stencil as the mask
state.m_bEnable = true; state.m_nTestMask = 0x80; state.m_PassOp = SHADER_STENCILOP_KEEP; state.m_CompareFunc = SHADER_STENCILFUNC_EQUAL; state.m_FailOp = SHADER_STENCILOP_KEEP; state.m_ZFailOp = SHADER_STENCILOP_KEEP; state.m_nReferenceValue = 0x80; pRenderContext->SetStencilState( state );
IMaterial *pLumCompareStencilMaterial; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pLumCompareStencilMaterial = CLumCompareStencilMaterialProxy::GetLumCompareStencilMaterial( materials ); else #endif
pLumCompareStencilMaterial = materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER, true);
pRenderContext->DrawScreenSpaceRectangle( pLumCompareStencilMaterial, nScreenMinX, nScreenMinY, 1 + nScreenMaxX - nScreenMinX, 1 + nScreenMaxY - nScreenMinY, nScreenMinX, nScreenMinY, nScreenMaxX, nScreenMaxY, nWindowWidth, nWindowHeight );
ShaderStencilState_t stateDisable; stateDisable.m_bEnable = false; pRenderContext->SetStencilState( stateDisable ); }
pRenderContext->EndOcclusionQueryDrawing( m_hOcclusionQueryHandle ); if ( m_state == HESTATE_INITIAL ) m_state = HESTATE_FIRST_QUERY_IN_FLIGHT; else m_state = HESTATE_QUERY_IN_FLIGHT; m_nFrameQueued = nFrameNum; }
#define HISTOGRAM_BAR_SIZE 200
class CTonemapSystem { CHistogramBucket m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS]; int m_nCurrentQueryFrame; int m_nCurrentAlgorithm;
float m_flTargetTonemapScale; float m_flCurrentTonemapScale;
int m_nNumMovingAverageValid; float m_movingAverageTonemapScale[10];
bool m_bOverrideTonemapScaleEnabled; float m_flOverrideTonemapScale;
public: void IssueAndReceiveBucketQueries(); void UpdateBucketRanges(); float FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTarget ); float ComputeTargetTonemapScalar( bool bGetIdealTargetForDebugMode );
void UpdateMaterialSystemTonemapScalar(); void SetTargetTonemappingScale( float flTonemapScale ); void ResetTonemappingScale( float flTonemapScale ); void SetTonemapScale( IMatRenderContext *pRenderContext, float newvalue, float minvalue, float maxvalue );
float GetTargetTonemappingScale() { return m_flTargetTonemapScale; } float GetCurrentTonemappingScale() { return m_flCurrentTonemapScale; }
void SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale );
// Dev functions
void DisplayHistogram();
// Constructor
CTonemapSystem() { m_nCurrentQueryFrame = 0; m_nCurrentAlgorithm = -1; m_flTargetTonemapScale = 1.0f; m_flCurrentTonemapScale = 1.0f;
m_nNumMovingAverageValid = 0; for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ) - 1; i++ ) { m_movingAverageTonemapScale[i] = 1.0f; }
m_bOverrideTonemapScaleEnabled = false; m_flOverrideTonemapScale = 1.0f;
UpdateBucketRanges(); } };
CTonemapSystem * GetCurrentTonemappingSystem() { static CTonemapSystem s_HDR_HistogramSystem[ MAX_SPLITSCREEN_PLAYERS ]; int slot = GET_ACTIVE_SPLITSCREEN_SLOT(); return &( s_HDR_HistogramSystem[ slot ] ); }
void CTonemapSystem::IssueAndReceiveBucketQueries() { UpdateBucketRanges();
// Find which histogram entries should have something done this frame
int nQueriesIssuedThisFrame = 0; m_nCurrentQueryFrame++;
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS; if ( mat_tonemap_algorithm.GetInt() == 1 ) nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
for ( int i = 0; i < nNumHistogramBuckets; i++ ) { switch ( m_histogramBucketArray[i].m_state ) { case HESTATE_INITIAL: if ( nQueriesIssuedThisFrame<MAX_QUERIES_PER_FRAME ) { m_histogramBucketArray[i].IssueQuery(m_nCurrentQueryFrame); nQueriesIssuedThisFrame++; } break;
case HESTATE_FIRST_QUERY_IN_FLIGHT: case HESTATE_QUERY_IN_FLIGHT: if ( m_nCurrentQueryFrame > m_histogramBucketArray[i].m_nFrameQueued + 2 ) { CMatRenderContextPtr pRenderContext( materials ); int np = pRenderContext->OcclusionQuery_GetNumPixelsRendered( m_histogramBucketArray[i].m_hOcclusionQueryHandle ); if ( np != -1 ) // -1 = Query not finished...wait until next time
{ m_histogramBucketArray[i].m_nPixelsInRange = np; m_histogramBucketArray[i].m_state = HESTATE_QUERY_DONE; } } break; } }
// Now, issue queries for the oldest finished queries we have
while ( nQueriesIssuedThisFrame < MAX_QUERIES_PER_FRAME ) { int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS; if ( mat_tonemap_algorithm.GetInt() == 1 ) nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
int nOldestSoFar = -1; for ( int i = 0; i < nNumHistogramBuckets; i++ ) { if ( ( m_histogramBucketArray[i].m_state == HESTATE_QUERY_DONE ) && ( ( nOldestSoFar == -1 ) || ( m_histogramBucketArray[i].m_nFrameQueued < m_histogramBucketArray[nOldestSoFar].m_nFrameQueued ) ) ) { nOldestSoFar = i; } }
if ( nOldestSoFar == -1 ) // Nothing to do
break;
m_histogramBucketArray[nOldestSoFar].IssueQuery( m_nCurrentQueryFrame ); nQueriesIssuedThisFrame++; } }
float CTonemapSystem::FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTargetToSnapToIfInSameBin = -1.0f ) { if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
{ int nTotalValidPixels = 0; for ( int i = 0; i < ( NUM_HISTOGRAM_BUCKETS_NEW - 1 ); i++ ) { if ( m_histogramBucketArray[i].ContainsValidData() ) { nTotalValidPixels += m_histogramBucketArray[i].m_nPixelsInRange; } }
if ( nTotalValidPixels == 0 ) { return -1.0f; }
// Find where percent range border is
float flTotalPercentRangeTested = 0.0f; float flTotalPercentPixelsTested = 0.0f; for ( int i = ( NUM_HISTOGRAM_BUCKETS_NEW - 2 ); i >= 0; i-- ) // Start at the bright end
{ if ( !m_histogramBucketArray[i].ContainsValidData() ) return -1.0f;
float flPixelPercentNeeded = ( flPercentBrightPixels / 100.0f ) - flTotalPercentPixelsTested; float flThisBinPercentOfTotalPixels = float( m_histogramBucketArray[i].m_nPixelsInRange ) / float( nTotalValidPixels ); float flThisBinLuminanceRange = m_histogramBucketArray[i].m_flMaxLuminance - m_histogramBucketArray[i].m_flMinLuminance; if ( flThisBinPercentOfTotalPixels >= flPixelPercentNeeded ) // We found the bin needed
{ if ( flPercentTargetToSnapToIfInSameBin >= 0.0f ) { if ( ( m_histogramBucketArray[i].m_flMinLuminance <= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) && ( m_histogramBucketArray[i].m_flMaxLuminance >= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) ) { // Sticky bin...We're in the same bin as the target so keep the tonemap scale where it is
return ( flPercentTargetToSnapToIfInSameBin / 100.0f ); } }
float flPercentOfThesePixelsNeeded = flPixelPercentNeeded / flThisBinPercentOfTotalPixels; float flPercentLocationOfBorder = 1.0f - ( flTotalPercentRangeTested + ( flThisBinLuminanceRange * flPercentOfThesePixelsNeeded ) ); flPercentLocationOfBorder = MAX( m_histogramBucketArray[i].m_flMinLuminance, MIN( m_histogramBucketArray[i].m_flMaxLuminance, flPercentLocationOfBorder ) ); // Clamp to this bin just in case
return flPercentLocationOfBorder; }
flTotalPercentPixelsTested += flThisBinPercentOfTotalPixels; flTotalPercentRangeTested += flThisBinLuminanceRange; }
return -1.0f; } else { // Don't know what to do for other algorithms yet
return -1.0f; } }
float CTonemapSystem::ComputeTargetTonemapScalar( bool bGetIdealTargetForDebugMode = false ) { if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
{ float flTonemapPercentTarget = mat_force_tonemap_percent_target.GetFloat() >= 0.0f ? mat_force_tonemap_percent_target.GetFloat() : g_flTonemapPercentTarget; float flTonemapPercentBrightPixels = mat_force_tonemap_percent_bright_pixels.GetFloat() >= 0.0f ? mat_force_tonemap_percent_bright_pixels.GetFloat() : g_flTonemapPercentBrightPixels; float flTonemapMinAvgLum = mat_force_tonemap_min_avglum.GetFloat() >= 0.0f ? mat_force_tonemap_min_avglum.GetFloat() : g_flTonemapMinAvgLum; float flPercentLocationOfTarget; if ( bGetIdealTargetForDebugMode == true) flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( flTonemapPercentBrightPixels ); // Don't pass in the second arg so the scalar doesn't snap to a bin
else flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( flTonemapPercentBrightPixels, flTonemapPercentTarget ); if ( flPercentLocationOfTarget < 0.0f ) // This is the return error code
{ flPercentLocationOfTarget = flTonemapPercentTarget / 100.0f; // Pretend we're at the target
}
// Make sure this is > 0.0f
flPercentLocationOfTarget = MAX( 0.0001f, flPercentLocationOfTarget );
// Compute target scalar
float flTargetScalar = ( flTonemapPercentTarget / 100.0f ) / flPercentLocationOfTarget;
// Compute secondary target scalar
float flAverageLuminanceLocation = FindLocationOfPercentBrightPixels( 50.0f ); if ( flAverageLuminanceLocation > 0.0f ) { float flTargetScalar2 = ( flTonemapMinAvgLum / 100.0f ) / flAverageLuminanceLocation;
// Only override it if it's trying to brighten the image more than the primary algorithm
if ( flTargetScalar2 > flTargetScalar ) { flTargetScalar = flTargetScalar2; } }
// Apply this against last frames scalar
CMatRenderContextPtr pRenderContext( materials ); float flLastScale = m_flCurrentTonemapScale; flTargetScalar *= flLastScale;
flTargetScalar = MAX( 0.001f, flTargetScalar ); return flTargetScalar; } else // Original tonemapping algorithm
{ float flScaleValue = 1.0f; if ( m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].ContainsValidData() ) { flScaleValue = m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].m_nPixels * ( 1.0f / m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS-1].m_nPixelsInRange ); }
if ( !IsFinite( flScaleValue ) ) { flScaleValue = 1.0f; }
float flTotal = 0.0f; int nTotalPixels = 0; for ( int i=0; i<NUM_HISTOGRAM_BUCKETS-1; i++ ) { if ( m_histogramBucketArray[i].ContainsValidData() ) { flTotal += flScaleValue * m_histogramBucketArray[i].m_nPixelsInRange * AVG( m_histogramBucketArray[i].m_flMinLuminance, m_histogramBucketArray[i].m_flMaxLuminance ); nTotalPixels += m_histogramBucketArray[i].m_nPixels; } }
float flAverageLuminance = 0.5f; if ( nTotalPixels > 0 ) flAverageLuminance = flTotal * ( 1.0f / nTotalPixels ); else flAverageLuminance = 0.5f;
// Make sure this is > 0.0f
flAverageLuminance = MAX( 0.0001f, flAverageLuminance );
// Compute target scalar
float flTargetScalar = 0.005f / flAverageLuminance;
return flTargetScalar; } }
static float GetCurrentBloomScale( void ) { // Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
float flCurrentBloomScale = 1.0f; if ( g_bUseCustomBloomScale ) { flCurrentBloomScale = g_flCustomBloomScale; } else { flCurrentBloomScale = mat_bloomscale.GetFloat(); } return flCurrentBloomScale; }
static void GetExposureRange( float *pflAutoExposureMin, float *pflAutoExposureMax ) { // Get min
if ( ( g_bUseCustomAutoExposureMin ) && ( g_flCustomAutoExposureMin > 0.0f ) ) { *pflAutoExposureMin = g_flCustomAutoExposureMin; } else { *pflAutoExposureMin = mat_autoexposure_min.GetFloat(); }
// Get max
if ( ( g_bUseCustomAutoExposureMax ) && ( g_flCustomAutoExposureMax > 0.0f ) ) { *pflAutoExposureMax = g_flCustomAutoExposureMax; } else { *pflAutoExposureMax = mat_autoexposure_max.GetFloat(); }
*pflAutoExposureMax *= mat_autoexposure_max_multiplier.GetFloat();
// Override
if ( mat_hdr_uncapexposure.GetInt() ) { *pflAutoExposureMax = 100.0f; *pflAutoExposureMin = 0.0f; }
// Make sure min <= max
if ( *pflAutoExposureMin > *pflAutoExposureMax ) { *pflAutoExposureMax = *pflAutoExposureMin; } }
void CTonemapSystem::UpdateBucketRanges() { // Only update if our mode changed
if ( m_nCurrentAlgorithm == mat_tonemap_algorithm.GetInt() ) return; m_nCurrentAlgorithm = mat_tonemap_algorithm.GetInt();
//==================================================================//
// Force fallback to original tone mapping algorithm for these mods //
//==================================================================//
static bool s_bFirstTime = true; if ( engine == NULL ) { // Force this code to get hit again so we can change algorithm based on the client
m_nCurrentAlgorithm = -1; } else if ( s_bFirstTime == true ) { s_bFirstTime = false;
// This seems like a bad idea but it's fine for now
const char *sModsForOriginalAlgorithm[] = { "dod", "cstrike", "lostcoast" }; for ( int i=0; i<3; i++ ) { if ( strlen( engine->GetGameDirectory() ) >= strlen( sModsForOriginalAlgorithm[i] ) ) { if ( stricmp( &( engine->GetGameDirectory()[strlen( engine->GetGameDirectory() ) - strlen( sModsForOriginalAlgorithm[i] )] ), sModsForOriginalAlgorithm[i] ) == 0 ) { mat_tonemap_algorithm.SetValue( 0 ); // Original algorithm
m_nCurrentAlgorithm = mat_tonemap_algorithm.GetInt(); break; } } } }
// Get num buckets
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS; if ( mat_tonemap_algorithm.GetInt() == 1 ) nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW;
m_nCurrentQueryFrame = 0; for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ ) { CHistogramBucket *pBucket = &( m_histogramBucketArray[ nBucket ] ); pBucket->m_state = HESTATE_INITIAL; pBucket->m_flScreenMinX = 0.0f; pBucket->m_flScreenMaxX = 1.0f; pBucket->m_flScreenMinY = 0.0f; pBucket->m_flScreenMaxY = 1.0f; if ( nBucket != ( nNumHistogramBuckets - 1 ) ) // Last bucket is special
{ if ( mat_tonemap_algorithm.GetInt() == 0 ) // Original algorithm
{ // Use a logarithmic ramp for high range in the low range
pBucket->m_flMinLuminance = -0.01f + exp( FLerp( log( 0.01f ), log( 0.01f + 1.0f ), 0.0f, nNumHistogramBuckets - 1.0f, nBucket ) ); pBucket->m_flMaxLuminance = -0.01f + exp( FLerp( log( 0.01f ), log( 0.01f + 1.0f ), 0.0f, nNumHistogramBuckets - 1.0f, nBucket + 1.0f ) ); } else { // Use even distribution
pBucket->m_flMinLuminance = float( nBucket ) / float( nNumHistogramBuckets - 1 ); pBucket->m_flMaxLuminance = float( nBucket + 1 ) / float( nNumHistogramBuckets - 1 );
// Use a distribution with slightly more bins in the low range
pBucket->m_flMinLuminance = pBucket->m_flMinLuminance > 0.0f ? powf( pBucket->m_flMinLuminance, 2.5f ) : pBucket->m_flMinLuminance; pBucket->m_flMaxLuminance = pBucket->m_flMaxLuminance > 0.0f ? powf( pBucket->m_flMaxLuminance, 2.5f ) : pBucket->m_flMaxLuminance; } } else { // The last bucket is used as a test to determine the return range for occlusion
// queries to use as a scale factor. some boards (nvidia) have their occlusion
// query return values larger when using AA.
pBucket->m_flMinLuminance = 0.0f; pBucket->m_flMaxLuminance = 100000.0f; }
//Warning( "Bucket %d: min/max %f / %f ", nBucket, pBucket->m_flMinLuminance, pBucket->m_flMaxLuminance );
} }
void CTonemapSystem::SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale ) { m_bOverrideTonemapScaleEnabled = bEnableOverride; m_flOverrideTonemapScale = flTonemapScale; }
void CTonemapSystem::DisplayHistogram() { if ( !mat_show_histogram.GetInt() || !mat_dynamic_tonemapping.GetInt() || ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) ) return;
// Get render context
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->PushRenderTargetAndViewport();
// Prep variables for drawing histogram
int nViewportX, nViewportY, nViewportWidth, nViewportHeight; pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
// Get num bins
int nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS-1; if ( mat_tonemap_algorithm.GetInt() == 1 ) nNumHistogramBuckets = NUM_HISTOGRAM_BUCKETS_NEW-1;
// Count total pixels in current bins
int nMaxValidPixels = 0; int nTotalValidPixels = 0; int nTotalGraphPixelsWide = 0; for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ ) { CHistogramBucket *pBucket = &( m_histogramBucketArray[ nBucket ] ); if ( pBucket->ContainsValidData() ) { nTotalValidPixels += pBucket->m_nPixelsInRange; if ( pBucket->m_nPixelsInRange > nMaxValidPixels ) { nMaxValidPixels = pBucket->m_nPixelsInRange; } }
int nWidth = MAX( 1, 500 * ( pBucket->m_flMaxLuminance - pBucket->m_flMinLuminance ) ); nTotalGraphPixelsWide += nWidth + 2; }
// Clear background to gray for screenshots
//int nBoxWidth = ( nTotalGraphPixelsWide + 20 );
//pRenderContext->ClearColor3ub( 150, 150, 150 );
//pRenderContext->Viewport( nViewportWidth - nBoxWidth, 0, nBoxWidth, 245 );
//pRenderContext->ClearBuffers( true, true );
// Output some text data
if ( !IsGameConsole() && ( mat_show_histogram.GetInt() == 1 ) ) { float flTonemapMinAvgLum = mat_force_tonemap_min_avglum.GetFloat() >= 0.0f ? mat_force_tonemap_min_avglum.GetFloat() : g_flTonemapMinAvgLum; engine->Con_NPrintf( 23 + ( nViewportY / 10 ), "(Histogram luminance is in linear space)" );
engine->Con_NPrintf( 27 + ( nViewportY / 10 ), "AvgLum @ %4.2f%% flTonemapMinAvgLum = %4.2f%% Using %d pixels Override(%s): %4.2f", MAX( 0.0f, FindLocationOfPercentBrightPixels( 50.0f ) ) * 100.0f, flTonemapMinAvgLum, nTotalValidPixels, m_bOverrideTonemapScaleEnabled ? "On" : "Off", m_flOverrideTonemapScale ); engine->Con_NPrintf( 29 + ( nViewportY / 10 ), "BloomScale = %4.2f flTonemapRate = %4.2f mat_accelerate_adjust_exposure_down = %4.2f", GetCurrentBloomScale(), g_flTonemapRate, mat_accelerate_adjust_exposure_down.GetFloat() ); }
int xpStart = nViewportX + nViewportWidth - nTotalGraphPixelsWide - 10; if ( IsGameConsole() ) { xpStart -= 50; }
int yOffset = 4 + nViewportY;
if ( mat_show_histogram.GetInt() == 1 ) { int xp = xpStart; for ( int nBucket = 0; nBucket < nNumHistogramBuckets; nBucket++ ) { int np = 0; CHistogramBucket &e = m_histogramBucketArray[ nBucket ]; if ( e.ContainsValidData() ) np += e.m_nPixelsInRange; int width = MAX( 1, 500 * ( e.m_flMaxLuminance - e.m_flMinLuminance ) ); //Warning( "Bucket %d: min/max %f / %f. m_nPixelsInRange=%d m_nPixels=%d\n", nBucket, e.m_flMinLuminance, e.m_flMaxLuminance, e.m_nPixelsInRange, e.m_nPixels );
if ( np ) { int height = MAX( 1, MIN( HISTOGRAM_BAR_SIZE, ( (float)np / (float)nMaxValidPixels ) * HISTOGRAM_BAR_SIZE ) ); pRenderContext->ClearColor3ub( 255, 0, 0 ); pRenderContext->Viewport( xp, yOffset + HISTOGRAM_BAR_SIZE - height, width, height ); pRenderContext->ClearBuffers( true, true ); } else { int height = 1; pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->Viewport( xp, yOffset + HISTOGRAM_BAR_SIZE - height, width, height ); pRenderContext->ClearBuffers( true, true ); } xp += width + 2; } if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm only
{ float flTonemapPercentTarget = mat_force_tonemap_percent_target.GetFloat() >= 0.0f ? mat_force_tonemap_percent_target.GetFloat() : g_flTonemapPercentTarget; float flYellowTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * flTonemapPercentTarget / 100.0f ) );
float flTonemapMinAvgLum = mat_force_tonemap_min_avglum.GetFloat() >= 0.0f ? mat_force_tonemap_min_avglum.GetFloat() : g_flTonemapMinAvgLum; float flYellowAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * flTonemapMinAvgLum / 100.0f ) );
float flTonemapPercentBrightPixels = mat_force_tonemap_percent_bright_pixels.GetFloat() >= 0.0f ? mat_force_tonemap_percent_bright_pixels.GetFloat() : g_flTonemapPercentBrightPixels; float flTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( flTonemapPercentBrightPixels, flTonemapPercentTarget ) ) ); float flAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( 50.0f ) ) ); // Draw target yellow border bar
int nHeight = HISTOGRAM_BAR_SIZE * 3 / 4; int nHeightOffset = -( HISTOGRAM_BAR_SIZE - nHeight ) / 2; // Green is current percent target location
pRenderContext->Viewport( flYellowTargetPixelStart-1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight - 2, 8, nHeight + 4 ); pRenderContext->ClearColor3ub( 0, 127, 0 ); pRenderContext->ClearBuffers( true, true ); pRenderContext->Viewport( flYellowTargetPixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true ); pRenderContext->Viewport( flTargetPixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight ); pRenderContext->ClearColor3ub( 0, 255, 0 ); pRenderContext->ClearBuffers( true, true ); // Blue is average luminance location
pRenderContext->Viewport( flYellowAveragePixelStart-1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight - 2, 8, nHeight + 4 ); pRenderContext->ClearColor3ub( 0, 114, 188 ); pRenderContext->ClearBuffers( true, true ); pRenderContext->Viewport( flYellowAveragePixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true ); pRenderContext->Viewport( flAveragePixelStart+1, yOffset + nHeightOffset + HISTOGRAM_BAR_SIZE - nHeight, 4, nHeight ); pRenderContext->ClearColor3ub( 0, 191, 243 ); pRenderContext->ClearBuffers( true, true ); } }
// Show actual tonemap value
if ( 1 ) { float flAutoExposureMin; float flAutoExposureMax; GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
float flBarWidth = nTotalGraphPixelsWide; float flBarStart = xpStart;
float flHistogramBarSize = HISTOGRAM_BAR_SIZE; if ( mat_show_histogram.GetInt() == 2 ) // No histogram
{ flHistogramBarSize = 0.0f; }
pRenderContext->Viewport( flBarStart, yOffset + flHistogramBarSize - 4 + 20, flBarWidth, 4 ); pRenderContext->ClearColor3ub( 200, 200, 200 ); pRenderContext->ClearBuffers( true, true );
pRenderContext->Viewport( flBarStart, yOffset + flHistogramBarSize - 4 + 20 + 1, flBarWidth, 2 ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true );
pRenderContext->Viewport( flBarStart + ( flBarWidth * ( ( m_flCurrentTonemapScale - flAutoExposureMin ) / ( flAutoExposureMax - flAutoExposureMin ) ) ) - 1, yOffset + flHistogramBarSize - 4 + 20 - 6 - 1, 4 + 2, 16 + 2 ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true );
pRenderContext->Viewport( flBarStart + ( flBarWidth * ( ( m_flCurrentTonemapScale - flAutoExposureMin ) / ( flAutoExposureMax - flAutoExposureMin ) ) ), yOffset + flHistogramBarSize - 4 + 20 - 6, 4, 16 ); pRenderContext->ClearColor3ub( 255, 255, 0 ); pRenderContext->ClearBuffers( true, true );
if ( !IsGameConsole() ) { int nHeight = 21; if ( mat_show_histogram.GetInt() == 2 ) // No histogram
{ nHeight = 1; }
engine->Con_NPrintf( nHeight + ( nViewportY / 10 ), "%.2f %.2f %.2f", flAutoExposureMin, ( flAutoExposureMax + flAutoExposureMin ) / 2.0f, flAutoExposureMax ); } }
// Last bar doesn't clear properly so draw an extra pixel
pRenderContext->Viewport( 0, 0, 1, 1 ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true );
pRenderContext->PopRenderTargetAndViewport(); }
// Global postprocessing disable switch
static bool s_bOverridePostProcessingDisable = false;
void UpdateMaterialSystemTonemapScalar() { GetCurrentTonemappingSystem()->UpdateMaterialSystemTonemapScalar(); }
void CTonemapSystem::UpdateMaterialSystemTonemapScalar() { if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE ) { // Deal with forced tone map scalar
float flForcedTonemapScale = mat_force_tonemap_scale.GetFloat();
if ( mat_fullbright.GetInt() == 1 ) { flForcedTonemapScale = 1.0f; }
if ( flForcedTonemapScale > 0.0f ) { ResetTonemappingScale( flForcedTonemapScale );
// Send this value to the material system
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) ); return; }
// Override tone map scalar
if ( m_bOverrideTonemapScaleEnabled ) { float flAutoExposureMin; float flAutoExposureMax; GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
float fScale = clamp( m_flOverrideTonemapScale, flAutoExposureMin, flAutoExposureMax ); ResetTonemappingScale( fScale );
// Send this value to the material system
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) ); return; }
// Send this value to the material system
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetToneMappingScaleLinear( Vector( m_flCurrentTonemapScale, m_flCurrentTonemapScale, m_flCurrentTonemapScale ) ); } else { // Send 1.0 to the material system since HDR is disabled
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetToneMappingScaleLinear( Vector( 1.0f, 1.0f, 1.0f ) ); } }
void CTonemapSystem::ResetTonemappingScale( float flTonemapScale ) { if ( flTonemapScale <= 0.0f ) { // L4D Hack to reset the tonemapping scale to the average of min and max since we have such dark lighting
// compared to our other games. 1.0 is no longer a good value when changing spectator targets.
float flAutoExposureMin = 0.0f; float flAutoExposureMax = 0.0f; GetExposureRange( &flAutoExposureMin, &flAutoExposureMax ); flTonemapScale = ( flAutoExposureMin + flAutoExposureMax ) * 0.5f; flTonemapScale = clamp( flTonemapScale, 1.0f, 10.0f ); // Restrict this to the 1-10 range
}
// Force current and target tonemap scalar
m_flCurrentTonemapScale = flTonemapScale; m_flTargetTonemapScale = flTonemapScale;
// Clear averaging history
m_nNumMovingAverageValid = 0; }
void CTonemapSystem::SetTargetTonemappingScale( float flTonemapScale ) { Assert( IsFinite( flTonemapScale ) ); if ( IsFinite( flTonemapScale ) ) { m_flTargetTonemapScale = flTonemapScale; } }
// Local contrast setting
PostProcessParameters_t s_LocalPostProcessParameters[ MAX_SPLITSCREEN_PLAYERS ];
// view fade param settings
static Vector4D s_viewFadeColor[ MAX_SPLITSCREEN_PLAYERS ]; static bool s_bViewFadeModulate[ MAX_SPLITSCREEN_PLAYERS ];
class PPInit { public: PPInit() { for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i ) { s_viewFadeColor[ i ].Init( 0.0f, 0.0f, 0.0f, 0.0f ); s_bViewFadeModulate[ i ] = false; } } };
static PPInit g_PPInit;
void ResetToneMapping( float flTonemappingScale ) { GetCurrentTonemappingSystem()->ResetTonemappingScale( flTonemappingScale );
// Send this value to the material system
CMatRenderContextPtr pRenderContext( materials ); pRenderContext->SetToneMappingScaleLinear( Vector( flTonemappingScale, flTonemappingScale, flTonemappingScale ) ); }
void CTonemapSystem::SetTonemapScale( IMatRenderContext *pRenderContext, float flTargetTonemapScalar, float flMinValue, float flMaxValue ) { Assert( IsFinite( flTargetTonemapScalar ) ); if ( !IsFinite( flTargetTonemapScalar ) ) return;
//=========================================================================//
// Save off new target tonemap scalar so we can compute a weighted average //
//=========================================================================//
if ( m_nNumMovingAverageValid < ARRAYSIZE( m_movingAverageTonemapScale )) { m_movingAverageTonemapScale[ m_nNumMovingAverageValid++ ] = flTargetTonemapScalar; } else { // Scroll, losing oldest
for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ) - 1; i++ ) m_movingAverageTonemapScale[ i ] = m_movingAverageTonemapScale[ i + 1 ]; m_movingAverageTonemapScale[ ARRAYSIZE( m_movingAverageTonemapScale ) - 1 ] = flTargetTonemapScalar; }
//==================================================================//
// Compute a weighted average of the last 10 target tonemap scalars //
//==================================================================//
if ( m_nNumMovingAverageValid == ARRAYSIZE( m_movingAverageTonemapScale ) ) // If we have a full buffer
{ float flWeightedAverage = 0.0f; float flSumWeights = 0.0f; int iMidPoint = ARRAYSIZE( m_movingAverageTonemapScale ) / 2; for ( int i = 0; i < ARRAYSIZE( m_movingAverageTonemapScale ); i++ ) { float flWeight = abs( i - iMidPoint ) * ( 1.0f / ( ARRAYSIZE( m_movingAverageTonemapScale ) / 2 ) ); flSumWeights += flWeight; flWeightedAverage += flWeight * m_movingAverageTonemapScale[i]; } flWeightedAverage *= ( 1.0f / flSumWeights ); flWeightedAverage = clamp( flWeightedAverage, flMinValue, flMaxValue );
SetTargetTonemappingScale( flWeightedAverage ); } else { SetTargetTonemappingScale( flTargetTonemapScalar ); }
//=======================================//
// Smoothly lerp to the target over time //
//=======================================//
float flElapsedTime = MAX( gpGlobals->frametime, 0.0f ); // Clamp to positive
float flRate = g_flTonemapRate;
if ( mat_tonemap_algorithm.GetInt() == 1 ) { flRate *= 2.0f; // Default 2x for the new tone mapping algorithm so it feels the same as the original
}
if ( flRate == 0.0f ) // Zero indicates instantaneous tonemap scaling
{ m_flCurrentTonemapScale = m_flTargetTonemapScale; } else { if ( m_flTargetTonemapScale < m_flCurrentTonemapScale ) { float acc_exposure_adjust = mat_accelerate_adjust_exposure_down.GetFloat();
// Adjust at up to 4x rate when over-exposed.
flRate = MIN( ( acc_exposure_adjust * flRate ), FLerp( flRate, ( acc_exposure_adjust * flRate ), 0.0f, 1.5f, ( m_flCurrentTonemapScale - m_flTargetTonemapScale ) ) ); }
float flRateTimesTime = flRate * flElapsedTime; if ( mat_tonemap_algorithm.GetInt() == 1 ) { // For the new tone mapping algorithm, limit the rate based on the number of bins to
// help reduce the tone map scalar "riding the wave" of the histogram re-building
//Warning( "flRateTimesTime = %.4f", flRateTimesTime );
flRateTimesTime = MIN( flRateTimesTime, ( 1.0f / ( float )( NUM_HISTOGRAM_BUCKETS_NEW - 1 ) ) * 0.25f ); //Warning( " --> %.4f\n", flRateTimesTime );
}
float flAlpha = clamp( flRateTimesTime, 0.0f, 1.0f ); m_flCurrentTonemapScale = ( m_flTargetTonemapScale * flAlpha ) + ( m_flCurrentTonemapScale * ( 1.0f - flAlpha ) ); //m_flCurrentTonemapScale = FLerp( m_flCurrentTonemapScale, m_flTargetTonemapScale, flAlpha );
if ( !IsFinite( m_flCurrentTonemapScale ) ) { Assert( 0 ); m_flCurrentTonemapScale = m_flTargetTonemapScale; } }
//==========================================//
// Step on values if we're forcing a scalar //
//==========================================//
float flForcedTonemapScale = mat_force_tonemap_scale.GetFloat(); if ( flForcedTonemapScale > 0.0f ) { ResetTonemappingScale( flForcedTonemapScale ); } }
//=====================================================================================================================
// Public functions for messing with tone mapping
//=====================================================================================================================
float GetCurrentTonemapScale() { return GetCurrentTonemappingSystem()->GetCurrentTonemappingScale(); }
void SetOverrideTonemapScale( bool bEnableOverride, float flTonemapScale ) { GetCurrentTonemappingSystem()->SetOverrideTonemapScale( bEnableOverride, flTonemapScale ); }
void SetOverridePostProcessingDisable( bool bForceOff ) { s_bOverridePostProcessingDisable = bForceOff; }
void SetPostProcessParams( const PostProcessParameters_t *pPostProcessParameters ) { int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
s_LocalPostProcessParameters[ nSplitScreenSlot ] = *pPostProcessParameters; }
void SetViewFadeParams( byte r, byte g, byte b, byte a, bool bModulate ) { int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
s_viewFadeColor[ nSplitScreenSlot ].Init( float(r)/255.0f, float(g)/255.0f, float(b)/255.0f, float(a)/255.0f ); s_bViewFadeModulate[ nSplitScreenSlot ] = bModulate; }
//=====================================================================================================================
// BloomAdd material proxy ============================================================================================
//=====================================================================================================================
class CBloomAddMaterialProxy : public CEntityMaterialProxy { public: CBloomAddMaterialProxy(); virtual ~CBloomAddMaterialProxy() {} virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
private: IMaterialVar *m_pMaterialParam_BloomAmount;
public: static void SetBloomAmount( float flBloomAmount ) { s_flBloomAmount = flBloomAmount; }
private: static float s_flBloomAmount; };
float CBloomAddMaterialProxy::s_flBloomAmount = 1.0f;
CBloomAddMaterialProxy::CBloomAddMaterialProxy() : m_pMaterialParam_BloomAmount( NULL ) { }
bool CBloomAddMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false;
m_pMaterialParam_BloomAmount = pMaterial->FindVar( "$c0_x", &bFoundVar, false );
return true; }
void CBloomAddMaterialProxy::OnBind( C_BaseEntity *pEnt ) { if ( m_pMaterialParam_BloomAmount ) m_pMaterialParam_BloomAmount->SetFloatValue( s_flBloomAmount ); }
IMaterial *CBloomAddMaterialProxy::GetMaterial() { if ( m_pMaterialParam_BloomAmount == NULL) return NULL;
return m_pMaterialParam_BloomAmount->GetOwningMaterial(); }
EXPOSE_MATERIAL_PROXY( CBloomAddMaterialProxy, BloomAdd );
//=====================================================================================================================
// Engine_Post material proxy ============================================================================================
//=====================================================================================================================
static ConVar mat_software_aa_quality( "mat_software_aa_quality", "0", 0, "Software AA quality mode: (0 - 5-tap filter), (1 - 9-tap filter)" ); static ConVar mat_software_aa_edge_threshold( "mat_software_aa_edge_threshold", "1.0", 0, "Software AA - adjusts the sensitivity of the software AA shader's edge detection (default 1.0 - a lower value will soften more edges, a higher value will soften fewer)" ); static ConVar mat_software_aa_blur_one_pixel_lines( "mat_software_aa_blur_one_pixel_lines", "0.5", 0, "How much software AA should blur one-pixel thick lines: (0.0 - none), (1.0 - lots)" ); static ConVar mat_software_aa_tap_offset( "mat_software_aa_tap_offset", "1.0", 0, "Software AA - adjusts the displacement of the taps used by the software AA shader (default 1.0 - a lower value will make the image sharper, higher will make it blurrier)" ); static ConVar mat_software_aa_debug( "mat_software_aa_debug", "0", 0, "Software AA debug mode: (0 - off), (1 - show number of 'unlike' samples: 0->black, 1->red, 2->green, 3->blue), (2 - show anti-alias blend strength), (3 - show averaged 'unlike' colour)" ); static ConVar mat_software_aa_strength_vgui( "mat_software_aa_strength_vgui", "-1.0", 0, "Same as mat_software_aa_strength, but forced to this value when called by the post vgui AA pass." );
// FXAA convars - defaults taken from 3.11 implementation
static ConVar mat_fxaa_subpixel_C( "mat_fxaa_subpixel_C", "0.5", 0, "Effects sub-pixel AA quality and inversely sharpness (only used on FXAA Console): (0.33 - sharper), (0.5 - default)" ); static ConVar mat_fxaa_edge_sharpness_C( "mat_fxaa_edge_sharpness_C", "8.0", 0, "Does not affect PS3 which uses FXAA_CONSOLE_PS3_EDGE_SHARPNESS define due to being ALU bound (and only safe values are 2, 4, 8). On X360, (2.0 - really soft, good for vector graphics inputs), (4.0 - is softer), (8.0 - is sharper, default)" ); static ConVar mat_fxaa_edge_threshold_C( "mat_fxaa_edge_threshold_C", "0.125", 0, "Does not affect PS3 which uses FXAA_CONSOLE_PS3_EDGE_THRESHOLD define due to being ALU bound (and only safe values are 1/4, 1/8). On X360, (0.125 - default, leaves less aliasing, but is softer, 0.25 - leaves more aliasing and is sharper)" ); static ConVar mat_fxaa_edge_threshold_min_C( "mat_fxaa_edge_threshold_min_C", "0.0", 0, "Trims the algorithm from processing darks. Does not affect PS3 due to being ALU bound. (0.04 - slower and less aliasing in darks, 0.05 - default, 0.06 - faster but more aliasing in darks). Special note: when using FXAA_GREEN_AS_LUMA likely want to set this to zero" ); static ConVar mat_fxaa_subpixel_Q( "mat_fxaa_subpixel_Q", "0.75", 0, "Effects sub-pixel AA quality and inversely sharpness (only used on FXAA Quality): (0.0 - off), (1.0 - upper limit, softer), default = 0.75" ); static ConVar mat_fxaa_edge_threshold_Q( "mat_fxaa_edge_threshold_Q", IsGameConsole() ? "0.166" : ".35", 0, "The minimum amount of local contrast required to apply algorithm: (0.063 - overkill, slower), (0.125 - high quality), (0.166 - default), (0.250 - low quality), (0.333 - too little, faster)" ); static ConVar mat_fxaa_edge_threshold_min_Q( "mat_fxaa_edge_threshold_min_Q", "0.0", 0, "Trims the algorithm from processing darks: (0.0312 - visible limit, slower), (0.0625 - high quality, faster), (0.0833 - upper limit, the start of fisible unfiltered edges). Special note: when using FXAA_GREEN_AS_LUMA, likely want to set this to zero" );
class CEnginePostMaterialProxy : public CEntityMaterialProxy { public: CEnginePostMaterialProxy(); virtual ~CEnginePostMaterialProxy(); virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
private: IMaterialVar *m_pMaterialParam_FXAAValuesC; IMaterialVar *m_pMaterialParam_FXAAValuesQ; IMaterialVar *m_pMaterialParam_AAValues; IMaterialVar *m_pMaterialParam_AAValues2; IMaterialVar *m_pMaterialParam_BloomEnable; IMaterialVar *m_pMaterialParam_BloomAmount; IMaterialVar *m_pMaterialParam_BloomUVTransform; IMaterialVar *m_pMaterialParam_ColCorrectEnable; IMaterialVar *m_pMaterialParam_ColCorrectNumLookups; IMaterialVar *m_pMaterialParam_ColCorrectDefaultWeight; IMaterialVar *m_pMaterialParam_ColCorrectLookupWeights; IMaterialVar *m_pMaterialParam_LocalContrastStrength; IMaterialVar *m_pMaterialParam_LocalContrastEdgeStrength; IMaterialVar *m_pMaterialParam_VignetteStart; IMaterialVar *m_pMaterialParam_VignetteEnd; IMaterialVar *m_pMaterialParam_VignetteBlurEnable; IMaterialVar *m_pMaterialParam_VignetteBlurStrength; IMaterialVar *m_pMaterialParam_FadeToBlackStrength; IMaterialVar *m_pMaterialParam_DepthBlurFocalDistance; IMaterialVar *m_pMaterialParam_DepthBlurStrength; IMaterialVar *m_pMaterialParam_ScreenBlurStrength; IMaterialVar *m_pMaterialParam_FilmGrainStrength; IMaterialVar *m_pMaterialParam_VomitEnable; IMaterialVar *m_pMaterialParam_VomitColor1; IMaterialVar *m_pMaterialParam_VomitColor2; IMaterialVar *m_pMaterialParam_FadeColor; IMaterialVar *m_pMaterialParam_FadeType;
public: static void SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize, bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount ); static void SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength ); static void SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D destTexSize );
#if defined(_PS3)
static IMaterial *GetEnginePostMaterial( IMaterialSystem * materials ); static int GetLocalContrastEnable( IMaterialSystem * materials ); static ITexture *GetSrcTexture( IMaterialSystem * materials ); static ITexture *GetSrcPS3Texture( IMaterialSystem * materials ); static ITexture *GetDstRT0Texture( IMaterialSystem * materials ); static ITexture *GetDstRT1Texture( IMaterialSystem * materials ); #endif
private: static float s_vBloomAAValues[4]; static float s_vBloomAAValues2[4]; static float s_vFXAAValuesC[4]; static float s_vFXAAValuesQ[4]; static float s_vBloomUVTransform[4]; static int s_PostBloomEnable; static float s_PostBloomAmount;
#if defined(_PS3)
static IMaterial *s_pEnginePostMaterial; static ITexture *s_pSrcTexture; static ITexture *s_pSrcPS3Texture; static ITexture *s_pDstRT0Texture; static ITexture *s_pDstRT1Texture; static IMaterialVar *s_pMaterialParam_LocalContrastEnable; #endif
};
float CEnginePostMaterialProxy::s_vBloomAAValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vBloomAAValues2[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vFXAAValuesC[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vFXAAValuesQ[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float CEnginePostMaterialProxy::s_vBloomUVTransform[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; int CEnginePostMaterialProxy::s_PostBloomEnable = 1; float CEnginePostMaterialProxy::s_PostBloomAmount = 1.0f; #if defined(_PS3)
IMaterial *CEnginePostMaterialProxy::s_pEnginePostMaterial = NULL; ITexture *CEnginePostMaterialProxy::s_pSrcTexture = NULL; ITexture *CEnginePostMaterialProxy::s_pSrcPS3Texture = NULL; ITexture *CEnginePostMaterialProxy::s_pDstRT0Texture = NULL; ITexture *CEnginePostMaterialProxy::s_pDstRT1Texture = NULL; IMaterialVar *CEnginePostMaterialProxy::s_pMaterialParam_LocalContrastEnable = NULL; #endif
CEnginePostMaterialProxy::CEnginePostMaterialProxy() { m_pMaterialParam_FXAAValuesC = NULL; m_pMaterialParam_FXAAValuesQ = NULL; m_pMaterialParam_AAValues = NULL; m_pMaterialParam_AAValues2 = NULL; m_pMaterialParam_BloomUVTransform = NULL; m_pMaterialParam_BloomEnable = NULL; m_pMaterialParam_BloomAmount = NULL; m_pMaterialParam_ColCorrectEnable = NULL; m_pMaterialParam_ColCorrectNumLookups = NULL; m_pMaterialParam_ColCorrectDefaultWeight = NULL; m_pMaterialParam_ColCorrectLookupWeights = NULL; m_pMaterialParam_LocalContrastStrength = NULL; m_pMaterialParam_LocalContrastEdgeStrength = NULL; m_pMaterialParam_VignetteStart = NULL; m_pMaterialParam_VignetteEnd = NULL; m_pMaterialParam_VignetteBlurEnable = NULL; m_pMaterialParam_VignetteBlurStrength = NULL; m_pMaterialParam_FadeToBlackStrength = NULL; m_pMaterialParam_DepthBlurFocalDistance = NULL; m_pMaterialParam_DepthBlurStrength = NULL; m_pMaterialParam_ScreenBlurStrength = NULL; m_pMaterialParam_FilmGrainStrength = NULL;
#if defined(_PS3)
s_pMaterialParam_LocalContrastEnable = NULL; s_pEnginePostMaterial = NULL; s_pSrcTexture = NULL; s_pSrcPS3Texture = NULL; s_pDstRT0Texture = NULL; s_pDstRT1Texture = NULL; #endif
}
CEnginePostMaterialProxy::~CEnginePostMaterialProxy() { // Do nothing
}
bool CEnginePostMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false;
m_pMaterialParam_FXAAValuesC = pMaterial->FindVar( "$FXAAInternalC", &bFoundVar, false ); m_pMaterialParam_FXAAValuesQ = pMaterial->FindVar( "$FXAAInternalQ", &bFoundVar, false ); m_pMaterialParam_AAValues = pMaterial->FindVar( "$AAInternal1", &bFoundVar, false ); m_pMaterialParam_AAValues2 = pMaterial->FindVar( "$AAInternal3", &bFoundVar, false ); m_pMaterialParam_BloomUVTransform = pMaterial->FindVar( "$AAInternal2", &bFoundVar, false ); m_pMaterialParam_BloomEnable = pMaterial->FindVar( "$bloomEnable", &bFoundVar, false ); m_pMaterialParam_BloomAmount = pMaterial->FindVar( "$bloomAmount", &bFoundVar, false ); m_pMaterialParam_ColCorrectEnable = pMaterial->FindVar( "$colCorrectEnable", &bFoundVar, false ); m_pMaterialParam_ColCorrectNumLookups = pMaterial->FindVar( "$colCorrect_NumLookups", &bFoundVar, false ); m_pMaterialParam_ColCorrectDefaultWeight = pMaterial->FindVar( "$colCorrect_DefaultWeight", &bFoundVar, false ); m_pMaterialParam_ColCorrectLookupWeights = pMaterial->FindVar( "$colCorrect_LookupWeights", &bFoundVar, false ); m_pMaterialParam_LocalContrastStrength = pMaterial->FindVar( "$localContrastScale", &bFoundVar, false ); m_pMaterialParam_LocalContrastEdgeStrength = pMaterial->FindVar( "$localContrastEdgeScale", &bFoundVar, false ); m_pMaterialParam_VignetteStart = pMaterial->FindVar( "$localContrastVignetteStart", &bFoundVar, false ); m_pMaterialParam_VignetteEnd = pMaterial->FindVar( "$localContrastVignetteEnd", &bFoundVar, false ); m_pMaterialParam_VignetteBlurEnable = pMaterial->FindVar( "$blurredVignetteEnable", &bFoundVar, false ); m_pMaterialParam_VignetteBlurStrength = pMaterial->FindVar( "$blurredVignetteScale", &bFoundVar, false ); m_pMaterialParam_FadeToBlackStrength = pMaterial->FindVar( "$fadeToBlackScale", &bFoundVar, false ); m_pMaterialParam_DepthBlurFocalDistance = pMaterial->FindVar( "$depthBlurFocalDistance", &bFoundVar, false ); m_pMaterialParam_DepthBlurStrength = pMaterial->FindVar( "$depthBlurStrength", &bFoundVar, false ); m_pMaterialParam_ScreenBlurStrength = pMaterial->FindVar( "$screenBlurStrength", &bFoundVar, false ); m_pMaterialParam_FilmGrainStrength = pMaterial->FindVar( "$noiseScale", &bFoundVar, false ); m_pMaterialParam_VomitEnable = pMaterial->FindVar( "$vomitEnable", &bFoundVar, false ); m_pMaterialParam_VomitColor1 = pMaterial->FindVar( "$vomitColor1", &bFoundVar, false ); m_pMaterialParam_VomitColor2 = pMaterial->FindVar( "$vomitColor2", &bFoundVar, false ); m_pMaterialParam_FadeColor = pMaterial->FindVar( "$fadeColor", &bFoundVar, false ); m_pMaterialParam_FadeType = pMaterial->FindVar( "$fade", &bFoundVar, false );
#if defined(_PS3)
s_pEnginePostMaterial = NULL; s_pSrcTexture = NULL; s_pSrcPS3Texture = NULL; s_pDstRT0Texture = NULL; s_pDstRT1Texture = NULL; s_pMaterialParam_LocalContrastEnable = NULL; #endif
return true; }
void CEnginePostMaterialProxy::OnBind( C_BaseEntity *pEnt ) { int nSplitScreenSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
if ( m_pMaterialParam_FXAAValuesC ) m_pMaterialParam_FXAAValuesC->SetVecValue( s_vFXAAValuesC, 4 );
if ( m_pMaterialParam_FXAAValuesQ ) m_pMaterialParam_FXAAValuesQ->SetVecValue( s_vFXAAValuesQ, 4 );
if ( m_pMaterialParam_AAValues ) m_pMaterialParam_AAValues->SetVecValue( s_vBloomAAValues, 4 );
if ( m_pMaterialParam_AAValues2 ) m_pMaterialParam_AAValues2->SetVecValue( s_vBloomAAValues2, 4 );
if ( m_pMaterialParam_BloomUVTransform ) m_pMaterialParam_BloomUVTransform->SetVecValue( s_vBloomUVTransform, 4 );
if ( m_pMaterialParam_BloomEnable ) m_pMaterialParam_BloomEnable->SetIntValue( s_PostBloomEnable );
if ( m_pMaterialParam_BloomAmount ) m_pMaterialParam_BloomAmount->SetFloatValue( s_PostBloomAmount );
if ( m_pMaterialParam_LocalContrastStrength ) m_pMaterialParam_LocalContrastStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_LOCAL_CONTRAST_STRENGTH ] );
if ( m_pMaterialParam_LocalContrastEdgeStrength ) m_pMaterialParam_LocalContrastEdgeStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_LOCAL_CONTRAST_EDGE_STRENGTH ] );
if ( m_pMaterialParam_VignetteStart ) m_pMaterialParam_VignetteStart->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_START ] );
if ( m_pMaterialParam_VignetteEnd ) m_pMaterialParam_VignetteEnd->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_END ] );
if ( m_pMaterialParam_VignetteBlurEnable ) m_pMaterialParam_VignetteBlurEnable->SetIntValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] > 0.0f ? 1 : 0 );
if ( m_pMaterialParam_VignetteBlurStrength ) m_pMaterialParam_VignetteBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_VIGNETTE_BLUR_STRENGTH ] );
if ( m_pMaterialParam_FadeToBlackStrength ) m_pMaterialParam_FadeToBlackStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_FADE_TO_BLACK_STRENGTH ] );
if ( m_pMaterialParam_DepthBlurFocalDistance ) m_pMaterialParam_DepthBlurFocalDistance->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_DEPTH_BLUR_FOCAL_DISTANCE ] );
if ( m_pMaterialParam_DepthBlurStrength ) m_pMaterialParam_DepthBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_DEPTH_BLUR_STRENGTH ] );
if ( m_pMaterialParam_ScreenBlurStrength ) m_pMaterialParam_ScreenBlurStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_SCREEN_BLUR_STRENGTH ] );
if ( m_pMaterialParam_FilmGrainStrength ) m_pMaterialParam_FilmGrainStrength->SetFloatValue( s_LocalPostProcessParameters[ nSplitScreenSlot ].m_flParameters[ PPPN_FILM_GRAIN_STRENGTH ] );
#ifdef PORTAL2
const C_Portal_Player* pLocalPlayer = C_Portal_Player::GetLocalPortalPlayer(); const bool bScreenSpacePaintEffectIsActive = pLocalPlayer && pLocalPlayer->ScreenSpacePaintEffectIsActive(); if ( m_pMaterialParam_VomitEnable ) { m_pMaterialParam_VomitEnable->SetIntValue( bScreenSpacePaintEffectIsActive ? 1 : 0 ); }
if ( bScreenSpacePaintEffectIsActive && m_pMaterialParam_VomitColor1 && m_pMaterialParam_VomitColor2 ) { pLocalPlayer->SetScreenSpacePaintEffectColors( m_pMaterialParam_VomitColor1, m_pMaterialParam_VomitColor2 ); } #endif
if ( m_pMaterialParam_FadeType ) { int nFadeType = ( s_bViewFadeModulate[nSplitScreenSlot] ) ? 2 : 1; nFadeType = ( s_viewFadeColor[nSplitScreenSlot][3] > 0.0f ) ? nFadeType : 0; m_pMaterialParam_FadeType->SetIntValue( nFadeType ); }
if ( m_pMaterialParam_FadeColor ) { m_pMaterialParam_FadeColor->SetVecValue( s_viewFadeColor[nSplitScreenSlot].Base(), 4 ); } }
IMaterial *CEnginePostMaterialProxy::GetMaterial() { if ( m_pMaterialParam_AAValues == NULL) return NULL;
return m_pMaterialParam_AAValues->GetOwningMaterial(); }
#if defined(_PS3 )
IMaterial *CEnginePostMaterialProxy::GetEnginePostMaterial( IMaterialSystem * materials ) { if( s_pEnginePostMaterial == NULL) { s_pEnginePostMaterial = materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true ); }
return s_pEnginePostMaterial; }
int CEnginePostMaterialProxy::GetLocalContrastEnable( IMaterialSystem * materials ) { if( s_pMaterialParam_LocalContrastEnable == NULL ) { s_pMaterialParam_LocalContrastEnable = GetEnginePostMaterial( materials )->FindVar( "$localcontrastenable", NULL, false ); }
return s_pMaterialParam_LocalContrastEnable->GetIntValueFast(); }
ITexture *CEnginePostMaterialProxy::GetSrcTexture( IMaterialSystem * materials ) { if( s_pSrcTexture == NULL ) { s_pSrcTexture = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); }
return s_pSrcTexture; }
ITexture *CEnginePostMaterialProxy::GetSrcPS3Texture( IMaterialSystem * materials ) { if( s_pSrcPS3Texture == NULL ) { s_pSrcPS3Texture = materials->FindTexture( "^PS3^BACKBUFFER", TEXTURE_GROUP_RENDER_TARGET ); }
return s_pSrcPS3Texture; }
ITexture *CEnginePostMaterialProxy::GetDstRT0Texture( IMaterialSystem * materials ) { if( s_pDstRT0Texture == NULL ) { s_pDstRT0Texture = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET ); }
return s_pDstRT0Texture; }
ITexture *CEnginePostMaterialProxy::GetDstRT1Texture( IMaterialSystem * materials ) { if( s_pDstRT1Texture == NULL ) { s_pDstRT1Texture = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); }
return s_pDstRT1Texture; }
#endif
void CEnginePostMaterialProxy::SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength ) { if ( bPerformSoftwareAA ) { // Pass ConVars to the material by proxy
// FXAA
// Console
s_vFXAAValuesC[0] = mat_fxaa_subpixel_C.GetFloat(); s_vFXAAValuesC[1] = mat_fxaa_edge_sharpness_C.GetFloat(); s_vFXAAValuesC[2] = mat_fxaa_edge_threshold_C.GetFloat(); s_vFXAAValuesC[3] = mat_fxaa_edge_threshold_min_C.GetFloat();
// Quality
s_vFXAAValuesQ[0] = mat_fxaa_subpixel_Q.GetFloat(); s_vFXAAValuesC[1] = 0.0f; // unused
s_vFXAAValuesQ[2] = mat_fxaa_edge_threshold_Q.GetFloat(); s_vFXAAValuesC[3] = mat_fxaa_edge_threshold_min_Q.GetFloat();
// Software AA (old)
// - the strength of the AA effect (from 0 to 1)
// - how much to allow 1-pixel lines to be blurred (from 0 to 1)
// - pick one of the two quality modes (5-tap or 9-tap filter)
// - optionally enable one of several debug modes (via dynamic combos)
// NOTE: this order matches pixel shader constants in Engine_Post_ps2x.fxc
s_vBloomAAValues[0] = flAAStrength; s_vBloomAAValues[1] = 1.0f - mat_software_aa_blur_one_pixel_lines.GetFloat(); s_vBloomAAValues[2] = mat_software_aa_quality.GetInt(); s_vBloomAAValues[3] = mat_software_aa_debug.GetInt(); s_vBloomAAValues2[0] = mat_software_aa_edge_threshold.GetFloat(); s_vBloomAAValues2[1] = mat_software_aa_tap_offset.GetFloat(); //s_vBloomAAValues2[2] = unused;
//s_vBloomAAValues2[3] = unused;
} else { // Zero-strength AA is interpreted as "AA disabled"
s_vBloomAAValues[0] = 0.0f; } }
void CEnginePostMaterialProxy::SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D fbSize ) { // Engine_Post uses a UV transform (from (quarter-res) bloom texture coords ('1')
// to (full-res) framebuffer texture coords ('2')).
//
// We compute the UV transform as an offset and a scale, using the texture coordinates
// of the top-left corner of the screen to compute the offset and the coordinate
// change from the top-left to the bottom-right of the quad to compute the scale.
// Take texel coordinates (start = top-left, end = bottom-right):
Vector2D texelStart1 = Vector2D( fullViewportBloomUVs.x, fullViewportBloomUVs.y ); Vector2D texelStart2 = Vector2D( fullViewportFBUVs.x, fullViewportFBUVs.y ); Vector2D texelEnd1 = Vector2D( fullViewportBloomUVs.z, fullViewportBloomUVs.w ); Vector2D texelEnd2 = Vector2D( fullViewportFBUVs.z, fullViewportFBUVs.w ); // ...and transform to UV coordinates:
Vector2D texRes1 = fbSize / 4; Vector2D texRes2 = fbSize; Vector2D uvStart1 = ( texelStart1 + Vector2D(0.5,0.5) ) / texRes1; Vector2D uvStart2 = ( texelStart2 + Vector2D(0.5,0.5) ) / texRes2; Vector2D dUV1 = ( texelEnd1 - texelStart1 ) / texRes1; Vector2D dUV2 = ( texelEnd2 - texelStart2 ) / texRes2;
// We scale about the rect's top-left pixel centre (not the origin) in UV-space:
// uv' = ((uv - uvStart1)*uvScale + uvStart1) + uvOffset
// = uvScale*uv + uvOffset + uvStart1*(1 - uvScale)
Vector2D uvOffset = uvStart2 - uvStart1; Vector2D uvScale = dUV2 / dUV1; uvOffset = uvOffset + uvStart1*(Vector2D(1,1) - uvScale);
s_vBloomUVTransform[0] = uvOffset.x; s_vBloomUVTransform[1] = uvOffset.y; s_vBloomUVTransform[2] = uvScale.x; s_vBloomUVTransform[3] = uvScale.y; }
void CEnginePostMaterialProxy::SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize, bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength, float flBloomAmount ) { s_PostBloomEnable = bPerformBloom ? 1 : 0; s_PostBloomAmount = flBloomAmount;
SetupEnginePostMaterialAA( bPerformSoftwareAA, flAAStrength );
SetupEnginePostMaterialTextureTransform( fullViewportBloomUVs, fullViewportFBUVs, destTexSize ); }
EXPOSE_MATERIAL_PROXY( CEnginePostMaterialProxy, engine_post );
static void DrawBloomDebugBoxes( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight ) { // draw inset rects which should have a centered bloom
pRenderContext->PushRenderTargetAndViewport(); pRenderContext->SetRenderTarget( IsPS3() ? materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ) : NULL );
// full screen clear
pRenderContext->Viewport( nX, nY, nWidth, nHeight ); pRenderContext->ClearColor3ub( 0, 0, 0 ); pRenderContext->ClearBuffers( true, true );
// inset for screensafe
int inset = 64; int size = 32;
// centerish, translating
static int wx = 0; wx = ( wx + 1 ) & 63;
pRenderContext->Viewport( nWidth / 2 + nX + wx, nY + nHeight / 2, size, size ); pRenderContext->ClearColor3ub( 255, 255, 255 ); pRenderContext->ClearBuffers( true, true );
// upper left
pRenderContext->Viewport( nX + inset, nY + inset, size, size ); pRenderContext->ClearBuffers( true, true );
// upper right
pRenderContext->Viewport( nX + nWidth - inset - size, nY + inset, size, size ); pRenderContext->ClearBuffers( true, true ); // lower right
pRenderContext->Viewport( nX + nWidth - inset - size, nY + nHeight - inset - size, size, size ); pRenderContext->ClearBuffers( true, true ); // lower left
pRenderContext->Viewport( nX + inset, nX + nHeight - inset - size, size, size ); pRenderContext->ClearBuffers( true, true ); // restore
pRenderContext->PopRenderTargetAndViewport(); }
static float GetBloomAmount( void ) { HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
bool bBloomEnabled = (mat_hdr_level.GetInt() >= 1); if ( !engine->MapHasHDRLighting() ) bBloomEnabled = false; if ( mat_force_bloom.GetInt() ) bBloomEnabled = true; if ( mat_disable_bloom.GetInt() ) bBloomEnabled = false; if ( building_cubemaps.GetBool() ) bBloomEnabled = false; if ( mat_fullbright.GetInt() == 1 ) { bBloomEnabled = false; }
float flBloomAmount=0.0;
if (bBloomEnabled) { static float currentBloomAmount = 1.0f; float rate = mat_bloomamount_rate.GetFloat();
// Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
currentBloomAmount = GetCurrentBloomScale() * rate + ( 1.0f - rate ) * currentBloomAmount; flBloomAmount = currentBloomAmount;
if (IsGameConsole()) { //we want to scale the bloom effect down because the effect textures are lower reolution on the 360.
//target match 1280x1024
if ( (g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height == 720) ) { flBloomAmount *= (720.0f/1024.0f); } else //640x480
{ flBloomAmount *= (480.0f/1024.0f); } } }
if ( hdrType == HDR_TYPE_NONE ) { flBloomAmount *= mat_non_hdr_bloom_scalefactor.GetFloat(); }
flBloomAmount *= mat_bloom_scalefactor_scalar.GetFloat();
return flBloomAmount; }
// Control for dumping render targets to files for debugging
static ConVar mat_dump_rts( "mat_dump_rts", "0", FCVAR_DEVELOPMENTONLY ); static bool s_bDumpRenderTargets = false; static int s_nRTIndex = 0;
// Dump a rendertarget to a TGA. Useful for looking at intermediate render target results.
static void DumpTGAofRenderTarget( const int width, const int height, const char *pFilename ) { // Ensure that mat_queue_mode is zero...this ConVarRef lookup isn't cheap, but this is rarely-run debug code
ConVarRef mat_queue_mode( "mat_queue_mode" ); if ( mat_queue_mode.GetInt() != 0 ) { DevMsg( "Error: mat_queue_mode must be 0 to dump debug rendertargets\n" ); mat_dump_rts.SetValue( 0 ); // Just report this error once and stop trying to dump images
return; }
CMatRenderContextPtr pRenderContext( materials );
// Get the data from the render target and save to disk bitmap bits
unsigned char *pImage = ( unsigned char * )malloc( width * 4 * height );
// Get Bits from the material system
pRenderContext->ReadPixels( 0, 0, width, height, pImage, IMAGE_FORMAT_RGBA8888 );
// allocate a buffer to write the tga into
int iMaxTGASize = 1024 + (width * height * 4); void *pTGA = malloc( iMaxTGASize ); CUtlBuffer buffer( pTGA, iMaxTGASize );
if( !TGAWriter::WriteToBuffer( pImage, buffer, width, height, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 ) ) { Error( "Couldn't write bitmap data snapshot.\n" ); }
free( pImage );
// async write to disk (this will take ownership of the memory)
char szPathedFileName[_MAX_PATH]; Q_snprintf( szPathedFileName, sizeof(szPathedFileName), "//MOD/%d_%s_%s.tga", s_nRTIndex++, pFilename, IsOSX() ? "OSX" : "PC" );
FileHandle_t fileTGA = filesystem->Open( szPathedFileName, "wb" ); filesystem->Write( buffer.Base(), buffer.TellPut(), fileTGA ); filesystem->Close( fileTGA );
free( pTGA ); }
static bool s_bScreenEffectTextureIsUpdated = false;
// WARNING: This function sets rendertarget and viewport. Save and restore is left to the caller.
static void DownsampleFBQuarterSize( IMatRenderContext *pRenderContext, int nSrcWidth, int nSrcHeight, ITexture* pDest, bool bFloatHDR = false ) { Assert( pRenderContext ); Assert( pDest );
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
Assert( pDest->GetActualWidth() == nSrcWidth / 4 ); Assert( pDest->GetActualHeight() == nSrcHeight / 4 );
IMaterial *downsample_mat; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) { downsample_mat = CDownsampleMaterialProxy::GetDownsampleMaterial( materials ); CDownsampleMaterialProxy::SetupDownsampleMaterial( g_flBloomExponent, g_flBloomSaturation ); } else #endif
{ downsample_mat = materials->FindMaterial( bFloatHDR ? "dev/downsample" : "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true );
bool bFound; IMaterialVar *pbloomexpvar = downsample_mat->FindVar( "$bloomexp", &bFound, false ); if ( bFound ) { pbloomexpvar->SetFloatValue( g_flBloomExponent ); }
IMaterialVar *pbloomsaturationvar = downsample_mat->FindVar( "$bloomsaturation", &bFound, false ); if ( bFound ) { pbloomsaturationvar->SetFloatValue( g_flBloomSaturation ); } }
// downsample fb to rt0
SetRenderTargetAndViewPort( pDest ); // 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
pRenderContext->DrawScreenSpaceRectangle( downsample_mat, 0, 0, nSrcWidth/4, nSrcHeight/4, 0, 0, nSrcWidth-2, nSrcHeight-2, nSrcWidth, nSrcHeight );
if ( IsX360() ) { pRenderContext->CopyRenderTargetToTextureEx( pDest, 0, NULL, NULL ); } else if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "QuarterSizeFB" ); } }
static void Generate8BitBloomTexture( IMatRenderContext *pRenderContext, int x, int y, int w, int h, bool bExtractBloomRange, bool bClearRGB = true ) { pRenderContext->PushRenderTargetAndViewport();
ITexture *pSrc; IMaterial *xblur_mat; IMaterial *yblur_mat; ITexture *dest_rt0; ITexture *dest_rt1;
#if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) { pSrc = CEnginePostMaterialProxy::GetSrcTexture( materials );
// FIXME: assumes bClearRGB = false here
xblur_mat = CXBlurMaterialProxy::GetXBlurMaterial( materials ); yblur_mat = CYBlurMaterialProxy::GetYBlurMaterial( materials );
dest_rt0 = CEnginePostMaterialProxy::GetDstRT0Texture( materials ); dest_rt1 = CEnginePostMaterialProxy::GetDstRT1Texture( materials ); } else #endif
{ pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
xblur_mat = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true ); yblur_mat = NULL; if ( bClearRGB ) { yblur_mat = materials->FindMaterial( "dev/blurfiltery_nohdr_clear", TEXTURE_GROUP_OTHER, true ); } else { yblur_mat = materials->FindMaterial( "dev/blurfiltery_nohdr", TEXTURE_GROUP_OTHER, true ); }
dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET ); dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); }
int nSrcWidth = pSrc->GetActualWidth(); int nSrcHeight = pSrc->GetActualHeight(); //,nViewportHeight;
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 ); Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 ); Assert( dest_rt1->GetActualWidth() == pSrc->GetActualWidth() / 4 ); Assert( dest_rt1->GetActualHeight() == pSrc->GetActualHeight() / 4 );
// downsample fb to rt0
if ( bExtractBloomRange ) { DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0 ); } else { // just downsample, don't apply bloom extraction math
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0, true ); }
// Gaussian blur x rt0 to rt1
SetRenderTargetAndViewPort( dest_rt1 ); pRenderContext->DrawScreenSpaceRectangle( xblur_mat, 0, 0, nSrcWidth/4, nSrcHeight/4, 0, 0, nSrcWidth/4-1, nSrcHeight/4-1, nSrcWidth/4, nSrcHeight/4 ); if ( IsX360() ) { pRenderContext->CopyRenderTargetToTextureEx( dest_rt1, 0, NULL, NULL ); } else if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "BlurX" ); }
// Gaussian blur y rt1 to rt0
SetRenderTargetAndViewPort( dest_rt0 ); IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL ); pBloomAmountVar->SetFloatValue( 1.0f ); // the bloom amount is now applied in engine_post or bloomadd materials
pRenderContext->DrawScreenSpaceRectangle( yblur_mat, 0, 0, nSrcWidth / 4, nSrcHeight / 4, 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1, nSrcWidth / 4, nSrcHeight / 4 );
if ( IsX360() ) { pRenderContext->CopyRenderTargetToTextureEx( dest_rt0, 0, NULL, NULL ); } else if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "BlurYAndBloom" ); }
pRenderContext->PopRenderTargetAndViewport(); }
static void DoTonemapping( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight, float flAutoExposureMin, float flAutoExposureMax ) { // Skip if HDR disabled
if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) return;
// Update HDR histogram
if ( mat_dynamic_tonemapping.GetInt() ) { if ( s_bScreenEffectTextureIsUpdated == false && !IsPS3() ) { // FIXME: nX/nY/nWidth/nHeight are used here, but the equivalent parameters are ignored in Generate8BitBloomTexture
UpdateScreenEffectTexture( 0, nX, nY, nWidth, nHeight, false ); s_bScreenEffectTextureIsUpdated = true; }
GetCurrentTonemappingSystem()->IssueAndReceiveBucketQueries();
float flTargetScalar = GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar(); float flTargetScalarClamped = MAX( flAutoExposureMin, MIN( flAutoExposureMax, flTargetScalar ) ); flTargetScalarClamped = MAX( 0.001f, flTargetScalarClamped ); // Don't let this go to 0!
GetCurrentTonemappingSystem()->SetTonemapScale( pRenderContext, flTargetScalarClamped, flAutoExposureMin, flAutoExposureMax );
if ( mat_show_histogram.GetInt() ) { float flTonemapPercentTarget = mat_force_tonemap_percent_target.GetFloat() >= 0.0f ? mat_force_tonemap_percent_target.GetFloat() : g_flTonemapPercentTarget; float flTonemapPercentBrightPixels = mat_force_tonemap_percent_bright_pixels.GetFloat() >= 0.0f ? mat_force_tonemap_percent_bright_pixels.GetFloat() : g_flTonemapPercentBrightPixels; bool bDrawTextThisFrame = ( mat_show_histogram.GetInt() == 1 ); if ( IsGameConsole() ) { static float s_flLastTimeUpdate = 0.0f; if ( int( gpGlobals->curtime ) - int( s_flLastTimeUpdate ) >= 2 ) { s_flLastTimeUpdate = gpGlobals->curtime; bDrawTextThisFrame = true; } else { bDrawTextThisFrame = false; } }
if ( bDrawTextThisFrame == true ) { if ( mat_tonemap_algorithm.GetInt() == 0 ) { engine->Con_NPrintf( 25 + ( nY / 10 ), "(Original algorithm) Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Current Scalar: %4.2f", flTargetScalar, flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() ); } else { if ( IsGameConsole() ) { engine->Con_NPrintf( 25 + ( nY / 10 ), "[mat_show_histogram] Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f\n", GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar( true ), flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() ); } else { engine->Con_NPrintf( 25 + ( nY / 10 ), "%.2f%% of pixels above %d%% target @ %4.2f%% Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f", flTonemapPercentBrightPixels, (int)flTonemapPercentTarget, ( GetCurrentTonemappingSystem()->FindLocationOfPercentBrightPixels( flTonemapPercentBrightPixels, flTonemapPercentTarget ) * 100.0f ), GetCurrentTonemappingSystem()->ComputeTargetTonemapScalar( true ), flAutoExposureMin, flAutoExposureMax, GetCurrentTonemappingSystem()->GetCurrentTonemappingScale() ); } } } } } }
static void CenterScaleQuadUVs( Vector4D & quadUVs, const Vector2D & uvScale ) { Vector2D uvMid = 0.5f*Vector2D( ( quadUVs.z + quadUVs.x ), ( quadUVs.w + quadUVs.y ) ); Vector2D uvRange= 0.5f*Vector2D( ( quadUVs.z - quadUVs.x ), ( quadUVs.w - quadUVs.y ) ); quadUVs.x = uvMid.x - uvScale.x*uvRange.x; quadUVs.y = uvMid.y - uvScale.y*uvRange.y; quadUVs.z = uvMid.x + uvScale.x*uvRange.x; quadUVs.w = uvMid.y + uvScale.y*uvRange.y; }
#ifdef IRONSIGHT
bool ApplyIronSightScopeEffect( int x, int y, int w, int h, CViewSetup *pViewSetup, bool bPreparationStage ) { //the preparation stage returns true if following steps like rendering the scope stencil shape are necessary.
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if (pPlayer) { C_WeaponCSBase *pWeapon = (C_WeaponCSBase *)pPlayer->GetActiveWeapon(); if (pWeapon) { if ( pWeapon->GetIronSightController() ) { if ( bPreparationStage ) { return pWeapon->GetIronSightController()->PrepareScopeEffect( x, y, w, h, pViewSetup ); } else { pWeapon->GetIronSightController()->RenderScopeEffect( x, y, w, h, pViewSetup ); } } } } return false; } #endif
static ConVar r_queued_post_processing( "r_queued_post_processing", "0" );
// How much to dice up the screen during post-processing on 360
// This has really marginal effects, but 4x1 does seem vaguely better for post-processing
static ConVar mat_postprocess_x( "mat_postprocess_x", "4" ); static ConVar mat_postprocess_y( "mat_postprocess_y", "1" ); static ConVar mat_postprocess_enable( "mat_postprocess_enable", "1", FCVAR_CHEAT );
bool DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, bool bPostVGui ) { // don't do this if disabled or in alt-tab
if ( s_bOverridePostProcessingDisable || w <=0 || h <= 0 ) { return false; }
if ( s_bDumpRenderTargets ) { s_bDumpRenderTargets = false; // Turn off from previous frame
}
if ( mat_dump_rts.GetBool() ) { s_bDumpRenderTargets = true; // Dump intermediate render targets this frame
s_nRTIndex = 0; // Used for numbering the TGA files for easy browsing
mat_dump_rts.SetValue( 0 ); // We only want to capture one frame, on rising edge of this convar
} CMatRenderContextPtr pRenderContext( materials ); PIXEVENT( pRenderContext, "DoEnginePostProcessing" );
if ( r_queued_post_processing.GetInt() ) { ICallQueue *pCallQueue = pRenderContext->GetCallQueue(); if ( pCallQueue ) { pCallQueue->QueueCall( DoEnginePostProcessing, x, y, w, h, bFlashlightIsOn, bPostVGui ); return false; } }
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 16 ); //max out pixel shader threads
#endif
GetTonemapSettingsFromEnvTonemapController();
g_bFlashlightIsOn = bFlashlightIsOn;
// Use the appropriate autoexposure min / max settings.
// Mapmaker's overrides the convar settings.
float flAutoExposureMin; float flAutoExposureMax; GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
if ( mat_debug_bloom.GetInt() == 1 ) { DrawBloomDebugBoxes( pRenderContext, x, y, w, h ); }
s_bScreenEffectTextureIsUpdated = false; // Force an update in tone mapping code
DoTonemapping( pRenderContext, x, y, w, h, flAutoExposureMin, flAutoExposureMax );
if ( mat_postprocess_enable.GetInt() == 0 ) { GetCurrentTonemappingSystem()->DisplayHistogram();
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation(); #endif
return false; }
ConVarRef mat_software_aa_strength( "mat_software_aa_strength" );
// Set software-AA on by default for 360
if ( mat_software_aa_strength.GetFloat() == -1.0f ) { if ( IsGameConsole() ) { mat_software_aa_strength.SetValue( 1.0f ); if ( g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height > 480 ) { mat_software_aa_quality.SetValue( 0 ); } else { // For standard-def, we have fewer pixels so we can afford 'high quality' mode (5->9 taps/pixel)
mat_software_aa_quality.SetValue( 1 );
// Disable in 480p for now
mat_software_aa_strength.SetValue( 0.0f ); } } else { mat_software_aa_strength.SetValue( 0.0f ); } }
// Same trick for setting up the vgui aa strength
if ( mat_software_aa_strength_vgui.GetFloat() == -1.0f ) { if ( IsGameConsole() && (g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height == 720) ) { mat_software_aa_strength_vgui.SetValue( 2.0f ); } else { mat_software_aa_strength_vgui.SetValue( 1.0f ); } }
float flAAStrength;
// We do a second AA blur pass over the TF intro menus. use mat_software_aa_strength_vgui there instead
if ( IsGameConsole() && bPostVGui ) { flAAStrength = mat_software_aa_strength_vgui.GetFloat(); } else { flAAStrength = mat_software_aa_strength.GetFloat(); }
// Bloom, software-AA and color-correction (applied in 1 pass, after generation of the bloom texture)
float flBloomScale = GetBloomAmount(); bool bPerformSoftwareAA = ( flAAStrength != 0.0f ); bool bPerformBloom = !bPostVGui && ( flBloomScale > 0.0f ); bool bPerformColCorrect = !bPostVGui && g_pColorCorrectionMgr->HasNonZeroColorCorrectionWeights() && mat_colorcorrection.GetInt();
pRenderContext->EnableColorCorrection( bPerformColCorrect );
bool bPerformLocalContrastEnhancement = false; IMaterial* pPostMat; if ( engine->IsSplitScreenActive() ) pPostMat = materials->FindMaterial( "dev/engine_post_splitscreen", TEXTURE_GROUP_OTHER, true ); else { #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pPostMat = CEnginePostMaterialProxy::GetEnginePostMaterial( materials ); else pPostMat = materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true );
#else
pPostMat = materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true ); #endif
}
if ( pPostMat ) {
#if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) { bPerformLocalContrastEnhancement = CEnginePostMaterialProxy::GetLocalContrastEnable( materials ) && mat_local_contrast_enable.GetBool(); } else { IMaterialVar* pMatVar = pPostMat->FindVar( "$localcontrastenable", NULL, false );
if ( pMatVar ) { bPerformLocalContrastEnhancement = pMatVar->GetIntValue() && mat_local_contrast_enable.GetBool(); } } #else
IMaterialVar* pMatVar = pPostMat->FindVar( "$localcontrastenable", NULL, false );
if ( pMatVar ) { bPerformLocalContrastEnhancement = pMatVar->GetIntValue() && mat_local_contrast_enable.GetBool(); } #endif
}
bool bPerformedPostProcessPass = false;
if ( true ) { ITexture *pSrc; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pSrc = CEnginePostMaterialProxy::GetSrcTexture( materials ); else #endif
pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
int nSrcWidth = pSrc->GetActualWidth(); int nSrcHeight = pSrc->GetActualHeight();
ITexture *dest_rt1; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) dest_rt1 = CEnginePostMaterialProxy::GetDstRT1Texture( materials ); else #endif
dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
if ( !s_bScreenEffectTextureIsUpdated && !IsPS3() ) { UpdateScreenEffectTexture( 0, x, y, w, h, false ); s_bScreenEffectTextureIsUpdated = true; }
if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nSrcWidth, nSrcHeight, "FullFrameFB" ); }
if ( bPerformBloom || bPerformLocalContrastEnhancement ) { Generate8BitBloomTexture( pRenderContext, x, y, w, h, true, false ); }
#ifdef PORTAL2
// Note: the C_Portal_Player::RenderScreenSpaceEffect() call must stay right after
// Generate8BitBloomTexture(), because on the 360 it relies on the contents of the low-res blur buffer
// staying in EDRAM unaltered between the two calls. (Screenspace paint uses RGB channels, local contrast
// uses A of the low-res render target). Or at least this is roughly what the comment said for L4D when I
// acquired the Boomer vomit particle system by way of my actions.
// -Ted
C_Portal_Player::RenderLocalScreenSpaceEffect( PAINT_SCREEN_SPACE_EFFECT, pRenderContext, x, y, w, h ); #else if CSTRIKE15
C_CSPlayer::RenderLocalScreenSpaceEffect( AR_LEADER_SCREEN_SPACE_EFFECT, pRenderContext, x, y, w, h ); #endif
// Now add bloom (dest_rt0) to the framebuffer and perform software anti-aliasing and
// colour correction, all in one pass (improves performance, reduces quantization errors)
//
// First, set up texel coords (in the bloom and fb textures) at the centres of the outer pixel of the viewport:
float flFbWidth = ( float )pSrc->GetActualWidth(); float flFbHeight = ( float )pSrc->GetActualHeight();
Vector4D fullViewportPostSrcCorners( 0.0f, -0.5f, nSrcWidth/4-1, nSrcHeight/4-1 ); Vector4D fullViewportPostSrcRect( nSrcWidth * ( ( x + 0 ) / flFbWidth ) / 4.0f + 0.0f, nSrcHeight * ( ( y + 0 ) / flFbHeight ) / 4.0f - 0.5f, nSrcWidth * ( ( x + w ) / flFbWidth ) / 4.0f - 1.0f, nSrcHeight * ( ( y + h ) / flFbHeight ) / 4.0f - 1.0f ); Vector4D fullViewportPostDestCorners( 0.0f, 0.0f, nSrcWidth - 1, nSrcHeight - 1 ); Rect_t fullViewportPostDestRect = { x, y, w, h }; Vector2D destTexSize( nSrcWidth, nSrcHeight );
// When the viewport is not fullscreen, the UV-space size of a pixel changes
// (due to a stretchrect blit being used in UpdateScreenEffectTexture()), so
// we need to adjust the corner-pixel UVs sent to our drawrect call:
Vector2D uvScale( ( nSrcWidth - ( nSrcWidth / (float)w ) ) / ( nSrcWidth - 1 ), ( nSrcHeight - ( nSrcHeight / (float)h ) ) / ( nSrcHeight - 1 ) ); CenterScaleQuadUVs( fullViewportPostSrcCorners, uvScale ); CenterScaleQuadUVs( fullViewportPostDestCorners, uvScale );
Rect_t partialViewportPostDestRect = fullViewportPostDestRect; Vector4D partialViewportPostSrcCorners = fullViewportPostSrcCorners; if ( debug_postproc.GetInt() == 2 ) { // Restrict the post effects to the centre quarter of the screen
// (we only use a portion of the bloom texture, so this *does* affect bloom texture UVs)
partialViewportPostDestRect.x += 0.25f*fullViewportPostDestRect.width; partialViewportPostDestRect.y += 0.25f*fullViewportPostDestRect.height; partialViewportPostDestRect.width -= 0.50f*fullViewportPostDestRect.width; partialViewportPostDestRect.height -= 0.50f*fullViewportPostDestRect.height;
// This math interprets texel coords as being at corner pixel centers (*not* at corner vertices):
Vector2D uvScale( 1.0f - ( (w / 2) / (float)(w - 1) ), 1.0f - ( (h / 2) / (float)(h - 1) ) ); CenterScaleQuadUVs( partialViewportPostSrcCorners, uvScale ); }
// Temporary hack... Color correction was crashing on the first frame
// when run outside the debugger for some mods (DoD). This forces it to skip
// a frame, ensuring we don't get the weird texture crash we otherwise would.
// FIXME: This will be removed when the true cause is found [added: Main CL 144694]
static bool bFirstFrame = !IsGameConsole(); if ( !bFirstFrame || !bPerformColCorrect ) { HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType(); if ( hdrType == HDR_TYPE_FLOAT ) { // reset to render the final combine passes to the "real" display backbuffer
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_BACK_BUFFER_INDEX, BACK_BUFFER_INDEX_DEFAULT ); pRenderContext->SetRenderTarget( NULL ); }
Vector4D v4dFullViewportPostDestRect( fullViewportPostDestRect.x, fullViewportPostDestRect.y, fullViewportPostDestRect.x + fullViewportPostDestRect.width - 1, fullViewportPostDestRect.y + fullViewportPostDestRect.height - 1 );
CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcRect, v4dFullViewportPostDestRect, destTexSize, bPerformSoftwareAA, bPerformBloom, bPerformColCorrect, flAAStrength, flBloomScale );
pRenderContext->DrawScreenSpaceRectangle( pPostMat, 0, 0, partialViewportPostDestRect.width, partialViewportPostDestRect.height, fullViewportPostSrcRect.x, fullViewportPostSrcRect.y, fullViewportPostSrcRect.z, fullViewportPostSrcRect.w,
dest_rt1->GetActualWidth(), dest_rt1->GetActualHeight(), GetClientWorldEntity()->GetClientRenderable(), mat_postprocess_x.GetInt(), mat_postprocess_y.GetInt() );
bPerformedPostProcessPass = true;
if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( partialViewportPostDestRect.width, partialViewportPostDestRect.height, "EnginePost" ); } } bFirstFrame = false; }
GetCurrentTonemappingSystem()->DisplayHistogram();
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation(); #endif
return bPerformedPostProcessPass; }
void DoBlurFade( float flStrength, float flDesaturate, int x, int y, int w, int h ) { if ( flStrength < 0.0001f ) { return; }
UpdateScreenEffectTexture();
CMatRenderContextPtr pRenderContext( materials ); Generate8BitBloomTexture( pRenderContext, x, y, w, h, false, false );
int nViewportX, nViewportY, nViewportWidth, nViewportHeight; pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
int nRtWidth, nRtHeight; pRenderContext->GetRenderTargetDimensions( nRtWidth, nRtHeight );
IMaterial* pMat = materials->FindMaterial( "dev/fade_blur", TEXTURE_GROUP_OTHER, true ); bool bFound = false; IMaterialVar* pVar = pMat->FindVar( "$c0_x", &bFound ); if ( pVar && bFound ) { pVar->SetFloatValue( flStrength ); }
// Desaturate strength
pVar = pMat->FindVar( "$c1_x", &bFound ); if ( pVar && bFound ) { pVar->SetFloatValue( flDesaturate ); }
// Color fade
pVar = pMat->FindVar( "$c2_x", &bFound ); if ( pVar && bFound ) { pVar->SetFloatValue( mat_blur_r.GetFloat() ); }
pVar = pMat->FindVar( "$c2_y", &bFound ); if ( pVar && bFound ) { pVar->SetFloatValue( mat_blur_g.GetFloat() ); }
pVar = pMat->FindVar( "$c2_z", &bFound ); if ( pVar && bFound ) { pVar->SetFloatValue( mat_blur_b.GetFloat() ); }
// Draw
pRenderContext->DrawScreenSpaceRectangle( pMat, 0, 0, nViewportWidth, nViewportHeight, nViewportX, nViewportY, nViewportX + nViewportWidth - 1, nViewportY + nViewportHeight - 1, nRtWidth, nRtHeight ); if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nViewportWidth, nViewportHeight, "BlurFade" ); } }
// Motion Blur Material Proxy =========================================================================================
static float g_vMotionBlurValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; static float g_vMotionBlurViewportValues[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; class CMotionBlurMaterialProxy : public CEntityMaterialProxy { public: CMotionBlurMaterialProxy(); virtual ~CMotionBlurMaterialProxy(); virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pEntity ); virtual IMaterial *GetMaterial();
#if defined(_PS3)
static IMaterial *GetMotionBlurMaterial( IMaterialSystem * materials ); #endif
private: IMaterialVar *m_pMaterialParam; IMaterialVar *m_pMaterialParamViewport;
#if defined(_PS3)
static IMaterial *s_pMotionBlurMaterial; #endif
};
#if defined(_PS3)
IMaterial *CMotionBlurMaterialProxy::s_pMotionBlurMaterial = NULL; #endif
CMotionBlurMaterialProxy::CMotionBlurMaterialProxy() { m_pMaterialParam = NULL;
#if defined(_PS3)
s_pMotionBlurMaterial = NULL; #endif
}
CMotionBlurMaterialProxy::~CMotionBlurMaterialProxy() { // Do nothing
}
bool CMotionBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool bFoundVar = false;
m_pMaterialParam = pMaterial->FindVar( "$MotionBlurInternal", &bFoundVar, false ); if ( bFoundVar == false) return false;
m_pMaterialParamViewport = pMaterial->FindVar( "$MotionBlurViewportInternal", &bFoundVar, false ); if ( bFoundVar == false) return false;
return true; }
void CMotionBlurMaterialProxy::OnBind( C_BaseEntity *pEnt ) { if ( m_pMaterialParam != NULL ) { m_pMaterialParam->SetVecValue( g_vMotionBlurValues, 4 ); }
if ( m_pMaterialParamViewport != NULL ) { m_pMaterialParamViewport->SetVecValue( g_vMotionBlurViewportValues, 4 ); } }
IMaterial *CMotionBlurMaterialProxy::GetMaterial() { if ( m_pMaterialParam == NULL) return NULL;
return m_pMaterialParam->GetOwningMaterial(); }
#if defined(_PS3)
IMaterial *CMotionBlurMaterialProxy::GetMotionBlurMaterial( IMaterialSystem * materials ) { if( s_pMotionBlurMaterial == NULL) { s_pMotionBlurMaterial = materials->FindMaterial( "dev/motion_blur", TEXTURE_GROUP_OTHER, true ); }
return s_pMotionBlurMaterial; }
#endif
EXPOSE_MATERIAL_PROXY( CMotionBlurMaterialProxy, MotionBlur );
//=====================================================================================================================
// Image-space Motion Blur ============================================================================================
//=====================================================================================================================
#ifdef PORTAL2
ConVar mat_motion_blur_forward_enabled( "mat_motion_blur_forward_enabled", "1" ); ConVar mat_motion_blur_falling_min( "mat_motion_blur_falling_min", "8.0" ); #else
ConVar mat_motion_blur_forward_enabled( "mat_motion_blur_forward_enabled", "0" ); ConVar mat_motion_blur_falling_min( "mat_motion_blur_falling_min", "10.0" ); #endif
ConVar mat_motion_blur_falling_max( "mat_motion_blur_falling_max", "20.0" ); ConVar mat_motion_blur_falling_intensity( "mat_motion_blur_falling_intensity", "1.0" ); //ConVar mat_motion_blur_roll_intensity( "mat_motion_blur_roll_intensity", "1.0" );
ConVar mat_motion_blur_rotation_intensity( "mat_motion_blur_rotation_intensity", "1.0" ); ConVar mat_motion_blur_strength( "mat_motion_blur_strength", "1.0" );
struct MotionBlurHistory_t { MotionBlurHistory_t() { m_flLastTimeUpdate = 0.0f; m_flPreviousPitch = 0.0f; m_flPreviousYaw = 0.0f; m_vPreviousPositon.Init( 0.0f, 0.0f, 0.0f ); m_mPreviousFrameBasisVectors; m_flNoRotationalMotionBlurUntil = 0.0f; SetIdentityMatrix( m_mPreviousFrameBasisVectors ); }
float m_flLastTimeUpdate; float m_flPreviousPitch; float m_flPreviousYaw; Vector m_vPreviousPositon; matrix3x4_t m_mPreviousFrameBasisVectors; float m_flNoRotationalMotionBlurUntil; };
bool DoImageSpaceMotionBlur( const CViewSetup &view ) { #ifdef PORTAL2
// DEMO HACKS!!!
if( gpGlobals->maxClients == 2 ) return false; #endif
ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" ); if ( ( !mat_motion_blur_enabled.GetInt() ) || ( view.m_nMotionBlurMode == MOTION_BLUR_DISABLE ) ) { return false; }
int x = view.x; int y = view.y; int w = view.width; int h = view.height;
bool bSFMBlur = ( view.m_nMotionBlurMode == MOTION_BLUR_SFM );
//======================================================================================================//
// Get these convars here to make it easier to remove them later and to default each client differently //
//======================================================================================================//
float flMotionBlurRotationIntensity = mat_motion_blur_rotation_intensity.GetFloat() * 0.15f; // The default is to not blur past 15% of the range
float flMotionBlurRollIntensity = 0.3f; // * mat_motion_blur_roll_intensity.GetFloat(); // The default is to not blur past 30% of the range
float flMotionBlurFallingIntensity = mat_motion_blur_falling_intensity.GetFloat(); float flMotionBlurFallingMin = mat_motion_blur_falling_min.GetFloat(); float flMotionBlurFallingMax = mat_motion_blur_falling_max.GetFloat(); float flMotionBlurGlobalStrength = mat_motion_blur_strength.GetFloat();
//===============================================================================//
// Set global g_vMotionBlurValues[4] values so material proxy can get the values //
//===============================================================================//
if ( true ) { //=====================//
// Previous frame data //
//=====================//
static MotionBlurHistory_t s_History[ MAX_SPLITSCREEN_PLAYERS ];
ASSERT_LOCAL_PLAYER_RESOLVABLE(); MotionBlurHistory_t &history = s_History[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
//float vPreviousSideVec[3] = { s_mPreviousFrameBasisVectors[0][1], s_mPreviousFrameBasisVectors[1][1], s_mPreviousFrameBasisVectors[2][1] };
//float vPreviousForwardVec[3] = { s_mPreviousFrameBasisVectors[0][0], s_mPreviousFrameBasisVectors[1][0], s_mPreviousFrameBasisVectors[2][0] };
//float vPreviousUpVec[3] = { s_mPreviousFrameBasisVectors[0][2], s_mPreviousFrameBasisVectors[1][2], s_mPreviousFrameBasisVectors[2][2] };
float flTimeElapsed;
// Motion blur driven by CViewSetup, not engine time (currently only driven by SFM)
if ( bSFMBlur ) { history.m_flLastTimeUpdate = 0.0f; // Don't care about these, but zero them out
history.m_flNoRotationalMotionBlurUntil = 0.0f; //
flTimeElapsed = view.m_flShutterTime;
history.m_vPreviousPositon[0] = view.m_vShutterOpenPosition.x; //
history.m_vPreviousPositon[1] = view.m_vShutterOpenPosition.y; // Slam "previous" values to shutter open values
history.m_vPreviousPositon[2] = view.m_vShutterOpenPosition.z; //
AngleMatrix( view.m_shutterOpenAngles, history.m_mPreviousFrameBasisVectors );//
history.m_flPreviousPitch = view.m_shutterOpenAngles[PITCH]; // Get "previous" pitch & wrap to +-180
while ( history.m_flPreviousPitch > 180.0f ) history.m_flPreviousPitch -= 360.0f; while ( history.m_flPreviousPitch < -180.0f ) history.m_flPreviousPitch += 360.0f;
history.m_flPreviousYaw = view.m_shutterOpenAngles[YAW]; // Get "previous" yaw & wrap to +-180
while ( history.m_flPreviousYaw > 180.0f ) history.m_flPreviousYaw -= 360.0f; while ( history.m_flPreviousYaw < -180.0f ) history.m_flPreviousYaw += 360.0f; } else // view.m_nDoMotionBlurMode == MOTION_BLUR_GAME
{ flTimeElapsed = gpGlobals->realtime - history.m_flLastTimeUpdate; }
#ifdef PORTAL2
float flCurrentPitch = view.angles[PITCH]; float flCurrentYaw = view.angles[YAW]; CPortal_Player *pPortalPlayer = ToPortalPlayer( C_BasePlayer::GetLocalPlayer() ); if ( pPortalPlayer ) { Vector vUp = pPortalPlayer->GetPortalPlayerLocalData().m_Up;
// compute flCurrentPitch by getting angle between forward vector and up vector
matrix3x4_t mCurrentBasisVectors; AngleMatrix( view.angles, mCurrentBasisVectors ); Vector vCurrentForward( mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] ); Vector vCurrentRight( mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] ); AngleVectors( view.angles, &vCurrentForward ); flCurrentPitch = RAD2DEG( acosf( DotProduct( vUp, vCurrentForward ) ) ) - 90.f;
// compute flCurrentYaw by accumulating the offset
Vector vOldRight( history.m_mPreviousFrameBasisVectors[0][1], history.m_mPreviousFrameBasisVectors[1][1], history.m_mPreviousFrameBasisVectors[2][1] ); float flDot = DotProduct( vCurrentRight, vOldRight ); flDot = clamp( flDot, -1.f, 1.f ); if ( vCurrentRight == vOldRight ) flDot = 1.f; float flAcos = acosf( flDot );
Vector vCross = CrossProduct( vCurrentRight, vOldRight ); float flSign = -clamp( DotProduct( vCross, vUp ), -1.f, 1.f ); float flYawOffset = Sign( flSign ) * RAD2DEG( flAcos ); flCurrentYaw = history.m_flPreviousYaw + flYawOffset; }
// wrap pitch and yaw to +-180
while ( flCurrentPitch > 180.0f ) flCurrentPitch -= 360.0f; while ( flCurrentPitch < -180.0f ) flCurrentPitch += 360.0f;
while ( flCurrentYaw > 180.0f ) flCurrentYaw -= 360.0f; while ( flCurrentYaw < -180.0f ) flCurrentYaw += 360.0f; #else
//===================================//
// Get current pitch & wrap to +-180 //
//===================================//
float flCurrentPitch = view.angles[PITCH]; if ( bSFMBlur ) flCurrentPitch = view.m_shutterCloseAngles[PITCH]; while ( flCurrentPitch > 180.0f ) flCurrentPitch -= 360.0f; while ( flCurrentPitch < -180.0f ) flCurrentPitch += 360.0f;
//=================================//
// Get current yaw & wrap to +-180 //
//=================================//
float flCurrentYaw = view.angles[YAW]; if ( bSFMBlur ) flCurrentYaw = view.m_shutterCloseAngles[YAW]; while ( flCurrentYaw > 180.0f ) flCurrentYaw -= 360.0f; while ( flCurrentYaw < -180.0f ) flCurrentYaw += 360.0f; #endif
/*engine->Con_NPrintf( 0, "Blur Pitch: %6.2f Yaw: %6.2f", flCurrentPitch, flCurrentYaw );
engine->Con_NPrintf( 1, "Blur FOV: %6.2f Aspect: %6.2f Ortho: %s", view.fov, view.m_flAspectRatio, view.m_bOrtho ? "Yes" : "No" ); engine->Con_NPrintf( 2, "View Angles: %6.2f %6.2f %6.2f", XYZ(view.angles) );*/
//===========================//
// Get current basis vectors //
//===========================//
matrix3x4_t mCurrentBasisVectors;
if ( bSFMBlur ) { AngleMatrix( view.m_shutterCloseAngles, mCurrentBasisVectors ); } else { AngleMatrix( view.angles, mCurrentBasisVectors ); }
Vector vCurrentSideVec( mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] ); Vector vCurrentForwardVec( mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] ); //Vector vCurrentUpVec( mCurrentBasisVectors[0][2], mCurrentBasisVectors[1][2], mCurrentBasisVectors[2][2] );
//===========================================================================//
// Get current position (shutter close time when SFM is driving motion blur) //
//===========================================================================//
Vector vCurrentPosition = view.origin;
if ( bSFMBlur ) { vCurrentPosition[0] = view.m_vShutterClosePosition.x; vCurrentPosition[1] = view.m_vShutterClosePosition.y; vCurrentPosition[2] = view.m_vShutterClosePosition.z; }
//===============================================================//
// Evaluate change in position to determine if we need to update //
//===============================================================//
Vector vPositionChange( 0.0f, 0.0f, 0.0f ); VectorSubtract( history.m_vPreviousPositon, vCurrentPosition, vPositionChange ); if ( ( VectorLength( vPositionChange ) > 30.0f ) && ( flTimeElapsed >= 0.5f ) && !bSFMBlur ) { //=======================================================//
// If we moved a far distance in one frame and more than //
// half a second elapsed, disable motion blur this frame //
//=======================================================//
//engine->Con_NPrintf( 8, " Pos change && time > 0.5 seconds %f ", gpGlobals->realtime );
g_vMotionBlurValues[0] = 0.0f; g_vMotionBlurValues[1] = 0.0f; g_vMotionBlurValues[2] = 0.0f; g_vMotionBlurValues[3] = 0.0f; } else if ( ( flTimeElapsed > ( 1.0f / 15.0f ) ) && !bSFMBlur ) { //==========================================//
// If slower than 15 fps, don't motion blur //
//==========================================//
g_vMotionBlurValues[0] = 0.0f; g_vMotionBlurValues[1] = 0.0f; g_vMotionBlurValues[2] = 0.0f; g_vMotionBlurValues[3] = 0.0f; } else if ( ( VectorLength( vPositionChange ) > 50.0f ) && !bSFMBlur ) { //================================================================================//
// We moved a far distance in a frame, use the same motion blur as last frame //
// because I think we just went through a portal (should we ifdef this behavior?) //
//================================================================================//
//engine->Con_NPrintf( 8, " Position changed %f units @ %.2f time ", VectorLength( vPositionChange ), gpGlobals->realtime );
history.m_flNoRotationalMotionBlurUntil = gpGlobals->realtime + 1.0f; // Wait a second until the portal craziness calms down
} else { //====================//
// Normal update path //
//====================//
// Compute horizontal and vertical fov
float flHorizontalFov = view.fov; float flVerticalFov = ( view.m_flAspectRatio <= 0.0f ) ? ( view.fov ) : ( view.fov / view.m_flAspectRatio ); //engine->Con_NPrintf( 2, "Horizontal Fov: %6.2f Vertical Fov: %6.2f", flHorizontalFov, flVerticalFov );
//=====================//
// Forward motion blur //
//=====================//
float flViewDotMotion = DotProduct( vCurrentForwardVec, vPositionChange ); if ( mat_motion_blur_forward_enabled.GetBool() ) // Want forward and falling
g_vMotionBlurValues[2] = flViewDotMotion; else // Falling only
g_vMotionBlurValues[2] = flViewDotMotion * fabs( vCurrentForwardVec[2] ); // Only want this if we're looking up or down;
//====================================//
// Yaw (Compensate for circle strafe) //
//====================================//
float flSideDotMotion = DotProduct( vCurrentSideVec, vPositionChange ); float flYawDiffOriginal = history.m_flPreviousYaw - flCurrentYaw; if ( ( ( history.m_flPreviousYaw - flCurrentYaw > 180.0f ) || ( history.m_flPreviousYaw - flCurrentYaw < -180.0f ) ) && ( ( history.m_flPreviousYaw + flCurrentYaw > -180.0f ) && ( history.m_flPreviousYaw + flCurrentYaw < 180.0f ) ) ) flYawDiffOriginal = history.m_flPreviousYaw + flCurrentYaw;
float flYawDiffAdjusted = flYawDiffOriginal + ( flSideDotMotion / 3.0f ); // Yes, 3.0 is a magic number, sue me
// Make sure the adjustment only lessens the effect, not magnify it or reverse it
if ( flYawDiffOriginal < 0.0f ) flYawDiffAdjusted = clamp ( flYawDiffAdjusted, flYawDiffOriginal, 0.0f ); else flYawDiffAdjusted = clamp ( flYawDiffAdjusted, 0.0f, flYawDiffOriginal );
// Use pitch to dampen yaw
float flUndampenedYaw = flYawDiffAdjusted / flHorizontalFov; g_vMotionBlurValues[0] = flUndampenedYaw * ( 1.0f - ( fabs( flCurrentPitch ) / 90.0f ) ); // Dampen horizontal yaw blur based on pitch
//engine->Con_NPrintf( 4, "flSideDotMotion: %6.2f yaw diff: %6.2f ( %6.2f, %6.2f )", flSideDotMotion, ( s_flPreviousYaw - flCurrentYaw ), flYawDiffOriginal, flYawDiffAdjusted );
//=======================================//
// Pitch (Compensate for forward motion) //
//=======================================//
float flPitchCompensateMask = 1.0f - ( ( 1.0f - fabs( vCurrentForwardVec[2] ) ) * ( 1.0f - fabs( vCurrentForwardVec[2] ) ) ); float flPitchDiffOriginal = history.m_flPreviousPitch - flCurrentPitch; float flPitchDiffAdjusted = flPitchDiffOriginal;
if ( flCurrentPitch > 0.0f ) flPitchDiffAdjusted = flPitchDiffOriginal - ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
else flPitchDiffAdjusted = flPitchDiffOriginal + ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
// Make sure the adjustment only lessens the effect, not magnify it or reverse it
if ( flPitchDiffOriginal < 0.0f ) flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, flPitchDiffOriginal, 0.0f ); else flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, 0.0f, flPitchDiffOriginal );
g_vMotionBlurValues[1] = flPitchDiffAdjusted / flVerticalFov;
//engine->Con_NPrintf( 5, "flViewDotMotion %6.2f, flPitchCompensateMask %6.2f, flPitchDiffOriginal %6.2f, flPitchDiffAdjusted %6.2f, g_vMotionBlurValues[1] %6.2f", flViewDotMotion, flPitchCompensateMask, flPitchDiffOriginal, flPitchDiffAdjusted, g_vMotionBlurValues[1]);
//========================================================//
// Roll (Enabled when we're looking down and yaw changes) //
//========================================================//
g_vMotionBlurValues[3] = flUndampenedYaw; // Roll starts out as undampened yaw intensity and is then scaled by pitch
g_vMotionBlurValues[3] *= ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ); // Dampen roll based on pitch^3
//engine->Con_NPrintf( 4, "[2] before scale and bias: %6.2f", g_vMotionBlurValues[2] );
//engine->Con_NPrintf( 5, "[3] before scale and bias: %6.2f", g_vMotionBlurValues[3] );
//==============================================================//
// Time-adjust falling effect until we can do something smarter //
//==============================================================//
if ( flTimeElapsed > 0.0f ) g_vMotionBlurValues[2] /= flTimeElapsed * 30.0f; // 1/30th of a second?
else g_vMotionBlurValues[2] = 0.0f;
// Scale and bias values after time adjustment
g_vMotionBlurValues[2] = clamp( ( fabs( g_vMotionBlurValues[2] ) - flMotionBlurFallingMin ) / ( flMotionBlurFallingMax - flMotionBlurFallingMin ), 0.0f, 1.0f ) * ( g_vMotionBlurValues[2] >= 0.0f ? 1.0f : -1.0f ); g_vMotionBlurValues[2] /= 30.0f; // To counter-adjust for time adjustment above
//=================//
// Apply intensity //
//=================//
g_vMotionBlurValues[0] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength; g_vMotionBlurValues[1] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength; g_vMotionBlurValues[2] *= flMotionBlurFallingIntensity * flMotionBlurGlobalStrength; g_vMotionBlurValues[3] *= flMotionBlurRollIntensity * flMotionBlurGlobalStrength;
//===============================================================//
// Dampen motion blur from 100%-0% as fps drops from 50fps-30fps //
//===============================================================//
if ( !IsGameConsole() && !bSFMBlur ) // I'm not doing this on the 360 yet since I can't test it. SFM doesn't need it either
{ float flSlowFps = 30.0f; float flFastFps = 50.0f; float flCurrentFps = ( flTimeElapsed > 0.0f ) ? ( 1.0f / flTimeElapsed ) : 0.0f; float flDampenFactor = clamp( ( ( flCurrentFps - flSlowFps ) / ( flFastFps - flSlowFps ) ), 0.0f, 1.0f );
//engine->Con_NPrintf( 4, "gpGlobals->realtime %.2f gpGlobals->curtime %.2f", gpGlobals->realtime, gpGlobals->curtime );
//engine->Con_NPrintf( 5, "flCurrentFps %.2f", flCurrentFps );
//engine->Con_NPrintf( 7, "flTimeElapsed %.2f", flTimeElapsed );
g_vMotionBlurValues[0] *= flDampenFactor; g_vMotionBlurValues[1] *= flDampenFactor; g_vMotionBlurValues[2] *= flDampenFactor; g_vMotionBlurValues[3] *= flDampenFactor;
//engine->Con_NPrintf( 6, "Dampen: %.2f", flDampenFactor );
}
//engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f );
}
//============================================//
// Zero out blur if still in that time window //
//============================================//
if ( !bSFMBlur && ( gpGlobals->realtime < history.m_flNoRotationalMotionBlurUntil ) ) { //engine->Con_NPrintf( 9, " No Rotation @ %f ", gpGlobals->realtime );
// Zero out rotational blur but leave forward/falling blur alone
g_vMotionBlurValues[0] = 0.0f; // X
g_vMotionBlurValues[1] = 0.0f; // Y
g_vMotionBlurValues[3] = 0.0f; // Roll
} else { history.m_flNoRotationalMotionBlurUntil = 0.0f; }
//================================================================================//
// Disable roll and forward blur if in split screen and reduce the blur intensity //
//================================================================================//
if ( engine->IsSplitScreenActive() ) { g_vMotionBlurValues[0] *= 0.25f; // X
g_vMotionBlurValues[1] *= 0.25f; // Y
g_vMotionBlurValues[2] = 0.0f; g_vMotionBlurValues[3] = 0.0f; }
//====================================//
// Store current frame for next frame //
//====================================//
VectorCopy( vCurrentPosition, history.m_vPreviousPositon ); history.m_mPreviousFrameBasisVectors = mCurrentBasisVectors; history.m_flPreviousPitch = flCurrentPitch; history.m_flPreviousYaw = flCurrentYaw; history.m_flLastTimeUpdate = gpGlobals->realtime; }
//engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f );
#if defined ( PORTAL2 )
C_Portal_Player* pLocalPlayer = C_Portal_Player::GetLocalPortalPlayer( GET_ACTIVE_SPLITSCREEN_SLOT() ); if ( pLocalPlayer && pLocalPlayer->GetMotionBlurAmount() > 0.0f ) { g_vMotionBlurValues[2] = pLocalPlayer->GetMotionBlurAmount(); } #endif
//==========================================//
// Set global g_vMotionBlurViewportValues[] //
//==========================================//
if ( true ) { ITexture *pSrc; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pSrc = CEnginePostMaterialProxy::GetSrcTexture( materials ); else #endif
pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
float flSrcWidth = ( float )pSrc->GetActualWidth(); float flSrcHeight = ( float )pSrc->GetActualHeight();
// NOTE #1: float4 stored as ( minx, miny, maxy, maxx )...z&w have been swapped to save pixel shader instructions
// NOTE #2: This code should definitely work for 2 players (horizontal or vertical), or 4 players (4 corners), but
// it might have to be modified if we ever want to support other split screen configurations
int nOffset; // Offset by one pixel to land in the correct half
// Left
nOffset = ( x > 0 ) ? 1 : 0; g_vMotionBlurViewportValues[0] = ( float )( x + nOffset ) / ( flSrcWidth - 1 );
// Right
nOffset = ( x < ( flSrcWidth - 1 ) ) ? -1 : 0; g_vMotionBlurViewportValues[3] = ( float )( x + w + nOffset ) / ( flSrcWidth - 1 );
// Top
nOffset = ( y > 0 ) ? 1 : 0; // Offset by one pixel to land in the correct half
g_vMotionBlurViewportValues[1] = ( float )( y + nOffset ) / ( flSrcHeight - 1 );
// Bottom
nOffset = ( y < ( flSrcHeight - 1 ) ) ? -1 : 0; g_vMotionBlurViewportValues[2] = ( float )( y + h + nOffset ) / ( flSrcHeight - 1 );
// Only allow clamping to happen in the middle of the screen, so nudge the clamp values out if they're on the border of the screen
for ( int i = 0; i < 4; i++ ) { if ( g_vMotionBlurViewportValues[i] <= 0.0f ) g_vMotionBlurViewportValues[i] = -1.0f; else if ( g_vMotionBlurViewportValues[i] >= 1.0f ) g_vMotionBlurViewportValues[i] = 2.0f; } }
//=============================================================================================//
// Render quad and let material proxy pick up the g_vMotionBlurValues[4] values just set above //
//=============================================================================================//
bool bPerformedMotionBlur = false; if ( true ) { CMatRenderContextPtr pRenderContext( materials ); ITexture *pSrc; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pSrc = CEnginePostMaterialProxy::GetSrcPS3Texture( materials ); else #endif
pSrc = materials->FindTexture( IsPS3() ? "^PS3^BACKBUFFER" : "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
int nSrcWidth = pSrc->GetActualWidth(); int nSrcHeight = pSrc->GetActualHeight(); int nViewportWidth, nViewportHeight, nDummy; pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight );
if ( !IsPS3() ) { UpdateScreenEffectTexture( 0, x, y, w, h, false ); } // Get material pointer
IMaterial *pMatMotionBlur; #if defined(_PS3)
if( mat_PS3_findpostvarsfast.GetInt() ) pMatMotionBlur = CMotionBlurMaterialProxy::GetMotionBlurMaterial( materials ); else #endif
pMatMotionBlur = materials->FindMaterial( "dev/motion_blur", TEXTURE_GROUP_OTHER, true );
//SetRenderTargetAndViewPort( dest_rt0 );
//pRenderContext->PopRenderTargetAndViewport();
if ( pMatMotionBlur != NULL && nSrcWidth > 0 && nSrcHeight > 0 ) { pRenderContext->DrawScreenSpaceRectangle( pMatMotionBlur, 0, 0, nViewportWidth, nViewportHeight, x, y, x + w-1, y + h-1, nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() ); bPerformedMotionBlur = true;
if ( s_bDumpRenderTargets ) { DumpTGAofRenderTarget( nViewportWidth, nViewportHeight, "MotionBlur" ); } } }
return bPerformedMotionBlur; }
//=====================================================================================================================
// Depth of field =====================================================================================================
//=====================================================================================================================
ConVar mat_dof_enabled( "mat_dof_enabled", "1" ); ConVar mat_dof_override( "mat_dof_override", "0" ); ConVar mat_dof_near_blur_depth( "mat_dof_near_blur_depth", "20.0" ); ConVar mat_dof_near_focus_depth( "mat_dof_near_focus_depth", "100.0" ); ConVar mat_dof_far_focus_depth( "mat_dof_far_focus_depth", "250.0" ); ConVar mat_dof_far_blur_depth( "mat_dof_far_blur_depth", "1000.0" ); ConVar mat_dof_near_blur_radius( "mat_dof_near_blur_radius", "10.0" ); ConVar mat_dof_far_blur_radius( "mat_dof_far_blur_radius", "5.0" ); ConVar mat_dof_quality( "mat_dof_quality", "0" );
static float GetNearBlurDepth() { return mat_dof_override.GetBool() ? mat_dof_near_blur_depth.GetFloat() : g_flDOFNearBlurDepth; }
static float GetNearFocusDepth() { return mat_dof_override.GetBool() ? mat_dof_near_focus_depth.GetFloat() : g_flDOFNearFocusDepth; }
static float GetFarFocusDepth() { return mat_dof_override.GetBool() ? mat_dof_far_focus_depth.GetFloat() : g_flDOFFarFocusDepth; }
static float GetFarBlurDepth() { return mat_dof_override.GetBool() ? mat_dof_far_blur_depth.GetFloat() : g_flDOFFarBlurDepth; }
static float GetNearBlurRadius() { return mat_dof_override.GetBool() ? mat_dof_near_blur_radius.GetFloat() : g_flDOFNearBlurRadius; }
static float GetFarBlurRadius() { return mat_dof_override.GetBool() ? mat_dof_far_blur_radius.GetFloat() : g_flDOFFarBlurRadius; }
bool IsDepthOfFieldEnabled() { const CViewSetup *pViewSetup = view->GetViewSetup(); if ( !pViewSetup ) return false;
// We need high-precision depth, which we currently only get in float HDR mode
if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT ) return false;
if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 92 ) return false;
// Only SFM sets this at the moment...it supersedes mat_dof_ convars if true
if ( pViewSetup->m_bDoDepthOfField ) return true;
if ( !mat_dof_enabled.GetBool() ) return false;
if ( mat_dof_override.GetBool() == true ) { return mat_dof_enabled.GetBool(); } else { return g_bDOFEnabled; } }
static inline bool SetMaterialVarFloat( IMaterial* pMat, const char* pVarName, float flValue ) { Assert( pMat != NULL ); Assert( pVarName != NULL ); if ( pMat == NULL || pVarName == NULL ) { return false; }
bool bFound = false; IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound ); if ( bFound ) { pVar->SetFloatValue( flValue ); }
return bFound; }
static inline bool SetMaterialVarInt( IMaterial* pMat, const char* pVarName, int nValue ) { Assert( pMat != NULL ); Assert( pVarName != NULL ); if ( pMat == NULL || pVarName == NULL ) { return false; }
bool bFound = false; IMaterialVar* pVar = pMat->FindVar( pVarName, &bFound ); if ( bFound ) { pVar->SetIntValue( nValue ); } return bFound; }
void DoDepthOfField( const CViewSetup &view ) { if ( !IsDepthOfFieldEnabled() ) { return; }
// Copy from backbuffer to _rt_FullFrameFB
UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false ); // Do we need to check if we already did this?
CMatRenderContextPtr pRenderContext( materials );
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); int nSrcWidth = pSrc->GetActualWidth(); int nSrcHeight = pSrc->GetActualHeight();
if ( mat_dof_quality.GetInt() < 2 ) { /////////////////////////////////////
// Downsample backbuffer to 1/4 size
/////////////////////////////////////
// Update downsampled framebuffer. TODO: Don't do this again for the bloom if we already did it here...
pRenderContext->PushRenderTargetAndViewport(); ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 ); Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 );
// Downsample fb to rt0
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt0, true ); //////////////////////////////////////
// Additional blur using 3x3 Gaussian
//////////////////////////////////////
IMaterial *pMat = materials->FindMaterial( "dev/blurgaussian_3x3", TEXTURE_GROUP_OTHER, true );
if ( pMat == NULL ) return;
SetMaterialVarFloat( pMat, "$c0_x", 0.5f / (float)dest_rt0->GetActualWidth() ); SetMaterialVarFloat( pMat, "$c0_y", 0.5f / (float)dest_rt0->GetActualHeight() ); SetMaterialVarFloat( pMat, "$c1_x", -0.5f / (float)dest_rt0->GetActualWidth() ); SetMaterialVarFloat( pMat, "$c1_y", 0.5f / (float)dest_rt0->GetActualHeight() );
ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET ); SetRenderTargetAndViewPort( dest_rt1 );
pRenderContext->DrawScreenSpaceRectangle( pMat, 0, 0, nSrcWidth/4, nSrcHeight/4, 0, 0, dest_rt0->GetActualWidth()-1, dest_rt0->GetActualHeight()-1, dest_rt0->GetActualWidth(), dest_rt0->GetActualHeight() );
if ( IsGameConsole() ) { pRenderContext->CopyRenderTargetToTextureEx( dest_rt1, 0, NULL, NULL ); }
pRenderContext->PopRenderTargetAndViewport(); }
// Render depth-of-field quad
int nViewportWidth = 0; int nViewportHeight = 0; int nDummy = 0; pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight );
IMaterial *pMatDOF = materials->FindMaterial( "dev/depth_of_field", TEXTURE_GROUP_OTHER, true );
if ( pMatDOF == NULL ) return;
SetMaterialVarFloat( pMatDOF, "$nearPlane", view.zNear ); SetMaterialVarFloat( pMatDOF, "$farPlane", view.zFar );
// Only SFM drives this bool at the moment...
if ( view.m_bDoDepthOfField ) { SetMaterialVarFloat( pMatDOF, "$nearBlurDepth", view.m_flNearBlurDepth ); SetMaterialVarFloat( pMatDOF, "$nearFocusDepth", view.m_flNearFocusDepth ); SetMaterialVarFloat( pMatDOF, "$farFocusDepth", view.m_flFarFocusDepth ); SetMaterialVarFloat( pMatDOF, "$farBlurDepth", view.m_flFarBlurDepth ); SetMaterialVarFloat( pMatDOF, "$nearBlurRadius", view.m_flNearBlurRadius ); SetMaterialVarFloat( pMatDOF, "$farBlurRadius", view.m_flFarBlurRadius ); SetMaterialVarInt( pMatDOF, "$quality", view.m_nDoFQuality ); } else // pull from convars/globals
{ SetMaterialVarFloat( pMatDOF, "$nearBlurDepth", GetNearBlurDepth() ); SetMaterialVarFloat( pMatDOF, "$nearFocusDepth", GetNearFocusDepth() ); SetMaterialVarFloat( pMatDOF, "$farFocusDepth", GetFarFocusDepth() ); SetMaterialVarFloat( pMatDOF, "$farBlurDepth", GetFarBlurDepth() ); SetMaterialVarFloat( pMatDOF, "$nearBlurRadius", GetNearBlurRadius() ); SetMaterialVarFloat( pMatDOF, "$farBlurRadius", GetFarBlurRadius() ); SetMaterialVarInt( pMatDOF, "$quality", mat_dof_quality.GetInt() ); }
pRenderContext->DrawScreenSpaceRectangle( pMatDOF, 0, 0, nViewportWidth, nViewportHeight, 0, 0, nSrcWidth-1, nSrcHeight-1, nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() ); }
void DrawModulationQuad( IMaterial *pMaterial, IMatRenderContext *pRenderContext, uint8 r, uint8 g, uint8 b, uint8 a, float fDepth ) { pRenderContext->EnableClipping( false ); pRenderContext->Bind( pMaterial );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity();
int w, h;
pRenderContext->GetRenderTargetDimensions( w, h ); if ( ( w == 0 ) || ( h == 0 ) ) return;
// This is the size of the back-buffer we're reading from.
int bw, bh; bw = w; bh = h;
float s0, t0; float s1, t1;
float flOffsetS = (bw != 0.0f) ? 1.0f / bw : 0.0f; float flOffsetT = (bh != 0.0f) ? 1.0f / bh : 0.0f; s0 = 0.5f * flOffsetS; t0 = 0.5f * flOffsetT; s1 = (w-0.5f) * flOffsetS; t1 = (h-0.5f) * flOffsetT;
CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( -1.0f, -1.0f, fDepth ); //meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 0, s0, t1 ); meshBuilder.Color4ub( r, g, b, a ); meshBuilder.AdvanceVertex();
meshBuilder.Position3f( -1.0f, 1, fDepth ); //meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 0, s0, t0 ); meshBuilder.Color4ub( r, g, b, a ); meshBuilder.AdvanceVertex();
meshBuilder.Position3f( 1, 1, fDepth ); //meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 0, s1, t0 ); meshBuilder.Color4ub( r, g, b, a ); meshBuilder.AdvanceVertex();
meshBuilder.Position3f( 1, -1.0f, fDepth ); //meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
//meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
//meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 0, s1, t1 ); meshBuilder.Color4ub( r, g, b, a ); meshBuilder.AdvanceVertex();
meshBuilder.End(); pMesh->Draw();
pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PopMatrix(); pRenderContext->EnableClipping( true ); }
ConVar cl_blurClearAlpha( "cl_blurClearAlpha", "0", 0, "0-255, but 0 has errors at the moment" ); ConVar cl_blurDebug( "cl_blurDebug", "0" ); ConVar cl_blurTapSize( "cl_blurTapSize", "0.5" ); ConVar cl_blurPasses( "cl_blurPasses", "1" );
void BlurEntity( IClientRenderable *pRenderable, bool bPreDraw, int drawFlags, const RenderableInstance_t &instance, const CViewSetup &view, int x, int y, int w, int h ) { ITexture *pFullFrameFB = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); ITexture *dest_rt[2]; dest_rt[0] = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET ); dest_rt[1] = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
IMaterial *pBlurPass[2]; pBlurPass[0] = materials->FindMaterial( "dev/blurentity_blurpass0", TEXTURE_GROUP_OTHER ); pBlurPass[1] = materials->FindMaterial( "dev/blurentity_blurpass1", TEXTURE_GROUP_OTHER ); IMaterial *pEntBlurCopyBack[2]; pEntBlurCopyBack[0] = materials->FindMaterial( "dev/blurentity_copyback0", TEXTURE_GROUP_OTHER ); pEntBlurCopyBack[1] = materials->FindMaterial( "dev/blurentity_copyback1", TEXTURE_GROUP_OTHER ); IMaterial *pEntBlurAlphaSilhoutte = materials->FindMaterial( "dev/blurentity_alphasilhoutte", TEXTURE_GROUP_OTHER );
if( !pFullFrameFB || !dest_rt[0] || !dest_rt[1] || !pBlurPass[0] || !pBlurPass[1] || !pEntBlurCopyBack[0] || !pEntBlurCopyBack[1] || !pEntBlurAlphaSilhoutte ) { return; //missing a vital texture/material
}
// Copy from backbuffer to _rt_FullFrameFB
UpdateScreenEffectTexture( 0, x, y, w, h, true ); // Do we need to check if we already did this?
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport();
int nSrcWidth = pFullFrameFB->GetActualWidth(); int nSrcHeight = pFullFrameFB->GetActualHeight();
pRenderContext->OverrideAlphaWriteEnable( true, true ); //ensure we're always copying alpha values in every shader since we're using alpha as a mask when drawing to the back buffer
//replace the alpha channel with a silhoutte of the desired entity. We'll use the blurred alpha when rendering back to the back buffer
{ SetRenderTargetAndViewPort( pFullFrameFB ); pRenderContext->ClearColor4ub( 255, 255, 255, cl_blurClearAlpha.GetInt() ); pRenderContext->ClearBuffersObeyStencilEx( cl_blurDebug.GetBool(), true, true ); //clear out the existing alpha and depth
if( bPreDraw ) //in pre-draw mode, this renderable hasn't drawn it's colors anywhere yet, add them to _rt_FullFrameFB
pRenderable->DrawModel( drawFlags, instance );
//just write 1.0 to alpha, don't alter color information
if( !cl_blurDebug.GetBool() ) pRenderContext->OverrideColorWriteEnable( true, false ); modelrender->ForcedMaterialOverride( pEntBlurAlphaSilhoutte ); pRenderable->DrawModel( drawFlags, instance ); modelrender->ForcedMaterialOverride( NULL ); if( !cl_blurDebug.GetBool() ) pRenderContext->OverrideColorWriteEnable( false, false ); }
IMaterial *pEntBlurCopyBackFinal = NULL; //the material to use when copying the blur back to the backbuffer
//generate blur texture
{ /////////////////////////////////////
// Downsample backbuffer to 1/4 size
/////////////////////////////////////
// *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
Assert( dest_rt[0]->GetActualWidth() == pFullFrameFB->GetActualWidth() / 4 ); Assert( dest_rt[0]->GetActualHeight() == pFullFrameFB->GetActualHeight() / 4 );
// Downsample fb to rt0
DownsampleFBQuarterSize( pRenderContext, nSrcWidth, nSrcHeight, dest_rt[0], true );
//////////////////////////////////////
// Additional blur
//////////////////////////////////////
float flBlurTapSize = cl_blurTapSize.GetFloat(); for( int i = 0; i != 2; ++i ) { SetMaterialVarFloat( pBlurPass[i], "$c0_x", flBlurTapSize / (float)dest_rt[i]->GetActualWidth() ); SetMaterialVarFloat( pBlurPass[i], "$c0_y", flBlurTapSize / (float)dest_rt[i]->GetActualHeight() ); }
int iBlurPasses = cl_blurPasses.GetInt(); for( int i = 0; i < iBlurPasses; ++i ) { int iSrc = i & 1; int iDest = 1 - iSrc; SetRenderTargetAndViewPort( dest_rt[iDest] );
pRenderContext->DrawScreenSpaceRectangle( pBlurPass[iSrc], 0, 0, nSrcWidth/4, nSrcHeight/4, 0, 0, dest_rt[iSrc]->GetActualWidth()-1, dest_rt[iSrc]->GetActualHeight()-1, dest_rt[iSrc]->GetActualWidth(), dest_rt[iSrc]->GetActualHeight() );
if ( IsGameConsole() ) { pRenderContext->CopyRenderTargetToTextureEx( dest_rt[iDest], 0, NULL, NULL ); } } pEntBlurCopyBackFinal = pEntBlurCopyBack[iBlurPasses & 1]; } pRenderContext->OverrideAlphaWriteEnable( false, true );
pRenderContext->PopRenderTargetAndViewport();
//render back to the screen. We use the depth of the closest bbox point as our quad depth
{ const Vector &vRenderOrigin = pRenderable->GetRenderOrigin(); const QAngle &qRenderAngles = pRenderable->GetRenderAngles(); Vector vMins, vMaxs; pRenderable->GetRenderBounds( vMins, vMaxs );
VMatrix matWorld, matView, matProj, matWorldView, matWorldViewProj; //since the model matrix isn't necessarily set for this renderable, construct it manually
matWorld.SetupMatrixOrgAngles( vRenderOrigin, qRenderAngles ); pRenderContext->GetMatrix( MATERIAL_VIEW, &matView ); pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj ); MatrixMultiply( matView, matWorld, matWorldView ); MatrixMultiply( matProj, matWorldView, matWorldViewProj );
float fClosestBBoxDepth = 1.0f; Vector4D vTest; vTest.w = 1.0f; for( int i = 0; i != 8; ++i ) { vTest.x = (i & (1 << 0)) ? vMaxs.x : vMins.x; vTest.y = (i & (1 << 1)) ? vMaxs.y : vMins.y; vTest.z = (i & (1 << 2)) ? vMaxs.z : vMins.z; Vector4D vOut; matWorldViewProj.V4Mul( vTest, vOut ); float fDepth = vOut.z/vOut.w; if( fDepth < fClosestBBoxDepth ) fClosestBBoxDepth = fDepth; }
if( fClosestBBoxDepth < 0.0f ) fClosestBBoxDepth = 0.0f; DrawModulationQuad( pEntBlurCopyBackFinal, pRenderContext, 255, 255, 255, 255, fClosestBBoxDepth ); }
if( bPreDraw && ( instance.m_nAlpha == 255 ) && ( ( drawFlags & STUDIO_TRANSPARENCY ) == 0 ) ) //write depth out to the depth buffer
{ modelrender->ForcedMaterialOverride( NULL, OVERRIDE_DEPTH_WRITE ); pRenderable->DrawModel( drawFlags, instance ); modelrender->ForcedMaterialOverride( NULL ); } }
ConVar cl_teamid( "cl_teamid", "0" );
class CTeamIdMaterialProxy : public CEntityMaterialProxy { public: CTeamIdMaterialProxy() { m_pMaterial = NULL; m_pVar = NULL; }
virtual ~CTeamIdMaterialProxy() { }
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { m_pMaterial = pMaterial; bool found; m_pVar = m_pMaterial->FindVar( "$SelfIllumFresnelEnabledThisFrame", &found ); if ( !found ) { m_pVar = NULL; return false; } return true; }
virtual void OnBind( C_BaseEntity *pC_BaseEntity ) { if ( cl_teamid.GetInt() == 0 ) { m_pVar->SetIntValue( 0 ); return; }
if ( pC_BaseEntity->InLocalTeam() ) { m_pVar->SetIntValue( 1 ); } else { m_pVar->SetIntValue( 0 ); } }
virtual IMaterial *GetMaterial() { return m_pMaterial; }
protected: IMaterial *m_pMaterial; IMaterialVar *m_pVar; };
EXPOSE_MATERIAL_PROXY( CTeamIdMaterialProxy, TeamIdMaterialProxy );
|