1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-05 17:13:36 +08:00
hl2sdk/cl_dll/view_scene.cpp
2020-05-18 17:42:53 -07:00

4698 lines
150 KiB
C++

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Responsible for drawing the scene
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "view.h"
#include "iviewrender.h"
#include "view_shared.h"
#include "ivieweffects.h"
#include "iinput.h"
#include "model_types.h"
#include "clientsideeffects.h"
#include "particlemgr.h"
#include "viewrender.h"
#include "iclientmode.h"
#include "voice_status.h"
#include "glow_overlay.h"
#include "materialsystem/imesh.h"
#include "materialsystem/ITexture.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/IMaterialVar.h"
#include "materialsystem/IColorCorrection.h"
#include "DetailObjectSystem.h"
#include "tier0/vprof.h"
#include "datacache/imdlcache.h"
#include "engine/IEngineTrace.h"
#include "engine/ivmodelinfo.h"
#include "vstdlib/icommandline.h"
#include "view_scene.h"
#include "particles_ez.h"
#include "engine/IStaticPropMgr.h"
#include "engine/ivdebugoverlay.h"
#include "smoke_fog_overlay.h"
#include "c_pixel_visibility.h"
#include "ClientEffectPrecacheSystem.h"
#include "c_rope.h"
#include "c_effects.h"
#include "smoke_fog_overlay.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "vgui_int.h"
#include "ienginevgui.h"
#include "datacache/imdlcache.h"
#include "ScreenSpaceEffects.h"
#if defined( HL2_CLIENT_DLL ) || defined( CSTRIKE_DLL )
#define USE_MONITORS
#endif
// GR
#include "rendertexture.h"
#ifdef _XBOX
#include "tier0/mem.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern void ComputeCameraVariables( const Vector &vecOrigin, const QAngle &vecAngles,
Vector *pVecForward, Vector *pVecRight, Vector *pVecUp, VMatrix *pMatCamInverse );
static ConVar cl_drawmonitors( "cl_drawmonitors", "1" );
static ConVar cl_overdraw_test( "cl_overdraw_test", "0", FCVAR_CHEAT | FCVAR_NEVER_AS_STRING );
static ConVar r_eyewaterepsilon( "r_eyewaterepsilon", "7.0f", FCVAR_CHEAT );
static void SetClearColorToFogColor( unsigned char ucFogColor[3] )
{
materials->GetFogColor( ucFogColor );
if( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER )
{
float scale = LinearToGammaFullRange( materials->GetToneMappingScaleLinear().x );
ucFogColor[0] *= scale;
ucFogColor[1] *= scale;
ucFogColor[2] *= scale;
}
materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
}
// Used to verify frame syncing.
void GenerateOverdrawForTesting()
{
if ( !cl_overdraw_test.GetInt() )
return;
for ( int i=0; i < 40; i++ )
{
g_SmokeFogOverlayAlpha = 20 / 255.0;
DrawSmokeFogOverlay();
}
g_SmokeFogOverlayAlpha = 0;
}
//-----------------------------------------------------------------------------
// Convars related to controlling rendering
//-----------------------------------------------------------------------------
static ConVar cl_maxrenderable_dist("cl_maxrenderable_dist", "3000", FCVAR_CHEAT, "Max distance from the camera at which things will be rendered" );
ConVar r_updaterefracttexture( "r_updaterefracttexture", "1" );
ConVar r_entityclips( "r_entityclips", "1" ); //FIXME: Nvidia drivers before 81.94 on cards that support user clip planes will have problems with this, require driver update? Detect and disable?
// Matches the version in the engine
static ConVar r_drawopaqueworld( "r_drawopaqueworld", "1", FCVAR_CHEAT );
static ConVar r_drawtranslucentworld( "r_drawtranslucentworld", "1", FCVAR_CHEAT );
static ConVar r_3dsky( "r_3dsky","1", 0, "Enable the rendering of 3d sky boxes" );
static ConVar r_skybox( "r_skybox","1", FCVAR_CHEAT, "Enable the rendering of sky boxes" );
ConVar r_drawviewmodel( "r_drawviewmodel","1", FCVAR_CHEAT );
static ConVar r_drawtranslucentrenderables( "r_drawtranslucentrenderables", "1", FCVAR_CHEAT );
static ConVar r_drawopaquerenderables( "r_drawopaquerenderables", "1", FCVAR_CHEAT );
static ConVar r_flashlightdrawdepth( "r_flashlightdrawdepth", "0" );
ConVar r_DrawDetailProps( "r_DrawDetailProps", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" );
ConVar mat_bloomscale( "mat_bloomscale", "1" );
//-----------------------------------------------------------------------------
// Convars related to fog color
//-----------------------------------------------------------------------------
static ConVar fog_override( "fog_override", "0", FCVAR_CHEAT );
// set any of these to use the maps fog
static ConVar fog_start( "fog_start", "-1" );
static ConVar fog_end( "fog_end", "-1" );
static ConVar fog_color( "fog_color", "-1 -1 -1" );
static ConVar fog_enable( "fog_enable", "1" );
static ConVar fog_startskybox( "fog_startskybox", "-1" );
static ConVar fog_endskybox( "fog_endskybox", "-1" );
static ConVar fog_colorskybox( "fog_colorskybox", "-1 -1 -1" );
static ConVar fog_enableskybox( "fog_enableskybox", "1" );
//-----------------------------------------------------------------------------
// Water-related convars
//-----------------------------------------------------------------------------
static ConVar r_debugcheapwater( "r_debugcheapwater", "0", FCVAR_CHEAT );
static ConVar r_waterforceexpensive( "r_waterforceexpensive", "0" );
static ConVar r_waterforcereflectentities( "r_waterforcereflectentities", "0" );
static ConVar r_WaterDrawRefraction( "r_WaterDrawRefraction", "1", 0, "Enable water refraction" );
static ConVar r_WaterDrawReflection( "r_WaterDrawReflection", "1", 0, "Enable water reflection" );
static ConVar r_ForceWaterLeaf( "r_ForceWaterLeaf", "1", 0, "Enable for optimization to water - considers view in leaf under water for purposes of culling" );
static ConVar mat_drawwater( "mat_drawwater", "1", FCVAR_CHEAT );
static ConVar mat_clipz( "mat_clipz", "1" );
//-----------------------------------------------------------------------------
// debugging
//-----------------------------------------------------------------------------
static ConVar r_visocclusion( "r_visocclusion", "0", FCVAR_CHEAT );
// (the engine owns this cvar).
ConVar mat_wireframe( "mat_wireframe", "0", FCVAR_CHEAT );
// sv_cheats is replicated and lives in the engine. re-declaring it here without FCVAR_REPLICATED
// breaks the linkage, and declaring it here with FCVAR_REPLICATED wants it to be declared in the server also.
const ConVar *sv_cheats = NULL;
//-----------------------------------------------------------------------------
// debugging overlays
//-----------------------------------------------------------------------------
static ConVar cl_drawmaterial( "cl_drawmaterial", "", FCVAR_CHEAT, "Draw a particular material over the frame" );
static ConVar cl_drawshadowtexture( "cl_drawshadowtexture", "0", FCVAR_CHEAT );
static ConVar mat_showwatertextures( "mat_showwatertextures", "0", FCVAR_CHEAT );
static ConVar mat_wateroverlaysize( "mat_wateroverlaysize", "128" );
static ConVar mat_showframebuffertexture( "mat_showframebuffertexture", "0", FCVAR_CHEAT );
static ConVar mat_framebuffercopyoverlaysize( "mat_framebuffercopyoverlaysize", "128" );
static ConVar mat_showcamerarendertarget( "mat_showcamerarendertarget", "0", FCVAR_CHEAT );
static ConVar mat_camerarendertargetoverlaysize( "mat_camerarendertargetoverlaysize", "128", FCVAR_CHEAT );
static ConVar mat_hsv( "mat_hsv", "0" );
static ConVar mat_yuv( "mat_yuv", "0" );
#ifdef _XBOX
static ConVar mat_viewTextureEnable("mat_viewTextureEnable", "", 0, "Enable view texture");
static ConVar mat_viewTextureName("mat_viewTextureName", "", 0, "Set the view texture name");
static ConVar mat_viewTextureScale("mat_viewTextureScale", "1", 0, "Set the view texture scale");
#endif
//-----------------------------------------------------------------------------
// Other convars
//-----------------------------------------------------------------------------
ConVar r_DoCovertTransitions("r_DoCovertTransitions", "1", FCVAR_NEVER_AS_STRING ); // internally used by game code and engine to choose when to allow LOD transitions.
ConVar r_TransitionSensitivity("r_TransitionSensitivity", "6", 0, "Controls when LODs are changed. Lower numbers cause more overt LOD transitions.");
static ConVar r_screenfademinsize( "r_screenfademinsize", "0" );
static ConVar r_screenfademaxsize( "r_screenfademaxsize", "0" );
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
static Vector g_vecCurrentRenderOrigin(0,0,0);
static QAngle g_vecCurrentRenderAngles(0,0,0);
static Vector g_vecCurrentVForward(0,0,0), g_vecCurrentVRight(0,0,0), g_vecCurrentVUp(0,0,0);
static VMatrix g_matCurrentCamInverse;
static bool s_bCanAccessCurrentView = false;
IntroData_t *g_pIntroData = NULL;
static bool g_bRenderingView = false; // For debugging...
static int g_CurrentViewID = VIEW_NONE;
bool g_bRenderingScreenshot = false;
int g_viewscene_refractUpdateFrame = 0;
// 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;
//-----------------------------------------------------------------------------
// Precache of necessary materials
//-----------------------------------------------------------------------------
#ifdef HL2_CLIENT_DLL
CLIENTEFFECT_REGISTER_BEGIN( PrecacheViewRender )
CLIENTEFFECT_MATERIAL( "scripted/intro_screenspaceeffect" )
CLIENTEFFECT_REGISTER_END()
#endif
#ifndef _XBOX
CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffects )
CLIENTEFFECT_MATERIAL( "dev/downsample" )
CLIENTEFFECT_MATERIAL( "dev/downsample_non_hdr" )
CLIENTEFFECT_MATERIAL( "dev/blurfiltery_and_add_nohdr" )
CLIENTEFFECT_MATERIAL( "dev/no_pixel_write" )
CLIENTEFFECT_MATERIAL( "dev/lumcompare" )
CLIENTEFFECT_MATERIAL( "dev/blurfilterx" )
CLIENTEFFECT_MATERIAL( "dev/blurfilterx_nohdr" )
CLIENTEFFECT_MATERIAL( "dev/floattoscreen_combine" )
CLIENTEFFECT_MATERIAL( "dev/copyfullframefb_vanilla" )
CLIENTEFFECT_MATERIAL( "dev/copyfullframefb" )
CLIENTEFFECT_MATERIAL( "dev/blurfiltery" )
CLIENTEFFECT_REGISTER_END_CONDITIONAL( engine->GetDXSupportLevel() >= 90)
#endif
//-----------------------------------------------------------------------------
// Accessors to return the current view being rendered
//-----------------------------------------------------------------------------
const Vector &CurrentViewOrigin()
{
Assert( s_bCanAccessCurrentView );
return g_vecCurrentRenderOrigin;
}
const QAngle &CurrentViewAngles()
{
Assert( s_bCanAccessCurrentView );
return g_vecCurrentRenderAngles;
}
const Vector &CurrentViewForward()
{
Assert( s_bCanAccessCurrentView );
return g_vecCurrentVForward;
}
const Vector &CurrentViewRight()
{
Assert( s_bCanAccessCurrentView );
return g_vecCurrentVRight;
}
const Vector &CurrentViewUp()
{
Assert( s_bCanAccessCurrentView );
return g_vecCurrentVUp;
}
const VMatrix &CurrentWorldToViewMatrix()
{
Assert( s_bCanAccessCurrentView );
return g_matCurrentCamInverse;
}
//-----------------------------------------------------------------------------
// Methods to set the current view/guard access to view parameters
//-----------------------------------------------------------------------------
void AllowCurrentViewAccess( bool allow )
{
s_bCanAccessCurrentView = allow;
}
bool IsCurrentViewAccessAllowed()
{
return s_bCanAccessCurrentView;
}
void SetupCurrentView( const Vector &vecOrigin, const QAngle &angles, view_id_t viewID )
{
// Store off view origin and angles
g_vecCurrentRenderOrigin = vecOrigin;
g_vecCurrentRenderAngles = angles;
// Compute the world->main camera transform
ComputeCameraVariables( vecOrigin, angles,
&g_vecCurrentVForward, &g_vecCurrentVRight, &g_vecCurrentVUp, &g_matCurrentCamInverse );
g_CurrentViewID = viewID;
s_bCanAccessCurrentView = true;
// Cache off fade distances
float flScreenFadeMinSize, flScreenFadeMaxSize;
view->GetScreenFadeDistances( &flScreenFadeMinSize, &flScreenFadeMaxSize );
modelinfo->SetViewScreenFadeRange( flScreenFadeMinSize, flScreenFadeMaxSize );
}
view_id_t CurrentViewID()
{
return ( view_id_t )g_CurrentViewID;
}
void FinishCurrentView()
{
s_bCanAccessCurrentView = false;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CViewRender::CViewRender()
{
m_AnglesHistoryCounter = 0;
memset(m_AnglesHistory, 0, sizeof(m_AnglesHistory));
m_flCheapWaterStartDistance = 0.0f;
m_flCheapWaterEndDistance = 0.1f;
m_BaseDrawFlags = m_DrawFlags = 0;
m_bOverrideVisData = false;
m_iForceViewLeaf = -1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
inline bool CViewRender::ShouldDrawEntities( void )
{
return ( !m_pDrawEntities || (m_pDrawEntities->GetInt() != 0) );
}
//-----------------------------------------------------------------------------
// Purpose: Check all conditions which would prevent drawing the view model
// Input : drawViewmodel -
// *viewmodel -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CViewRender::ShouldDrawViewModel( bool bDrawViewmodel )
{
if ( !bDrawViewmodel )
return false;
if ( !r_drawviewmodel.GetBool() )
return false;
if ( C_BasePlayer::ShouldDrawLocalPlayer() )
return false;
if ( !ShouldDrawEntities() )
return false;
if ( render->GetViewEntity() > gpGlobals->maxClients )
return false;
return true;
}
void CViewRender::DrawRenderablesInList( CUtlVector< IClientRenderable * > &list )
{
int nCount = list.Count();
for( int i=0; i < nCount; ++i )
{
IClientUnknown *pUnk = list[i]->GetIClientUnknown();
Assert( pUnk );
C_BaseEntity *ent = pUnk->GetBaseEntity();
Assert( ent );
// Non-view models wanting to render in view model list...
if ( ent->ShouldDraw() )
{
ent->DrawModel( STUDIO_RENDER );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Actually draw the view model
// Input : drawViewModel -
//-----------------------------------------------------------------------------
void CViewRender::DrawViewModels( const CViewSetup &view, bool drawViewmodel )
{
VPROF( "CViewRender::DrawViewModel" );
if ( !ShouldDrawViewModel( drawViewmodel ) )
return;
// Restore the matrices
materials->MatrixMode( MATERIAL_PROJECTION );
materials->PushMatrix();
// Set up for drawing the view model
render->SetProjectionMatrix( view.fovViewmodel, view.zNearViewmodel, view.zFarViewmodel );
const bool bUseDepthHack = true;
// FIXME: Add code to read the current depth range
float depthmin = 0.0f;
float depthmax = 1.0f;
// HACK HACK: Munge the depth range to prevent view model from poking into walls, etc.
// Force clipped down range
if( bUseDepthHack )
materials->DepthRange( 0.0f, 0.1f );
CUtlVector< IClientRenderable * > opaqueViewModelList( 32 );
CUtlVector< IClientRenderable * > translucentViewModelList( 32 );
ClientLeafSystem()->CollateViewModelRenderables( opaqueViewModelList, translucentViewModelList );
DrawRenderablesInList( opaqueViewModelList );
DrawRenderablesInList( translucentViewModelList );
// Reset the depth range to the original values
if( bUseDepthHack )
materials->DepthRange( depthmin, depthmax );
// Restore the matrices
materials->MatrixMode( MATERIAL_PROJECTION );
materials->PopMatrix();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEnt -
// Output : int
//-----------------------------------------------------------------------------
VPlane* CViewRender::GetFrustum()
{
// The frustum is only valid while in a RenderView call.
Assert(g_bRenderingView || g_bRenderingCameraView || g_bRenderingScreenshot);
return m_Frustum;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CViewRender::ShouldDrawBrushModels( void )
{
if ( m_pDrawBrushModels && !m_pDrawBrushModels->GetInt() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Sort entities in a back-to-front ordering
//-----------------------------------------------------------------------------
void SortEntities( CRenderList::CEntry *pEntities, int nEntities )
{
// Don't sort if we only have 1 entity
if ( nEntities <= 1 )
return;
float dists[CRenderList::MAX_GROUP_ENTITIES];
const Vector &vecRenderOrigin = CurrentViewOrigin();
const Vector &vecRenderForward = CurrentViewForward();
// First get a distance for each entity.
int i;
for( i=0; i < nEntities; i++ )
{
IClientRenderable *pRenderable = pEntities[i].m_pRenderable;
// Compute the center of the object (needed for translucent brush models)
Vector boxcenter;
Vector mins,maxs;
pRenderable->GetRenderBounds( mins, maxs );
VectorAdd( mins, maxs, boxcenter );
VectorMA( pRenderable->GetRenderOrigin(), 0.5f, boxcenter, boxcenter );
// Compute distance...
Vector delta;
VectorSubtract( boxcenter, vecRenderOrigin, delta );
dists[i] = DotProduct( delta, vecRenderForward );
}
// H-sort.
int stepSize = 4;
while( stepSize )
{
int end = nEntities - stepSize;
for( i=0; i < end; i += stepSize )
{
if( dists[i] > dists[i+stepSize] )
{
V_swap( pEntities[i], pEntities[i+stepSize] );
V_swap( dists[i], dists[i+stepSize] );
if( i == 0 )
{
i = -stepSize;
}
else
{
i -= stepSize << 1;
}
}
}
stepSize >>= 1;
}
}
void CViewRender::SetupRenderList( const CViewSetup *pView, ClientWorldListInfo_t& info, CRenderList &renderList )
{
VPROF( "CViewRender::SetupRenderList" );
// Clear the list.
int i;
for( i=0; i < RENDER_GROUP_COUNT; i++ )
{
renderList.m_RenderGroupCounts[i] = 0;
}
// Precache information used commonly in CollateRenderables
SetupRenderInfo_t setupInfo;
setupInfo.m_nRenderFrame = m_BuildRenderableListsNumber;
setupInfo.m_nDetailBuildFrame = m_BuildWorldListsNumber;
setupInfo.m_pRenderList = &renderList;
setupInfo.m_bDrawDetailObjects = g_pClientMode->ShouldDrawDetailObjects() && r_DrawDetailProps.GetInt();
if (pView)
{
setupInfo.m_vecRenderOrigin = pView->origin;
setupInfo.m_flRenderDistSq = cl_maxrenderable_dist.GetFloat();
setupInfo.m_flRenderDistSq *= setupInfo.m_flRenderDistSq;
}
else
{
setupInfo.m_flRenderDistSq = 0.0f;
}
// Now collate the entities in the leaves.
if( ShouldDrawEntities() )
{
IClientLeafSystem *pClientLeafSystem = ClientLeafSystem();
for( i=0; i < info.m_LeafCount; i++ )
{
int nTranslucent = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY];
// Add renderables from this leaf...
pClientLeafSystem->CollateRenderablesInLeaf( info.m_pLeafList[i], i, setupInfo );
int nNewTranslucent = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - nTranslucent;
if( nNewTranslucent )
{
// Sort the new translucent entities.
SortEntities( &renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY][nTranslucent], nNewTranslucent );
}
}
}
}
static void OverlayWaterTexture( IMaterial *pMaterial, int xOffset, int yOffset, bool bFlip )
{
#ifndef _XBOX
float xBaseOffset = 0;
float yBaseOffset = 0;
#else
// screen safe
float xBaseOffset = 32;
float yBaseOffset = 32;
#endif
float offsetS = ( 0.5f / 256.0f );
float offsetT = ( 0.5f / 256.0f );
float fFlip0 = bFlip ? 1.0f : 0.0f;
float fFlip1 = bFlip ? 0.0f : 1.0f;
if( !IsErrorMaterial( pMaterial ) )
{
materials->Bind( pMaterial );
IMesh* pMesh = materials->GetDynamicMesh( true );
float w = mat_wateroverlaysize.GetFloat();
float h = mat_wateroverlaysize.GetFloat();
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( xBaseOffset + xOffset * w, yBaseOffset + yOffset * h, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, fFlip1 + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( xBaseOffset + ( xOffset + 1 ) * w, yBaseOffset + yOffset * h, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, fFlip1 + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( xBaseOffset + ( xOffset + 1 ) * w, yBaseOffset + ( yOffset + 1 ) * h, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, fFlip0 + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( xBaseOffset + xOffset * w, yBaseOffset + ( yOffset + 1 ) * h, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, fFlip0 + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
}
static void OverlayWaterTextures( void )
{
OverlayWaterTexture( materials->FindMaterial( "debug/debugreflect", NULL ), 0, 0, false );
OverlayWaterTexture( materials->FindMaterial( "debug/debugrefract", NULL ), 0, 1, true );
}
void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float flY, float w, float h )
{
float offsetS = ( 0.5f / 256.0f );
float offsetT = ( 0.5f / 256.0f );
IMaterial *pMaterial;
pMaterial = materials->FindMaterial( pszMaterialName, TEXTURE_GROUP_OTHER, true );
if( !IsErrorMaterial( pMaterial ) )
{
materials->Bind( pMaterial );
IMesh* pMesh = materials->GetDynamicMesh( true );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( flX, flY, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 0.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( flX+w, flY, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 0.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( flX+w, flY+h, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 1.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( flX, flY+h, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 1.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
}
static void OverlayFrameBufferTexture( int nFrameBufferIndex )
{
float offsetS = ( 0.5f / 256.0f );
float offsetT = ( 0.5f / 256.0f );
IMaterial *pMaterial;
char buf[MAX_PATH];
Q_snprintf( buf, MAX_PATH, "debug/debugfbtexture%d", nFrameBufferIndex );
pMaterial = materials->FindMaterial( buf, TEXTURE_GROUP_OTHER, true );
if( !IsErrorMaterial( pMaterial ) )
{
materials->Bind( pMaterial );
IMesh* pMesh = materials->GetDynamicMesh( true );
float w = mat_framebuffercopyoverlaysize.GetFloat();
float h = mat_framebuffercopyoverlaysize.GetFloat();
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( w * nFrameBufferIndex, 0.0f, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 0.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( w * ( nFrameBufferIndex + 1 ), 0.0f, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 0.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( w * ( nFrameBufferIndex + 1 ), h, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f + offsetS, 1.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( w * nFrameBufferIndex, h, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f + offsetS, 1.0f + offsetT );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
}
void UpdateClientRenderableInPVSStatus()
{
// Vis for this view should already be setup at this point.
// For each client-only entity, notify it if it's newly coming into the PVS.
CUtlLinkedList<CClientEntityList::CPVSNotifyInfo,unsigned short> &theList = ClientEntityList().GetPVSNotifiers();
FOR_EACH_LL( theList, i )
{
CClientEntityList::CPVSNotifyInfo *pInfo = &theList[i];
if ( pInfo->m_InPVSStatus & INPVS_YES )
{
// Ok, this entity already thinks it's in the PVS. No need to notify it.
// We need to set the INPVS_YES_THISFRAME flag if it's in this frame at all, so we
// don't tell the entity it's not in the PVS anymore at the end of the frame.
if ( !( pInfo->m_InPVSStatus & INPVS_THISFRAME ) )
{
if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) )
{
pInfo->m_InPVSStatus |= INPVS_THISFRAME;
}
}
}
else
{
// This entity doesn't think it's in the PVS yet. If it is now in the PVS, let it know.
if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) )
{
pInfo->m_pNotify->OnPVSStatusChanged( true );
pInfo->m_InPVSStatus |= INPVS_YES;
pInfo->m_InPVSStatus |= INPVS_THISFRAME;
}
}
}
}
//-----------------------------------------------------------------------------
// Debugging aid to display a texture
//-----------------------------------------------------------------------------
#ifdef _XBOX
static void OverlayShowTexture( const char* textureName, float scale )
{
bool foundVar;
IMaterial *pMaterial;
IMaterialVar *BaseTextureVar;
ITexture *pTex;
float x, y, w, h;
// screen safe
x = 32;
y = 32;
pMaterial = materials->FindMaterial( "___debug", TEXTURE_GROUP_OTHER, true );
BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false );
if (!foundVar)
return;
if ( textureName && textureName[0] )
{
pTex = materials->FindTexture( textureName, TEXTURE_GROUP_OTHER, false );
BaseTextureVar->SetTextureValue( pTex );
w = pTex->GetActualWidth() * scale;
h = pTex->GetActualHeight() * scale;
}
else
{
w = h = 64.0f * scale;
}
materials->Bind( pMaterial );
IMesh* pMesh = materials->GetDynamicMesh( true );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x+w, y, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x+w, y+h, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x, x+h, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
#endif
extern ConVar cl_leveloverview;
//-----------------------------------------------------------------------------
// Purpose: Builds lists of things to render in the world, called once per view
//-----------------------------------------------------------------------------
void CViewRender::BuildWorldRenderLists( const CViewSetup *pView,
ClientWorldListInfo_t& info, bool bUpdateLightmaps, bool bDrawEntities, int iForceViewLeaf )
{
VPROF_BUDGET( "BuildWorldRenderLists", VPROF_BUDGETGROUP_WORLD_RENDERING );
// Server entities already know which ones are in the PVS, but client-only entities don't.
// We need to know which client-only entities are in the PVS at this point so the shadow
// manager can project/occlude their shadows correctly.
UpdateClientRenderableInPVSStatus();
++m_BuildWorldListsNumber;
// Override vis data if specified this render, otherwise use default behavior with NULL
VisOverrideData_t* pVisData = (m_bOverrideVisData)?(&m_VisData):(NULL);
render->BuildWorldLists_VisOverride( &info, bUpdateLightmaps, iForceViewLeaf, pVisData );
// Return to default area portal behavior by using the CurrentView as the starting area
m_bOverrideVisData = false;
m_iForceViewLeaf = -1;
if ( bDrawEntities )
{
// Now that we have the list of all leaves, regenerate shadows cast
g_pClientShadowMgr->ComputeShadowTextures( pView, info.m_LeafCount, info.m_pLeafList );
// Compute the prop opacity based on the view position and fov zoom scale
float flFactor = 1.0f;
C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer();
if ( pLocal )
{
flFactor = pLocal->GetFOVDistanceAdjustFactor();
}
if ( cl_leveloverview.GetFloat() > 0 )
{
// disable prop fading
flFactor = -1;
}
// When zoomed in, tweak the opacity to stay visible from further away
staticpropmgr->ComputePropOpacity( CurrentViewOrigin(), flFactor );
// Build a list of detail props to render
DetailObjectSystem()->BuildDetailObjectRenderLists();
}
}
//-----------------------------------------------------------------------------
// Purpose: Computes the actual world list info based on the render flags
//-----------------------------------------------------------------------------
ClientWorldListInfo_t *CViewRender::ComputeActualWorldListInfo( const ClientWorldListInfo_t& info, int nDrawFlags, ClientWorldListInfo_t& tmpInfo )
{
// Drawing everything? Just return the world list info as-is
int nWaterDrawFlags = nDrawFlags & (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER);
if ( nWaterDrawFlags == (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER) )
return const_cast<ClientWorldListInfo_t*>(&info);
tmpInfo.m_ViewFogVolume = info.m_ViewFogVolume;
tmpInfo.m_LeafCount = 0;
// Not drawing anything? Then don't bother with renderable lists
if ( nWaterDrawFlags == 0 )
return &tmpInfo;
// Create a sub-list based on the actual leaves being rendered
bool bRenderingUnderwater = (nWaterDrawFlags & DF_RENDER_UNDERWATER) != 0;
for ( int i = 0; i < info.m_LeafCount; ++i )
{
bool bLeafIsUnderwater = ( info.m_pLeafFogVolume[i] != -1 );
if ( bRenderingUnderwater == bLeafIsUnderwater )
{
tmpInfo.m_pLeafList[ tmpInfo.m_LeafCount ] = info.m_pLeafList[ i ];
tmpInfo.m_pLeafFogVolume[ tmpInfo.m_LeafCount ] = info.m_pLeafFogVolume[ i ];
tmpInfo.m_pActualLeafIndex[ tmpInfo.m_LeafCount ] = i;
++tmpInfo.m_LeafCount;
}
}
return &tmpInfo;
}
//-----------------------------------------------------------------------------
// Purpose: Builds render lists for renderables. Called once for refraction, once for over water
//-----------------------------------------------------------------------------
void CViewRender::BuildRenderableRenderLists( const CViewSetup *pView, ClientWorldListInfo_t& info, CRenderList &renderList )
{
MDLCACHE_CRITICAL_SECTION();
++m_BuildRenderableListsNumber;
// For better sorting, find out the leaf *nearest* to the camera
// and render translucent objects as if they are in that leaf.
if( ShouldDrawEntities() )
{
ClientLeafSystem()->ComputeTranslucentRenderLeaf(
info.m_LeafCount, info.m_pLeafList, info.m_pLeafFogVolume, m_BuildRenderableListsNumber );
}
SetupRenderList( pView, info, renderList );
}
//-----------------------------------------------------------------------------
// Computes draw flags for the engine to build its world surface lists
//-----------------------------------------------------------------------------
static inline unsigned long BuildDrawFlags( bool bDrawSkybox, bool bDrawUnderWater, bool bDrawAboveWater, bool bDrawWaterSurface, bool bClipSkybox )
{
unsigned long nEngineFlags = 0;
if ( bDrawSkybox )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_SKYBOX;
}
if ( bDrawAboveWater )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYABOVEWATER;
nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER;
}
if ( bDrawUnderWater )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYUNDERWATER;
nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER;
}
if ( bDrawWaterSurface )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_WATERSURFACE;
}
if( bClipSkybox )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_CLIPSKYBOX;
}
return nEngineFlags;
}
void CViewRender::DrawWorld( ClientWorldListInfo_t& info, CRenderList &renderList, int flags, float waterZAdjust )
{
VPROF_INCREMENT_COUNTER( "RenderWorld", 1 );
VPROF_BUDGET( "DrawWorld", VPROF_BUDGETGROUP_WORLD_RENDERING );
if( !r_drawopaqueworld.GetBool() )
{
return;
}
bool drawSkybox = (flags & DF_DRAWSKYBOX) != 0;
bool drawUnderWater = (flags & DF_RENDER_UNDERWATER) != 0;
bool drawAboveWater = (flags & DF_RENDER_ABOVEWATER) != 0;
bool drawWaterSurface = (flags & DF_RENDER_WATER) != 0;
bool bClipSkybox = (flags & DF_CLIP_SKYBOX ) != 0;
unsigned long engineFlags = BuildDrawFlags( drawSkybox, drawUnderWater, drawAboveWater, drawWaterSurface, bClipSkybox );
if ( flags & DF_MAINTAINWORLDLISTS )
{
engineFlags |= DRAWWORLDLISTS_MAINTAIN_RENDER_LISTS;
}
int oldDrawFlags = m_DrawFlags;
m_DrawFlags |= flags;
render->DrawWorldLists( engineFlags, waterZAdjust );
m_DrawFlags = oldDrawFlags;
}
static inline void DrawOpaqueRenderable( IClientRenderable *pEnt, bool twoPass )
{
float color[3];
pEnt->GetColorModulation( color );
render->SetColorModulation( color );
int flags = STUDIO_RENDER;
if (twoPass)
{
flags |= STUDIO_TWOPASS;
}
float *pRenderClipPlane = NULL;
if( !materials->UsingFastClipping() && //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though
r_entityclips.GetBool() ) //checking here reduces the r_entityclips.GetBool() check from 2 times to 1
pRenderClipPlane = pEnt->GetRenderClipPlane();
if( pRenderClipPlane )
materials->PushCustomClipPlane( pRenderClipPlane );
pEnt->DrawModel( flags );
if( pRenderClipPlane )
materials->PopCustomClipPlane();
}
static inline void DrawTranslucentRenderable( IClientRenderable *pEnt, bool twoPass )
{
// Determine blending amount and tell engine
float blend = (float)( pEnt->GetFxBlend() / 255.0f );
// Totally gone
if ( blend <= 0.0f )
return;
// Tell engine
render->SetBlend( blend );
float color[3];
pEnt->GetColorModulation( color );
render->SetColorModulation( color );
int flags = STUDIO_RENDER | STUDIO_TRANSPARENCY;
if (twoPass)
flags |= STUDIO_TWOPASS;
#if 0
Vector mins, maxs;
pEnt->GetRenderBounds( mins, maxs );
debugoverlay->AddBoxOverlay( pEnt->GetRenderOrigin(), mins, maxs, pEnt->GetRenderAngles(), 255, 255, 255, 64, 0.01 );
if ( pEnt->GetModel() )
{
const char *pName = modelinfo->GetModelName( pEnt->GetModel() );
if ( Q_stricmp( pName, "models/props_c17/tv_monitor01_screen.mdl" ) )
{
debugoverlay->AddTextOverlay( pEnt->GetRenderOrigin(), 0.01, pName );
}
}
#endif
float *pRenderClipPlane = NULL;
if( !materials->UsingFastClipping() && //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though
r_entityclips.GetBool() ) //checking here reduces the r_entityclips.GetBool() check from 2 times to 1
pRenderClipPlane = pEnt->GetRenderClipPlane();
if( pRenderClipPlane )
materials->PushCustomClipPlane( pRenderClipPlane );
pEnt->DrawModel( flags );
if( pRenderClipPlane )
materials->PopCustomClipPlane();
}
//-----------------------------------------------------------------------------
// Draws all opaque renderables in leaves that were rendered
//-----------------------------------------------------------------------------
void CViewRender::DrawOpaqueRenderables( ClientWorldListInfo_t& info, CRenderList &renderList )
{
VPROF("CViewRender::DrawOpaqueRenderables");
if( !r_drawopaquerenderables.GetBool() )
return;
if( !ShouldDrawEntities() )
return;
render->SetBlend( 1 );
// Iterate over all leaves that were visible, and draw opaque things in them.
RopeManager()->ResetRenderCache();
// Iterate over all leaves that were visible, and draw opaque things in them.
int i;
// first do the brush models
CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH];
int nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH];
for( i=0; i < nOpaque; ++i )
{
Assert(pEntities[i].m_TwoPass==0);
DrawOpaqueRenderable( pEntities[i].m_pRenderable, false );
}
// now the static props
pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_STATIC];
nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_STATIC];
if ( nOpaque )
{
nOpaque = MIN( nOpaque, 512 );
IClientRenderable *pStatics[512];
for( i=0; i < nOpaque; ++i )
{
pStatics[i] = pEntities[i].m_pRenderable;
}
staticpropmgr->DrawStaticProps( pStatics, nOpaque );
}
// now the other opaque entities
pEntities = renderList.m_RenderGroups[RENDER_GROUP_OPAQUE_ENTITY];
nOpaque = renderList.m_RenderGroupCounts[RENDER_GROUP_OPAQUE_ENTITY];
for( i=0; i < nOpaque; ++i )
{
DrawOpaqueRenderable( pEntities[i].m_pRenderable, (pEntities[i].m_TwoPass != 0) );
}
RopeManager()->DrawRenderCache();
}
//-----------------------------------------------------------------------------
// Renders all translucent world + detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CViewRender::DrawTranslucentWorldInLeaves( int iCurLeafIndex, int iFinalLeafIndex, ClientWorldListInfo_t &info, int nDrawFlags )
{
VPROF_BUDGET( "CViewRender::DrawTranslucentWorldInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING );
for( ; iCurLeafIndex >= iFinalLeafIndex; iCurLeafIndex-- )
{
int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex;
Assert( nActualLeafIndex != INVALID_LEAF_INDEX );
if ( render->LeafContainsTranslucentSurfaces( nActualLeafIndex, nDrawFlags ) )
{
// Now draw the surfaces in this leaf
render->DrawTranslucentSurfaces( nActualLeafIndex, nDrawFlags );
}
}
}
//-----------------------------------------------------------------------------
// Renders all translucent world + detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CViewRender::DrawTranslucentWorldAndDetailPropsInLeaves( int iCurLeafIndex, int iFinalLeafIndex,
ClientWorldListInfo_t &info, int nDrawFlags, int &nDetailLeafCount, LeafIndex_t* pDetailLeafList )
{
VPROF_BUDGET( "CViewRender::DrawTranslucentWorldAndDetailPropsInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING );
for( ; iCurLeafIndex >= iFinalLeafIndex; iCurLeafIndex-- )
{
int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex;
Assert( nActualLeafIndex != INVALID_LEAF_INDEX );
if ( render->LeafContainsTranslucentSurfaces( nActualLeafIndex, nDrawFlags ) )
{
// First draw any queued-up detail props from previously visited leaves
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList );
nDetailLeafCount = 0;
// Now draw the surfaces in this leaf
render->DrawTranslucentSurfaces( nActualLeafIndex, nDrawFlags );
}
// Queue up detail props that existed in this leaf
if ( ClientLeafSystem()->ShouldDrawDetailObjectsInLeaf( info.m_pLeafList[iCurLeafIndex], m_BuildWorldListsNumber ) )
{
pDetailLeafList[nDetailLeafCount] = info.m_pLeafList[iCurLeafIndex];
++nDetailLeafCount;
}
}
}
//-----------------------------------------------------------------------------
// Renders all translucent entities in the render list
//-----------------------------------------------------------------------------
void CViewRender::DrawTranslucentRenderablesNoWorld( CRenderList &renderList, bool bInSkybox )
{
VPROF( "CViewRender::DrawTranslucentRenderablesNoWorld" );
if ( !ShouldDrawEntities() || !r_drawtranslucentrenderables.GetBool() )
return;
// Draw the particle singletons.
DrawParticleSingletons( bInSkybox );
CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int iCurTranslucentEntity = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1;
while( iCurTranslucentEntity >= 0 )
{
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
if ( pRenderable->UsesFrameBufferTexture() )
{
UpdateRefractTexture();
}
DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) );
--iCurTranslucentEntity;
}
// Reset the blend state.
render->SetBlend( 1 );
}
//-----------------------------------------------------------------------------
// Renders all translucent world, entities, and detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CViewRender::DrawTranslucentRenderables( ClientWorldListInfo_t& info, CRenderList &renderList,
int nFlags, bool bInSkybox )
{
if ( !r_drawtranslucentworld.GetBool() )
{
DrawTranslucentRenderablesNoWorld( renderList, bInSkybox );
return;
}
VPROF( "CViewRender::DrawTranslucentRenderables" );
int iPrevLeaf = info.m_LeafCount - 1;
int nDetailLeafCount = 0;
LeafIndex_t *pDetailLeafList = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) );
bool bDrawUnderWater = (nFlags & DF_RENDER_UNDERWATER) != 0;
bool bDrawAboveWater = (nFlags & DF_RENDER_ABOVEWATER) != 0;
bool bDrawWater = (nFlags & DF_RENDER_WATER) != 0;
bool bClipSkybox = (nFlags & DF_CLIP_SKYBOX ) != 0;
unsigned long nDrawFlags = BuildDrawFlags( false, bDrawUnderWater, bDrawAboveWater, bDrawWater, bClipSkybox );
DetailObjectSystem()->BeginTranslucentDetailRendering();
if ( ShouldDrawEntities() && r_drawtranslucentrenderables.GetBool() )
{
// Draw the particle singletons.
DrawParticleSingletons( bInSkybox );
CRenderList::CEntry *pEntities = renderList.m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int iCurTranslucentEntity = renderList.m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1;
bool bRenderingWaterRenderTargets = nFlags & ( DF_RENDER_REFRACTION | DF_RENDER_REFLECTION );
while( iCurTranslucentEntity >= 0 )
{
// Seek the current leaf up to our current translucent-entity leaf.
int iThisLeaf = pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf;
// First draw the translucent parts of the world up to and including those in this leaf
DrawTranslucentWorldAndDetailPropsInLeaves( iPrevLeaf, iThisLeaf, info, nDrawFlags, nDetailLeafCount, pDetailLeafList );
// We're traversing the leaf list backwards to get the appropriate sort ordering (back to front)
iPrevLeaf = iThisLeaf - 1;
// Draw all the translucent entities with this leaf.
int nLeaf = info.m_pLeafList[iThisLeaf];
bool bDrawDetailProps = ClientLeafSystem()->ShouldDrawDetailObjectsInLeaf( nLeaf, m_BuildWorldListsNumber );
if ( bDrawDetailProps )
{
// Draw detail props up to but not including this leaf
Assert( nDetailLeafCount > 0 );
--nDetailLeafCount;
Assert( pDetailLeafList[nDetailLeafCount] == nLeaf );
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList );
// Draw translucent renderables in the leaf interspersed with detail props
for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity )
{
MDLCACHE_CRITICAL_SECTION();
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
// Draw any detail props in this leaf that's farther than the entity
const Vector &vecRenderOrigin = pRenderable->GetRenderOrigin();
DetailObjectSystem()->RenderTranslucentDetailObjectsInLeaf(
CurrentViewOrigin(), CurrentViewForward(), nLeaf, &vecRenderOrigin );
if ( pRenderable->UsesFrameBufferTexture() )
{
if( bRenderingWaterRenderTargets )
{
continue;
}
UpdateRefractTexture();
}
// Then draw the translucent renderable
DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) );
}
// Draw all remaining props in this leaf
DetailObjectSystem()->RenderTranslucentDetailObjectsInLeaf( CurrentViewOrigin(), CurrentViewForward(), nLeaf, NULL );
}
else
{
// Draw queued up detail props (we know that the list of detail leaves won't include this leaf, since ShouldDrawDetailObjectsInLeaf is false)
// Therefore no fixup on nDetailLeafCount is required as in the above section
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList );
for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity )
{
MDLCACHE_CRITICAL_SECTION();
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
if ( pRenderable->UsesFrameBufferTexture() )
{
if( bRenderingWaterRenderTargets )
{
continue;
}
UpdateRefractTexture();
}
DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0) );
}
}
nDetailLeafCount = 0;
}
}
// Draw the rest of the surfaces in world leaves
DrawTranslucentWorldAndDetailPropsInLeaves( iPrevLeaf, 0, info, nDrawFlags, nDetailLeafCount, pDetailLeafList );
// Draw any queued-up detail props from previously visited leaves
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), nDetailLeafCount, pDetailLeafList );
// Reset the blend state.
render->SetBlend( 1 );
}
//-----------------------------------------------------------------------------
// Renders the shadow texture to screen...
//-----------------------------------------------------------------------------
static void RenderMaterial( const char *pMaterialName )
{
// So it's not in the very top left
float x = 100.0f, y = 100.0f;
// float x = 0.0f, y = 0.0f;
IMaterial *pMaterial = materials->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER, false );
if ( !IsErrorMaterial( pMaterial ) )
{
materials->Bind( pMaterial );
IMesh* pMesh = materials->GetDynamicMesh( true );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + pMaterial->GetMappingWidth(), y, 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x + pMaterial->GetMappingWidth(), y + pMaterial->GetMappingHeight(), 0.0f );
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( x, y + pMaterial->GetMappingHeight(), 0.0f );
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.Color4ub( 255, 255, 255, 255 );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
}
//-----------------------------------------------------------------------------
// Draws the screen effect
//-----------------------------------------------------------------------------
static void DrawScreenEffectMaterial( IMaterial *pMaterial, int x, int y, int w, int h )
{
Rect_t actualRect;
UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect );
ITexture *pTexture = GetFullFrameFrameBufferTexture( 0 );
materials->DrawScreenSpaceRectangle( pMaterial, x, y, w, h,
actualRect.x, actualRect.y, actualRect.x+actualRect.width-1, actualRect.y+actualRect.height-1,
pTexture->GetActualWidth(), pTexture->GetActualHeight() );
}
//-----------------------------------------------------------------------------
// Purpose: Performs screen space effects, if any
//-----------------------------------------------------------------------------
void CViewRender::PerformScreenSpaceEffects( int x, int y, int w, int h )
{
VPROF("CViewRender::PerformScreenSpaceEffects()");
// FIXME: Screen-space effects are busted in the editor
if ( engine->IsHammerRunning() )
return;
g_pScreenSpaceEffects->RenderEffects( x, y, w, h );
}
//-----------------------------------------------------------------------------
// Purpose: Sets the screen space effect material (can't be done during rendering)
//-----------------------------------------------------------------------------
void CViewRender::SetScreenOverlayMaterial( IMaterial *pMaterial )
{
m_ScreenOverlayMaterial.Init( pMaterial );
}
IMaterial *CViewRender::GetScreenOverlayMaterial( )
{
return m_ScreenOverlayMaterial;
}
//-----------------------------------------------------------------------------
// Purpose: Performs screen space effects, if any
//-----------------------------------------------------------------------------
void CViewRender::PerformScreenOverlay( int x, int y, int w, int h )
{
VPROF("CViewRender::PerformScreenOverlay()");
if (m_ScreenOverlayMaterial)
{
if ( m_ScreenOverlayMaterial->NeedsFullFrameBufferTexture() )
{
DrawScreenEffectMaterial( m_ScreenOverlayMaterial, x, y, w, h );
}
else if ( m_ScreenOverlayMaterial->NeedsPowerOfTwoFrameBufferTexture() )
{
// First copy the FB off to the offscreen texture
UpdateRefractTexture( x, y, w, h, true );
// Now draw the entire screen using the material...
ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( );
int sw = pTexture->GetActualWidth();
int sh = pTexture->GetActualHeight();
materials->DrawScreenSpaceRectangle( m_ScreenOverlayMaterial, x, y, w, h,
0, 0, sw-1, sh-1, sw, sh );
}
else
{
byte color[4] = { 255, 255, 255, 255 };
render->ViewDrawFade( color, m_ScreenOverlayMaterial );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Renders world and all entities, etc.
//-----------------------------------------------------------------------------
#include "tier0/memdbgoff.h"
void CViewRender::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &view, int nClearFlags )
{
VPROF("CViewRender::DrawWorldAndEntities");
MDLCACHE_CRITICAL_SECTION();
ClientWorldListInfo_t info;
#ifndef _XBOX
CRenderList renderList;
#else
void *ptr = MemAllocScratch(sizeof(CRenderList));
CRenderList *renderList = new (ptr) CRenderList;
#endif
EnableWorldFog();
int nFlags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER;
if (bDrawSkybox)
{
nFlags |= DF_DRAWSKYBOX;
}
render->Push3DView( view, nClearFlags, false, NULL, m_Frustum );
BuildWorldRenderLists( &view, info, true, true, m_iForceViewLeaf );
#ifndef _XBOX
BuildRenderableRenderLists( &view, info, renderList );
#else
BuildRenderableRenderLists( &view, info, *renderList );
#endif
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
m_DrawFlags = m_BaseDrawFlags;
#ifndef _XBOX
DrawWorld( info, renderList, nFlags, 0.0f );
DrawOpaqueRenderables( info, renderList );
DrawTranslucentRenderables( info, renderList, nFlags, false );
#else
DrawWorld( info, *renderList, nFlags, 0.0f );
DrawOpaqueRenderables( info, *renderList );
DrawTranslucentRenderables( info, *renderList, nFlags, false );
MemFreeScratch();
#endif
m_DrawFlags = 0;
ParticleMgr()->DrawBeforeViewModelEffects();
render->PopView( m_Frustum );
}
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Computes us some geometry to render the frustum planes
//-----------------------------------------------------------------------------
void CViewRender::ComputeFrustumRenderGeometry( Vector pRenderPoint[8] )
{
Vector viewPoint = CurrentViewOrigin();
// Find lines along each of the plane intersections.
// We know these lines are perpendicular to both plane normals,
// so we can take the cross product to find them.
static int edgeIdx[4][2] =
{
{ 0, 2 }, { 0, 3 }, { 1, 3 }, { 1, 2 }
};
int i;
Vector edges[4];
for ( i = 0; i < 4; ++i)
{
CrossProduct( GetFrustum()[edgeIdx[i][0]].m_Normal,
GetFrustum()[edgeIdx[i][1]].m_Normal, edges[i] );
VectorNormalize( edges[i] );
}
// Figure out four near points by intersection lines with the near plane
// Figure out four far points by intersection with lines against far plane
for (i = 0; i < 4; ++i)
{
float t = (GetFrustum()[4].m_Dist - DotProduct(GetFrustum()[4].m_Normal, viewPoint)) /
DotProduct(GetFrustum()[4].m_Normal, edges[i]);
VectorMA( viewPoint, t, edges[i], pRenderPoint[i] );
/*
t = (m_FrustumPlanes[5][3] - DotProduct(GetFrustum()[5], viewPoint)) /
DotProduct(GetFrustum()[5], edges[i]);
VectorMA( viewPoint, t, edges[i], pRenderPoint[i + 4] );
*/
if (t < 0)
{
edges[i] *= -1;
}
VectorMA( pRenderPoint[i], 200.0, edges[i], pRenderPoint[i + 4] );
}
}
//-----------------------------------------------------------------------------
// renders the frustum
//-----------------------------------------------------------------------------
void CViewRender::RenderFrustum( )
{
static int indices[] =
{
0, 1, 1, 2, 2, 3, 3, 0, // near square
4, 5, 5, 6, 6, 7, 7, 4, // far square
0, 4, 1, 5, 2, 6, 3, 7 // connections between them
};
int numIndices = sizeof(indices) / sizeof(int);
Vector vecFrustumRenderPoint[8];
ComputeFrustumRenderGeometry( vecFrustumRenderPoint );
int i;
for ( i = 0; i < numIndices; i += 2 )
{
debugoverlay->AddLineOverlay( vecFrustumRenderPoint[indices[i]],
vecFrustumRenderPoint[indices[i+1]], 0, 0, 255, 255, 1.0f );
}
}
//-----------------------------------------------------------------------------
// Draws all the debugging info
//-----------------------------------------------------------------------------
void CViewRender::Draw3DDebuggingInfo( const CViewSetup &view )
{
VPROF("CViewRender::Draw3DDebuggingInfo");
// Draw 3d overlays
render->Draw3DDebugOverlays();
// Draw the line file used for debugging leaks
render->DrawLineFile();
// Draw client side effects
// NOTE: These are not sorted against the rest of the frame
clienteffects->DrawEffects( gpGlobals->frametime );
// Mark the frame as locked down for client fx additions
SetFXCreationAllowed( false );
}
//-----------------------------------------------------------------------------
// Draws all the debugging info
//-----------------------------------------------------------------------------
void CViewRender::Draw2DDebuggingInfo( const CViewSetup &view )
{
if ( IsXbox() && IsRetail() )
return;
// HDRFIXME: Assert NULL rendertarget
if ( mat_yuv.GetInt() && (engine->GetDXSupportLevel() >= 80) )
{
IMaterial *pMaterial;
pMaterial = materials->FindMaterial( "debug/yuv", TEXTURE_GROUP_OTHER, true );
if( !IsErrorMaterial( pMaterial ) )
{
DrawScreenEffectMaterial( pMaterial, view.x, view.y, view.width, view.height );
}
}
#ifndef _XBOX
if ( mat_hsv.GetInt() && (engine->GetDXSupportLevel() >= 90) )
{
IMaterial *pMaterial;
pMaterial = materials->FindMaterial( "debug/hsv", TEXTURE_GROUP_OTHER, true );
if( !IsErrorMaterial( pMaterial ) )
{
DrawScreenEffectMaterial( pMaterial, view.x, view.y, view.width, view.height );
}
}
#endif
// Draw debugging lightmaps
render->DrawLightmaps();
if ( cl_drawshadowtexture.GetInt() )
{
g_pClientShadowMgr->RenderShadowTexture( 256, 256 );
}
const char *pDrawMaterial = cl_drawmaterial.GetString();
if ( pDrawMaterial && pDrawMaterial[0] )
{
RenderMaterial( pDrawMaterial );
}
if ( mat_showwatertextures.GetBool() )
{
OverlayWaterTextures();
}
if ( mat_showcamerarendertarget.GetBool() )
{
float w = mat_wateroverlaysize.GetFloat();
float h = mat_wateroverlaysize.GetFloat();
OverlayCameraRenderTarget( "debug/debugcamerarendertarget", 0, 0, w, h );
}
if ( mat_showframebuffertexture.GetBool() )
{
// HDRFIXME: Get rid of these rendertarget sets assuming that the assert at the top of this function is true.
materials->PushRenderTargetAndViewport( NULL );
OverlayFrameBufferTexture( 0 );
OverlayFrameBufferTexture( 1 );
materials->PopRenderTargetAndViewport( );
}
#ifdef _XBOX
if ( mat_viewTextureEnable.GetBool() )
{
OverlayShowTexture( mat_viewTextureName.GetString(), mat_viewTextureScale.GetFloat() );
}
#endif
if( r_flashlightdrawdepth.GetBool() )
{
shadowmgr->DrawFlashlightDepthTexture( );
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the min/max fade distances
//-----------------------------------------------------------------------------
void CViewRender::GetScreenFadeDistances( float *min, float *max )
{
if ( min )
{
*min = r_screenfademinsize.GetFloat();
}
if ( max )
{
*max = r_screenfademaxsize.GetFloat();
}
}
//-----------------------------------------------------------------------------
// Purpose: Renders world and all entities, etc.
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene( bool bDrew3dSkybox, bool bSkyboxVisible, const CViewSetup &view,
int nClearFlags, view_id_t viewID, bool bDrawViewModel, int baseDrawFlags )
{
VPROF( "CViewRender::ViewDrawScene" );
m_BaseDrawFlags = baseDrawFlags;
m_DrawFlags = 0;
SetupCurrentView( view.origin, view.angles, viewID );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view
unsigned int visFlags;
SetupVis( view, visFlags );
if ( !bDrew3dSkybox &&
!bSkyboxVisible && ( visFlags & IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS ) )
{
// This covers the case where we don't see a 3dskybox, yet radial vis is clipping
// the far plane. Need to clear to fog color in this case.
nClearFlags |= VIEW_CLEAR_COLOR;
unsigned char ucFogColor[3];
SetClearColorToFogColor( ucFogColor );
}
bool drawSkybox = r_skybox.GetBool();
if ( bDrew3dSkybox || !bSkyboxVisible )
{
drawSkybox = false;
}
ParticleMgr()->IncrementFrameCode();
WaterDrawWorldAndEntities( drawSkybox, view, nClearFlags );
// Disable fog for the rest of the stuff
DisableFog();
// UNDONE: Don't do this with masked brush models, they should probably be in a separate list
// render->DrawMaskEntities()
// Here are the overlays...
// This is an overlay that goes over everything else
CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState );
// issue the pixel visibility tests
PixelVisibility_EndCurrentView();
// Draw rain..
DrawPrecipitation();
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Debugging info goes over the top
Draw3DDebuggingInfo( view );
// Invoke post-render methods
IGameSystem::PostRenderAllSystems();
FinishCurrentView();
m_DrawFlags = 0;
}
void CheckAndTransitionColor( float flPercent, float *pColor, float *pLerpToColor )
{
if ( pLerpToColor[0] != pColor[0] || pLerpToColor[1] != pColor[1] || pLerpToColor[2] != pColor[2] )
{
float flDestColor[3];
flDestColor[0] = pLerpToColor[0];
flDestColor[1] = pLerpToColor[1];
flDestColor[2] = pLerpToColor[2];
pColor[0] = FLerp( pColor[0], flDestColor[0], flPercent );
pColor[1] = FLerp( pColor[1], flDestColor[1], flPercent );
pColor[2] = FLerp( pColor[2], flDestColor[2], flPercent );
}
else
{
pColor[0] = pLerpToColor[0];
pColor[1] = pLerpToColor[1];
pColor[2] = pLerpToColor[2];
}
}
static void GetFogColorTransition( CPlayerLocalData *local, float *pColorPrimary, float *pColorSecondary )
{
if ( local == NULL )
return;
if ( local->m_fog.lerptime >= gpGlobals->curtime )
{
float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration );
float flPrimaryColorLerp[3] = { local->m_fog.colorPrimaryLerpTo.GetR(), local->m_fog.colorPrimaryLerpTo.GetG(), local->m_fog.colorPrimaryLerpTo.GetB() };
float flSecondaryColorLerp[3] = { local->m_fog.colorSecondaryLerpTo.GetR(), local->m_fog.colorSecondaryLerpTo.GetG(), local->m_fog.colorSecondaryLerpTo.GetB() };
CheckAndTransitionColor( flPercent, pColorPrimary, flPrimaryColorLerp );
CheckAndTransitionColor( flPercent, pColorSecondary, flSecondaryColorLerp );
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the fog color to use in rendering the current frame.
//-----------------------------------------------------------------------------
static void GetFogColor( float *pColor )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return;
}
CPlayerLocalData *local = &pbp->m_Local;
const char *fogColorString = fog_color.GetString();
if( fog_override.GetInt() && fogColorString )
{
sscanf( fogColorString, "%f%f%f", pColor, pColor+1, pColor+2 );
}
else
{
float flPrimaryColor[3] = { local->m_fog.colorPrimary.GetR(), local->m_fog.colorPrimary.GetG(), local->m_fog.colorPrimary.GetB() };
float flSecondaryColor[3] = { local->m_fog.colorSecondary.GetR(), local->m_fog.colorSecondary.GetG(), local->m_fog.colorSecondary.GetB() };
GetFogColorTransition( local, flPrimaryColor, flSecondaryColor );
if( local->m_fog.blend )
{
//
// Blend between two fog colors based on viewing angle.
// The secondary fog color is at 180 degrees to the primary fog color.
//
Vector forward;
AngleVectors(pbp->GetAbsAngles(), &forward);
Vector vNormalized = local->m_fog.dirPrimary;
VectorNormalize( vNormalized );
local->m_fog.dirPrimary = vNormalized;
float flBlendFactor = 0.5 * forward.Dot( local->m_fog.dirPrimary ) + 0.5;
// FIXME: convert to linear colorspace
pColor[0] = flPrimaryColor[0] * flBlendFactor + flSecondaryColor[0] * ( 1 - flBlendFactor );
pColor[1] = flPrimaryColor[1] * flBlendFactor + flSecondaryColor[1] * ( 1 - flBlendFactor );
pColor[2] = flPrimaryColor[2] * flBlendFactor + flSecondaryColor[2] * ( 1 - flBlendFactor );
}
else
{
pColor[0] = flPrimaryColor[0];
pColor[1] = flPrimaryColor[1];
pColor[2] = flPrimaryColor[2];
}
}
VectorScale( pColor, 1.0f / 255.0f, pColor );
}
static float GetFogStart( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return 0.0f;
}
CPlayerLocalData *local = &pbp->m_Local;
if( fog_override.GetInt() )
{
if( fog_start.GetFloat() == -1.0f )
{
return local->m_fog.start;
}
else
{
return fog_start.GetFloat();
}
}
else
{
if ( local->m_fog.lerptime > gpGlobals->curtime )
{
if ( local->m_fog.start != local->m_fog.startLerpTo )
{
if ( local->m_fog.lerptime > gpGlobals->curtime )
{
float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration );
return FLerp( local->m_fog.start, local->m_fog.startLerpTo, flPercent );
}
else
{
if ( local->m_fog.start != local->m_fog.startLerpTo )
{
local->m_fog.start = local->m_fog.startLerpTo;
}
}
}
}
return local->m_fog.start;
}
}
static float GetFogEnd( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return 0.0f;
}
CPlayerLocalData *local = &pbp->m_Local;
if( fog_override.GetInt() )
{
if( fog_end.GetFloat() == -1.0f )
{
return local->m_fog.end;
}
else
{
return fog_end.GetFloat();
}
}
else
{
if ( local->m_fog.lerptime > gpGlobals->curtime )
{
if ( local->m_fog.end != local->m_fog.endLerpTo )
{
if ( local->m_fog.lerptime > gpGlobals->curtime )
{
float flPercent = 1.0f - (( local->m_fog.lerptime - gpGlobals->curtime ) / local->m_fog.duration );
return FLerp( local->m_fog.end, local->m_fog.endLerpTo, flPercent );
}
else
{
if ( local->m_fog.end != local->m_fog.endLerpTo )
{
local->m_fog.end = local->m_fog.endLerpTo;
}
}
}
}
return local->m_fog.end;
}
}
static bool GetFogEnable( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return false;
}
CPlayerLocalData *local = &pbp->m_Local;
if ( cl_leveloverview.GetFloat() > 0 )
return false;
// Ask the clientmode
if ( g_pClientMode->ShouldDrawFog() == false )
return false;
if( fog_override.GetInt() )
{
if( fog_enable.GetInt() )
{
return true;
}
else
{
return false;
}
}
else
{
return local->m_fog.enable != false;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the skybox fog color to use in rendering the current frame.
//-----------------------------------------------------------------------------
static void GetSkyboxFogColor( float *pColor )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return;
}
CPlayerLocalData *local = &pbp->m_Local;
const char *fogColorString = fog_colorskybox.GetString();
if( fog_override.GetInt() && fogColorString )
{
sscanf( fogColorString, "%f%f%f", pColor, pColor+1, pColor+2 );
}
else
{
if( local->m_skybox3d.fog.blend )
{
//
// Blend between two fog colors based on viewing angle.
// The secondary fog color is at 180 degrees to the primary fog color.
//
Vector forward;
AngleVectors( pbp->GetAbsAngles(), &forward );
Vector vNormalized = local->m_fog.dirPrimary;
VectorNormalize( vNormalized );
local->m_fog.dirPrimary = vNormalized;
float flBlendFactor = 0.5 * forward.Dot( local->m_fog.dirPrimary ) + 0.5;
// FIXME: convert to linear colorspace
pColor[0] = local->m_skybox3d.fog.colorPrimary.GetR() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetR() * ( 1 - flBlendFactor );
pColor[1] = local->m_skybox3d.fog.colorPrimary.GetG() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetG() * ( 1 - flBlendFactor );
pColor[2] = local->m_skybox3d.fog.colorPrimary.GetB() * flBlendFactor + local->m_skybox3d.fog.colorSecondary.GetB() * ( 1 - flBlendFactor );
}
else
{
pColor[0] = local->m_skybox3d.fog.colorPrimary.GetR();
pColor[1] = local->m_skybox3d.fog.colorPrimary.GetG();
pColor[2] = local->m_skybox3d.fog.colorPrimary.GetB();
}
}
VectorScale( pColor, 1.0f / 255.0f, pColor );
}
static float GetSkyboxFogStart( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return 0.0f;
}
CPlayerLocalData *local = &pbp->m_Local;
if( fog_override.GetInt() )
{
if( fog_startskybox.GetFloat() == -1.0f )
{
return local->m_skybox3d.fog.start;
}
else
{
return fog_startskybox.GetFloat();
}
}
else
{
return local->m_skybox3d.fog.start;
}
}
static float GetSkyboxFogEnd( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return 0.0f;
}
CPlayerLocalData *local = &pbp->m_Local;
if( fog_override.GetInt() )
{
if( fog_endskybox.GetFloat() == -1.0f )
{
return local->m_skybox3d.fog.end;
}
else
{
return fog_endskybox.GetFloat();
}
}
else
{
return local->m_skybox3d.fog.end;
}
}
static bool GetSkyboxFogEnable( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return false;
}
CPlayerLocalData *local = &pbp->m_Local;
if( fog_override.GetInt() )
{
if( fog_enableskybox.GetInt() )
{
return true;
}
else
{
return false;
}
}
else
{
return !!local->m_skybox3d.fog.enable;
}
}
void CViewRender::EnableWorldFog( void )
{
VPROF("CViewRender::EnableWorldFog");
if( GetFogEnable() )
{
float fogColor[3];
GetFogColor( fogColor );
materials->FogMode( MATERIAL_FOG_LINEAR );
materials->FogColor3fv( fogColor );
materials->FogStart( GetFogStart() );
materials->FogEnd( GetFogEnd() );
}
else
{
materials->FogMode( MATERIAL_FOG_NONE );
}
}
void CViewRender::Enable3dSkyboxFog( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return;
}
CPlayerLocalData *local = &pbp->m_Local;
if( GetSkyboxFogEnable() )
{
float fogColor[3];
GetSkyboxFogColor( fogColor );
float scale = 1.0f;
if ( local->m_skybox3d.scale > 0.0f )
{
scale = 1.0f / local->m_skybox3d.scale;
}
materials->FogMode( MATERIAL_FOG_LINEAR );
materials->FogColor3fv( fogColor );
materials->FogStart( GetSkyboxFogStart() * scale );
materials->FogEnd( GetSkyboxFogEnd() * scale );
}
else
{
materials->FogMode( MATERIAL_FOG_NONE );
}
}
void CViewRender::DisableFog( void )
{
VPROF("CViewRander::DisableFog()");
materials->FogMode( MATERIAL_FOG_NONE );
}
#include "tier0/memdbgoff.h"
void CViewRender::Draw3dSkyboxworld( const CViewSetup &view, int &nClearFlags,
bool &bDrew3dSkybox, bool &bSkyboxVisible )
{
VPROF_BUDGET( "CViewRender::Draw3dSkyboxworld", "3D Skybox" );
bDrew3dSkybox = false;
// The skybox might not be visible from here
bSkyboxVisible = engine->IsSkyboxVisibleFromPoint( view.origin );
if ( !bSkyboxVisible && r_3dsky.GetInt() != 2 )
{
return;
}
// render the 3D skybox
if ( !r_3dsky.GetInt() )
{
return;
}
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
// No local player object yet...
if ( !pbp )
return;
CPlayerLocalData* local = &pbp->m_Local;
if ( local->m_skybox3d.area == 255 )
return;
unsigned char **areabits = render->GetAreaBits();
unsigned char *savebits;
unsigned char tmpbits[ 32 ];
savebits = *areabits;
memset( tmpbits, 0, sizeof(tmpbits) );
// set the sky area bit
tmpbits[local->m_skybox3d.area>>3] |= 1 << (local->m_skybox3d.area&7);
*areabits = tmpbits;
CViewSetup skyView = view;
skyView.zNear = 0.5;
skyView.zFar = 18000;
// scale origin by sky scale
if ( local->m_skybox3d.scale > 0 )
{
float scale = 1.0f / local->m_skybox3d.scale;
VectorScale( skyView.origin, scale, skyView.origin );
}
Enable3dSkyboxFog();
VectorAdd( skyView.origin, local->m_skybox3d.origin, skyView.origin );
skyView.m_vUnreflectedOrigin = skyView.origin;
// BUGBUG: Fix this!!! We shouldn't need to call setup vis for the sky if we're connecting
// the areas. We'd have to mark all the clusters in the skybox area in the PVS of any
// cluster with sky. Then we could just connect the areas to do our vis.
//m_bOverrideVisOrigin could hose us here, so call direct
render->ViewSetupVis( false, 1, &local->m_skybox3d.origin.Get() );
render->Push3DView( skyView, nClearFlags, false, NULL, m_Frustum );
// At this point, we've cleared everything we need to clear
// The next path will need to clear depth, though.
nClearFlags &= ~(VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH | VIEW_CLEAR_FULL_TARGET);
nClearFlags |= VIEW_CLEAR_DEPTH;
// Store off view origin and angles
SetupCurrentView( skyView.origin, skyView.angles, VIEW_3DSKY );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
ClientWorldListInfo_t info;
#ifndef _XBOX
CRenderList renderList;
CRenderList *pRenderList = &renderList;
#else
void *ptr = MemAllocScratch(sizeof(CRenderList));
CRenderList *pRenderList = new (ptr) CRenderList;
#endif
BuildWorldRenderLists( NULL, info, true, true, m_iForceViewLeaf );
BuildRenderableRenderLists( NULL, info, *pRenderList );
int flags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER;
if( r_skybox.GetBool() )
{
flags |= DF_DRAWSKYBOX;
}
int oldDrawFlags = m_DrawFlags;
m_DrawFlags |= flags;
DrawWorld( info, *pRenderList, flags, 0.0f );
// Iterate over all leaves and render objects in those leaves
DrawOpaqueRenderables( info, *pRenderList );
// Iterate over all leaves and render objects in those leaves
DrawTranslucentRenderables( info, *pRenderList, flags, true );
DisableFog();
CGlowOverlay::UpdateSkyOverlays( skyView.zFar, view.m_bCacheFullSceneState );
PixelVisibility_EndCurrentView();
// restore old area bits
*areabits = savebits;
// Invoke post-render methods
IGameSystem::PostRenderAllSystems();
FinishCurrentView();
m_DrawFlags = oldDrawFlags;
#ifdef _XBOX
MemFreeScratch();
#endif
render->PopView( m_Frustum );
bDrew3dSkybox = true;
}
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::SetupVis( const CViewSetup& view, unsigned int &visFlags )
{
VPROF( "CViewRender::SetupVis" );
if ( m_bOverrideVisOrigin )
{
// Pass array or vis origins to merge
render->ViewSetupVisEx( m_bForceNoVis, m_nNumVisOrigins, m_rgVisOrigins, visFlags );
}
else
{
// Use render origin as vis origin by default
render->ViewSetupVisEx( m_bForceNoVis, 1, &view.origin, visFlags );
}
}
#ifndef _XBOX
// hdr parameters
ConVar mat_hdr_level( "mat_hdr_level", "2" );
ConVar mat_bloomamount_rate( "mat_bloomamount_rate", "0.05f" );
static ConVar debug_postproc( "mat_debug_postprocessing_effects", "0" );
static ConVar split_postproc( "mat_debug_process_halfscreen", "0" );
static ConVar mat_dynamic_tonemapping( "mat_dynamic_tonemapping", "0" );
static ConVar mat_show_ab_hdr( "mat_show_ab_hdr", "0" );
static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "1" );
ConVar mat_debug_autoexposure("mat_debug_autoexposure","0");
static ConVar mat_autoexposure_max( "mat_autoexposure_max", "2" );
static ConVar mat_autoexposure_min( "mat_autoexposure_min", "0.5" );
static ConVar mat_show_histogram( "mat_show_histogram", "0" );
ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT );
ConVar mat_force_bloom("mat_force_bloom","0");
ConVar mat_disable_bloom("mat_disable_bloom","0");
ConVar mat_debug_bloom("mat_debug_bloom","0");
// 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");
enum PostProcessingCondition {
PPP_ALWAYS,
PPP_IF_COND_VAR,
PPP_IF_NOT_COND_VAR
};
struct PostProcessingPass {
PostProcessingCondition ppp_test;
ConVar const *cvar_to_test;
char const *material_name; // terminate list with null
char const *dest_rendering_target;
char const *src_rendering_target; // can be null. needed for source scaling
int xdest_scale,ydest_scale; // allows scaling down
int xsrc_scale,ysrc_scale; // allows scaling down
CMaterialReference m_mat_ref; // so we don't have to keep searching
};
#define PPP_PROCESS_PARTIAL_SRC(srcmatname,dest_rt_name,src_tname,scale) \
{PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,scale,scale}
#define PPP_PROCESS_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,scale) \
{PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,scale,scale,1,1}
#define PPP_PROCESS_PARTIAL_SRC_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,srcscale,destscale) \
{PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,destscale,destscale,srcscale,srcscale}
#define PPP_END {PPP_ALWAYS,0,NULL,NULL,0,0,0,0,0}
#define PPP_PROCESS(srcmatname,dest_rt_name) {PPP_ALWAYS,0,srcmatname,dest_rt_name,0,1,1,1,1}
#define PPP_PROCESS_IF_CVAR(cvarptr,srcmatname,dest_rt_name) \
{PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1}
#define PPP_PROCESS_IF_NOT_CVAR(cvarptr,srcmatname,dest_rt_name) \
{PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1}
#define PPP_PROCESS_IF_NOT_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_tname,dest_rt_name) \
{PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_tname,1,1,1,1}
#define PPP_PROCESS_IF_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_txtrname,dest_rt_name) \
{PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_txtrname,1,1,1,1}
#define PPP_PROCESS_SRCTEXTURE(srcmatname,src_tname,dest_rt_name) \
{PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,1,1}
struct ClipBox
{
int m_minx, m_miny;
int m_maxx, m_maxy;
};
static void DrawClippedScreenSpaceRectangle(
IMaterial *pMaterial,
int destx, int desty,
int width, int height,
float src_texture_x0, float src_texture_y0, // which texel you want to appear at
// destx/y
float src_texture_x1, float src_texture_y1, // which texel you want to appear at
// destx+width-1, desty+height-1
int src_texture_width, int src_texture_height, // needed for fixup
ClipBox const *clipbox
)
{
if (clipbox)
{
if ((destx>clipbox->m_maxx) || (desty>clipbox->m_maxy))
return;
if ((destx+width-1<clipbox->m_minx) || (desty+height-1<clipbox->m_miny))
return;
// left clip
if (destx<clipbox->m_minx)
{
src_texture_x0=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,clipbox->m_minx);
width-=(clipbox->m_minx-destx);
destx=clipbox->m_minx;
}
// top clip
if (desty<clipbox->m_miny)
{
src_texture_y0=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,clipbox->m_miny);
height-=(clipbox->m_miny-desty);
desty=clipbox->m_miny;
}
// right clip
if (destx+width-1>clipbox->m_maxx)
{
src_texture_x1=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,clipbox->m_maxx);
width=clipbox->m_maxx-destx;
}
// bottom clip
if (desty+height-1>clipbox->m_maxy)
{
src_texture_y1=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,clipbox->m_maxy);
height=clipbox->m_maxy-desty;
}
}
materials->DrawScreenSpaceRectangle(pMaterial,destx,desty,width,height,src_texture_x0,
src_texture_y0,src_texture_x1,src_texture_y1,
src_texture_width,src_texture_height);
}
void ApplyPostProcessingPasses(PostProcessingPass *pass_list, // table of effects to apply
ClipBox const *clipbox=0, // clipping box for these effects
ClipBox *dest_coords_out=0) // receives dest coords of last blit
{
ITexture *pSaveRenderTarget = materials->GetRenderTarget();
int pcount=0;
if ( debug_postproc.GetInt() )
{
materials->SetRenderTarget(NULL);
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
materials->Viewport( 0, 0, dest_width, dest_height );
materials->ClearColor3ub(255,0,0);
// materials->ClearBuffers(true,true);
}
while(pass_list->material_name)
{
bool do_it=true;
switch(pass_list->ppp_test)
{
case PPP_IF_COND_VAR:
do_it=(pass_list->cvar_to_test)->GetBool();
break;
case PPP_IF_NOT_COND_VAR:
do_it=! ((pass_list->cvar_to_test)->GetBool());
break;
}
if ((pass_list->dest_rendering_target==0) && (debug_postproc.GetInt()))
do_it=0;
if (do_it)
{
ClipBox const *cb=0;
if (pass_list->dest_rendering_target==0)
{
cb=clipbox;
}
IMaterial *src_mat=pass_list->m_mat_ref;
if (! src_mat)
{
src_mat=materials->FindMaterial(pass_list->material_name,
TEXTURE_GROUP_OTHER,true);
if (src_mat)
{
pass_list->m_mat_ref.Init(src_mat);
}
}
if (pass_list->dest_rendering_target)
{
ITexture *dest_rt=materials->FindTexture(pass_list->dest_rendering_target,
TEXTURE_GROUP_RENDER_TARGET);
materials->SetRenderTarget( dest_rt);
}
else
{
materials->SetRenderTarget( NULL );
}
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
materials->Viewport( 0, 0, dest_width, dest_height );
dest_width/=pass_list->xdest_scale;
dest_height/=pass_list->ydest_scale;
if (pass_list->src_rendering_target)
{
ITexture *src_rt=materials->FindTexture(pass_list->src_rendering_target,
TEXTURE_GROUP_RENDER_TARGET);
int src_width=src_rt->GetActualWidth();
int src_height=src_rt->GetActualHeight();
int ssrc_width=(src_width-1)/pass_list->xsrc_scale;
int ssrc_height=(src_height-1)/pass_list->ysrc_scale;
DrawClippedScreenSpaceRectangle(
src_mat,0,0,dest_width,dest_height,
0,0,ssrc_width,ssrc_height,src_width,src_height,cb);
if ((pass_list->dest_rendering_target) && debug_postproc.GetInt())
{
materials->SetRenderTarget(NULL);
int row=pcount/2;
int col=pcount %2;
int vdest_width,vdest_height;
materials->GetRenderTargetDimensions( vdest_width, vdest_height );
materials->Viewport( 0, 0, vdest_width, vdest_height );
materials->DrawScreenSpaceRectangle(
src_mat,col*400,200+row*300,dest_width,dest_height,
0,0,ssrc_width,ssrc_height,src_width,src_height);
}
}
else
{
// just draw the whole source
if ((pass_list->dest_rendering_target==0) && split_postproc.GetInt())
{
DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width/2,dest_height,
0,0,.5,1,1,1,cb);
}
else
{
DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width,dest_height,
0,0,1,1,1,1,cb);
}
if ((pass_list->dest_rendering_target) && debug_postproc.GetInt())
{
materials->SetRenderTarget(NULL);
int row=pcount/4;
int col=pcount %4;
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
materials->Viewport( 0, 0, dest_width, dest_height );
DrawClippedScreenSpaceRectangle(src_mat,10+col*220,10+row*220,
200,200,
0,0,1,1,1,1,cb);
}
}
if (dest_coords_out)
{
dest_coords_out->m_minx=0;
dest_coords_out->m_maxx=dest_width-1;
dest_coords_out->m_miny=0;
dest_coords_out->m_maxy=dest_height-1;
}
}
pass_list++;
pcount++;
}
materials->SetRenderTarget(pSaveRenderTarget);
}
PostProcessingPass HDRFinal_Float[] =
{
PPP_PROCESS_SRCTEXTURE("dev/downsample","_rt_FullFrameFB", "_rt_SmallFB0"),
PPP_PROCESS("dev/blurfilterx","_rt_SmallFB1"),
PPP_PROCESS("dev/blurfiltery","_rt_SmallFB0"),
PPP_PROCESS("dev/floattoscreen_combine",NULL),
PPP_END
};
PostProcessingPass HDRFinal_Float_NoBloom[] =
{
PPP_PROCESS_SRCTEXTURE("dev/copyfullframefb", "_rt_FullFrameFB",NULL),
PPP_END
};
PostProcessingPass HDRSimulate_NonHDR[]={
PPP_PROCESS("dev/copyfullframefb_vanilla",NULL),
PPP_END
};
static bool IsSplitScreenHDR(void)
{
return ( mat_show_ab_hdr.GetInt() &&
(g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE) );
}
static void SetRenderTargetAndViewPort(ITexture *rt)
{
materials->SetRenderTarget(rt);
materials->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight());
}
#define FILTER_KERNEL_SLOP 20
// Note carefully about the downsampling: the first downsampling samples from the full rendertarget
// down to a temp. When doing this sampling, the texture source clamping will take care of the out
// of bounds sampling done because of the filter kernels's width. However, on any of the subsequent
// sampling operations, we will be sampling from a partially filled render target. So, texture
// coordinate clamping cannot help us here. So, we need to always render a few more pixels to the
// destination than we actually intend to, so as to replicate the border pixels so that garbage
// pixels do not get sucked into the sampling. To deal with this, we always add FILTER_KERNEL_SLOP
// to our widths/heights if there is room for them in the destination.
static void DrawScreenSpaceRectangleWithSlop(
ITexture *dest_rt,
IMaterial *pMaterial,
int destx, int desty,
int width, int height,
float src_texture_x0, float src_texture_y0, // which texel you want to appear at
// destx/y
float src_texture_x1, float src_texture_y1, // which texel you want to appear at
// destx+width-1, desty+height-1
int src_texture_width, int src_texture_height // needed for fixup
)
{
// add slop
int slopwidth=width+FILTER_KERNEL_SLOP; //MIN(dest_rt->GetActualWidth()-destx,width+FILTER_KERNEL_SLOP);
int slopheight=height+FILTER_KERNEL_SLOP; //MIN(dest_rt->GetActualHeight()-desty,height+FILTER_KERNEL_SLOP);
// adjust coordinates for slop
src_texture_x1=FLerp(src_texture_x0,src_texture_x1,destx,destx+width-1,destx+slopwidth-1);
src_texture_y1=FLerp(src_texture_y0,src_texture_y1,desty,desty+height-1,desty+slopheight-1);
width=slopwidth;
height=slopheight;
materials->DrawScreenSpaceRectangle(pMaterial,destx,desty,width,height,
src_texture_x0,src_texture_y0,
src_texture_x1,src_texture_y1,
src_texture_width,src_texture_height);
}
enum Histogram_entry_state_t
{
HESTATE_INITIAL=0,
HESTATE_FIRST_QUERY_IN_FLIGHT,
HESTATE_QUERY_IN_FLIGHT,
HESTATE_QUERY_DONE,
};
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_flashlight( "mat_exposure_center_region_x_flashlight","0.33", FCVAR_CHEAT );
ConVar mat_exposure_center_region_y_flashlight( "mat_exposure_center_region_y_flashlight","0.33", FCVAR_CHEAT );
#define N_LUMINANCE_RANGES 31
#define MAX_QUERIES_PER_FRAME 1
class CHistogram_entry_t
{
public:
Histogram_entry_state_t m_state;
OcclusionQueryObjectHandle_t m_occ_handle; // the occlusion query handle
int m_frame_queued; // when this query was last queued
int m_npixels; // # of pixels this histogram represents
int m_npixels_in_range;
float m_min_lum, m_max_lum; // the luminance range this entry was queried with
float m_minx,m_miny,m_maxx,m_maxy; // 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 frm_num )
{
if ( ! m_occ_handle)
m_occ_handle=materials->CreateOcclusionQueryObject();
int xl,yl,dest_width,dest_height;
materials->GetViewport( xl,yl,dest_width,dest_height);
// first, set stencil bits where the colors match
IMaterial *test_mat=materials->FindMaterial(
"dev/lumcompare",
TEXTURE_GROUP_OTHER,true);
IMaterialVar *pMinVar = test_mat->FindVar( "$C0_X", NULL );
pMinVar->SetFloatValue( m_min_lum);
IMaterialVar *pMaxVar = test_mat->FindVar( "$C0_Y", NULL );
if (m_max_lum==1.0)
pMaxVar->SetFloatValue( 10000.0); // count all pixels >1.0 as 1.0
else
pMaxVar->SetFloatValue( m_max_lum);
int scrx_min=FLerp(xl,xl+dest_width-1,0,1,m_minx);
int scrx_max=FLerp(xl,xl+dest_width-1,0,1,m_maxx);
int scry_min=FLerp(yl,yl+dest_height-1,0,1,m_miny);
int scry_max=FLerp(yl,yl+dest_height-1,0,1,m_maxy);
float exposure_width_scale, exposure_height_scale;
// now, shrink region of interest if the flashlight is on
C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer();
if ( pLocal->IsEffectActive( EF_DIMLIGHT ) ) // check flashlight
{
exposure_width_scale=
(0.5*(1.0-mat_exposure_center_region_x_flashlight.GetFloat()));
exposure_height_scale=
(0.5*(1.0-mat_exposure_center_region_y_flashlight.GetFloat()));
}
else
{
exposure_width_scale=
(0.5*(1.0-mat_exposure_center_region_x.GetFloat()));
exposure_height_scale=
(0.5*(1.0-mat_exposure_center_region_y.GetFloat()));
}
int skip_edgex=(1+scrx_max-scrx_min)*exposure_width_scale;
int skip_edgey=(1+scry_max-scry_min)*exposure_height_scale;
// now, do luminance compare
float tscale=1.0;
if (g_pMaterialSystemHardwareConfig->GetHDRType()==HDR_TYPE_FLOAT)
{
tscale=materials->GetToneMappingScaleLinear().x;
}
IMaterialVar *use_t_scale = test_mat->FindVar( "$C0_Z", NULL );
use_t_scale->SetFloatValue( tscale);
m_npixels=(1+scrx_max-scrx_min)*(1+scry_max-scry_min);
if (mat_tonemapping_occlusion_use_stencil.GetInt())
{
materials->SetStencilWriteMask(1);
materials->ClearStencilBufferRectangle(scrx_min,scry_min,scrx_max,scry_max,0);
materials->SetStencilEnable(true);
materials->SetStencilPassOperation(STENCILOPERATION_REPLACE);
materials->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_ALWAYS);
materials->SetStencilFailOperation(STENCILOPERATION_KEEP);
materials->SetStencilZFailOperation(STENCILOPERATION_KEEP);
materials->SetStencilReferenceValue(1);
}
else
materials->BeginOcclusionQueryDrawing(m_occ_handle);
scrx_min+=skip_edgex;
scry_min+=skip_edgey;
scrx_max-=skip_edgex;
scry_max-=skip_edgey;
materials->DrawScreenSpaceRectangle(test_mat,
scrx_min,scry_min,
1+scrx_max-scrx_min,
1+scry_max-scry_min,
scrx_min,scry_min,
scrx_max, scry_max,
dest_width,dest_height);
if (mat_tonemapping_occlusion_use_stencil.GetInt())
{
// now, start counting how many pixels had their stencil bit set via an occlusion query
materials->BeginOcclusionQueryDrawing(m_occ_handle);
// now, issue an occlusion query using stencil as the mask
materials->SetStencilEnable(true);
materials->SetStencilTestMask(1);
materials->SetStencilPassOperation(STENCILOPERATION_KEEP);
materials->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL);
materials->SetStencilFailOperation(STENCILOPERATION_KEEP);
materials->SetStencilZFailOperation(STENCILOPERATION_KEEP);
materials->SetStencilReferenceValue(1);
IMaterial *stest_mat=materials->FindMaterial(
"dev/no_pixel_write",
TEXTURE_GROUP_OTHER,true);
materials->DrawScreenSpaceRectangle(stest_mat,
scrx_min,scry_min,
1+scrx_max-scrx_min,
1+scry_max-scry_min,
scrx_min,scry_min,
scrx_max, scry_max,
dest_width,dest_height);
materials->SetStencilEnable(false);
}
materials->EndOcclusionQueryDrawing(m_occ_handle);
if (m_state==HESTATE_INITIAL)
m_state=HESTATE_FIRST_QUERY_IN_FLIGHT;
else
m_state=HESTATE_QUERY_IN_FLIGHT;
m_frame_queued=frm_num;
}
};
#define HISTOGRAM_BAR_SIZE 200
class CLuminanceHistogramSystem
{
CHistogram_entry_t CurHistogram[N_LUMINANCE_RANGES];
int cur_query_frame;
public:
float GetAverageLuminance( void )
{
float total=0;
int total_pixels=0;
float scale_value=1.0;
if (CurHistogram[N_LUMINANCE_RANGES-1].ContainsValidData())
{
scale_value=CurHistogram[N_LUMINANCE_RANGES-1].m_npixels*
(1.0/CurHistogram[N_LUMINANCE_RANGES-1].m_npixels_in_range);
if (mat_debug_autoexposure.GetInt())
Warning("scale value=%f\n",scale_value);
}
else
return 0.5;
for(int i=0;i<NELEMS( CurHistogram )-1;i++)
{
if ( CurHistogram[i].ContainsValidData() )
{
total+=scale_value*CurHistogram[i].m_npixels_in_range*
AVG( CurHistogram[i].m_min_lum,CurHistogram[i].m_max_lum );
total_pixels+=CurHistogram[i].m_npixels;
}
else
return 0.5; // always return 0.5 until we've queried a whole frame
}
if (total_pixels > 0)
return total*(1.0/total_pixels);
else
return 0.5;
}
void Update(void)
{
// find which histogram entries should have something done this frame
int n_queries_issued_this_frame=0;
cur_query_frame++;
for(int i=0;i<NELEMS( CurHistogram );i++)
{
switch( CurHistogram[i].m_state )
{
case HESTATE_INITIAL:
if ( n_queries_issued_this_frame<MAX_QUERIES_PER_FRAME )
{
CurHistogram[i].IssueQuery(cur_query_frame);
n_queries_issued_this_frame++;
}
break;
case HESTATE_FIRST_QUERY_IN_FLIGHT:
case HESTATE_QUERY_IN_FLIGHT:
if ( cur_query_frame>CurHistogram[i].m_frame_queued+2 )
{
int np= materials->OcclusionQuery_GetNumPixelsRendered(
CurHistogram[i].m_occ_handle);
if (np!=-1) // -1=query not finished. wait until
// next time
{
CurHistogram[i].m_npixels_in_range=np;
// if (mat_debug_autoexposure.GetInt())
// Warning("min=%f max=%f np = %d\n",CurHistogram[i].m_min_lum,CurHistogram[i].m_max_lum,np);
CurHistogram[i].m_state=HESTATE_QUERY_DONE;
}
}
break;
}
}
// now, issue queries for the oldest finished queries we have
while( n_queries_issued_this_frame<MAX_QUERIES_PER_FRAME )
{
int oldest_so_far=-1;
for(int i=0;i<NELEMS( CurHistogram );i++)
if ( (CurHistogram[i].m_state==HESTATE_QUERY_DONE ) &&
( (oldest_so_far==-1) ||
(CurHistogram[i].m_frame_queued<
CurHistogram[oldest_so_far].m_frame_queued) ) )
oldest_so_far=i;
if ( oldest_so_far==-1 ) // nothing to do
break;
CurHistogram[oldest_so_far].IssueQuery(cur_query_frame);
n_queries_issued_this_frame++;
}
}
void DisplayHistogram(void)
{
int xp=10;
for(int l=0;l<N_LUMINANCE_RANGES-1;l++)
{
int np=0;
CHistogram_entry_t &e=CurHistogram[l];
if (e.ContainsValidData())
np+=e.m_npixels_in_range;
int width=500*(e.m_max_lum-e.m_min_lum);
if (np)
{
int height=MAX(1,MIN(HISTOGRAM_BAR_SIZE,np/6000));
materials->Viewport(xp,HISTOGRAM_BAR_SIZE-height,width,height);
materials->ClearColor3ub(255,0,0);
materials->ClearBuffers(true,true);
}
xp+=width+2;
}
}
CLuminanceHistogramSystem(void)
{
cur_query_frame=0;
for(int bucket=0;bucket<N_LUMINANCE_RANGES;bucket++)
{
int idx=bucket;
CHistogram_entry_t &e=CurHistogram[idx];
e.m_state=HESTATE_INITIAL;
e.m_minx=0;
e.m_maxx=1;
e.m_miny=0;
e.m_maxy=1;
if (bucket!=N_LUMINANCE_RANGES-1) // last bucket is special
{
// use a logarithmic ramp for high range in the low range
e.m_min_lum=-.01+exp(FLerp(log(.01),log(.01+1),0,N_LUMINANCE_RANGES-1,bucket));
e.m_max_lum=-.01+exp(FLerp(log(.01),log(.01+1),0,N_LUMINANCE_RANGES-1,bucket+1));
}
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.
e.m_min_lum=0;
e.m_max_lum=100000.0;
}
}
}
};
static CLuminanceHistogramSystem g_HDR_HistogramSystem;
static float s_MovingAverageToneMapScale[10];
static int s_nInAverage;
void ResetToneMapping(float value)
{
s_nInAverage=0;
materials->ResetToneMappingScale(value);
}
static void SetToneMapScale(float newvalue, float minvalue, float maxvalue)
{
mat_hdr_tonemapscale.SetValue(newvalue);
#if 1
if (s_nInAverage<NELEMS(s_MovingAverageToneMapScale))
s_MovingAverageToneMapScale[s_nInAverage++]=newvalue;
else
{
// scroll, losing oldest
for(int i=0;i<NELEMS(s_MovingAverageToneMapScale)-1;i++)
s_MovingAverageToneMapScale[i]=s_MovingAverageToneMapScale[i+1];
s_MovingAverageToneMapScale[NELEMS(s_MovingAverageToneMapScale)-1]=newvalue;
}
// now, use the average of the last tonemap calculations as our goal scale
if (s_nInAverage==NELEMS(s_MovingAverageToneMapScale)) // got full buffer yet?
{
float avg=0.;
float sumweights=0;
int sample_pt=NELEMS(s_MovingAverageToneMapScale)/2;
for(int i=0;i<NELEMS(s_MovingAverageToneMapScale);i++)
{
float weight=abs(i-sample_pt)*(1.0/(NELEMS(s_MovingAverageToneMapScale)/2));
sumweights+=weight;
avg+=weight*s_MovingAverageToneMapScale[i];
}
avg*=(1.0/sumweights);
avg=MIN(maxvalue,MAX(minvalue,avg));
mat_hdr_tonemapscale.SetValue(avg);
}
#endif
}
static void DoPostProcessingEffects( int x, int y, int w, int h )
{
bool bloom_enabled = (mat_hdr_level.GetInt() >= 1);
if ( !engine->MapHasHDRLighting() )
bloom_enabled = false;
if ( mat_force_bloom.GetInt() )
bloom_enabled = true;
if ( mat_disable_bloom.GetInt() )
bloom_enabled = false;
if ( building_cubemaps.GetBool() )
bloom_enabled = false;
HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
float bloom_coeff=0.0;
if (bloom_enabled)
{
static float currentBloomAmount = 1.0f;
float rate = mat_bloomamount_rate.GetFloat();
// 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();
}
currentBloomAmount = flCurrentBloomScale * rate + ( 1.0f - rate ) * currentBloomAmount;
bloom_coeff=currentBloomAmount;
}
if ( hdrType == HDR_TYPE_NONE )
bloom_coeff *= mat_non_hdr_bloom_scalefactor.GetFloat();
// Use the appropriate autoexposure min / max settings.
// Mapmaker's overrides the convar settings.
float flAutoExposureMin;
float flAutoExposureMax;
if ( g_bUseCustomAutoExposureMin )
{
flAutoExposureMin = g_flCustomAutoExposureMin;
}
else
{
flAutoExposureMin = mat_autoexposure_min.GetFloat();
}
if ( g_bUseCustomAutoExposureMax )
{
flAutoExposureMax = g_flCustomAutoExposureMax;
}
else
{
flAutoExposureMax = mat_autoexposure_max.GetFloat();
}
switch( hdrType )
{
case HDR_TYPE_NONE:
case HDR_TYPE_INTEGER:
{
if (mat_debug_bloom.GetInt()==1)
{
static int wx=0;
wx=(wx+1) & 63;
materials->SetRenderTarget(NULL);
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
materials->Viewport( 0, 0, dest_width, dest_height );
materials->ClearColor3ub(0,0,0);
materials->ClearBuffers(true,true);
materials->Viewport( wx+100, 100, 20,20 );
materials->ClearColor3ub(255,255,255);
materials->ClearBuffers(true,true);
materials->Viewport( 20,20,20,20 );
materials->ClearBuffers(true,true);
materials->Viewport( 950,20,20,20 );
materials->ClearBuffers(true,true);
materials->Viewport( 950,950,20,20 );
materials->ClearBuffers(true,true);
materials->Viewport( 20,950,20,20 );
materials->ClearBuffers(true,true);
materials->Viewport( 0, 0, dest_width, dest_height );
}
bool did_update=false;
if ( ( hdrType != HDR_TYPE_NONE && mat_dynamic_tonemapping.GetInt() ) || mat_show_histogram.GetInt())
{
did_update=true;
// if (mat_debug_autoexposure.GetInt())
// {
// materials->ClearColor3ub(128,0,0);
// materials->ClearBuffers(true,true);
// }
Rect_t actualRect;
UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect );
// UpdateScreenEffectTexture( 0, x, y, w, h, true );
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
g_HDR_HistogramSystem.Update();
float avg_lum=g_HDR_HistogramSystem.GetAverageLuminance();
float last_scale=materials->GetToneMappingScaleLinear().x;
float actual_unscaled_luminance=avg_lum*(1.0/last_scale);
actual_unscaled_luminance=MAX(0.000000001,avg_lum);
float target_scale=0.005/actual_unscaled_luminance;
float scalevalue=MAX(flAutoExposureMin,
MIN(flAutoExposureMax,target_scale));
if (mat_debug_autoexposure.GetInt())
Warning("avg_lum=%f ra=%f target=%f adj target=%f\n",
avg_lum,actual_unscaled_luminance,target_scale,scalevalue);
if (mat_dynamic_tonemapping.GetInt())
SetToneMapScale(scalevalue,flAutoExposureMin,flAutoExposureMax);
}
// bloom
if ( bloom_enabled && ( bloom_coeff > 0.0 ) && ( engine->GetDXSupportLevel() >= 80 ) )
{
materials->SetRenderTarget(NULL);
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
materials->Viewport( 0, 0, dest_width, dest_height );
if ( !did_update )
{
UpdateScreenEffectTexture( 0, x, y, w, h, true );
did_update=true;
}
IMaterial *downsample_mat = materials->FindMaterial( "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true);
IMaterial *xblur_mat = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true );
IMaterial *yblur_mat = materials->FindMaterial( "dev/blurfiltery_and_add_nohdr", TEXTURE_GROUP_OTHER, true );
ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
// downsample fb to rt0
SetRenderTargetAndViewPort( dest_rt0 );
// 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
materials->DrawScreenSpaceRectangle( downsample_mat, 0, 0, dest_width/4, dest_height/4,
0, 0, dest_width-2, dest_height-2,
dest_width, dest_height );
// guassian blur x rt0 to rt1
SetRenderTargetAndViewPort( dest_rt1 );
materials->DrawScreenSpaceRectangle( xblur_mat, 0, 0, dest_width/4, dest_height/4,
0, 0, dest_width/4-1, dest_height/4-1,
dest_width/4, dest_height/4 );
// gaussian blur y and add rt1 to frame buffer
materials->Viewport( 0, 0, dest_width, dest_height );
materials->SetRenderTarget( NULL );
IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL );
pBloomAmountVar->SetFloatValue( bloom_coeff );
int bloomadd_x=0;
int bloomadd_y=0;
int bloomadd_width=dest_width;
int bloomadd_height=dest_height;
float bloomsrc_x=0;
float bloomsrc_y=-.5; // compensate for phase shift due to mismatched input/output sizes
float bloomsrc_maxx=dest_width/4-1;
float bloomsrc_maxy=dest_height/4-1;
if ( IsSplitScreenHDR() )
{
bloomadd_x+=dest_width/2;
bloomadd_width-=dest_width/2;
bloomsrc_x+=dest_width/8;
}
materials->DrawScreenSpaceRectangle(yblur_mat,bloomadd_x,bloomadd_y,
bloomadd_width,bloomadd_height,
bloomsrc_x,bloomsrc_y,
bloomsrc_maxx,bloomsrc_maxy,
dest_rt1->GetActualWidth(),
dest_rt1->GetActualHeight());
}
if ( mat_show_histogram.GetInt() && ( engine->GetDXSupportLevel() >= 90 ) )
{
g_HDR_HistogramSystem.DisplayHistogram();
}
}
break;
case HDR_TYPE_FLOAT:
{
int dest_width,dest_height;
materials->GetRenderTargetDimensions( dest_width, dest_height );
if (mat_dynamic_tonemapping.GetInt() || mat_show_histogram.GetInt())
{
g_HDR_HistogramSystem.Update();
// Warning("avg_lum=%f\n",g_HDR_HistogramSystem.GetAverageLuminance());
if (mat_dynamic_tonemapping.GetInt())
{
float avg_lum=MAX(0.0001,g_HDR_HistogramSystem.GetAverageLuminance());
float scalevalue=MAX(flAutoExposureMin,
MIN(flAutoExposureMax,0.18/avg_lum));
mat_hdr_tonemapscale.SetValue(scalevalue);
}
}
IMaterial *pBloomMaterial;
pBloomMaterial = materials->FindMaterial( "dev/floattoscreen_combine", "" );
IMaterialVar *pBloomAmountVar = pBloomMaterial->FindVar( "$bloomamount", NULL );
pBloomAmountVar->SetFloatValue( bloom_coeff);
if ( IsSplitScreenHDR() )
{
// do split screen
ClipBox mycb;
mycb.m_minx=mycb.m_miny=0;
mycb.m_maxx=dest_width/2;
mycb.m_maxy=dest_height-1;
ApplyPostProcessingPasses(HDRSimulate_NonHDR,&mycb);
mycb.m_minx=mycb.m_maxx;
mycb.m_maxx=dest_width-1;
ApplyPostProcessingPasses(HDRFinal_Float,&mycb);
}
else
{
if( bloom_enabled)
ApplyPostProcessingPasses(HDRFinal_Float);
else
ApplyPostProcessingPasses(HDRFinal_Float_NoBloom);
}
materials->SetRenderTarget(NULL);
if ( mat_show_histogram.GetInt() && (engine->GetDXSupportLevel()>=90))
g_HDR_HistogramSystem.DisplayHistogram();
if (mat_dynamic_tonemapping.GetInt())
{
float avg_lum=MAX(0.0001,g_HDR_HistogramSystem.GetAverageLuminance());
float scalevalue=MAX(flAutoExposureMin,
MIN(flAutoExposureMax,0.023/avg_lum));
SetToneMapScale(scalevalue,flAutoExposureMin,flAutoExposureMax);
}
materials->SetRenderTarget( NULL );
break;
}
}
}
#else
void ResetToneMapping(float value)
{
}
#endif // !_XBOX
#ifdef _XBOX
#define FULLSCREEN_WIDTH 640
#define FULLSCREEN_HEIGHT 480
#define DOWNSAMPLE_WIDTH (FULLSCREEN_WIDTH>>1)
#define DOWNSAMPLE_HEIGHT (FULLSCREEN_HEIGHT>>1)
void CViewRender::DoScreenSpaceBloom()
{
int rt_width, rt_height;
if ( !mat_bloomscale.GetFloat() || !m_BloomDownsample.IsValid() )
{
return;
}
// rt_waterReflection = DownSample( backBuffer )
ITexture *dest_rt = GetWaterReflectionTexture();
materials->PushRenderTargetAndViewport( dest_rt );
materials->GetRenderTargetDimensions( rt_width, rt_height );
Assert( rt_width >= DOWNSAMPLE_WIDTH && rt_height >= DOWNSAMPLE_HEIGHT );
materials->DrawScreenSpaceRectangle(
m_BloomDownsample, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT,
0, 0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT, 1, 1 );
// rt_waterRefraction = BlurX( rt_waterReflection )
dest_rt = GetWaterRefractionTexture();
materials->SetRenderTarget( dest_rt );
materials->DrawScreenSpaceRectangle(
m_BloomBlurX, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT,
0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height );
// rt_waterReflection = BlurY( rt_waterRefraction )
dest_rt = GetWaterReflectionTexture();
materials->SetRenderTarget( dest_rt );
materials->DrawScreenSpaceRectangle(
m_BloomBlurY, 0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT,
0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height );
// restore screen
materials->PopRenderTargetAndViewport();
// FrameBuffer += rt_waterReflection
materials->DrawScreenSpaceRectangle(
m_BloomAdd, 0, 0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT,
0, 0, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT, rt_width, rt_height );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Renders voice feedback and other sprites attached to players
// Input : none
//-----------------------------------------------------------------------------
void CViewRender::RenderPlayerSprites()
{
#ifndef _XBOX
GetClientVoiceMgr()->DrawHeadLabels();
#endif
}
//-----------------------------------------------------------------------------
// Sets up, cleans up the main 3D view
//-----------------------------------------------------------------------------
void CViewRender::SetupMain3DView( const CViewSetup &view, int &nClearFlags )
{
// FIXME: I really want these fields removed from CViewSetup
// and passed in as independent flags
// Clear the color here if requested.
nClearFlags &= ~VIEW_CLEAR_DEPTH;
if ( nClearFlags & VIEW_CLEAR_COLOR )
{
nClearFlags |= VIEW_CLEAR_DEPTH;
}
// If we are using HDR, we render to the HDR full frame buffer texture
// instead of whatever was previously the render target
if( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT )
{
render->Push3DView( view, nClearFlags, true, GetFullFrameFrameBufferTexture( 0 ), m_Frustum );
}
else
{
render->Push3DView( view, nClearFlags, false, NULL, m_Frustum );
}
// If we didn't clear the depth here, we'll need to clear it later
nClearFlags ^= VIEW_CLEAR_DEPTH;
if ( nClearFlags & VIEW_CLEAR_COLOR )
{
// If we cleared the color here, we don't need to clear it later
nClearFlags &= ~( VIEW_CLEAR_COLOR | VIEW_CLEAR_FULL_TARGET );
}
}
void CViewRender::CleanupMain3DView( const CViewSetup &view )
{
render->PopView( m_Frustum );
}
//-----------------------------------------------------------------------------
// Queues up an overlay rendering
//-----------------------------------------------------------------------------
void CViewRender::QueueOverlayRenderView( const CViewSetup &view, int nClearFlags, int whatToDraw )
{
#ifndef _XBOX
// Can't have 2 in a single scene
Assert( !m_bDrawOverlay );
m_bDrawOverlay = true;
m_OverlayViewSetup = view;
m_OverlayClearFlags = nClearFlags;
m_OverlayDrawFlags = whatToDraw;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: This renders the entire 3D view and the in-game hud/viewmodel
// Input : &view -
// whatToDraw -
//-----------------------------------------------------------------------------
// This renders the entire 3D view.
void CViewRender::RenderViewEx( const CViewSetup &view, int nClearFlags, int whatToDraw )
{
m_CurrentView = view;
VPROF( "CViewRender::RenderViewEx" );
ITexture *saveRenderTarget = materials->GetRenderTarget();
g_pClientShadowMgr->AdvanceFrame();
#ifdef USE_MONITORS
if ( cl_drawmonitors.GetBool() && g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 )
{
DrawMonitors( view );
}
#endif
g_bRenderingView = true;
// Must be first
render->SceneBegin();
materials->TurnOnToneMapping();
// clear happens here probably
SetupMain3DView( view, nClearFlags );
bool bDrew3dSkybox = false;
bool bSkyboxVisible = false;
// if the 3d skybox world is drawn, then don't draw the normal skybox
Draw3dSkyboxworld( view, nClearFlags, bDrew3dSkybox, bSkyboxVisible );
// Force it to clear the framebuffer if they're in solid space.
if ( ( nClearFlags & VIEW_CLEAR_COLOR ) == 0 )
{
if ( enginetrace->GetPointContents( view.origin ) == CONTENTS_SOLID )
{
nClearFlags |= VIEW_CLEAR_COLOR;
}
}
// Render world and all entities, particles, etc.
if( !g_pIntroData )
{
ViewDrawScene( bDrew3dSkybox, bSkyboxVisible, view, nClearFlags, VIEW_MAIN, whatToDraw & RENDERVIEW_DRAWVIEWMODEL );
}
else
{
ViewDrawScene_Intro( view, nClearFlags, *g_pIntroData );
}
// We can still use the 'current view' stuff set up in ViewDrawScene
s_bCanAccessCurrentView = true;
if ( !IsXbox() )
{
engine->DrawPortals();
}
DisableFog();
// Finish scene
render->SceneEnd();
// Draw lightsources if enabled
render->DrawLights();
RenderPlayerSprites();
// Now actually draw the viewmodel
DrawViewModels( view, whatToDraw & RENDERVIEW_DRAWVIEWMODEL );
PixelVisibility_EndScene();
// Draw fade over entire screen if needed
byte color[4];
bool blend;
vieweffects->GetFadeParams( view.context, &color[0], &color[1], &color[2], &color[3], &blend );
// Draw an overlay to make it even harder to see inside smoke particle systems.
DrawSmokeFogOverlay();
// Overlay screen fade on entire screen
IMaterial* pMaterial = blend ? m_ModulateSingleColor : m_TranslucentSingleColor;
render->ViewDrawFade( color, pMaterial );
PerformScreenOverlay( view.x, view.y, view.width, view.height );
// Prevent sound stutter if going slow
engine->Sound_ExtraUpdate();
#ifndef _XBOX
if ( !building_cubemaps.GetBool() && view.m_bDoBloomAndToneMapping )
{
DoPostProcessingEffects( view.x, view.y, view.width, view.height );
}
#else
DoScreenSpaceBloom();
#endif
// And here are the screen-space effects
// Grab the pre-color corrected frame for editing purposes
engine->GrabPreColorCorrectedFrame( view.x, view.y, view.width, view.height );
PerformScreenSpaceEffects( view.x, view.y, view.width, view.height );
if ( !IsXbox() && g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER )
{
materials->SetToneMappingScaleLinear(Vector(1,1,1));
}
CleanupMain3DView( view );
materials->SetRenderTarget( saveRenderTarget );
#ifndef _XBOX
// Draw the overlay
if ( m_bDrawOverlay )
{
// This allows us to be ok if there are nested overlay views
CViewSetup currentView = m_CurrentView;
CViewSetup tempView = m_OverlayViewSetup;
tempView.fov = ScaleFOVByWidthRatio( tempView.fov, tempView.m_flAspectRatio / ( 4.0f / 3.0f ) );
tempView.m_bDoBloomAndToneMapping = false; // FIXME: Hack to get Mark up and running
m_bDrawOverlay = false;
RenderViewEx( tempView, m_OverlayClearFlags, m_OverlayDrawFlags );
m_CurrentView = currentView;
}
#endif
// Draw the 2D graphics
render->Push2DView( view, 0, false, NULL, m_Frustum );
Render2DEffectsPreHUD( view );
if ( whatToDraw & RENDERVIEW_DRAWHUD )
{
VPROF_BUDGET( "VGui_DrawHud", VPROF_BUDGETGROUP_OTHER_VGUI );
// paint the vgui screen
VGui_PreRender();
// Make sure the client .dll root panel is at the proper point before doing the "SolveTraverse" calls
vgui::VPANEL root = enginevgui->GetPanel( PANEL_CLIENTDLL );
if ( root != 0 )
{
vgui::ipanel()->SetPos( root, view.x, view.y );
vgui::ipanel()->SetSize( root, view.width, view.height );
}
// Same for client .dll tools
root = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS );
if ( root != 0 )
{
vgui::ipanel()->SetPos( root, view.x, view.y );
vgui::ipanel()->SetSize( root, view.width, view.height );
}
// The crosshair, etc. needs to get at the current setup stuff
AllowCurrentViewAccess( true );
// Draw the in-game stuff based on the actual viewport being used
render->VGui_Paint( PAINT_INGAMEPANELS );
AllowCurrentViewAccess( false );
VGui_PostRender();
g_pClientMode->PostRenderVGui();
materials->Flush();
}
Draw2DDebuggingInfo( view );
m_AnglesHistory[m_AnglesHistoryCounter] = view.angles;
m_AnglesHistoryCounter = (m_AnglesHistoryCounter+1) & ANGLESHISTORY_MASK;
// If the angles are moving fast enough, allow LOD transitions.
float angleMovementDelta = 0;
for(int i=0; i < ANGLESHISTORY_SIZE; i++)
{
angleMovementDelta += (m_AnglesHistory[(i+1) & ANGLESHISTORY_MASK] - m_AnglesHistory[i]).Length();
}
angleMovementDelta /= ANGLESHISTORY_SIZE;
if(angleMovementDelta > r_TransitionSensitivity.GetFloat())
{
r_DoCovertTransitions.SetValue(1);
}
else
{
r_DoCovertTransitions.SetValue(0);
}
Render2DEffectsPostHUD( view );
g_bRenderingView = false;
// We can no longer use the 'current view' stuff set up in ViewDrawScene
s_bCanAccessCurrentView = false;
// Next frame!
++m_FrameNumber;
if ( !IsXbox() )
{
GenerateOverdrawForTesting();
}
render->PopView( m_Frustum );
}
//-----------------------------------------------------------------------------
// Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack
//-----------------------------------------------------------------------------
void CViewRender::Render2DEffectsPreHUD( const CViewSetup &view )
{
}
//-----------------------------------------------------------------------------
// Purpose: Renders extra 2D effects in derived classes while the 2D view is on the stack
//-----------------------------------------------------------------------------
void CViewRender::Render2DEffectsPostHUD( const CViewSetup &view )
{
}
//-----------------------------------------------------------------------------
// Purpose: Renders entire view
// Input : &view -
// drawViewModel -
//-----------------------------------------------------------------------------
void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, bool bDrawViewModel )
{
int flags = RENDERVIEW_DRAWHUD;
if ( bDrawViewModel )
{
flags |= RENDERVIEW_DRAWVIEWMODEL;
}
RenderViewEx( view, nClearFlags, flags );
}
int CViewRender::GetDrawFlags()
{
return m_DrawFlags;
}
void ViewTransform( const Vector &worldSpace, Vector &viewSpace )
{
const VMatrix &viewMatrix = engine->WorldToViewMatrix();
Vector3DMultiplyPosition( viewMatrix, worldSpace, viewSpace );
}
//-----------------------------------------------------------------------------
// Purpose: UNDONE: Clean this up some, handle off-screen vertices
// Input : *point -
// *screen -
// Output : int
//-----------------------------------------------------------------------------
int ScreenTransform( const Vector& point, Vector& screen )
{
// UNDONE: Clean this up some, handle off-screen vertices
float w;
const VMatrix &worldToScreen = engine->WorldToScreenMatrix();
screen.x = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3];
screen.y = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3];
// z = worldToScreen[2][0] * point[0] + worldToScreen[2][1] * point[1] + worldToScreen[2][2] * point[2] + worldToScreen[2][3];
w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3];
// Just so we have something valid here
screen.z = 0.0f;
bool behind;
if( w < 0.001f )
{
behind = true;
screen.x *= 100000;
screen.y *= 100000;
}
else
{
behind = false;
float invw = 1.0f / w;
screen.x *= invw;
screen.y *= invw;
}
return behind;
}
//-----------------------------------------------------------------------------
//
// NOTE: Below here is all of the stuff that needs to be done for water rendering
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Determines what kind of water we're going to use
//-----------------------------------------------------------------------------
void CViewRender::DetermineWaterRenderInfo( const VisibleFogVolumeInfo_t &fogVolumeInfo, CViewRender::WaterRenderInfo_t &info )
{
// By default, assume cheap water (even if there's no water in the scene!)
info.m_bCheapWater = true;
info.m_bRefract = false;
info.m_bReflect = false;
info.m_bReflectEntities = false;
info.m_bDrawWaterSurface = false;
info.m_bOpaqueWater = true;
IMaterial *pWaterMaterial = fogVolumeInfo.m_pFogVolumeMaterial;
if (( fogVolumeInfo.m_nVisibleFogVolume == -1 ) || !pWaterMaterial )
return;
// Use cheap water if mat_drawwater is set
info.m_bDrawWaterSurface = mat_drawwater.GetBool();
if ( !info.m_bDrawWaterSurface )
{
info.m_bOpaqueWater = false;
return;
}
// Determine if the water surface is opaque or not
info.m_bOpaqueWater = !pWaterMaterial->IsTranslucent();
// DX level 70 can't handle anything but cheap water
if (engine->GetDXSupportLevel() < 80)
return;
bool bForceCheap = false;
bool bForceExpensive = r_waterforceexpensive.GetBool();
// The material can override the default settings though
IMaterialVar *pForceCheapVar = pWaterMaterial->FindVar( "$forcecheap", NULL, false );
IMaterialVar *pForceExpensiveVar = pWaterMaterial->FindVar( "$forceexpensive", NULL, false );
if ( pForceCheapVar && pForceCheapVar->IsDefined() )
{
bForceCheap = ( pForceCheapVar->GetIntValue() != 0 );
if ( bForceCheap )
{
bForceExpensive = false;
}
}
if ( !bForceCheap && pForceExpensiveVar && pForceExpensiveVar->IsDefined() )
{
bForceExpensive = bForceExpensive || ( pForceExpensiveVar->GetIntValue() != 0 );
}
bool bDebugCheapWater = r_debugcheapwater.GetBool();
if( bDebugCheapWater )
{
Msg( "Water material: %s dist to water: %f\nforcecheap: %s forceexpensive: %s\n",
pWaterMaterial->GetName(), fogVolumeInfo.m_flDistanceToWater,
bForceCheap ? "true" : "false", bForceExpensive ? "true" : "false" );
}
// Unless expensive water is active, reflections are off.
bool bLocalReflection;
if( !bForceExpensive || !r_WaterDrawReflection.GetBool() )
{
bLocalReflection = false;
}
else
{
IMaterialVar *pReflectTextureVar = pWaterMaterial->FindVar( "$reflecttexture", NULL, false );
bLocalReflection = pReflectTextureVar && (pReflectTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE);
}
// FIXME: I disabled cheap water LOD when local specular is specified.
// There are very few places that appear to actually
// take advantage of it (places where water is in the PVS, but outside of LOD range).
// It was 2 hours before code lock, and I had the choice of either doubling fill-rate everywhere
// by making cheap water lod actually work (the water LOD wasn't actually rendering!!!)
// or to just always render the reflection + refraction if there's a local specular specified.
// Note that water LOD *does* work with refract-only water
// Check if the water is out of the cheap water LOD range; if so, use cheap water
if ( ( (fogVolumeInfo.m_flDistanceToWater >= m_flCheapWaterEndDistance) && !bLocalReflection ) || bForceCheap )
return;
// Get the material that is for the water surface that is visible and check to see
// what render targets need to be rendered, if any.
if ( !r_WaterDrawRefraction.GetBool() )
{
info.m_bRefract = false;
}
else
{
IMaterialVar *pRefractTextureVar = pWaterMaterial->FindVar( "$refracttexture", NULL, false );
info.m_bRefract = pRefractTextureVar && (pRefractTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE);
// Refractive water can be seen through
if ( info.m_bRefract )
{
info.m_bOpaqueWater = false;
}
}
#ifndef _XBOX
info.m_bReflect = bLocalReflection;
if ( info.m_bReflect )
{
if( r_waterforcereflectentities.GetBool() )
{
info.m_bReflectEntities = true;
}
else
{
IMaterialVar *pReflectEntitiesVar = pWaterMaterial->FindVar( "$reflectentities", NULL, false );
info.m_bReflectEntities = pReflectEntitiesVar && (pReflectEntitiesVar->GetIntValue() != 0);
}
}
#endif
info.m_bCheapWater = !info.m_bReflect && !info.m_bRefract;
if( bDebugCheapWater )
{
Warning( "refract: %s reflect: %s\n", info.m_bRefract ? "true" : "false", info.m_bReflect ? "true" : "false" );
}
}
//-----------------------------------------------------------------------------
// Draws the world and all entities
//-----------------------------------------------------------------------------
void CViewRender::WaterDrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &viewIn, int nClearFlags )
{
MDLCACHE_CRITICAL_SECTION();
VisibleFogVolumeInfo_t fogVolumeInfo;
render->GetVisibleFogVolume( viewIn.origin, &fogVolumeInfo );
WaterRenderInfo_t info;
DetermineWaterRenderInfo( fogVolumeInfo, info );
if ( info.m_bCheapWater )
{
ViewDrawScene_NoWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info );
return;
}
// Blat out the visible fog leaf if we're not going to use it
if ( !r_ForceWaterLeaf.GetBool() )
{
fogVolumeInfo.m_nVisibleFogVolumeLeaf = -1;
}
// We can see water of some sort
if ( !fogVolumeInfo.m_bEyeInFogVolume )
{
ViewDrawScene_EyeAboveWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info );
}
else
{
ViewDrawScene_EyeUnderWater( bDrawSkybox, viewIn, nClearFlags, fogVolumeInfo, info );
}
}
//-----------------------------------------------------------------------------
// Pushes a water render target
//-----------------------------------------------------------------------------
static Vector SavedLinearLightMapScale(-1,-1,-1); // x<0 = no saved scale
static void SetLightmapScaleForWater(void)
{
if (g_pMaterialSystemHardwareConfig->GetHDRType()==HDR_TYPE_INTEGER)
{
SavedLinearLightMapScale=materials->GetToneMappingScaleLinear();
Vector t25=SavedLinearLightMapScale;
t25*=0.25;
materials->SetToneMappingScaleLinear(t25);
}
}
void CViewRender::PushWaterRenderTarget( CViewSetup &view, int nClearFlags, float waterHeight, int flags )
{
float spread = 2.0f;
float origWaterHeight = waterHeight;
if( flags & DF_FUDGE_UP )
{
waterHeight += spread;
}
else
{
waterHeight -= spread;
}
MaterialHeightClipMode_t clipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE;
if ( ( flags & DF_CLIP_Z ) && mat_clipz.GetBool() )
{
if( flags & DF_CLIP_BELOW )
{
clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT;
}
else
{
clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT;
}
}
if( flags & DF_RENDER_REFRACTION )
{
ITexture *pTexture = GetWaterRefractionTexture();
// Use the aspect ratio of the main view! So, don't recompute it here
view.x = view.y = 0;
view.width = pTexture->GetActualWidth();
view.height = pTexture->GetActualHeight();
materials->SetFogZ( waterHeight );
materials->SetHeightClipZ( waterHeight );
materials->SetHeightClipMode( clipMode );
// Have to re-set up the view since we reset the size
render->Push3DView( view, nClearFlags, true, pTexture, m_Frustum );
return;
}
if( flags & DF_RENDER_REFLECTION )
{
ITexture *pTexture = GetWaterReflectionTexture();
materials->SetFogZ( waterHeight );
// Use the aspect ratio of the main view! So, don't recompute it here
view.x = view.y = 0;
view.width = pTexture->GetActualWidth();
view.height = pTexture->GetActualHeight();
view.angles[0] = -view.angles[0];
view.angles[2] = -view.angles[2];
view.origin[2] -= 2.0f * ( view.origin[2] - (origWaterHeight));
bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
if( bSoftwareUserClipPlane && ( view.origin[2] > waterHeight - r_eyewaterepsilon.GetFloat() ) )
{
waterHeight = view.origin[2] + r_eyewaterepsilon.GetFloat();
}
materials->SetHeightClipZ( waterHeight );
materials->SetHeightClipMode( clipMode );
render->Push3DView( view, nClearFlags, true, pTexture, m_Frustum );
SetLightmapScaleForWater();
return;
}
if ( nClearFlags & (VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR) )
{
materials->ClearBuffers( nClearFlags & VIEW_CLEAR_COLOR, nClearFlags & VIEW_CLEAR_DEPTH );
}
materials->SetHeightClipMode( clipMode );
if ( clipMode != MATERIAL_HEIGHTCLIPMODE_DISABLE )
{
materials->SetHeightClipZ( waterHeight );
}
}
//-----------------------------------------------------------------------------
// Pops a water render target
//-----------------------------------------------------------------------------
void CViewRender::PopWaterRenderTarget( int nFlags )
{
materials->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
if( nFlags & (DF_RENDER_REFRACTION | DF_RENDER_REFLECTION) )
{
render->PopView( m_Frustum );
if (SavedLinearLightMapScale.x>=0)
{
materials->SetToneMappingScaleLinear(SavedLinearLightMapScale);
SavedLinearLightMapScale.x=-1;
}
}
}
//-----------------------------------------------------------------------------
// Draws the world + entities
//-----------------------------------------------------------------------------
void CViewRender::WaterDrawHelper( const CViewSetup &view, ClientWorldListInfo_t &info, CRenderList &renderList,
float waterHeight, int flags, view_id_t viewID, float waterZAdjust, int iForceViewLeaf )
{
int savedViewID = g_CurrentViewID;
g_CurrentViewID = viewID;
// Need a copy of the view since pushing a water render target
// make change the aspect ratio of the projection space transform
CViewSetup actualView = view;
int nClearFlags = 0;
if ( flags & DF_CLEARDEPTH )
{
nClearFlags |= VIEW_CLEAR_DEPTH;
}
if ( flags & DF_CLEARCOLOR )
{
nClearFlags |= VIEW_CLEAR_COLOR;
}
PushWaterRenderTarget( actualView, nClearFlags, waterHeight, flags );
if( flags & DF_BUILDWORLDLISTS )
{
bool bDrawEntities = ( flags & DF_DRAW_ENTITITES ) != 0;
bool bUpdateLightmaps = ( flags & DF_UPDATELIGHTMAPS ) != 0;
BuildWorldRenderLists( &actualView, info, bUpdateLightmaps, bDrawEntities, iForceViewLeaf );
}
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Update our render view flags.
m_DrawFlags = flags | m_BaseDrawFlags;
ITexture *pSaveFrameBufferCopyTexture = materials->GetFrameBufferCopyTexture( 0 );
if ( engine->GetDXSupportLevel() >= 80 )
{
materials->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() );
}
DrawWorld( info, renderList, m_DrawFlags, waterZAdjust );
// FIXME: This may have to be static, not sure if we'll get a stack overflow
ClientWorldListInfo_t tmpInfo;
tmpInfo.m_pLeafList = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) );
tmpInfo.m_pLeafFogVolume = (LeafFogVolume_t*)stackalloc( info.m_LeafCount * sizeof(LeafFogVolume_t) );
tmpInfo.m_pActualLeafIndex = (LeafIndex_t*)stackalloc( info.m_LeafCount * sizeof(LeafIndex_t) );
ClientWorldListInfo_t *pInfo = ComputeActualWorldListInfo( info, m_DrawFlags, tmpInfo );
if ( flags & DF_DRAW_ENTITITES )
{
BuildRenderableRenderLists( &actualView, *pInfo, renderList );
DrawOpaqueRenderables( *pInfo, renderList );
DrawTranslucentRenderables( *pInfo, renderList, m_DrawFlags, false );
}
else
{
// Draw translucent world brushes only, no entities
DrawTranslucentWorldInLeaves( pInfo->m_LeafCount - 1, 0, *pInfo, m_DrawFlags );
}
// issue the pixel visibility tests
if ( CurrentViewID() != VIEW_MAIN )
{
PixelVisibility_EndCurrentView();
}
materials->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture );
PopWaterRenderTarget( m_DrawFlags );
m_DrawFlags = m_BaseDrawFlags;
g_CurrentViewID = savedViewID;
}
//-----------------------------------------------------------------------------
// Returns true if the view plane intersects the water
//-----------------------------------------------------------------------------
bool DoesViewPlaneIntersectWater( float waterZ, int leafWaterDataID )
{
if ( leafWaterDataID == -1 )
return false;
VMatrix viewMatrix, projectionMatrix, viewProjectionMatrix, inverseViewProjectionMatrix;
materials->GetMatrix( MATERIAL_VIEW, &viewMatrix );
materials->GetMatrix( MATERIAL_PROJECTION, &projectionMatrix );
MatrixMultiply( projectionMatrix, viewMatrix, viewProjectionMatrix );
MatrixInverseGeneral( viewProjectionMatrix, inverseViewProjectionMatrix );
Vector mins, maxs;
ClearBounds( mins, maxs );
Vector testPoint[4];
testPoint[0].Init( -1.0f, -1.0f, 0.0f );
testPoint[1].Init( -1.0f, 1.0f, 0.0f );
testPoint[2].Init( 1.0f, -1.0f, 0.0f );
testPoint[3].Init( 1.0f, 1.0f, 0.0f );
int i;
bool bAbove = false;
bool bBelow = false;
float fudge = 7.0f;
for( i = 0; i < 4; i++ )
{
Vector worldPos;
Vector3DMultiplyPositionProjective( inverseViewProjectionMatrix, testPoint[i], worldPos );
AddPointToBounds( worldPos, mins, maxs );
// Warning( "viewplanez: %f waterZ: %f\n", worldPos.z, waterZ );
if( worldPos.z + fudge > waterZ )
{
bAbove = true;
}
if( worldPos.z - fudge < waterZ )
{
bBelow = true;
}
}
// early out if the near plane doesn't cross the z plane of the water.
if( !( bAbove && bBelow ) )
return false;
Vector vecFudge( fudge, fudge, fudge );
mins -= vecFudge;
maxs += vecFudge;
// the near plane does cross the z value for the visible water volume. Call into
// the engine to find out if the near plane intersects the water volume.
return render->DoesBoxIntersectWaterVolume( mins, maxs, leafWaterDataID );
}
static void CalcWaterEyeAdjustments( const CViewSetup &view, const VisibleFogVolumeInfo_t &fogInfo,
float &newWaterHeight, float &waterZAdjust, bool bSoftwareUserClipPlane )
{
if( !bSoftwareUserClipPlane )
{
newWaterHeight = fogInfo.m_flWaterHeight;
waterZAdjust = 0.0f;
return;
}
newWaterHeight = fogInfo.m_flWaterHeight;
float eyeToWaterZDelta = view.origin[2] - fogInfo.m_flWaterHeight;
float epsilon = r_eyewaterepsilon.GetFloat();
waterZAdjust = 0.0f;
if( fabs( eyeToWaterZDelta ) < epsilon )
{
if( eyeToWaterZDelta > 0 )
{
newWaterHeight = view.origin[2] - epsilon;
}
else
{
newWaterHeight = view.origin[2] + epsilon;
}
waterZAdjust = newWaterHeight - fogInfo.m_flWaterHeight;
}
// Warning( "view.origin[2]: %f newWaterHeight: %f fogInfo.m_flWaterHeight: %f waterZAdjust: %f\n",
// ( float )view.origin[2], newWaterHeight, fogInfo.m_flWaterHeight, waterZAdjust );
}
#ifdef _XBOX
//-----------------------------------------------------------------------------
// Draws a perspective-correct dudv map into the reflection texture
//-----------------------------------------------------------------------------
void CViewRender::DrawScreenSpaceWaterDuDv( const CViewSetup &view, float waterZAdjust )
{
ITexture *pTexture = GetWaterReflectionTexture();
// Use the aspect ratio of the main view! So, don't recompute it here
CViewSetup actualView = view;
actualView.width = pTexture->GetActualWidth();
actualView.height = pTexture->GetActualHeight();
// these DU/DV constants need to be 64, not 128 on xbox because the texture is signed and we only use the positive half of that space
materials->ClearColor4ub( 255, 64, 64, 0 );
render->Push3DView( actualView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, true, pTexture, m_Frustum );
materials->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
render->DrawWaterDuDv( waterZAdjust );
render->PopView( m_Frustum );
}
#endif // _XBOX
//-----------------------------------------------------------------------------
// Draws the scene when the view point is above the level of the water
//-----------------------------------------------------------------------------
#include "tier0/memdbgoff.h"
void CViewRender::ViewDrawScene_EyeAboveWater( bool bDrawSkybox, const CViewSetup &view,
int nClearFlags, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo )
{
VPROF( "CViewRender::ViewDrawScene_EyeAboveWater" );
bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
ClientWorldListInfo_t info;
#ifndef _XBOX
CRenderList renderList;
CRenderList *pRenderList = &renderList;
#else
void *ptr = MemAllocScratch(sizeof(CRenderList));
CRenderList *pRenderList = new (ptr) CRenderList;
#endif
float newWaterHeight, waterZAdjust;
CalcWaterEyeAdjustments( view, fogInfo, newWaterHeight, waterZAdjust, bSoftwareUserClipPlane );
// eye is outside of water
// render the reflection
if( waterInfo.m_bReflect )
{
// NOTE: Clearing the color is unnecessary since we're drawing the skybox
// and dest-alpha is never used in the reflection
int nFlags = DF_RENDER_REFLECTION | DF_CLIP_Z | DF_CLIP_BELOW |
DF_RENDER_ABOVEWATER | DF_CLEARDEPTH | DF_UPDATELIGHTMAPS | DF_BUILDWORLDLISTS;
// NOTE: This will cause us to draw the 2d skybox in the reflection
// (which we want to do instead of drawing the 3d skybox)
nFlags |= DF_DRAWSKYBOX;
if( waterInfo.m_bReflectEntities )
{
nFlags |= DF_DRAW_ENTITITES;
}
// Disable occlusion visualization in reflection
bool bVisOcclusion = r_visocclusion.GetInt();
r_visocclusion.SetValue( 0 );
EnableWorldFog();
WaterDrawHelper( view, info, *pRenderList, fogInfo.m_flWaterHeight, nFlags, VIEW_REFLECTION,
0.0f, fogInfo.m_nVisibleFogVolumeLeaf );
r_visocclusion.SetValue( bVisOcclusion );
materials->Flush();
}
unsigned char ucFogColor[3];
int nFlags;
bool bViewIntersectsWater = false;
// render refraction
int nFirstPassFlags = DF_BUILDWORLDLISTS | DF_UPDATELIGHTMAPS | DF_CLEARCOLOR;
if ( waterInfo.m_bRefract )
{
nFlags = nFirstPassFlags | DF_RENDER_REFRACTION | DF_CLIP_Z |
DF_RENDER_UNDERWATER | DF_FUDGE_UP |
DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS | DF_CLEARDEPTH;
nFirstPassFlags = 0;
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true );
SetClearColorToFogColor( ucFogColor );
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, VIEW_REFRACTION,
waterZAdjust, -1 );
if( !bSoftwareUserClipPlane )
{
bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume );
}
#ifdef _XBOX
// This is a workaround for the lack of perspective correction on the XBox for dudv maps
EnableWorldFog();
DrawScreenSpaceWaterDuDv( view, waterZAdjust );
#endif
materials->ClearColor4ub( 0, 0, 0, 255 );
}
materials->Flush();
EnableWorldFog();
// render the world
nFlags = nFirstPassFlags | DF_RENDER_ABOVEWATER | DF_CLEARDEPTH | DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS;
if( bViewIntersectsWater && !bSoftwareUserClipPlane )
{
// This is necessary to keep the non-water fogged world from drawing underwater in
// the case where we want to partially see into the water.
nFlags |= DF_CLIP_Z | DF_CLIP_BELOW;
}
if ( bDrawSkybox )
{
nFlags |= DF_DRAWSKYBOX;
nFlags &= ~DF_CLEARCOLOR;
}
if ( waterInfo.m_bDrawWaterSurface )
{
nFlags |= DF_RENDER_WATER;
}
if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater )
{
nFlags |= DF_RENDER_UNDERWATER;
}
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 );
if( waterZAdjust != 0.0f && bSoftwareUserClipPlane && waterInfo.m_bRefract )
{
nFlags = DF_RENDER_UNDERWATER;
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust,-1 );
}
else if( bViewIntersectsWater )
{
materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true );
nFlags = DF_RENDER_UNDERWATER | DF_CLIP_Z | DF_DRAW_ENTITITES;
WaterDrawHelper( view, info, *pRenderList, fogInfo.m_flWaterHeight, nFlags, VIEW_NONE, 0.0f, -1 );
}
materials->ClearColor4ub( 0, 0, 0, 255 );
#ifdef _XBOX
MemFreeScratch();
#endif
}
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Draws the scene when the view point is under the level of the water
//-----------------------------------------------------------------------------
#include "tier0/memdbgoff.h"
void CViewRender::ViewDrawScene_EyeUnderWater( bool bDrawSkybox, const CViewSetup &view,
int nClearFlags, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo )
{
// FIXME: The 3d skybox shouldn't be drawn when the eye is under water
VPROF( "CViewRender::ViewDrawScene_EyeUnderWater" );
bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
float newWaterHeight, waterZAdjust;
CalcWaterEyeAdjustments( view, fogInfo, newWaterHeight, waterZAdjust, bSoftwareUserClipPlane );
ClientWorldListInfo_t info;
#ifndef _XBOX
CRenderList renderList;
CRenderList *pRenderList = &renderList;
#else
void *ptr = MemAllocScratch(sizeof(CRenderList));
CRenderList *pRenderList = new (ptr) CRenderList;
#endif
int nFirstPassFlags = DF_BUILDWORLDLISTS | DF_UPDATELIGHTMAPS;
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, true );
unsigned char ucFogColor[3];
materials->GetFogColor( ucFogColor );
materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
// render refraction (out of water)
if ( waterInfo.m_bRefract )
{
// NOTE: Refraction renders into the back buffer, over the top of the 3D skybox
// It is then blitted out into the refraction target. This is so that
// we only have to set up 3d sky vis once, and only render it once also!
int nFlags = nFirstPassFlags | DF_CLIP_Z |
DF_CLIP_BELOW | DF_RENDER_ABOVEWATER |
DF_DRAW_ENTITITES | DF_MAINTAINWORLDLISTS | DF_CLEARDEPTH;
if ( bDrawSkybox )
{
nFlags |= DF_DRAWSKYBOX | DF_CLIP_SKYBOX | DF_CLEARCOLOR;
}
nFirstPassFlags = 0;
EnableWorldFog();
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, VIEW_REFRACTION, waterZAdjust, -1 );
Rect_t srcRect;
srcRect.x = view.x;
srcRect.y = view.y;
srcRect.width = view.width;
srcRect.height = view.height;
ITexture *pTexture = GetWaterRefractionTexture();
materials->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL );
#ifdef _XBOX
// This is a workaround for the lack of perspective correction on the XBox for dudv maps
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false );
DrawScreenSpaceWaterDuDv( view, waterZAdjust );
materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
#endif
}
// NOTE: We're not drawing the 2d skybox under water since it's assumed to not be visible.
// render the world underwater
// Clear the color to get the appropriate underwater fog color
int nFlags = nFirstPassFlags | DF_MAINTAINWORLDLISTS | DF_FUDGE_UP |
DF_RENDER_UNDERWATER | DF_CLEARDEPTH | DF_DRAW_ENTITITES;
if( !bSoftwareUserClipPlane )
{
nFlags |= DF_CLIP_Z;
}
if ( waterInfo.m_bDrawWaterSurface )
{
nFlags |= DF_RENDER_WATER;
}
if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater )
{
nFlags |= DF_RENDER_ABOVEWATER;
}
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false );
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 );
if( waterZAdjust != 0.0f && bSoftwareUserClipPlane && waterInfo.m_bRefract )
{
nFlags = DF_RENDER_ABOVEWATER;
WaterDrawHelper( view, info, *pRenderList, newWaterHeight, nFlags, CurrentViewID(), waterZAdjust, -1 );
}
materials->ClearColor4ub( 0, 0, 0, 255 );
#ifdef _XBOX
MemFreeScratch();
#endif
}
//-----------------------------------------------------------------------------
// Draws the scene when there's no water or cheap water
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene_NoWater( bool bDrawSkybox, const CViewSetup &view, int nClearFlags,
const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo )
{
VPROF( "CViewRender::ViewDrawScene_NoWater" );
ClientWorldListInfo_t info;
#ifndef _XBOX
CRenderList renderList;
CRenderList *pRenderList = &renderList;
#else
void *ptr = MemAllocScratch(sizeof(CRenderList));
CRenderList *pRenderList = new (ptr) CRenderList;
#endif
int nFlags = DF_BUILDWORLDLISTS | DF_DRAW_ENTITITES | DF_UPDATELIGHTMAPS;
if ( nClearFlags & VIEW_CLEAR_COLOR )
{
nFlags |= DF_CLEARCOLOR;
}
if ( nClearFlags & VIEW_CLEAR_DEPTH )
{
nFlags |= DF_CLEARDEPTH;
}
if ( !waterInfo.m_bOpaqueWater )
{
nFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER;
}
else
{
bool bViewIntersectsWater = DoesViewPlaneIntersectWater( fogInfo.m_flWaterHeight, fogInfo.m_nVisibleFogVolume );
if( bViewIntersectsWater )
{
// have to draw both sides if we can see both.
nFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER;
}
else if ( fogInfo.m_bEyeInFogVolume )
{
nFlags |= DF_RENDER_UNDERWATER;
}
else
{
nFlags |= DF_RENDER_ABOVEWATER;
}
}
if ( waterInfo.m_bDrawWaterSurface )
{
nFlags |= DF_RENDER_WATER;
}
if ( !fogInfo.m_bEyeInFogVolume )
{
if ( bDrawSkybox )
{
nFlags |= DF_DRAWSKYBOX;
}
EnableWorldFog();
}
else
{
nFlags |= DF_CLEARCOLOR;
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, false );
unsigned char ucFogColor[3];
materials->GetFogColor( ucFogColor );
materials->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
}
WaterDrawHelper( view, info, *pRenderList, 0.0f, nFlags, CurrentViewID(), 0.0f, m_iForceViewLeaf );
materials->ClearColor4ub( 0, 0, 0, 255 );
#ifdef _XBOX
MemFreeScratch();
#endif
}
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Methods related to controlling the cheap water distance
//-----------------------------------------------------------------------------
void CViewRender::SetCheapWaterStartDistance( float flCheapWaterStartDistance )
{
m_flCheapWaterStartDistance = flCheapWaterStartDistance;
}
void CViewRender::SetCheapWaterEndDistance( float flCheapWaterEndDistance )
{
m_flCheapWaterEndDistance = flCheapWaterEndDistance;
}
void CViewRender::GetWaterLODParams( float &flCheapWaterStartDistance, float &flCheapWaterEndDistance )
{
flCheapWaterStartDistance = m_flCheapWaterStartDistance;
flCheapWaterEndDistance = m_flCheapWaterEndDistance;
}
static void CheapWaterStart_f( void )
{
if( engine->Cmd_Argc() == 2 )
{
float dist = atof( engine->Cmd_Argv( 1 ) );
view->SetCheapWaterStartDistance( dist );
}
else
{
float start, end;
view->GetWaterLODParams( start, end );
Warning( "r_cheapwaterstart: %f\n", start );
}
}
static void CheapWaterEnd_f( void )
{
if( engine->Cmd_Argc() == 2 )
{
float dist = atof( engine->Cmd_Argv( 1 ) );
view->SetCheapWaterEndDistance( dist );
}
else
{
float start, end;
view->GetWaterLODParams( start, end );
Warning( "r_cheapwaterend: %f\n", end );
}
}
//-----------------------------------------------------------------------------
// A console command allowing you to draw a material as an overlay
//-----------------------------------------------------------------------------
static void ScreenOverlay_f( void )
{
if( engine->Cmd_Argc() == 2 )
{
if ( !Q_stricmp( "off", engine->Cmd_Argv(1) ) )
{
view->SetScreenOverlayMaterial( NULL );
}
else
{
IMaterial *pMaterial = materials->FindMaterial( engine->Cmd_Argv(1), TEXTURE_GROUP_OTHER, false );
if ( !IsErrorMaterial( pMaterial ) )
{
view->SetScreenOverlayMaterial( pMaterial );
}
else
{
view->SetScreenOverlayMaterial( NULL );
}
}
}
else
{
IMaterial *pMaterial = view->GetScreenOverlayMaterial();
Warning( "r_screenoverlay: %s\n", pMaterial ? pMaterial->GetName() : "off" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &view -
// &introData -
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData )
{
VPROF( "CViewRender::ViewDrawScene" );
// -----------------------------------------------------------------------
// Set the clear color to black since we are going to be adding up things
// in the frame buffer.
// -----------------------------------------------------------------------
// Clear alpha to 255 so that masking with the vortigaunts (0) works properly.
materials->ClearColor4ub( 0, 0, 0, 255 );
// -----------------------------------------------------------------------
// Draw the primary scene and copy it to the first framebuffer texture
// -----------------------------------------------------------------------
CViewSetup playerView = view;
playerView.origin = introData.m_vecCameraView;
playerView.m_vUnreflectedOrigin = introData.m_vecCameraView;
playerView.angles = introData.m_vecCameraViewAngles;
if ( introData.m_playerViewFOV )
{
playerView.fov = ScaleFOVByWidthRatio( introData.m_playerViewFOV, engine->GetScreenAspectRatio() / ( 4.0f / 3.0f ) );
}
SetupCurrentView( playerView.origin, playerView.angles, VIEW_INTRO_PLAYER );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view, clear frame/z buffer if necessary
unsigned int visFlags;
SetupVis( playerView, visFlags );
if( introData.m_bDrawSecondary )
{
// NOTE: We only increment this once since time doesn't move forward.
ParticleMgr()->IncrementFrameCode();
}
if( introData.m_bDrawPrimary )
{
DrawWorldAndEntities( true /* drawSkybox */, playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH );
}
else
{
materials->ClearBuffers( true, true );
}
Rect_t actualRect;
UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false, &actualRect );
// -----------------------------------------------------------------------
// Draw the secondary scene and copy it to the second framebuffer texture
// -----------------------------------------------------------------------
CViewSetup cameraView = view;
SetupCurrentView( cameraView.origin, cameraView.angles, VIEW_INTRO_CAMERA );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view, clear frame/z buffer if necessary
SetupVis( cameraView, visFlags );
// Clear alpha to 255 so that masking with the vortigaunts (0) works properly.
materials->ClearColor4ub( 0, 0, 0, 255 );
if( introData.m_bDrawSecondary )
{
DrawWorldAndEntities( true /* drawSkybox */, cameraView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH );
}
else
{
materials->ClearBuffers( true, true );
}
UpdateScreenEffectTexture( 1, view.x, view.y, view.width, view.height );
// -----------------------------------------------------------------------
// Draw quads on the screen for each screenspace pass.
// -----------------------------------------------------------------------
// Find the material that we use to render the overlays
IMaterial *pOverlayMaterial = materials->FindMaterial( "scripted/intro_screenspaceeffect", TEXTURE_GROUP_OTHER );
IMaterialVar *pModeVar = pOverlayMaterial->FindVar( "$mode", NULL );
IMaterialVar *pAlphaVar = pOverlayMaterial->FindVar( "$alpha", NULL );
materials->ClearBuffers( true, true );
materials->MatrixMode( MATERIAL_VIEW );
materials->PushMatrix();
materials->LoadIdentity();
materials->MatrixMode( MATERIAL_PROJECTION );
materials->PushMatrix();
materials->LoadIdentity();
int passID;
for( passID = 0; passID < introData.m_Passes.Count(); passID++ )
{
const IntroDataBlendPass_t& pass = introData.m_Passes[passID];
if ( pass.m_Alpha == 0 )
continue;
// Pick one of the blend modes for the material.
if( pass.m_BlendMode >= 0 && pass.m_BlendMode < 9 )
{
pModeVar->SetIntValue( pass.m_BlendMode );
}
else
{
Assert(0);
}
// Set the alpha value for the material.
pAlphaVar->SetFloatValue( pass.m_Alpha );
// Draw a quad for this pass.
ITexture *pTexture = GetFullFrameFrameBufferTexture( 0 );
materials->DrawScreenSpaceRectangle( pOverlayMaterial, view.x, view.y, view.width, view.height,
actualRect.x, actualRect.y, actualRect.x+actualRect.width-1, actualRect.y+actualRect.height-1,
pTexture->GetActualWidth(), pTexture->GetActualHeight() );
}
materials->MatrixMode( MATERIAL_VIEW );
materials->PopMatrix();
materials->MatrixMode( MATERIAL_PROJECTION );
materials->PopMatrix();
// Draw the starfield
// FIXME
// blur?
// Disable fog for the rest of the stuff
DisableFog();
// Here are the overlays...
CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState );
// issue the pixel visibility tests
PixelVisibility_EndCurrentView();
// And here are the screen-space effects
PerformScreenSpaceEffects( view.x, view.y, view.width, view.height );
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Debugging info goes over the top
Draw3DDebuggingInfo( view );
// Let the particle manager simulate things that haven't been simulated.
ParticleMgr()->PostRender();
FinishCurrentView();
}
#ifdef _XBOX
void ViewTexture_f()
{
int argc;
char* enable;
argc = engine->Cmd_Argc();
if (argc == 1)
{
// toggle
enable = mat_viewTextureEnable.GetInt() ? "0" : "1";
}
else
{
// force on
enable = "1";
}
mat_viewTextureEnable.SetValue(enable);
if (argc >= 2)
mat_viewTextureName.SetValue(engine->Cmd_Argv(1));
if (argc >= 3)
mat_viewTextureScale.SetValue(engine->Cmd_Argv(2));
}
static ConCommand mat_viewTexture("mat_viewTexture", ViewTexture_f, "Show/Hide texture [name] [scale]");
#endif
static ConCommand r_cheapwaterstart( "r_cheapwaterstart", CheapWaterStart_f );
static ConCommand r_cheapwaterend( "r_cheapwaterend", CheapWaterEnd_f );
static ConCommand r_screenspacematerial( "r_screenoverlay", ScreenOverlay_f );