source-sdk-2013/mp/src/game/client/viewrender.cpp
Joe Ludwig beaae8ac45 Updated the SDK with the latest code from the TF and HL2 branches
* Adds support for Visual Studio 2012 and 2013
* VR Mode:
. Switches from headtrack.dll to sourcevr.dll
. Improved readability of the UI in VR
. Removed the IPD calibration tool. TF2 will now obey the Oculus
configuration file. Use the Oculus calibration tool in your SDK or
install and run "OpenVR" under Tools in Steam to calibrate your IPD.
. Added dropdown to enable VR mode in the Video options. Removed the -vr
command line option.
. Added the ability to switch in and out of VR mode without quitting the
game
. By default VR mode will run full screen. To switch back to a
borderless window set the vr_force_windowed convar.
. Added support for VR mode on Linux
* Many assorted bug fixes and other changes from Team Fortress in
various shared files
2013-12-03 08:54:16 -08:00

6260 lines
196 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Responsible for drawing the scene
//
//===========================================================================//
#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/imaterialsystem.h"
#include "detailobjectsystem.h"
#include "tier0/vprof.h"
#include "tier1/mempool.h"
#include "vstdlib/jobthread.h"
#include "datacache/imdlcache.h"
#include "engine/IEngineTrace.h"
#include "engine/ivmodelinfo.h"
#include "tier0/icommandline.h"
#include "view_scene.h"
#include "particles_ez.h"
#include "engine/IStaticPropMgr.h"
#include "engine/ivdebugoverlay.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 "VGuiMatSurface/IMatSystemSurface.h"
#include "vgui_int.h"
#include "ienginevgui.h"
#include "ScreenSpaceEffects.h"
#include "toolframework_client.h"
#include "c_func_reflective_glass.h"
#include "KeyValues.h"
#include "renderparm.h"
#include "studio_stats.h"
#include "con_nprint.h"
#include "clientmode_shared.h"
#include "sourcevr/isourcevirtualreality.h"
#include "client_virtualreality.h"
#ifdef PORTAL
//#include "C_Portal_Player.h"
#include "portal_render_targets.h"
#include "PortalRender.h"
#endif
#if defined( HL2_CLIENT_DLL ) || defined( CSTRIKE_DLL )
#define USE_MONITORS
#endif
#include "rendertexture.h"
#include "viewpostprocess.h"
#include "viewdebug.h"
#if defined USES_ECON_ITEMS
#include "econ_wearable.h"
#endif
#ifdef USE_MONITORS
#include "c_point_camera.h"
#endif // USE_MONITORS
// Projective textures
#include "C_Env_Projected_Texture.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static void testfreezeframe_f( void )
{
view->FreezeFrame( 3.0 );
}
static ConCommand test_freezeframe( "test_freezeframe", testfreezeframe_f, "Test the freeze frame code.", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
static ConVar r_visocclusion( "r_visocclusion", "0", FCVAR_CHEAT );
extern ConVar r_flashlightdepthtexture;
extern ConVar vcollide_wireframe;
extern ConVar mat_motion_blur_enabled;
extern ConVar r_depthoverlay;
extern ConVar mat_viewportscale;
extern ConVar mat_viewportupscale;
extern bool g_bDumpRenderTargets;
//-----------------------------------------------------------------------------
// 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_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" );
#ifdef TF_CLIENT_DLL
ConVar r_drawviewmodel( "r_drawviewmodel","1", FCVAR_ARCHIVE );
#else
ConVar r_drawviewmodel( "r_drawviewmodel","1", FCVAR_CHEAT );
#endif
static ConVar r_drawtranslucentrenderables( "r_drawtranslucentrenderables", "1", FCVAR_CHEAT );
static ConVar r_drawopaquerenderables( "r_drawopaquerenderables", "1", FCVAR_CHEAT );
static ConVar r_threaded_renderables( "r_threaded_renderables", "0" );
// FIXME: This is not static because we needed to turn it off for TF2 playtests
ConVar r_DrawDetailProps( "r_DrawDetailProps", "1", FCVAR_NONE, "0=Off, 1=Normal, 2=Wireframe" );
ConVar r_worldlistcache( "r_worldlistcache", "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", FCVAR_CHEAT );
static ConVar fog_end( "fog_end", "-1", FCVAR_CHEAT );
static ConVar fog_color( "fog_color", "-1 -1 -1", FCVAR_CHEAT );
static ConVar fog_enable( "fog_enable", "1", FCVAR_CHEAT );
static ConVar fog_startskybox( "fog_startskybox", "-1", FCVAR_CHEAT );
static ConVar fog_endskybox( "fog_endskybox", "-1", FCVAR_CHEAT );
static ConVar fog_maxdensityskybox( "fog_maxdensityskybox", "-1", FCVAR_CHEAT );
static ConVar fog_colorskybox( "fog_colorskybox", "-1 -1 -1", FCVAR_CHEAT );
static ConVar fog_enableskybox( "fog_enableskybox", "1", FCVAR_CHEAT );
static ConVar fog_maxdensity( "fog_maxdensity", "-1", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Water-related convars
//-----------------------------------------------------------------------------
static ConVar r_debugcheapwater( "r_debugcheapwater", "0", FCVAR_CHEAT );
#ifndef _X360
static ConVar r_waterforceexpensive( "r_waterforceexpensive", "0", FCVAR_ARCHIVE );
#endif
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" );
//-----------------------------------------------------------------------------
// Other convars
//-----------------------------------------------------------------------------
static ConVar r_screenfademinsize( "r_screenfademinsize", "0" );
static ConVar r_screenfademaxsize( "r_screenfademaxsize", "0" );
static ConVar cl_drawmonitors( "cl_drawmonitors", "1" );
static ConVar r_eyewaterepsilon( "r_eyewaterepsilon", "10.0f", FCVAR_CHEAT );
#ifdef TF_CLIENT_DLL
static ConVar pyro_dof( "pyro_dof", "1", FCVAR_ARCHIVE );
#endif
extern ConVar cl_leveloverview;
extern ConVar localplayer_visionflags;
//-----------------------------------------------------------------------------
// 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;
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;
#define FREEZECAM_SNAPSHOT_FADE_SPEED 340
float g_flFreezeFlash = 0.0f;
//-----------------------------------------------------------------------------
CON_COMMAND( r_cheapwaterstart, "" )
{
if( args.ArgC() == 2 )
{
float dist = atof( args[ 1 ] );
view->SetCheapWaterStartDistance( dist );
}
else
{
float start, end;
view->GetWaterLODParams( start, end );
Warning( "r_cheapwaterstart: %f\n", start );
}
}
CON_COMMAND( r_cheapwaterend, "" )
{
if( args.ArgC() == 2 )
{
float dist = atof( args[ 1 ] );
view->SetCheapWaterEndDistance( dist );
}
else
{
float start, end;
view->GetWaterLODParams( start, end );
Warning( "r_cheapwaterend: %f\n", end );
}
}
//-----------------------------------------------------------------------------
// Describes a pruned set of leaves to be rendered this view. Reference counted
// because potentially shared by a number of views
//-----------------------------------------------------------------------------
struct ClientWorldListInfo_t : public CRefCounted1<WorldListInfo_t>
{
ClientWorldListInfo_t()
{
memset( (WorldListInfo_t *)this, 0, sizeof(WorldListInfo_t) );
m_pActualLeafIndex = NULL;
m_bPooledAlloc = false;
}
// Allocate a list intended for pruning
static ClientWorldListInfo_t *AllocPooled( const ClientWorldListInfo_t &exemplar );
// Because we remap leaves to eliminate unused leaves, we need a remap
// when drawing translucent surfaces, which requires the *original* leaf index
// using m_pActualLeafMap[ remapped leaf index ] == actual leaf index
LeafIndex_t *m_pActualLeafIndex;
private:
virtual bool OnFinalRelease();
bool m_bPooledAlloc;
static CObjectPool<ClientWorldListInfo_t> gm_Pool;
};
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
class CWorldListCache
{
public:
CWorldListCache()
{
}
void Flush()
{
for ( int i = m_Entries.FirstInorder(); i != m_Entries.InvalidIndex(); i = m_Entries.NextInorder( i ) )
{
delete m_Entries[i];
}
m_Entries.RemoveAll();
}
bool Find( const CViewSetup &viewSetup, IWorldRenderList **ppList, ClientWorldListInfo_t **ppListInfo )
{
Entry_t lookup( viewSetup );
int i = m_Entries.Find( &lookup );
if ( i != m_Entries.InvalidIndex() )
{
Entry_t *pEntry = m_Entries[i];
*ppList = InlineAddRef( pEntry->pList );
*ppListInfo = InlineAddRef( pEntry->pListInfo );
return true;
}
return false;
}
void Add( const CViewSetup &viewSetup, IWorldRenderList *pList, ClientWorldListInfo_t *pListInfo )
{
m_Entries.Insert( new Entry_t( viewSetup, pList, pListInfo ) );
}
private:
struct Entry_t
{
Entry_t( const CViewSetup &viewSetup, IWorldRenderList *pList = NULL, ClientWorldListInfo_t *pListInfo = NULL ) :
pList( ( pList ) ? InlineAddRef( pList ) : NULL ),
pListInfo( ( pListInfo ) ? InlineAddRef( pListInfo ) : NULL )
{
// @NOTE (toml 8/18/2006): because doing memcmp, need to fill all of the fields and the padding!
memset( &m_bOrtho, 0, offsetof(Entry_t, pList ) - offsetof(Entry_t, m_bOrtho ) );
m_bOrtho = viewSetup.m_bOrtho;
m_OrthoLeft = viewSetup.m_OrthoLeft;
m_OrthoTop = viewSetup.m_OrthoTop;
m_OrthoRight = viewSetup.m_OrthoRight;
m_OrthoBottom = viewSetup.m_OrthoBottom;
fov = viewSetup.fov;
origin = viewSetup.origin;
angles = viewSetup.angles;
zNear = viewSetup.zNear;
zFar = viewSetup.zFar;
m_flAspectRatio = viewSetup.m_flAspectRatio;
m_bOffCenter = viewSetup.m_bOffCenter;
m_flOffCenterTop = viewSetup.m_flOffCenterTop;
m_flOffCenterBottom = viewSetup.m_flOffCenterBottom;
m_flOffCenterLeft = viewSetup.m_flOffCenterLeft;
m_flOffCenterRight = viewSetup.m_flOffCenterRight;
}
~Entry_t()
{
if ( pList )
pList->Release();
if ( pListInfo )
pListInfo->Release();
}
// The fields from CViewSetup that would actually affect the list
float m_OrthoLeft;
float m_OrthoTop;
float m_OrthoRight;
float m_OrthoBottom;
float fov;
Vector origin;
QAngle angles;
float zNear;
float zFar;
float m_flAspectRatio;
float m_flOffCenterTop;
float m_flOffCenterBottom;
float m_flOffCenterLeft;
float m_flOffCenterRight;
bool m_bOrtho;
bool m_bOffCenter;
IWorldRenderList *pList;
ClientWorldListInfo_t *pListInfo;
};
class CEntryComparator
{
public:
CEntryComparator( int ) {}
bool operator!() const { return false; }
bool operator()( const Entry_t *lhs, const Entry_t *rhs ) const
{
return ( memcmp( lhs, rhs, sizeof(Entry_t) - ( sizeof(Entry_t) - offsetof(Entry_t, pList ) ) ) < 0 );
}
};
CUtlRBTree<Entry_t *, unsigned short, CEntryComparator> m_Entries;
};
CWorldListCache g_WorldListCache;
//-----------------------------------------------------------------------------
// Standard 3d skybox view
//-----------------------------------------------------------------------------
class CSkyboxView : public CRendering3dView
{
DECLARE_CLASS( CSkyboxView, CRendering3dView );
public:
CSkyboxView(CViewRender *pMainView) :
CRendering3dView( pMainView ),
m_pSky3dParams( NULL )
{
}
bool Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible );
void Draw();
protected:
#ifdef PORTAL
virtual bool ShouldDrawPortals() { return false; }
#endif
virtual SkyboxVisibility_t ComputeSkyboxVisibility();
bool GetSkyboxFogEnable();
void Enable3dSkyboxFog( void );
void DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostRender, ITexture *pRenderTarget, ITexture *pDepthTarget );
sky3dparams_t * PreRender3dSkyboxWorld( SkyboxVisibility_t nSkyboxVisible );
sky3dparams_t *m_pSky3dParams;
};
//-----------------------------------------------------------------------------
// 3d skybox view when drawing portals
//-----------------------------------------------------------------------------
#ifdef PORTAL
class CPortalSkyboxView : public CSkyboxView
{
DECLARE_CLASS( CPortalSkyboxView, CSkyboxView );
public:
CPortalSkyboxView(CViewRender *pMainView) :
CSkyboxView( pMainView ),
m_pRenderTarget( NULL )
{}
bool Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible, ITexture *pRenderTarget = NULL );
//Skybox drawing through portals with workarounds to fix area bits, position/scaling, view id's..........
void Draw();
private:
virtual SkyboxVisibility_t ComputeSkyboxVisibility();
ITexture *m_pRenderTarget;
};
#endif
//-----------------------------------------------------------------------------
// Shadow depth texture
//-----------------------------------------------------------------------------
class CShadowDepthView : public CRendering3dView
{
DECLARE_CLASS( CShadowDepthView, CRendering3dView );
public:
CShadowDepthView(CViewRender *pMainView) : CRendering3dView( pMainView ) {}
void Setup( const CViewSetup &shadowViewIn, ITexture *pRenderTarget, ITexture *pDepthTexture );
void Draw();
private:
ITexture *m_pRenderTarget;
ITexture *m_pDepthTexture;
};
//-----------------------------------------------------------------------------
// Freeze frame. Redraws the frame at which it was enabled.
//-----------------------------------------------------------------------------
class CFreezeFrameView : public CRendering3dView
{
DECLARE_CLASS( CFreezeFrameView, CRendering3dView );
public:
CFreezeFrameView(CViewRender *pMainView) : CRendering3dView( pMainView ) {}
void Setup( const CViewSetup &view );
void Draw();
private:
CMaterialReference m_pFreezeFrame;
CMaterialReference m_TranslucentSingleColor;
};
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
class CBaseWorldView : public CRendering3dView
{
DECLARE_CLASS( CBaseWorldView, CRendering3dView );
protected:
CBaseWorldView(CViewRender *pMainView) : CRendering3dView( pMainView ) {}
virtual bool AdjustView( float waterHeight );
void DrawSetup( float waterHeight, int flags, float waterZAdjust, int iForceViewLeaf = -1 );
void DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust );
virtual void PushView( float waterHeight );
virtual void PopView();
void SSAO_DepthPass();
void DrawDepthOfField();
};
//-----------------------------------------------------------------------------
// Draws the scene when there's no water or only cheap water
//-----------------------------------------------------------------------------
class CSimpleWorldView : public CBaseWorldView
{
DECLARE_CLASS( CSimpleWorldView, CBaseWorldView );
public:
CSimpleWorldView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& info, ViewCustomVisibility_t *pCustomVisibility = NULL );
void Draw();
private:
VisibleFogVolumeInfo_t m_fogInfo;
};
//-----------------------------------------------------------------------------
// Base class for scenes with water
//-----------------------------------------------------------------------------
class CBaseWaterView : public CBaseWorldView
{
DECLARE_CLASS( CBaseWaterView, CBaseWorldView );
public:
CBaseWaterView(CViewRender *pMainView) :
CBaseWorldView( pMainView ),
m_SoftwareIntersectionView( pMainView )
{}
// void Setup( const CViewSetup &, const WaterRenderInfo_t& info );
protected:
void CalcWaterEyeAdjustments( const VisibleFogVolumeInfo_t &fogInfo, float &newWaterHeight, float &waterZAdjust, bool bSoftwareUserClipPlane );
class CSoftwareIntersectionView : public CBaseWorldView
{
DECLARE_CLASS( CSoftwareIntersectionView, CBaseWorldView );
public:
CSoftwareIntersectionView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup( bool bAboveWater );
void Draw();
private:
CBaseWaterView *GetOuter() { return GET_OUTER( CBaseWaterView, m_SoftwareIntersectionView ); }
};
friend class CSoftwareIntersectionView;
CSoftwareIntersectionView m_SoftwareIntersectionView;
WaterRenderInfo_t m_waterInfo;
float m_waterHeight;
float m_waterZAdjust;
bool m_bSoftwareUserClipPlane;
VisibleFogVolumeInfo_t m_fogInfo;
};
//-----------------------------------------------------------------------------
// Scenes above water
//-----------------------------------------------------------------------------
class CAboveWaterView : public CBaseWaterView
{
DECLARE_CLASS( CAboveWaterView, CBaseWaterView );
public:
CAboveWaterView(CViewRender *pMainView) :
CBaseWaterView( pMainView ),
m_ReflectionView( pMainView ),
m_RefractionView( pMainView ),
m_IntersectionView( pMainView )
{}
void Setup( const CViewSetup &view, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo );
void Draw();
class CReflectionView : public CBaseWorldView
{
DECLARE_CLASS( CReflectionView, CBaseWorldView );
public:
CReflectionView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup( bool bReflectEntities );
void Draw();
private:
CAboveWaterView *GetOuter() { return GET_OUTER( CAboveWaterView, m_ReflectionView ); }
};
class CRefractionView : public CBaseWorldView
{
DECLARE_CLASS( CRefractionView, CBaseWorldView );
public:
CRefractionView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup();
void Draw();
private:
CAboveWaterView *GetOuter() { return GET_OUTER( CAboveWaterView, m_RefractionView ); }
};
class CIntersectionView : public CBaseWorldView
{
DECLARE_CLASS( CIntersectionView, CBaseWorldView );
public:
CIntersectionView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup();
void Draw();
private:
CAboveWaterView *GetOuter() { return GET_OUTER( CAboveWaterView, m_IntersectionView ); }
};
friend class CRefractionView;
friend class CReflectionView;
friend class CIntersectionView;
bool m_bViewIntersectsWater;
CReflectionView m_ReflectionView;
CRefractionView m_RefractionView;
CIntersectionView m_IntersectionView;
};
//-----------------------------------------------------------------------------
// Scenes below water
//-----------------------------------------------------------------------------
class CUnderWaterView : public CBaseWaterView
{
DECLARE_CLASS( CUnderWaterView, CBaseWaterView );
public:
CUnderWaterView(CViewRender *pMainView) :
CBaseWaterView( pMainView ),
m_RefractionView( pMainView )
{}
void Setup( const CViewSetup &view, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& info );
void Draw();
class CRefractionView : public CBaseWorldView
{
DECLARE_CLASS( CRefractionView, CBaseWorldView );
public:
CRefractionView(CViewRender *pMainView) : CBaseWorldView( pMainView ) {}
void Setup();
void Draw();
private:
CUnderWaterView *GetOuter() { return GET_OUTER( CUnderWaterView, m_RefractionView ); }
};
friend class CRefractionView;
bool m_bDrawSkybox; // @MULTICORE (toml 8/17/2006): remove after setup hoisted
CRefractionView m_RefractionView;
};
//-----------------------------------------------------------------------------
// Scenes containing reflective glass
//-----------------------------------------------------------------------------
class CReflectiveGlassView : public CSimpleWorldView
{
DECLARE_CLASS( CReflectiveGlassView, CSimpleWorldView );
public:
CReflectiveGlassView( CViewRender *pMainView ) : BaseClass( pMainView )
{
}
virtual bool AdjustView( float flWaterHeight );
virtual void PushView( float waterHeight );
virtual void PopView( );
void Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t &waterInfo, const cplane_t &reflectionPlane );
void Draw();
cplane_t m_ReflectionPlane;
};
class CRefractiveGlassView : public CSimpleWorldView
{
DECLARE_CLASS( CRefractiveGlassView, CSimpleWorldView );
public:
CRefractiveGlassView( CViewRender *pMainView ) : BaseClass( pMainView )
{
}
virtual bool AdjustView( float flWaterHeight );
virtual void PushView( float waterHeight );
virtual void PopView( );
void Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t &waterInfo, const cplane_t &reflectionPlane );
void Draw();
cplane_t m_ReflectionPlane;
};
//-----------------------------------------------------------------------------
// Computes draw flags for the engine to build its world surface lists
//-----------------------------------------------------------------------------
static inline unsigned long BuildEngineDrawWorldListFlags( unsigned nDrawFlags )
{
unsigned long nEngineFlags = 0;
if ( nDrawFlags & DF_DRAWSKYBOX )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_SKYBOX;
}
if ( nDrawFlags & DF_RENDER_ABOVEWATER )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYABOVEWATER;
nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER;
}
if ( nDrawFlags & DF_RENDER_UNDERWATER )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_STRICTLYUNDERWATER;
nEngineFlags |= DRAWWORLDLISTS_DRAW_INTERSECTSWATER;
}
if ( nDrawFlags & DF_RENDER_WATER )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_WATERSURFACE;
}
if( nDrawFlags & DF_CLIP_SKYBOX )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_CLIPSKYBOX;
}
if( nDrawFlags & DF_SHADOW_DEPTH_MAP )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_SHADOWDEPTH;
}
if( nDrawFlags & DF_RENDER_REFRACTION )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_REFRACTION;
}
if( nDrawFlags & DF_RENDER_REFLECTION )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_REFLECTION;
}
if( nDrawFlags & DF_SSAO_DEPTH_PASS )
{
nEngineFlags |= DRAWWORLDLISTS_DRAW_SSAO | DRAWWORLDLISTS_DRAW_STRICTLYUNDERWATER | DRAWWORLDLISTS_DRAW_INTERSECTSWATER | DRAWWORLDLISTS_DRAW_STRICTLYABOVEWATER ;
nEngineFlags &= ~( DRAWWORLDLISTS_DRAW_WATERSURFACE | DRAWWORLDLISTS_DRAW_REFRACTION | DRAWWORLDLISTS_DRAW_REFLECTION );
}
return nEngineFlags;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void SetClearColorToFogColor()
{
unsigned char ucFogColor[3];
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->GetFogColor( ucFogColor );
if( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER )
{
// @MULTICORE (toml 8/16/2006): Find a way to not do this twice in eye above water case
float scale = LinearToGammaFullRange( pRenderContext->GetToneMappingScaleLinear().x );
ucFogColor[0] *= scale;
ucFogColor[1] *= scale;
ucFogColor[2] *= scale;
}
pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
}
//-----------------------------------------------------------------------------
// Precache of necessary materials
//-----------------------------------------------------------------------------
#ifdef HL2_CLIENT_DLL
CLIENTEFFECT_REGISTER_BEGIN( PrecacheViewRender )
CLIENTEFFECT_MATERIAL( "scripted/intro_screenspaceeffect" )
CLIENTEFFECT_REGISTER_END()
#endif
CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffects )
CLIENTEFFECT_MATERIAL( "dev/blurfiltery_and_add_nohdr" )
CLIENTEFFECT_MATERIAL( "dev/blurfilterx" )
CLIENTEFFECT_MATERIAL( "dev/blurfilterx_nohdr" )
CLIENTEFFECT_MATERIAL( "dev/blurfiltery" )
CLIENTEFFECT_MATERIAL( "dev/blurfiltery_nohdr" )
CLIENTEFFECT_MATERIAL( "dev/bloomadd" )
CLIENTEFFECT_MATERIAL( "dev/downsample" )
#ifdef CSTRIKE_DLL
CLIENTEFFECT_MATERIAL( "dev/downsample_non_hdr_cstrike" )
#else
CLIENTEFFECT_MATERIAL( "dev/downsample_non_hdr" )
#endif
CLIENTEFFECT_MATERIAL( "dev/no_pixel_write" )
CLIENTEFFECT_MATERIAL( "dev/lumcompare" )
CLIENTEFFECT_MATERIAL( "dev/floattoscreen_combine" )
CLIENTEFFECT_MATERIAL( "dev/copyfullframefb_vanilla" )
CLIENTEFFECT_MATERIAL( "dev/copyfullframefb" )
CLIENTEFFECT_MATERIAL( "dev/engine_post" )
CLIENTEFFECT_MATERIAL( "dev/motion_blur" )
CLIENTEFFECT_MATERIAL( "dev/upscale" )
#ifdef TF_CLIENT_DLL
CLIENTEFFECT_MATERIAL( "dev/pyro_blur_filter_y" )
CLIENTEFFECT_MATERIAL( "dev/pyro_blur_filter_x" )
CLIENTEFFECT_MATERIAL( "dev/pyro_dof" )
CLIENTEFFECT_MATERIAL( "dev/pyro_vignette_border" )
CLIENTEFFECT_MATERIAL( "dev/pyro_vignette" )
CLIENTEFFECT_MATERIAL( "dev/pyro_post" )
#endif
CLIENTEFFECT_REGISTER_END_CONDITIONAL( engine->GetDXSupportLevel() >= 90 )
//-----------------------------------------------------------------------------
// 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 )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// 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 );
CMatRenderContextPtr pRenderContext( materials );
#ifdef PORTAL
if ( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, ((viewID == VIEW_MAIN) || (viewID == VIEW_3DSKY)) ? 1 : 0 );
}
#else
pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, ((viewID == VIEW_MAIN) || (viewID == VIEW_3DSKY)) ? 1 : 0 );
#endif
}
view_id_t CurrentViewID()
{
Assert( g_CurrentViewID != VIEW_ILLEGAL );
return ( view_id_t )g_CurrentViewID;
}
//-----------------------------------------------------------------------------
// Purpose: Portal views are considered 'Main' views. This function tests a view id
// against all view ids used by portal renderables, as well as the main view.
//-----------------------------------------------------------------------------
bool IsMainView ( view_id_t id )
{
#if defined(PORTAL)
return ( (id == VIEW_MAIN) || g_pPortalRender->IsPortalViewID( id ) );
#else
return (id == VIEW_MAIN);
#endif
}
void FinishCurrentView()
{
s_bCanAccessCurrentView = false;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
void CSimpleRenderExecutor::AddView( CRendering3dView *pView )
{
CBase3dView *pPrevRenderer = m_pMainView->SetActiveRenderer( pView );
pView->Draw();
m_pMainView->SetActiveRenderer( pPrevRenderer );
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CViewRender::CViewRender()
: m_SimpleExecutor( this )
{
m_flCheapWaterStartDistance = 0.0f;
m_flCheapWaterEndDistance = 0.1f;
m_BaseDrawFlags = 0;
m_pActiveRenderer = NULL;
m_pCurrentlyDrawingEntity = NULL;
}
//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CViewRender::UpdateRefractIfNeededByList( CUtlVector< IClientRenderable * > &list )
{
int nCount = list.Count();
for( int i=0; i < nCount; ++i )
{
IClientUnknown *pUnk = list[i]->GetIClientUnknown();
Assert( pUnk );
IClientRenderable *pRenderable = pUnk->GetClientRenderable();
Assert( pRenderable );
if ( pRenderable->UsesPowerOfTwoFrameBufferTexture() )
{
UpdateRefractTexture();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::DrawRenderablesInList( CUtlVector< IClientRenderable * > &list, int flags )
{
Assert( m_pCurrentlyDrawingEntity == NULL );
int nCount = list.Count();
for( int i=0; i < nCount; ++i )
{
IClientUnknown *pUnk = list[i]->GetIClientUnknown();
Assert( pUnk );
IClientRenderable *pRenderable = pUnk->GetClientRenderable();
Assert( pRenderable );
// Non-view models wanting to render in view model list...
if ( pRenderable->ShouldDraw() )
{
m_pCurrentlyDrawingEntity = pUnk->GetBaseEntity();
pRenderable->DrawModel( STUDIO_RENDER | flags );
}
}
m_pCurrentlyDrawingEntity = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Actually draw the view model
// Input : drawViewModel -
//-----------------------------------------------------------------------------
void CViewRender::DrawViewModels( const CViewSetup &view, bool drawViewmodel )
{
VPROF( "CViewRender::DrawViewModel" );
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
#ifdef PORTAL //in portal, we'd like a copy of the front buffer without the gun in it for use with the depth doubler
g_pPortalRender->UpdateDepthDoublerTexture( view );
#endif
bool bShouldDrawPlayerViewModel = ShouldDrawViewModel( drawViewmodel );
bool bShouldDrawToolViewModels = ToolsEnabled();
CMatRenderContextPtr pRenderContext( materials );
PIXEVENT( pRenderContext, "DrawViewModels" );
// Restore the matrices
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
CViewSetup viewModelSetup( view );
viewModelSetup.zNear = view.zNearViewmodel;
viewModelSetup.zFar = view.zFarViewmodel;
viewModelSetup.fov = view.fovViewmodel;
viewModelSetup.m_flAspectRatio = engine->GetScreenAspectRatio();
ITexture *pRTColor = NULL;
ITexture *pRTDepth = NULL;
if( view.m_eStereoEye != STEREO_EYE_MONO )
{
pRTColor = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(view.m_eStereoEye-1), ISourceVirtualReality::RT_Color );
pRTDepth = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(view.m_eStereoEye-1), ISourceVirtualReality::RT_Depth );
}
render->Push3DView( viewModelSetup, 0, pRTColor, GetFrustum(), pRTDepth );
#ifdef PORTAL //the depth range hack doesn't work well enough for the portal mod (and messing with the depth hack values makes some models draw incorrectly)
//step up to a full depth clear if we're extremely close to a portal (in a portal environment)
extern bool LocalPlayerIsCloseToPortal( void ); //defined in C_Portal_Player.cpp, abstracting to a single bool function to remove explicit dependence on c_portal_player.h/cpp, you can define the function as a "return true" in other build configurations at the cost of some perf
bool bUseDepthHack = !LocalPlayerIsCloseToPortal();
if( !bUseDepthHack )
pRenderContext->ClearBuffers( false, true, false );
#else
const bool bUseDepthHack = true;
#endif
// 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 )
pRenderContext->DepthRange( 0.0f, 0.1f );
if ( bShouldDrawPlayerViewModel || bShouldDrawToolViewModels )
{
CUtlVector< IClientRenderable * > opaqueViewModelList( 32 );
CUtlVector< IClientRenderable * > translucentViewModelList( 32 );
ClientLeafSystem()->CollateViewModelRenderables( opaqueViewModelList, translucentViewModelList );
if ( ToolsEnabled() && ( !bShouldDrawPlayerViewModel || !bShouldDrawToolViewModels ) )
{
int nOpaque = opaqueViewModelList.Count();
for ( int i = nOpaque-1; i >= 0; --i )
{
IClientRenderable *pRenderable = opaqueViewModelList[ i ];
bool bEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( ( bEntity && !bShouldDrawPlayerViewModel ) || ( !bEntity && !bShouldDrawToolViewModels ) )
{
opaqueViewModelList.FastRemove( i );
}
}
int nTranslucent = translucentViewModelList.Count();
for ( int i = nTranslucent-1; i >= 0; --i )
{
IClientRenderable *pRenderable = translucentViewModelList[ i ];
bool bEntity = pRenderable->GetIClientUnknown()->GetBaseEntity();
if ( ( bEntity && !bShouldDrawPlayerViewModel ) || ( !bEntity && !bShouldDrawToolViewModels ) )
{
translucentViewModelList.FastRemove( i );
}
}
}
if ( !UpdateRefractIfNeededByList( opaqueViewModelList ) )
{
UpdateRefractIfNeededByList( translucentViewModelList );
}
DrawRenderablesInList( opaqueViewModelList );
DrawRenderablesInList( translucentViewModelList, STUDIO_TRANSPARENCY );
}
// Reset the depth range to the original values
if( bUseDepthHack )
pRenderContext->DepthRange( depthmin, depthmax );
render->PopView( GetFrustum() );
// Restore the matrices
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CViewRender::ShouldDrawBrushModels( void )
{
if ( m_pDrawBrushModels && !m_pDrawBrushModels->GetInt() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Performs screen space effects, if any
//-----------------------------------------------------------------------------
void CViewRender::PerformScreenSpaceEffects( int x, int y, int w, int h )
{
VPROF("CViewRender::PerformScreenSpaceEffects()");
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// 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)
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
if ( m_ScreenOverlayMaterial->NeedsFullFrameBufferTexture() )
{
// FIXME: check with multi/sub-rect renders. Should this be 0,0,w,h instead?
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...
CMatRenderContextPtr pRenderContext( materials );
ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( );
int sw = pTexture->GetActualWidth();
int sh = pTexture->GetActualHeight();
// Note - don't offset by x,y - already done by the viewport.
pRenderContext->DrawScreenSpaceRectangle( m_ScreenOverlayMaterial, 0, 0, w, h,
0, 0, sw-1, sh-1, sw, sh );
}
else
{
byte color[4] = { 255, 255, 255, 255 };
render->ViewDrawFade( color, m_ScreenOverlayMaterial );
}
}
}
void CViewRender::DrawUnderwaterOverlay( void )
{
IMaterial *pOverlayMat = m_UnderWaterOverlayMaterial;
if ( pOverlayMat )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
CMatRenderContextPtr pRenderContext( materials );
int x, y, w, h;
pRenderContext->GetViewport( x, y, w, h );
if ( pOverlayMat->NeedsFullFrameBufferTexture() )
{
// FIXME: check with multi/sub-rect renders. Should this be 0,0,w,h instead?
DrawScreenEffectMaterial( pOverlayMat, x, y, w, h );
}
else if ( pOverlayMat->NeedsPowerOfTwoFrameBufferTexture() )
{
// First copy the FB off to the offscreen texture
UpdateRefractTexture( x, y, w, h, true );
// Now draw the entire screen using the material...
CMatRenderContextPtr pRenderContext( materials );
ITexture *pTexture = GetPowerOfTwoFrameBufferTexture( );
int sw = pTexture->GetActualWidth();
int sh = pTexture->GetActualHeight();
// Note - don't offset by x,y - already done by the viewport.
pRenderContext->DrawScreenSpaceRectangle( pOverlayMat, 0, 0, w, h,
0, 0, sw-1, sh-1, sw, sh );
}
else
{
// Note - don't offset by x,y - already done by the viewport.
// FIXME: actually test this code path.
pRenderContext->DrawScreenSpaceRectangle( pOverlayMat, 0, 0, w, h,
0, 0, 1, 1, 1, 1 );
}
}
}
//-----------------------------------------------------------------------------
// 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();
}
}
C_BaseEntity *CViewRender::GetCurrentlyDrawingEntity()
{
return m_pCurrentlyDrawingEntity;
}
void CViewRender::SetCurrentlyDrawingEntity( C_BaseEntity *pEnt )
{
m_pCurrentlyDrawingEntity = pEnt;
}
bool CViewRender::UpdateShadowDepthTexture( ITexture *pRenderTarget, ITexture *pDepthTexture, const CViewSetup &shadowViewIn )
{
VPROF_INCREMENT_COUNTER( "shadow depth textures rendered", 1 );
CMatRenderContextPtr pRenderContext( materials );
char szPIXEventName[128];
sprintf( szPIXEventName, "UpdateShadowDepthTexture (%s)", pDepthTexture->GetName() );
PIXEVENT( pRenderContext, szPIXEventName );
CRefPtr<CShadowDepthView> pShadowDepthView = new CShadowDepthView( this );
pShadowDepthView->Setup( shadowViewIn, pRenderTarget, pDepthTexture );
AddViewToScene( pShadowDepthView );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Renders world and all entities, etc.
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &view,
int nClearFlags, view_id_t viewID, bool bDrawViewModel, int baseDrawFlags, ViewCustomVisibility_t *pCustomVisibility )
{
VPROF( "CViewRender::ViewDrawScene" );
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// this allows the refract texture to be updated once per *scene* on 360
// (e.g. once for a monitor scene and once for the main scene)
g_viewscene_refractUpdateFrame = gpGlobals->framecount - 1;
g_pClientShadowMgr->PreRender();
// Shadowed flashlights supported on ps_2_b and up...
if ( r_flashlightdepthtexture.GetBool() && (viewID == VIEW_MAIN) )
{
g_pClientShadowMgr->ComputeShadowDepthTextures( view );
}
m_BaseDrawFlags = baseDrawFlags;
SetupCurrentView( view.origin, view.angles, viewID );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view
unsigned int visFlags;
SetupVis( view, visFlags, pCustomVisibility );
if ( !bDrew3dSkybox &&
( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) && ( 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;
SetClearColorToFogColor( );
}
bool drawSkybox = r_skybox.GetBool();
if ( bDrew3dSkybox || ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) )
{
drawSkybox = false;
}
ParticleMgr()->IncrementFrameCode();
DrawWorldAndEntities( drawSkybox, view, nClearFlags, pCustomVisibility );
// 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...
CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState );
// issue the pixel visibility tests
if ( IsMainView( CurrentViewID() ) )
{
PixelVisibility_EndCurrentView();
}
// Draw rain..
DrawPrecipitation();
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Debugging info goes over the top
CDebugViewRender::Draw3DDebuggingInfo( view );
// 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 );
// Invoke post-render methods
IGameSystem::PostRenderAllSystems();
FinishCurrentView();
// Free shadow depth textures for use in future view
if ( r_flashlightdepthtexture.GetBool() )
{
g_pClientShadowMgr->UnlockAllShadowDepthTextures();
}
}
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( fogparams_t *pFogParams, float *pColorPrimary, float *pColorSecondary )
{
if ( !pFogParams )
return;
if ( pFogParams->lerptime >= gpGlobals->curtime )
{
float flPercent = 1.0f - (( pFogParams->lerptime - gpGlobals->curtime ) / pFogParams->duration );
float flPrimaryColorLerp[3] = { pFogParams->colorPrimaryLerpTo.GetR(), pFogParams->colorPrimaryLerpTo.GetG(), pFogParams->colorPrimaryLerpTo.GetB() };
float flSecondaryColorLerp[3] = { pFogParams->colorSecondaryLerpTo.GetR(), pFogParams->colorSecondaryLerpTo.GetG(), pFogParams->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( fogparams_t *pFogParams, float *pColor )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if ( !pbp || !pFogParams )
return;
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] = { pFogParams->colorPrimary.GetR(), pFogParams->colorPrimary.GetG(), pFogParams->colorPrimary.GetB() };
float flSecondaryColor[3] = { pFogParams->colorSecondary.GetR(), pFogParams->colorSecondary.GetG(), pFogParams->colorSecondary.GetB() };
GetFogColorTransition( pFogParams, flPrimaryColor, flSecondaryColor );
if( pFogParams->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;
pbp->EyeVectors( &forward, NULL, NULL );
Vector vNormalized = pFogParams->dirPrimary;
VectorNormalize( vNormalized );
pFogParams->dirPrimary = vNormalized;
float flBlendFactor = 0.5 * forward.Dot( pFogParams->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( fogparams_t *pFogParams )
{
if( !pFogParams )
return 0.0f;
if( fog_override.GetInt() )
{
if( fog_start.GetFloat() == -1.0f )
{
return pFogParams->start;
}
else
{
return fog_start.GetFloat();
}
}
else
{
if ( pFogParams->lerptime > gpGlobals->curtime )
{
if ( pFogParams->start != pFogParams->startLerpTo )
{
if ( pFogParams->lerptime > gpGlobals->curtime )
{
float flPercent = 1.0f - (( pFogParams->lerptime - gpGlobals->curtime ) / pFogParams->duration );
return FLerp( pFogParams->start, pFogParams->startLerpTo, flPercent );
}
else
{
if ( pFogParams->start != pFogParams->startLerpTo )
{
pFogParams->start = pFogParams->startLerpTo;
}
}
}
}
return pFogParams->start;
}
}
static float GetFogEnd( fogparams_t *pFogParams )
{
if( !pFogParams )
return 0.0f;
if( fog_override.GetInt() )
{
if( fog_end.GetFloat() == -1.0f )
{
return pFogParams->end;
}
else
{
return fog_end.GetFloat();
}
}
else
{
if ( pFogParams->lerptime > gpGlobals->curtime )
{
if ( pFogParams->end != pFogParams->endLerpTo )
{
if ( pFogParams->lerptime > gpGlobals->curtime )
{
float flPercent = 1.0f - (( pFogParams->lerptime - gpGlobals->curtime ) / pFogParams->duration );
return FLerp( pFogParams->end, pFogParams->endLerpTo, flPercent );
}
else
{
if ( pFogParams->end != pFogParams->endLerpTo )
{
pFogParams->end = pFogParams->endLerpTo;
}
}
}
}
return pFogParams->end;
}
}
static bool GetFogEnable( fogparams_t *pFogParams )
{
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
{
if( pFogParams )
return pFogParams->enable != false;
return false;
}
}
static float GetFogMaxDensity( fogparams_t *pFogParams )
{
if( !pFogParams )
return 1.0f;
if ( cl_leveloverview.GetFloat() > 0 )
return 1.0f;
// Ask the clientmode
if ( !g_pClientMode->ShouldDrawFog() )
return 1.0f;
if ( fog_override.GetInt() )
{
if ( fog_maxdensity.GetFloat() == -1.0f )
return pFogParams->maxdensity;
else
return fog_maxdensity.GetFloat();
}
else
return pFogParams->maxdensity;
}
//-----------------------------------------------------------------------------
// 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;
pbp->EyeVectors( &forward, NULL, NULL );
Vector vNormalized = local->m_skybox3d.fog.dirPrimary;
VectorNormalize( vNormalized );
local->m_skybox3d.fog.dirPrimary = vNormalized;
float flBlendFactor = 0.5 * forward.Dot( local->m_skybox3d.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 float GetSkyboxFogMaxDensity()
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if ( !pbp )
return 1.0f;
CPlayerLocalData *local = &pbp->m_Local;
if ( cl_leveloverview.GetFloat() > 0 )
return 1.0f;
// Ask the clientmode
if ( !g_pClientMode->ShouldDrawFog() )
return 1.0f;
if ( fog_override.GetInt() )
{
if ( fog_maxdensityskybox.GetFloat() == -1.0f )
return local->m_skybox3d.fog.maxdensity;
else
return fog_maxdensityskybox.GetFloat();
}
else
return local->m_skybox3d.fog.maxdensity;
}
void CViewRender::DisableFog( void )
{
VPROF("CViewRander::DisableFog()");
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->FogMode( MATERIAL_FOG_NONE );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender::SetupVis( const CViewSetup& view, unsigned int &visFlags, ViewCustomVisibility_t *pCustomVisibility )
{
VPROF( "CViewRender::SetupVis" );
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
if ( pCustomVisibility && pCustomVisibility->m_nNumVisOrigins )
{
// Pass array or vis origins to merge
render->ViewSetupVisEx( ShouldForceNoVis(), pCustomVisibility->m_nNumVisOrigins, pCustomVisibility->m_rgVisOrigins, visFlags );
}
else
{
// Use render origin as vis origin by default
render->ViewSetupVisEx( ShouldForceNoVis(), 1, &view.origin, visFlags );
}
}
//-----------------------------------------------------------------------------
// Purpose: Renders voice feedback and other sprites attached to players
// Input : none
//-----------------------------------------------------------------------------
void CViewRender::RenderPlayerSprites()
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
GetClientVoiceMgr()->DrawHeadLabels();
}
//-----------------------------------------------------------------------------
// Sets up, cleans up the main 3D view
//-----------------------------------------------------------------------------
void CViewRender::SetupMain3DView( const CViewSetup &view, int &nClearFlags )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// FIXME: I really want these fields removed from CViewSetup
// and passed in as independent flags
// Clear the color here if requested.
int nDepthStencilFlags = nClearFlags & ( VIEW_CLEAR_DEPTH | VIEW_CLEAR_STENCIL );
nClearFlags &= ~( nDepthStencilFlags ); // Clear these flags
if ( nClearFlags & VIEW_CLEAR_COLOR )
{
nClearFlags |= nDepthStencilFlags; // Add them back in if we're clearing color
}
// 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, GetFullFrameFrameBufferTexture( 0 ), GetFrustum() );
}
else
{
ITexture *pRTColor = NULL;
ITexture *pRTDepth = NULL;
if( view.m_eStereoEye != STEREO_EYE_MONO )
{
pRTColor = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(view.m_eStereoEye-1), ISourceVirtualReality::RT_Color );
pRTDepth = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(view.m_eStereoEye-1), ISourceVirtualReality::RT_Depth );
}
render->Push3DView( view, nClearFlags, pRTColor, GetFrustum(), pRTDepth );
}
// If we didn't clear the depth here, we'll need to clear it later
nClearFlags ^= nDepthStencilFlags; // Toggle these bits
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( GetFrustum() );
}
//-----------------------------------------------------------------------------
// Queues up an overlay rendering
//-----------------------------------------------------------------------------
void CViewRender::QueueOverlayRenderView( const CViewSetup &view, int nClearFlags, int whatToDraw )
{
// Can't have 2 in a single scene
Assert( !m_bDrawOverlay );
m_bDrawOverlay = true;
m_OverlayViewSetup = view;
m_OverlayClearFlags = nClearFlags;
m_OverlayDrawFlags = whatToDraw;
}
//-----------------------------------------------------------------------------
// Purpose: Force the view to freeze on the next frame for the specified time
//-----------------------------------------------------------------------------
void CViewRender::FreezeFrame( float flFreezeTime )
{
if ( flFreezeTime == 0 )
{
m_flFreezeFrameUntil = 0;
for( int i=0; i < STEREO_EYE_MAX; i++ )
{
m_rbTakeFreezeFrame[ i ] = false;
}
}
else
{
if ( m_flFreezeFrameUntil > gpGlobals->curtime )
{
m_flFreezeFrameUntil += flFreezeTime;
}
else
{
m_flFreezeFrameUntil = gpGlobals->curtime + flFreezeTime;
for( int i=GetFirstEye(); i <= GetLastEye(); i++ )
{
m_rbTakeFreezeFrame[ i ] = true;
}
}
}
}
const char *COM_GetModDirectory();
//-----------------------------------------------------------------------------
// Purpose: This renders the entire 3D view and the in-game hud/viewmodel
// Input : &view -
// whatToDraw -
//-----------------------------------------------------------------------------
// This renders the entire 3D view.
void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatToDraw )
{
m_UnderWaterOverlayMaterial.Shutdown(); // underwater view will set
m_CurrentView = view;
C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
VPROF( "CViewRender::RenderView" );
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// Don't want TF2 running less than DX 8
if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 )
{
// We know they were running at least 8.0 when the game started...we check the
// value in ClientDLL_Init()...so they must be messing with their DirectX settings.
if ( ( Q_stricmp( COM_GetModDirectory(), "tf" ) == 0 ) || ( Q_stricmp( COM_GetModDirectory(), "tf_beta" ) == 0 ) )
{
static bool bFirstTime = true;
if ( bFirstTime )
{
bFirstTime = false;
Msg( "This game has a minimum requirement of DirectX 8.0 to run properly.\n" );
}
return;
}
}
CMatRenderContextPtr pRenderContext( materials );
ITexture *saveRenderTarget = pRenderContext->GetRenderTarget();
pRenderContext.SafeRelease(); // don't want to hold for long periods in case in a locking active share thread mode
if ( !m_rbTakeFreezeFrame[ view.m_eStereoEye ] && m_flFreezeFrameUntil > gpGlobals->curtime )
{
CRefPtr<CFreezeFrameView> pFreezeFrameView = new CFreezeFrameView( this );
pFreezeFrameView->Setup( view );
AddViewToScene( pFreezeFrameView );
g_bRenderingView = true;
s_bCanAccessCurrentView = true;
}
else
{
g_flFreezeFlash = 0.0f;
g_pClientShadowMgr->AdvanceFrame();
#ifdef USE_MONITORS
if ( cl_drawmonitors.GetBool() &&
( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 ) &&
( ( whatToDraw & RENDERVIEW_SUPPRESSMONITORRENDERING ) == 0 ) )
{
CViewSetup viewMiddle = GetView( STEREO_EYE_MONO );
DrawMonitors( viewMiddle );
}
#endif
g_bRenderingView = true;
// Must be first
render->SceneBegin();
pRenderContext.GetFrom( materials );
pRenderContext->TurnOnToneMapping();
pRenderContext.SafeRelease();
// clear happens here probably
SetupMain3DView( view, nClearFlags );
bool bDrew3dSkybox = false;
SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE;
// if the 3d skybox world is drawn, then don't draw the normal skybox
CSkyboxView *pSkyView = new CSkyboxView( this );
if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible ) ) != false )
{
AddViewToScene( pSkyView );
}
SafeRelease( pSkyView );
// 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, nSkyboxVisible, 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;
engine->DrawPortals();
DisableFog();
// Finish scene
render->SceneEnd();
// Draw lightsources if enabled
render->DrawLights();
RenderPlayerSprites();
// Image-space motion blur
if ( !building_cubemaps.GetBool() && view.m_bDoBloomAndToneMapping ) // We probably should use a different view. variable here
{
if ( ( mat_motion_blur_enabled.GetInt() ) && ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 ) )
{
pRenderContext.GetFrom( materials );
{
PIXEVENT( pRenderContext, "DoImageSpaceMotionBlur" );
DoImageSpaceMotionBlur( view, view.x, view.y, view.width, view.height );
}
pRenderContext.SafeRelease();
}
}
GetClientModeNormal()->DoPostScreenSpaceEffects( &view );
// Now actually draw the viewmodel
DrawViewModels( view, whatToDraw & RENDERVIEW_DRAWVIEWMODEL );
DrawUnderwaterOverlay();
PixelVisibility_EndScene();
// Draw fade over entire screen if needed
byte color[4];
bool blend;
vieweffects->GetFadeParams( &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();
if ( !building_cubemaps.GetBool() && view.m_bDoBloomAndToneMapping )
{
pRenderContext.GetFrom( materials );
{
PIXEVENT( pRenderContext, "DoEnginePostProcessing" );
bool bFlashlightIsOn = false;
C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer();
if ( pLocal )
{
bFlashlightIsOn = pLocal->IsEffectActive( EF_DIMLIGHT );
}
DoEnginePostProcessing( view.x, view.y, view.width, view.height, bFlashlightIsOn );
}
pRenderContext.SafeRelease();
}
// And here are the screen-space effects
if ( IsPC() )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "GrabPreColorCorrectedFrame" );
// Grab the pre-color corrected frame for editing purposes
engine->GrabPreColorCorrectedFrame( view.x, view.y, view.width, view.height );
}
PerformScreenSpaceEffects( 0, 0, view.width, view.height );
if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER )
{
pRenderContext.GetFrom( materials );
pRenderContext->SetToneMappingScaleLinear(Vector(1,1,1));
pRenderContext.SafeRelease();
}
CleanupMain3DView( view );
if ( m_rbTakeFreezeFrame[ view.m_eStereoEye ] )
{
Rect_t rect;
rect.x = view.x;
rect.y = view.y;
rect.width = view.width;
rect.height = view.height;
pRenderContext = materials->GetRenderContext();
if ( IsX360() )
{
// 360 doesn't create the Fullscreen texture
pRenderContext->CopyRenderTargetToTextureEx( GetFullFrameFrameBufferTexture( 1 ), 0, &rect, &rect );
}
else
{
pRenderContext->CopyRenderTargetToTextureEx( GetFullscreenTexture(), 0, &rect, &rect );
}
pRenderContext.SafeRelease();
m_rbTakeFreezeFrame[ view.m_eStereoEye ] = false;
}
pRenderContext = materials->GetRenderContext();
pRenderContext->SetRenderTarget( saveRenderTarget );
pRenderContext.SafeRelease();
// Draw the overlay
if ( m_bDrawOverlay )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "DrawOverlay" );
// 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;
RenderView( tempView, m_OverlayClearFlags, m_OverlayDrawFlags );
m_CurrentView = currentView;
}
}
if ( mat_viewportupscale.GetBool() && mat_viewportscale.GetFloat() < 1.0f )
{
CMatRenderContextPtr pRenderContext( materials );
ITexture *pFullFrameFB1 = materials->FindTexture( "_rt_FullFrameFB1", TEXTURE_GROUP_RENDER_TARGET );
IMaterial *pCopyMaterial = materials->FindMaterial( "dev/upscale", TEXTURE_GROUP_OTHER );
pCopyMaterial->IncrementReferenceCount();
Rect_t DownscaleRect, UpscaleRect;
DownscaleRect.x = view.x;
DownscaleRect.y = view.y;
DownscaleRect.width = view.width;
DownscaleRect.height = view.height;
UpscaleRect.x = view.m_nUnscaledX;
UpscaleRect.y = view.m_nUnscaledY;
UpscaleRect.width = view.m_nUnscaledWidth;
UpscaleRect.height = view.m_nUnscaledHeight;
pRenderContext->CopyRenderTargetToTextureEx( pFullFrameFB1, 0, &DownscaleRect, &DownscaleRect );
pRenderContext->DrawScreenSpaceRectangle( pCopyMaterial, UpscaleRect.x, UpscaleRect.y, UpscaleRect.width, UpscaleRect.height,
DownscaleRect.x, DownscaleRect.y, DownscaleRect.x+DownscaleRect.width-1, DownscaleRect.y+DownscaleRect.height-1,
pFullFrameFB1->GetActualWidth(), pFullFrameFB1->GetActualHeight() );
pCopyMaterial->DecrementReferenceCount();
}
// if we're in VR mode we might need to override the render target
if( UseVR() )
{
saveRenderTarget = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(view.m_eStereoEye - 1), ISourceVirtualReality::RT_Color );
}
// Draw the 2D graphics
render->Push2DView( view, 0, saveRenderTarget, GetFrustum() );
Render2DEffectsPreHUD( view );
if ( whatToDraw & RENDERVIEW_DRAWHUD )
{
VPROF_BUDGET( "VGui_DrawHud", VPROF_BUDGETGROUP_OTHER_VGUI );
int viewWidth = view.m_nUnscaledWidth;
int viewHeight = view.m_nUnscaledHeight;
int viewActualWidth = view.m_nUnscaledWidth;
int viewActualHeight = view.m_nUnscaledHeight;
int viewX = view.m_nUnscaledX;
int viewY = view.m_nUnscaledY;
int viewFramebufferX = 0;
int viewFramebufferY = 0;
int viewFramebufferWidth = viewWidth;
int viewFramebufferHeight = viewHeight;
bool bClear = false;
bool bPaintMainMenu = false;
ITexture *pTexture = NULL;
if( UseVR() )
{
if( g_ClientVirtualReality.ShouldRenderHUDInWorld() )
{
pTexture = materials->FindTexture( "_rt_gui", NULL, false );
if( pTexture )
{
bPaintMainMenu = true;
bClear = true;
viewX = 0;
viewY = 0;
viewActualWidth = pTexture->GetActualWidth();
viewActualHeight = pTexture->GetActualHeight();
vgui::surface()->GetScreenSize( viewWidth, viewHeight );
viewFramebufferX = 0;
if( view.m_eStereoEye == STEREO_EYE_RIGHT && !saveRenderTarget )
viewFramebufferX = viewFramebufferWidth;
viewFramebufferY = 0;
}
}
else
{
viewFramebufferX = view.m_eStereoEye == STEREO_EYE_RIGHT ? viewWidth : 0;
viewFramebufferY = 0;
}
}
// Get the render context out of materials to avoid some debug stuff.
// WARNING THIS REQUIRES THE .SafeRelease below or it'll never release the ref
pRenderContext = materials->GetRenderContext();
// clear depth in the backbuffer before we push the render target
if( bClear )
{
pRenderContext->ClearBuffers( false, true, true );
}
// constrain where VGUI can render to the view
pRenderContext->PushRenderTargetAndViewport( pTexture, NULL, viewX, viewY, viewActualWidth, viewActualHeight );
// If drawing off-screen, force alpha for that pass
if (pTexture)
{
pRenderContext->OverrideAlphaWriteEnable( true, true );
}
// let vgui know where to render stuff for the forced-to-framebuffer panels
if( UseVR() )
{
vgui::surface()->SetFullscreenViewportAndRenderTarget( viewFramebufferX, viewFramebufferY, viewFramebufferWidth, viewFramebufferHeight, saveRenderTarget );
}
// clear the render target if we need to
if( bClear )
{
pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
pRenderContext->ClearBuffers( true, false );
}
pRenderContext.SafeRelease();
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "VGui_DrawHud", __FUNCTION__ );
// 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()->SetSize( root, viewWidth, viewHeight );
}
// Same for client .dll tools
root = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS );
if ( root != 0 )
{
vgui::ipanel()->SetSize( root, viewWidth, viewHeight );
}
// 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 );
// maybe paint the main menu and cursor too if we're in stereo hud mode
if( bPaintMainMenu )
render->VGui_Paint( PAINT_UIPANELS | PAINT_CURSOR );
AllowCurrentViewAccess( false );
VGui_PostRender();
g_pClientMode->PostRenderVGui();
pRenderContext = materials->GetRenderContext();
if (pTexture)
{
pRenderContext->OverrideAlphaWriteEnable( false, true );
}
pRenderContext->PopRenderTargetAndViewport();
if ( UseVR() )
{
// figure out if we really want to draw the HUD based on freeze cam
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
bool bInFreezeCam = ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM );
// draw the HUD after the view model so its "I'm closer" depth queues work right.
if( !bInFreezeCam && g_ClientVirtualReality.ShouldRenderHUDInWorld() )
{
// Now we've rendered the HUD to its texture, actually get it on the screen.
// Since we're drawing it as a 3D object, we need correctly set up frustum, etc.
int ClearFlags = 0;
SetupMain3DView( view, ClearFlags );
// TODO - a bit of a shonky test - basically trying to catch the main menu, the briefing screen, the loadout screen, etc.
bool bTranslucent = !g_pMatSystemSurface->IsCursorVisible();
g_ClientVirtualReality.RenderHUDQuad( g_pClientMode->ShouldBlackoutAroundHUD(), bTranslucent );
CleanupMain3DView( view );
}
}
pRenderContext->Flush();
pRenderContext.SafeRelease();
}
CDebugViewRender::Draw2DDebuggingInfo( view );
Render2DEffectsPostHUD( view );
g_bRenderingView = false;
// We can no longer use the 'current view' stuff set up in ViewDrawScene
s_bCanAccessCurrentView = false;
if ( IsPC() )
{
CDebugViewRender::GenerateOverdrawForTesting();
}
render->PopView( GetFrustum() );
g_WorldListCache.Flush();
}
//-----------------------------------------------------------------------------
// 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 )
{
}
//-----------------------------------------------------------------------------
//
// 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, WaterRenderInfo_t &info )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// 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;
}
#ifdef _X360
bool bForceExpensive = false;
#else
bool bForceExpensive = r_waterforceexpensive.GetBool();
#endif
bool bForceReflectEntities = r_waterforcereflectentities.GetBool();
#ifdef PORTAL
switch( g_pPortalRender->ShouldForceCheaperWaterLevel() )
{
case 0: //force cheap water
info.m_bCheapWater = true;
return;
case 1: //downgrade level to "simple reflection"
bForceExpensive = false;
case 2: //downgrade level to "reflect world"
bForceReflectEntities = false;
default:
break;
};
#endif
// 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;
// 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->GetIntValueFast() != 0 );
if ( bForceCheap )
{
bForceExpensive = false;
}
}
if ( !bForceCheap && pForceExpensiveVar && pForceExpensiveVar->IsDefined() )
{
bForceExpensive = bForceExpensive || ( pForceExpensiveVar->GetIntValueFast() != 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;
#ifdef _X360
if( !r_WaterDrawReflection.GetBool() )
#else
if( !bForceExpensive || !r_WaterDrawReflection.GetBool() )
#endif
{
bLocalReflection = false;
}
else
{
IMaterialVar *pReflectTextureVar = pWaterMaterial->FindVar( "$reflecttexture", NULL, false );
bLocalReflection = pReflectTextureVar && (pReflectTextureVar->GetType() == MATERIAL_VAR_TYPE_TEXTURE);
}
// Brian says 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
// Gary says: I'm reverting this change so that water LOD works on dx9 for ep2.
// Check if the water is out of the cheap water LOD range; if so, use cheap water
#ifdef _X360
if ( !bForceExpensive && ( bForceCheap || ( fogVolumeInfo.m_flDistanceToWater >= m_flCheapWaterEndDistance ) ) )
{
return;
}
#else
if ( ( (fogVolumeInfo.m_flDistanceToWater >= m_flCheapWaterEndDistance) && !bLocalReflection ) || bForceCheap )
return;
#endif
// 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;
}
}
info.m_bReflect = bLocalReflection;
if ( info.m_bReflect )
{
if( bForceReflectEntities )
{
info.m_bReflectEntities = true;
}
else
{
IMaterialVar *pReflectEntitiesVar = pWaterMaterial->FindVar( "$reflectentities", NULL, false );
info.m_bReflectEntities = pReflectEntitiesVar && (pReflectEntitiesVar->GetIntValueFast() != 0);
}
}
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::DrawWorldAndEntities( bool bDrawSkybox, const CViewSetup &viewIn, int nClearFlags, ViewCustomVisibility_t *pCustomVisibility )
{
MDLCACHE_CRITICAL_SECTION();
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
VisibleFogVolumeInfo_t fogVolumeInfo;
#ifdef PORTAL //in portal, we can't use the fog volume for the camera since it's almost never in the same fog volume as what's in front of the portal
if( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
render->GetVisibleFogVolume( viewIn.origin, &fogVolumeInfo );
}
else
{
render->GetVisibleFogVolume( g_pPortalRender->GetExitPortalFogOrigin(), &fogVolumeInfo );
}
#else
render->GetVisibleFogVolume( viewIn.origin, &fogVolumeInfo );
#endif
WaterRenderInfo_t info;
DetermineWaterRenderInfo( fogVolumeInfo, info );
if ( info.m_bCheapWater )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "bCheapWater" );
cplane_t glassReflectionPlane;
if ( IsReflectiveGlassInView( viewIn, glassReflectionPlane ) )
{
CRefPtr<CReflectiveGlassView> pGlassReflectionView = new CReflectiveGlassView( this );
pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane );
AddViewToScene( pGlassReflectionView );
CRefPtr<CRefractiveGlassView> pGlassRefractionView = new CRefractiveGlassView( this );
pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane );
AddViewToScene( pGlassRefractionView );
}
CRefPtr<CSimpleWorldView> pNoWaterView = new CSimpleWorldView( this );
pNoWaterView->Setup( viewIn, nClearFlags, bDrawSkybox, fogVolumeInfo, info, pCustomVisibility );
AddViewToScene( pNoWaterView );
return;
}
Assert( !pCustomVisibility );
// 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 )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "CAboveWaterView" );
CRefPtr<CAboveWaterView> pAboveWaterView = new CAboveWaterView( this );
pAboveWaterView->Setup( viewIn, bDrawSkybox, fogVolumeInfo, info );
AddViewToScene( pAboveWaterView );
}
else
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "CUnderWaterView" );
CRefPtr<CUnderWaterView> pUnderWaterView = new CUnderWaterView( this );
pUnderWaterView->Setup( viewIn, bDrawSkybox, fogVolumeInfo, info );
AddViewToScene( pUnderWaterView );
}
}
//-----------------------------------------------------------------------------
// 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)
{
CMatRenderContextPtr pRenderContext( materials );
SavedLinearLightMapScale=pRenderContext->GetToneMappingScaleLinear();
Vector t25=SavedLinearLightMapScale;
t25*=0.25;
pRenderContext->SetToneMappingScaleLinear(t25);
}
}
//-----------------------------------------------------------------------------
// Returns true if the view plane intersects the water
//-----------------------------------------------------------------------------
bool DoesViewPlaneIntersectWater( float waterZ, int leafWaterDataID )
{
if ( leafWaterDataID == -1 )
return false;
#ifdef PORTAL //when rendering portal views point/plane intersections just don't cut it.
if( g_pPortalRender->GetViewRecursionLevel() != 0 )
return g_pPortalRender->DoesExitPortalViewIntersectWaterPlane( waterZ, leafWaterDataID );
#endif
CMatRenderContextPtr pRenderContext( materials );
VMatrix viewMatrix, projectionMatrix, viewProjectionMatrix, inverseViewProjectionMatrix;
pRenderContext->GetMatrix( MATERIAL_VIEW, &viewMatrix );
pRenderContext->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 );
}
#ifdef PORTAL
//-----------------------------------------------------------------------------
// Purpose: Draw the scene during another draw scene call. We must draw our portals
// after opaques but before translucents, so this ViewDrawScene resets the view
// and doesn't flag the rendering as ended when it ends.
// Input : bDrawSkybox - do we draw the skybox
// &view - the camera view to render from
// nClearFlags - how to clear the buffer
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene_PortalStencil( const CViewSetup &viewIn, ViewCustomVisibility_t *pCustomVisibility )
{
VPROF( "CViewRender::ViewDrawScene_PortalStencil" );
CViewSetup view( viewIn );
// Record old view stats
Vector vecOldOrigin = CurrentViewOrigin();
QAngle vecOldAngles = CurrentViewAngles();
int iCurrentViewID = g_CurrentViewID;
int iRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
Assert( iRecursionLevel > 0 );
//get references to reflection textures
CTextureReference pPrimaryWaterReflectionTexture;
pPrimaryWaterReflectionTexture.Init( GetWaterReflectionTexture() );
CTextureReference pReplacementWaterReflectionTexture;
pReplacementWaterReflectionTexture.Init( portalrendertargets->GetWaterReflectionTextureForStencilDepth( iRecursionLevel ) );
//get references to refraction textures
CTextureReference pPrimaryWaterRefractionTexture;
pPrimaryWaterRefractionTexture.Init( GetWaterRefractionTexture() );
CTextureReference pReplacementWaterRefractionTexture;
pReplacementWaterRefractionTexture.Init( portalrendertargets->GetWaterRefractionTextureForStencilDepth( iRecursionLevel ) );
//swap texture contents for the primary render targets with those we set aside for this recursion level
if( pReplacementWaterReflectionTexture != NULL )
pPrimaryWaterReflectionTexture->SwapContents( pReplacementWaterReflectionTexture );
if( pReplacementWaterRefractionTexture != NULL )
pPrimaryWaterRefractionTexture->SwapContents( pReplacementWaterRefractionTexture );
bool bDrew3dSkybox = false;
SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE;
int iClearFlags = 0;
Draw3dSkyboxworld_Portal( view, iClearFlags, bDrew3dSkybox, nSkyboxVisible );
bool drawSkybox = r_skybox.GetBool();
if ( bDrew3dSkybox || ( nSkyboxVisible == SKYBOX_NOT_VISIBLE ) )
{
drawSkybox = false;
}
//generate unique view ID's for each stencil view
view_id_t iNewViewID = (view_id_t)g_pPortalRender->GetCurrentViewId();
SetupCurrentView( view.origin, view.angles, (view_id_t)iNewViewID );
// update vis data
unsigned int visFlags;
SetupVis( view, visFlags, pCustomVisibility );
VisibleFogVolumeInfo_t fogInfo;
if( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
render->GetVisibleFogVolume( view.origin, &fogInfo );
}
else
{
render->GetVisibleFogVolume( g_pPortalRender->GetExitPortalFogOrigin(), &fogInfo );
}
WaterRenderInfo_t waterInfo;
DetermineWaterRenderInfo( fogInfo, waterInfo );
if ( waterInfo.m_bCheapWater )
{
cplane_t glassReflectionPlane;
if ( IsReflectiveGlassInView( viewIn, glassReflectionPlane ) )
{
CRefPtr<CReflectiveGlassView> pGlassReflectionView = new CReflectiveGlassView( this );
pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL, drawSkybox, fogInfo, waterInfo, glassReflectionPlane );
AddViewToScene( pGlassReflectionView );
CRefPtr<CRefractiveGlassView> pGlassRefractionView = new CRefractiveGlassView( this );
pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL, drawSkybox, fogInfo, waterInfo, glassReflectionPlane );
AddViewToScene( pGlassRefractionView );
}
CSimpleWorldView *pClientView = new CSimpleWorldView( this );
pClientView->Setup( view, VIEW_CLEAR_OBEY_STENCIL, drawSkybox, fogInfo, waterInfo, pCustomVisibility );
AddViewToScene( pClientView );
SafeRelease( pClientView );
}
else
{
// We can see water of some sort
if ( !fogInfo.m_bEyeInFogVolume )
{
CRefPtr<CAboveWaterView> pAboveWaterView = new CAboveWaterView( this );
pAboveWaterView->Setup( viewIn, drawSkybox, fogInfo, waterInfo );
AddViewToScene( pAboveWaterView );
}
else
{
CRefPtr<CUnderWaterView> pUnderWaterView = new CUnderWaterView( this );
pUnderWaterView->Setup( viewIn, drawSkybox, fogInfo, waterInfo );
AddViewToScene( pUnderWaterView );
}
}
// Disable fog for the rest of the stuff
DisableFog();
CGlowOverlay::DrawOverlays( view.m_bCacheFullSceneState );
// Draw rain..
DrawPrecipitation();
//prerender version only
// issue the pixel visibility tests
PixelVisibility_EndCurrentView();
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Debugging info goes over the top
CDebugViewRender::Draw3DDebuggingInfo( view );
// Return to the previous view
SetupCurrentView( vecOldOrigin, vecOldAngles, (view_id_t)iCurrentViewID );
g_CurrentViewID = iCurrentViewID; //just in case the cast to view_id_t screwed up the id #
//swap back the water render targets
if( pReplacementWaterReflectionTexture != NULL )
pPrimaryWaterReflectionTexture->SwapContents( pReplacementWaterReflectionTexture );
if( pReplacementWaterRefractionTexture != NULL )
pPrimaryWaterRefractionTexture->SwapContents( pReplacementWaterRefractionTexture );
}
void CViewRender::Draw3dSkyboxworld_Portal( const CViewSetup &view, int &nClearFlags, bool &bDrew3dSkybox, SkyboxVisibility_t &nSkyboxVisible, ITexture *pRenderTarget )
{
CRefPtr<CPortalSkyboxView> pSkyView = new CPortalSkyboxView( this );
if ( ( bDrew3dSkybox = pSkyView->Setup( view, &nClearFlags, &nSkyboxVisible, pRenderTarget ) ) == true )
{
AddViewToScene( pSkyView );
}
}
#endif //PORTAL
//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &view -
// &introData -
//-----------------------------------------------------------------------------
void CViewRender::ViewDrawScene_Intro( const CViewSetup &view, int nClearFlags, const IntroData_t &introData )
{
VPROF( "CViewRender::ViewDrawScene" );
CMatRenderContextPtr pRenderContext( materials );
// this allows the refract texture to be updated once per *scene* on 360
// (e.g. once for a monitor scene and once for the main scene)
g_viewscene_refractUpdateFrame = gpGlobals->framecount - 1;
// -----------------------------------------------------------------------
// 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.
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
// -----------------------------------------------------------------------
// Draw the primary scene and copy it to the first framebuffer texture
// -----------------------------------------------------------------------
unsigned int visFlags;
// NOTE: We only increment this once since time doesn't move forward.
ParticleMgr()->IncrementFrameCode();
if( introData.m_bDrawPrimary )
{
CViewSetup playerView( view );
playerView.origin = introData.m_vecCameraView;
playerView.angles = introData.m_vecCameraViewAngles;
if ( introData.m_playerViewFOV )
{
playerView.fov = ScaleFOVByWidthRatio( introData.m_playerViewFOV, engine->GetScreenAspectRatio() / ( 4.0f / 3.0f ) );
}
g_pClientShadowMgr->PreRender();
// Shadowed flashlights supported on ps_2_b and up...
if ( r_flashlightdepthtexture.GetBool() )
{
g_pClientShadowMgr->ComputeShadowDepthTextures( playerView );
}
SetupCurrentView( playerView.origin, playerView.angles, VIEW_INTRO_PLAYER );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view, clear frame/z buffer if necessary
SetupVis( playerView, visFlags );
render->Push3DView( playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH, NULL, GetFrustum() );
DrawWorldAndEntities( true /* drawSkybox */, playerView, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH );
render->PopView( GetFrustum() );
// Free shadow depth textures for use in future view
if ( r_flashlightdepthtexture.GetBool() )
{
g_pClientShadowMgr->UnlockAllShadowDepthTextures();
}
}
else
{
pRenderContext->ClearBuffers( true, true );
}
Rect_t actualRect;
UpdateScreenEffectTexture( 0, view.x, view.y, view.width, view.height, false, &actualRect );
g_pClientShadowMgr->PreRender();
// Shadowed flashlights supported on ps_2_b and up...
if ( r_flashlightdepthtexture.GetBool() )
{
g_pClientShadowMgr->ComputeShadowDepthTextures( view );
}
// -----------------------------------------------------------------------
// Draw the secondary scene and copy it to the second framebuffer texture
// -----------------------------------------------------------------------
SetupCurrentView( view.origin, view.angles, VIEW_INTRO_CAMERA );
// Invoke pre-render methods
IGameSystem::PreRenderAllSystems();
// Start view, clear frame/z buffer if necessary
SetupVis( view, visFlags );
// Clear alpha to 255 so that masking with the vortigaunts (0) works properly.
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
DrawWorldAndEntities( true /* drawSkybox */, view, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH );
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 );
pRenderContext->ClearBuffers( true, true );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->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 );
pRenderContext->DrawScreenSpaceRectangle( pOverlayMaterial, 0, 0, view.width, view.height,
actualRect.x, actualRect.y, actualRect.x+actualRect.width-1, actualRect.y+actualRect.height-1,
pTexture->GetActualWidth(), pTexture->GetActualHeight() );
}
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->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( 0, 0, view.width, view.height );
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
// Debugging info goes over the top
CDebugViewRender::Draw3DDebuggingInfo( view );
// Let the particle manager simulate things that haven't been simulated.
ParticleMgr()->PostRender();
FinishCurrentView();
// Free shadow depth textures for use in future view
if ( r_flashlightdepthtexture.GetBool() )
{
g_pClientShadowMgr->UnlockAllShadowDepthTextures();
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets up scene and renders camera view
// Input : cameraNum -
// &cameraView
// *localPlayer -
// x -
// y -
// width -
// height -
// highend -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CViewRender::DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_PointCamera *pCameraEnt,
const CViewSetup &cameraView, C_BasePlayer *localPlayer, int x, int y, int width, int height )
{
#ifdef USE_MONITORS
VPROF_INCREMENT_COUNTER( "cameras rendered", 1 );
// Setup fog state for the camera.
fogparams_t oldFogParams;
float flOldZFar = 0.0f;
bool fogEnabled = pCameraEnt->IsFogEnabled();
CViewSetup monitorView = cameraView;
fogparams_t *pFogParams = NULL;
if ( fogEnabled )
{
if ( !localPlayer )
return false;
pFogParams = localPlayer->GetFogParams();
// Save old fog data.
oldFogParams = *pFogParams;
flOldZFar = cameraView.zFar;
pFogParams->enable = true;
pFogParams->start = pCameraEnt->GetFogStart();
pFogParams->end = pCameraEnt->GetFogEnd();
pFogParams->farz = pCameraEnt->GetFogEnd();
pFogParams->maxdensity = pCameraEnt->GetFogMaxDensity();
unsigned char r, g, b;
pCameraEnt->GetFogColor( r, g, b );
pFogParams->colorPrimary.SetR( r );
pFogParams->colorPrimary.SetG( g );
pFogParams->colorPrimary.SetB( b );
monitorView.zFar = pCameraEnt->GetFogEnd();
}
monitorView.width = width;
monitorView.height = height;
monitorView.x = x;
monitorView.y = y;
monitorView.origin = pCameraEnt->GetAbsOrigin();
monitorView.angles = pCameraEnt->GetAbsAngles();
monitorView.fov = pCameraEnt->GetFOV();
monitorView.m_bOrtho = false;
monitorView.m_flAspectRatio = pCameraEnt->UseScreenAspectRatio() ? 0.0f : 1.0f;
monitorView.m_bViewToProjectionOverride = false;
// @MULTICORE (toml 8/11/2006): this should be a renderer....
Frustum frustum;
render->Push3DView( monitorView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pRenderTarget, (VPlane *)frustum );
ViewDrawScene( false, SKYBOX_2DSKYBOX_VISIBLE, monitorView, 0, VIEW_MONITOR );
render->PopView( frustum );
// Reset the world fog parameters.
if ( fogEnabled )
{
if ( pFogParams )
{
*pFogParams = oldFogParams;
}
monitorView.zFar = flOldZFar;
}
#endif // USE_MONITORS
return true;
}
void CViewRender::DrawMonitors( const CViewSetup &cameraView )
{
#ifdef PORTAL
g_pPortalRender->DrawPortalsToTextures( this, cameraView );
#endif
#ifdef USE_MONITORS
// Early out if no cameras
C_PointCamera *pCameraEnt = GetPointCameraList();
if ( !pCameraEnt )
return;
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
#ifdef _DEBUG
g_bRenderingCameraView = true;
#endif
// FIXME: this should check for the ability to do a render target maybe instead.
// FIXME: shouldn't have to truck through all of the visible entities for this!!!!
ITexture *pCameraTarget = GetCameraTexture();
int width = pCameraTarget->GetActualWidth();
int height = pCameraTarget->GetActualHeight();
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
int cameraNum;
for ( cameraNum = 0; pCameraEnt != NULL; pCameraEnt = pCameraEnt->m_pNext )
{
if ( !pCameraEnt->IsActive() || pCameraEnt->IsDormant() )
continue;
if ( !DrawOneMonitor( pCameraTarget, cameraNum, pCameraEnt, cameraView, player, 0, 0, width, height ) )
continue;
++cameraNum;
}
if ( IsX360() && cameraNum > 0 )
{
// resolve render target to system memory texture
// resolving *after* all monitors drawn to ensure a single blit using fastest resolve path
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushRenderTargetAndViewport( pCameraTarget );
pRenderContext->CopyRenderTargetToTextureEx( pCameraTarget, 0, NULL, NULL );
pRenderContext->PopRenderTargetAndViewport();
}
#ifdef _DEBUG
g_bRenderingCameraView = false;
#endif
#endif // USE_MONITORS
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
ClientWorldListInfo_t *ClientWorldListInfo_t::AllocPooled( const ClientWorldListInfo_t &exemplar )
{
size_t nBytes = AlignValue( ( exemplar.m_LeafCount * ((sizeof(LeafIndex_t) * 2) + sizeof(LeafFogVolume_t)) ), 4096 );
ClientWorldListInfo_t *pResult = gm_Pool.GetObject();
byte *pMemory = (byte *)pResult->m_pLeafList;
if ( pMemory )
{
// Previously allocated, add a reference. Otherwise comes out of GetObject as a new object with a refcount of 1
pResult->AddRef();
}
if ( !pMemory || _msize( pMemory ) < nBytes )
{
pMemory = (byte *)realloc( pMemory, nBytes );
}
pResult->m_pLeafList = (LeafIndex_t*)pMemory;
pResult->m_pLeafFogVolume = (LeafFogVolume_t*)( pMemory + exemplar.m_LeafCount * sizeof(LeafIndex_t) );
pResult->m_pActualLeafIndex = (LeafIndex_t*)( (byte *)( pResult->m_pLeafFogVolume ) + exemplar.m_LeafCount * sizeof(LeafFogVolume_t) );
pResult->m_bPooledAlloc = true;
return pResult;
}
bool ClientWorldListInfo_t::OnFinalRelease()
{
if ( m_bPooledAlloc )
{
Assert( m_pLeafList );
gm_Pool.PutObject( this );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CBase3dView::CBase3dView( CViewRender *pMainView ) :
m_pMainView( pMainView ),
m_Frustum( pMainView->m_Frustum )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEnt -
// Output : int
//-----------------------------------------------------------------------------
VPlane* CBase3dView::GetFrustum()
{
// The frustum is only valid while in a RenderView call.
// @MULTICORE (toml 8/11/2006): reimplement this when ready -- Assert(g_bRenderingView || g_bRenderingCameraView || g_bRenderingScreenshot);
return m_Frustum;
}
CObjectPool<ClientWorldListInfo_t> ClientWorldListInfo_t::gm_Pool;
//-----------------------------------------------------------------------------
// Base class for 3d views
//-----------------------------------------------------------------------------
CRendering3dView::CRendering3dView(CViewRender *pMainView) :
CBase3dView( pMainView ),
m_pWorldRenderList( NULL ),
m_pRenderablesList( NULL ),
m_pWorldListInfo( NULL ),
m_pCustomVisibility( NULL ),
m_DrawFlags( 0 ),
m_ClearFlags( 0 )
{
}
//-----------------------------------------------------------------------------
// Sort entities in a back-to-front ordering
//-----------------------------------------------------------------------------
void CRendering3dView::Setup( const CViewSetup &setup )
{
// @MULTICORE (toml 8/15/2006): don't reset if parameters don't require it. For now, just reset
memcpy( static_cast<CViewSetup *>(this), &setup, sizeof( setup ) );
ReleaseLists();
m_pRenderablesList = new CClientRenderablesList;
m_pCustomVisibility = NULL;
}
//-----------------------------------------------------------------------------
// Sort entities in a back-to-front ordering
//-----------------------------------------------------------------------------
void CRendering3dView::ReleaseLists()
{
SafeRelease( m_pWorldRenderList );
SafeRelease( m_pRenderablesList );
SafeRelease( m_pWorldListInfo );
m_pCustomVisibility = NULL;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CRendering3dView::SetupRenderablesList( int viewID )
{
VPROF( "CViewRender::SetupRenderablesList" );
// Clear the list.
int i;
for( i=0; i < RENDER_GROUP_COUNT; i++ )
{
m_pRenderablesList->m_RenderGroupCounts[i] = 0;
}
// Now collate the entities in the leaves.
if( m_pMainView->ShouldDrawEntities() )
{
// Precache information used commonly in CollateRenderables
SetupRenderInfo_t setupInfo;
setupInfo.m_pWorldListInfo = m_pWorldListInfo;
setupInfo.m_nRenderFrame = m_pMainView->BuildRenderablesListsNumber(); // only one incremented?
setupInfo.m_nDetailBuildFrame = m_pMainView->BuildWorldListsNumber(); //
setupInfo.m_pRenderList = m_pRenderablesList;
setupInfo.m_bDrawDetailObjects = g_pClientMode->ShouldDrawDetailObjects() && r_DrawDetailProps.GetInt();
setupInfo.m_bDrawTranslucentObjects = (viewID != VIEW_SHADOW_DEPTH_TEXTURE);
setupInfo.m_vecRenderOrigin = origin;
setupInfo.m_vecRenderForward = CurrentViewForward();
float fMaxDist = cl_maxrenderable_dist.GetFloat();
// Shadowing light typically has a smaller farz than cl_maxrenderable_dist
setupInfo.m_flRenderDistSq = (viewID == VIEW_SHADOW_DEPTH_TEXTURE) ? MIN(zFar, fMaxDist) : fMaxDist;
setupInfo.m_flRenderDistSq *= setupInfo.m_flRenderDistSq;
ClientLeafSystem()->BuildRenderablesList( setupInfo );
}
}
//-----------------------------------------------------------------------------
// Purpose: Builds lists of things to render in the world, called once per view
//-----------------------------------------------------------------------------
void CRendering3dView::UpdateRenderablesOpacity()
{
// 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( origin, flFactor );
// Build a list of detail props to render
DetailObjectSystem()->BuildDetailObjectRenderLists( origin );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
// Kinda awkward...three optional parameters at the end...
void CRendering3dView::BuildWorldRenderLists( bool bDrawEntities, int iForceViewLeaf /* = -1 */,
bool bUseCacheIfEnabled /* = true */, bool bShadowDepth /* = false */, float *pReflectionWaterHeight /*= NULL*/ )
{
VPROF_BUDGET( "BuildWorldRenderLists", VPROF_BUDGETGROUP_WORLD_RENDERING );
// @MULTICORE (toml 8/18/2006): to address....
extern void UpdateClientRenderableInPVSStatus();
UpdateClientRenderableInPVSStatus();
Assert( !m_pWorldRenderList && !m_pWorldListInfo);
m_pMainView->IncWorldListsNumber();
// Override vis data if specified this render, otherwise use default behavior with NULL
VisOverrideData_t* pVisData = ( m_pCustomVisibility && m_pCustomVisibility->m_VisData.m_fDistToAreaPortalTolerance != FLT_MAX ) ? &m_pCustomVisibility->m_VisData : NULL;
bool bUseCache = ( bUseCacheIfEnabled && r_worldlistcache.GetBool() );
if ( !bUseCache || pVisData || !g_WorldListCache.Find( *this, &m_pWorldRenderList, &m_pWorldListInfo ) )
{
// @MULTICORE (toml 8/18/2006): when make parallel, will have to change caching to be atomic, where follow ons receive a pointer to a list that is not yet built
m_pWorldRenderList = render->CreateWorldList();
m_pWorldListInfo = new ClientWorldListInfo_t;
render->BuildWorldLists( m_pWorldRenderList, m_pWorldListInfo,
( m_pCustomVisibility ) ? m_pCustomVisibility->m_iForceViewLeaf : iForceViewLeaf,
pVisData, bShadowDepth, pReflectionWaterHeight );
if ( bUseCache && !pVisData )
{
g_WorldListCache.Add( *this, m_pWorldRenderList, m_pWorldListInfo );
}
}
if ( bDrawEntities )
{
UpdateRenderablesOpacity();
}
}
//-----------------------------------------------------------------------------
// Purpose: Computes the actual world list info based on the render flags
//-----------------------------------------------------------------------------
void CRendering3dView::PruneWorldListInfo()
{
// Drawing everything? Just return the world list info as-is
int nWaterDrawFlags = m_DrawFlags & (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER);
if ( nWaterDrawFlags == (DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER) )
{
return;
}
ClientWorldListInfo_t *pNewInfo;
// Only allocate memory if actually will draw something
if ( m_pWorldListInfo->m_LeafCount > 0 && nWaterDrawFlags )
{
pNewInfo = ClientWorldListInfo_t::AllocPooled( *m_pWorldListInfo );
}
else
{
pNewInfo = new ClientWorldListInfo_t;
}
pNewInfo->m_ViewFogVolume = m_pWorldListInfo->m_ViewFogVolume;
pNewInfo->m_LeafCount = 0;
// Not drawing anything? Then don't bother with renderable lists
if ( nWaterDrawFlags != 0 )
{
// Create a sub-list based on the actual leaves being rendered
bool bRenderingUnderwater = (nWaterDrawFlags & DF_RENDER_UNDERWATER) != 0;
for ( int i = 0; i < m_pWorldListInfo->m_LeafCount; ++i )
{
bool bLeafIsUnderwater = ( m_pWorldListInfo->m_pLeafFogVolume[i] != -1 );
if ( bRenderingUnderwater == bLeafIsUnderwater )
{
pNewInfo->m_pLeafList[ pNewInfo->m_LeafCount ] = m_pWorldListInfo->m_pLeafList[ i ];
pNewInfo->m_pLeafFogVolume[ pNewInfo->m_LeafCount ] = m_pWorldListInfo->m_pLeafFogVolume[ i ];
pNewInfo->m_pActualLeafIndex[ pNewInfo->m_LeafCount ] = i;
++pNewInfo->m_LeafCount;
}
}
}
m_pWorldListInfo->Release();
m_pWorldListInfo = pNewInfo;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static inline void UpdateBrushModelLightmap( IClientRenderable *pEnt )
{
model_t *pModel = ( model_t * )pEnt->GetModel();
render->UpdateBrushModelLightmap( pModel, pEnt );
}
void CRendering3dView::BuildRenderableRenderLists( int viewID )
{
MDLCACHE_CRITICAL_SECTION();
if ( viewID != VIEW_SHADOW_DEPTH_TEXTURE )
{
render->BeginUpdateLightmaps();
}
m_pMainView->IncRenderablesListsNumber();
ClientWorldListInfo_t& info = *m_pWorldListInfo;
// For better sorting, find out the leaf *nearest* to the camera
// and render translucent objects as if they are in that leaf.
if( m_pMainView->ShouldDrawEntities() && ( viewID != VIEW_SHADOW_DEPTH_TEXTURE ) )
{
ClientLeafSystem()->ComputeTranslucentRenderLeaf(
info.m_LeafCount, info.m_pLeafList, info.m_pLeafFogVolume, m_pMainView->BuildRenderablesListsNumber(), viewID );
}
SetupRenderablesList( viewID );
if ( viewID == VIEW_MAIN )
{
StudioStats_FindClosestEntity( m_pRenderablesList );
}
if ( viewID != VIEW_SHADOW_DEPTH_TEXTURE )
{
// update lightmap on brush models if necessary
CClientRenderablesList::CEntry *pEntities = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH];
int nOpaque = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH];
int i;
for( i=0; i < nOpaque; ++i )
{
Assert(pEntities[i].m_TwoPass==0);
UpdateBrushModelLightmap( pEntities[i].m_pRenderable );
}
// update lightmap on brush models if necessary
pEntities = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int nTranslucent = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY];
for( i=0; i < nTranslucent; ++i )
{
const model_t *pModel = pEntities[i].m_pRenderable->GetModel();
if( pModel )
{
int nModelType = modelinfo->GetModelType( pModel );
if( nModelType == mod_brush )
{
UpdateBrushModelLightmap( pEntities[i].m_pRenderable );
}
}
}
render->EndUpdateLightmaps();
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CRendering3dView::DrawWorld( float waterZAdjust )
{
VPROF_INCREMENT_COUNTER( "RenderWorld", 1 );
VPROF_BUDGET( "DrawWorld", VPROF_BUDGETGROUP_WORLD_RENDERING );
if( !r_drawopaqueworld.GetBool() )
{
return;
}
unsigned long engineFlags = BuildEngineDrawWorldListFlags( m_DrawFlags );
render->DrawWorldLists( m_pWorldRenderList, engineFlags, waterZAdjust );
}
CMaterialReference g_material_WriteZ; //init'ed on by CViewRender::Init()
//-----------------------------------------------------------------------------
// Fakes per-entity clip planes on cards that don't support user clip planes.
// Achieves the effect by drawing an invisible box that writes to the depth buffer
// around the clipped area. It's not perfect, but better than nothing.
//-----------------------------------------------------------------------------
static void DrawClippedDepthBox( IClientRenderable *pEnt, float *pClipPlane )
{
//#define DEBUG_DRAWCLIPPEDDEPTHBOX //uncomment to draw the depth box as a colorful box
static const int iQuads[6][5] = { { 0, 4, 6, 2, 0 }, //always an extra copy of first index at end to make some algorithms simpler
{ 3, 7, 5, 1, 3 },
{ 1, 5, 4, 0, 1 },
{ 2, 6, 7, 3, 2 },
{ 0, 2, 3, 1, 0 },
{ 5, 7, 6, 4, 5 } };
static const int iLines[12][2] = { { 0, 1 },
{ 0, 2 },
{ 0, 4 },
{ 1, 3 },
{ 1, 5 },
{ 2, 3 },
{ 2, 6 },
{ 3, 7 },
{ 4, 6 },
{ 4, 5 },
{ 5, 7 },
{ 6, 7 } };
#ifdef DEBUG_DRAWCLIPPEDDEPTHBOX
static const float fColors[6][3] = { { 1.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f },
{ 1.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 1.0f },
{ 1.0f, 1.0f, 0.0f } };
#endif
Vector vNormal = *(Vector *)pClipPlane;
float fPlaneDist = pClipPlane[3];
Vector vMins, vMaxs;
pEnt->GetRenderBounds( vMins, vMaxs );
Vector vOrigin = pEnt->GetRenderOrigin();
QAngle qAngles = pEnt->GetRenderAngles();
Vector vForward, vUp, vRight;
AngleVectors( qAngles, &vForward, &vRight, &vUp );
Vector vPoints[8];
vPoints[0] = vOrigin + (vForward * vMins.x) + (vRight * vMins.y) + (vUp * vMins.z);
vPoints[1] = vOrigin + (vForward * vMaxs.x) + (vRight * vMins.y) + (vUp * vMins.z);
vPoints[2] = vOrigin + (vForward * vMins.x) + (vRight * vMaxs.y) + (vUp * vMins.z);
vPoints[3] = vOrigin + (vForward * vMaxs.x) + (vRight * vMaxs.y) + (vUp * vMins.z);
vPoints[4] = vOrigin + (vForward * vMins.x) + (vRight * vMins.y) + (vUp * vMaxs.z);
vPoints[5] = vOrigin + (vForward * vMaxs.x) + (vRight * vMins.y) + (vUp * vMaxs.z);
vPoints[6] = vOrigin + (vForward * vMins.x) + (vRight * vMaxs.y) + (vUp * vMaxs.z);
vPoints[7] = vOrigin + (vForward * vMaxs.x) + (vRight * vMaxs.y) + (vUp * vMaxs.z);
int iClipped[8];
float fDists[8];
for( int i = 0; i != 8; ++i )
{
fDists[i] = vPoints[i].Dot( vNormal ) - fPlaneDist;
iClipped[i] = (fDists[i] > 0.0f) ? 1 : 0;
}
Vector vSplitPoints[8][8]; //obviously there are only 12 lines, not 64 lines or 64 split points, but the indexing is way easier like this
int iLineStates[8][8]; //0 = unclipped, 2 = wholly clipped, 3 = first point clipped, 4 = second point clipped
//categorize lines and generate split points where needed
for( int i = 0; i != 12; ++i )
{
const int *pPoints = iLines[i];
int iLineState = (iClipped[pPoints[0]] + iClipped[pPoints[1]]);
if( iLineState != 1 ) //either both points are clipped, or neither are clipped
{
iLineStates[pPoints[0]][pPoints[1]] =
iLineStates[pPoints[1]][pPoints[0]] =
iLineState;
}
else
{
//one point is clipped, the other is not
if( iClipped[pPoints[0]] == 1 )
{
//first point was clipped, index 1 has the negative distance
float fInvTotalDist = 1.0f / (fDists[pPoints[0]] - fDists[pPoints[1]]);
vSplitPoints[pPoints[0]][pPoints[1]] =
vSplitPoints[pPoints[1]][pPoints[0]] =
(vPoints[pPoints[1]] * (fDists[pPoints[0]] * fInvTotalDist)) - (vPoints[pPoints[0]] * (fDists[pPoints[1]] * fInvTotalDist));
Assert( fabs( vNormal.Dot( vSplitPoints[pPoints[0]][pPoints[1]] ) - fPlaneDist ) < 0.01f );
iLineStates[pPoints[0]][pPoints[1]] = 3;
iLineStates[pPoints[1]][pPoints[0]] = 4;
}
else
{
//second point was clipped, index 0 has the negative distance
float fInvTotalDist = 1.0f / (fDists[pPoints[1]] - fDists[pPoints[0]]);
vSplitPoints[pPoints[0]][pPoints[1]] =
vSplitPoints[pPoints[1]][pPoints[0]] =
(vPoints[pPoints[0]] * (fDists[pPoints[1]] * fInvTotalDist)) - (vPoints[pPoints[1]] * (fDists[pPoints[0]] * fInvTotalDist));
Assert( fabs( vNormal.Dot( vSplitPoints[pPoints[0]][pPoints[1]] ) - fPlaneDist ) < 0.01f );
iLineStates[pPoints[0]][pPoints[1]] = 4;
iLineStates[pPoints[1]][pPoints[0]] = 3;
}
}
}
CMatRenderContextPtr pRenderContext( materials );
#ifdef DEBUG_DRAWCLIPPEDDEPTHBOX
pRenderContext->Bind( materials->FindMaterial( "debug/debugvertexcolor", TEXTURE_GROUP_OTHER ), NULL );
#else
pRenderContext->Bind( g_material_WriteZ, NULL );
#endif
CMeshBuilder meshBuilder;
IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 18 ); //6 sides, possible one cut per side. Any side is capable of having 3 tri's. Lots of padding for things that aren't possible
//going to draw as a collection of triangles, arranged as a triangle fan on each side
for( int i = 0; i != 6; ++i )
{
const int *pPoints = iQuads[i];
//can't start the fan on a wholly clipped line, so seek to one that isn't
int j = 0;
do
{
if( iLineStates[pPoints[j]][pPoints[j+1]] != 2 ) //at least part of this line will be drawn
break;
++j;
} while( j != 3 );
if( j == 3 ) //not enough lines to even form a triangle
continue;
float *pStartPoint = 0;
float *pTriangleFanPoints[4]; //at most, one of our fans will have 5 points total, with the first point being stored separately as pStartPoint
int iTriangleFanPointCount = 1; //the switch below creates the first for sure
//figure out how to start the fan
switch( iLineStates[pPoints[j]][pPoints[j+1]] )
{
case 0: //uncut
pStartPoint = &vPoints[pPoints[j]].x;
pTriangleFanPoints[0] = &vPoints[pPoints[j+1]].x;
break;
case 4: //second index was clipped
pStartPoint = &vPoints[pPoints[j]].x;
pTriangleFanPoints[0] = &vSplitPoints[pPoints[j]][pPoints[j+1]].x;
break;
case 3: //first index was clipped
pStartPoint = &vSplitPoints[pPoints[j]][pPoints[j+1]].x;
pTriangleFanPoints[0] = &vPoints[pPoints[j + 1]].x;
break;
default:
Assert( false );
break;
};
for( ++j; j != 3; ++j ) //add end points for the rest of the indices, we're assembling a triangle fan
{
switch( iLineStates[pPoints[j]][pPoints[j+1]] )
{
case 0: //uncut line, normal endpoint
pTriangleFanPoints[iTriangleFanPointCount] = &vPoints[pPoints[j+1]].x;
++iTriangleFanPointCount;
break;
case 2: //wholly cut line, no endpoint
break;
case 3: //first point is clipped, normal endpoint
//special case, adds start and end point
pTriangleFanPoints[iTriangleFanPointCount] = &vSplitPoints[pPoints[j]][pPoints[j+1]].x;
++iTriangleFanPointCount;
pTriangleFanPoints[iTriangleFanPointCount] = &vPoints[pPoints[j+1]].x;
++iTriangleFanPointCount;
break;
case 4: //second point is clipped
pTriangleFanPoints[iTriangleFanPointCount] = &vSplitPoints[pPoints[j]][pPoints[j+1]].x;
++iTriangleFanPointCount;
break;
default:
Assert( false );
break;
};
}
//special case endpoints, half-clipped lines have a connecting line between them and the next line (first line in this case)
switch( iLineStates[pPoints[j]][pPoints[j+1]] )
{
case 3:
case 4:
pTriangleFanPoints[iTriangleFanPointCount] = &vSplitPoints[pPoints[j]][pPoints[j+1]].x;
++iTriangleFanPointCount;
break;
};
Assert( iTriangleFanPointCount <= 4 );
//add the fan to the mesh
int iLoopStop = iTriangleFanPointCount - 1;
for( int k = 0; k != iLoopStop; ++k )
{
meshBuilder.Position3fv( pStartPoint );
#ifdef DEBUG_DRAWCLIPPEDDEPTHBOX
float fHalfColors[3] = { fColors[i][0] * 0.5f, fColors[i][1] * 0.5f, fColors[i][2] * 0.5f };
meshBuilder.Color3fv( fHalfColors );
#endif
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( pTriangleFanPoints[k] );
#ifdef DEBUG_DRAWCLIPPEDDEPTHBOX
meshBuilder.Color3fv( fColors[i] );
#endif
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( pTriangleFanPoints[k+1] );
#ifdef DEBUG_DRAWCLIPPEDDEPTHBOX
meshBuilder.Color3fv( fColors[i] );
#endif
meshBuilder.AdvanceVertex();
}
}
meshBuilder.End();
pMesh->Draw();
pRenderContext->Flush( false );
}
//-----------------------------------------------------------------------------
// Draws all opaque renderables in leaves that were rendered
//-----------------------------------------------------------------------------
static inline void DrawOpaqueRenderable( IClientRenderable *pEnt, bool bTwoPass, ERenderDepthMode DepthMode, int nDefaultFlags = 0 )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
float color[3];
pEnt->GetColorModulation( color );
render->SetColorModulation( color );
int flags = nDefaultFlags | STUDIO_RENDER;
if ( bTwoPass )
{
flags |= STUDIO_TWOPASS;
}
if ( DepthMode == DEPTH_MODE_SHADOW )
{
flags |= STUDIO_SHADOWDEPTHTEXTURE;
}
else if ( DepthMode == DEPTH_MODE_SSA0 )
{
flags |= STUDIO_SSAODEPTHTEXTURE;
}
float *pRenderClipPlane = NULL;
if( r_entityclips.GetBool() )
pRenderClipPlane = pEnt->GetRenderClipPlane();
if( pRenderClipPlane )
{
CMatRenderContextPtr pRenderContext( materials );
if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though
pRenderContext->PushCustomClipPlane( pRenderClipPlane );
else
DrawClippedDepthBox( pEnt, pRenderClipPlane );
Assert( view->GetCurrentlyDrawingEntity() == NULL );
view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() );
pEnt->DrawModel( flags );
view->SetCurrentlyDrawingEntity( NULL );
if( pRenderClipPlane && !materials->UsingFastClipping() )
pRenderContext->PopCustomClipPlane();
}
else
{
Assert( view->GetCurrentlyDrawingEntity() == NULL );
view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() );
pEnt->DrawModel( flags );
view->SetCurrentlyDrawingEntity( NULL );
}
}
//-------------------------------------
ConVar r_drawopaquestaticpropslast( "r_drawopaquestaticpropslast", "0", 0, "Whether opaque static props are rendered after non-npcs" );
#define DEBUG_BUCKETS 0
#if DEBUG_BUCKETS
ConVar r_drawopaque_old( "r_drawopaque_old", "0", 0, "Whether old unbucketed technique is used" );
ConVar r_drawopaquesbucket( "r_drawopaquesbucket", "0", FCVAR_CHEAT, "Draw only specific bucket: positive - props, negative - ents" );
ConVar r_drawopaquesbucket_stats( "r_drawopaquesbucket_stats", "0", FCVAR_CHEAT, "Draw distribution of props/ents in the buckets" );
#endif
static void SetupBonesOnBaseAnimating( C_BaseAnimating *&pBaseAnimating )
{
pBaseAnimating->SetupBones( NULL, -1, -1, gpGlobals->curtime );
}
static void DrawOpaqueRenderables_DrawBrushModels( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, ERenderDepthMode DepthMode )
{
for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity )
{
Assert( !itEntity->m_TwoPass );
DrawOpaqueRenderable( itEntity->m_pRenderable, false, DepthMode );
}
}
static void DrawOpaqueRenderables_DrawStaticProps( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, ERenderDepthMode DepthMode )
{
if ( pEntitiesEnd == pEntitiesBegin )
return;
float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
render->SetColorModulation( one );
render->SetBlend( 1.0f );
const int MAX_STATICS_PER_BATCH = 512;
IClientRenderable *pStatics[ MAX_STATICS_PER_BATCH ];
int numScheduled = 0, numAvailable = MAX_STATICS_PER_BATCH;
for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity )
{
if ( itEntity->m_pRenderable )
NULL;
else
continue;
if ( g_pStudioStatsEntity != NULL && g_CurrentViewID == VIEW_MAIN && itEntity->m_pRenderable == g_pStudioStatsEntity )
{
DrawOpaqueRenderable( itEntity->m_pRenderable, false, DepthMode, STUDIO_GENERATE_STATS );
continue;
}
pStatics[ numScheduled ++ ] = itEntity->m_pRenderable;
if ( -- numAvailable > 0 )
continue; // place a hint for compiler to predict more common case in the loop
staticpropmgr->DrawStaticProps( pStatics, numScheduled, DepthMode, vcollide_wireframe.GetBool() );
numScheduled = 0;
numAvailable = MAX_STATICS_PER_BATCH;
}
if ( numScheduled )
staticpropmgr->DrawStaticProps( pStatics, numScheduled, DepthMode, vcollide_wireframe.GetBool() );
}
static void DrawOpaqueRenderables_Range( CClientRenderablesList::CEntry *pEntitiesBegin, CClientRenderablesList::CEntry *pEntitiesEnd, ERenderDepthMode DepthMode )
{
for( CClientRenderablesList::CEntry *itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity )
{
if ( itEntity->m_pRenderable )
DrawOpaqueRenderable( itEntity->m_pRenderable, ( itEntity->m_TwoPass != 0 ), DepthMode );
}
}
void CRendering3dView::DrawOpaqueRenderables( ERenderDepthMode DepthMode )
{
VPROF_BUDGET("CViewRender::DrawOpaqueRenderables", "DrawOpaqueRenderables" );
if( !r_drawopaquerenderables.GetBool() )
return;
if( !m_pMainView->ShouldDrawEntities() )
return;
render->SetBlend( 1 );
//
// Prepare to iterate over all leaves that were visible, and draw opaque things in them.
//
RopeManager()->ResetRenderCache();
g_pParticleSystemMgr->ResetRenderCache();
bool const bDrawopaquestaticpropslast = r_drawopaquestaticpropslast.GetBool();
//
// First do the brush models
//
{
CClientRenderablesList::CEntry *pEntitiesBegin, *pEntitiesEnd;
pEntitiesBegin = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_OPAQUE_BRUSH];
pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_OPAQUE_BRUSH];
DrawOpaqueRenderables_DrawBrushModels( pEntitiesBegin, pEntitiesEnd, DepthMode );
}
#if DEBUG_BUCKETS
{
con_nprint_s nxPrn = { 0 };
nxPrn.index = 16;
nxPrn.time_to_live = -1;
nxPrn.color[0] = 0.9f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 0.9f;
nxPrn.fixed_width_font = true;
engine->Con_NXPrintf( &nxPrn, "Draw Opaque Technique : NEW" );
if ( r_drawopaque_old.GetBool() )
{
engine->Con_NXPrintf( &nxPrn, "Draw Opaque Technique : OLD" );
// now the static props
{
for ( int bucket = RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS - 1; bucket -- > 0; )
{
CClientRenderablesList::CEntry
* const pEntitiesBegin = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ],
* const pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ];
DrawOpaqueRenderables_DrawStaticProps( pEntitiesBegin, pEntitiesEnd, bShadowDepth );
}
}
// now the other opaque entities
for ( int bucket = RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS - 1; bucket -- > 0; )
{
CClientRenderablesList::CEntry
* const pEntitiesBegin = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ],
* const pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ];
DrawOpaqueRenderables_Range( pEntitiesBegin, pEntitiesEnd, bShadowDepth );
}
//
// Ropes and particles
//
RopeManager()->DrawRenderCache( bShadowDepth );
g_pParticleSystemMgr->DrawRenderCache( bShadowDepth );
return;
}
}
#endif
//
// Sort everything that's not a static prop
//
int numOpaqueEnts = 0;
for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket )
numOpaqueEnts += m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ];
CUtlVector< C_BaseAnimating * > arrBoneSetupNpcsLast( (C_BaseAnimating **)_alloca( numOpaqueEnts * sizeof( C_BaseAnimating * ) ), numOpaqueEnts, numOpaqueEnts );
CUtlVector< CClientRenderablesList::CEntry > arrRenderEntsNpcsFirst( (CClientRenderablesList::CEntry *)_alloca( numOpaqueEnts * sizeof( CClientRenderablesList::CEntry ) ), numOpaqueEnts, numOpaqueEnts );
int numNpcs = 0, numNonNpcsAnimating = 0;
for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket )
{
for( CClientRenderablesList::CEntry
* const pEntitiesBegin = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ],
* const pEntitiesEnd = pEntitiesBegin + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ],
*itEntity = pEntitiesBegin; itEntity < pEntitiesEnd; ++ itEntity )
{
C_BaseEntity *pEntity = itEntity->m_pRenderable ? itEntity->m_pRenderable->GetIClientUnknown()->GetBaseEntity() : NULL;
if ( pEntity )
{
if ( pEntity->IsNPC() )
{
C_BaseAnimating *pba = assert_cast<C_BaseAnimating *>( pEntity );
arrRenderEntsNpcsFirst[ numNpcs ++ ] = *itEntity;
arrBoneSetupNpcsLast[ numOpaqueEnts - numNpcs ] = pba;
itEntity->m_pRenderable = NULL; // We will render NPCs separately
itEntity->m_RenderHandle = NULL;
continue;
}
else if ( pEntity->GetBaseAnimating() )
{
C_BaseAnimating *pba = assert_cast<C_BaseAnimating *>( pEntity );
arrBoneSetupNpcsLast[ numNonNpcsAnimating ++ ] = pba;
// fall through
}
}
}
}
if ( 0 && r_threaded_renderables.GetBool() )
{
ParallelProcess( "BoneSetupNpcsLast", arrBoneSetupNpcsLast.Base() + numOpaqueEnts - numNpcs, numNpcs, &SetupBonesOnBaseAnimating );
ParallelProcess( "BoneSetupNpcsLast NonNPCs", arrBoneSetupNpcsLast.Base(), numNonNpcsAnimating, &SetupBonesOnBaseAnimating );
}
//
// Draw static props + opaque entities from the biggest bucket to the smallest
//
{
CClientRenderablesList::CEntry * pEnts[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2];
CClientRenderablesList::CEntry * pProps[ RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS ][2];
for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket )
{
pEnts[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ];
pEnts[bucket][1] = pEnts[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_ENTITY_HUGE + 2 * bucket ];
pProps[bucket][0] = m_pRenderablesList->m_RenderGroups[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ];
pProps[bucket][1] = pProps[bucket][0] + m_pRenderablesList->m_RenderGroupCounts[ RENDER_GROUP_OPAQUE_STATIC_HUGE + 2 * bucket ];
// Render sequence debugging
#if DEBUG_BUCKETS
if ( r_drawopaquesbucket_stats.GetBool() )
{
con_nprint_s nxPrn = { 0 };
nxPrn.index = 20 + bucket * 3;
nxPrn.time_to_live = -1;
nxPrn.color[0] = 0.9f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 0.9f;
nxPrn.fixed_width_font = true;
if ( bDrawopaquestaticpropslast )
engine->Con_NXPrintf( &nxPrn, "[ %2d ] Ents : %3d", bucket + 1, pEnts[bucket][1] - pEnts[bucket][0] ),
++ nxPrn.index,
engine->Con_NXPrintf( &nxPrn, "[ %2d ] Props: %3d", bucket + 1, pProps[bucket][1] - pProps[bucket][0] );
else
engine->Con_NXPrintf( &nxPrn, "[ %2d ] Props: %3d", bucket + 1, pProps[bucket][1] - pProps[bucket][0] ),
++ nxPrn.index,
engine->Con_NXPrintf( &nxPrn, "[ %2d ] Ents : %3d", bucket + 1, pEnts[bucket][1] - pEnts[bucket][0] );
}
#endif
}
#if DEBUG_BUCKETS
if ( int iBucket = r_drawopaquesbucket.GetInt() )
{
if ( iBucket > 0 && iBucket <= RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS )
{
DrawOpaqueRenderables_Range( pEnts[iBucket - 1][0], pEnts[iBucket - 1][1], bShadowDepth );
}
if ( iBucket < 0 && iBucket >= -RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS )
{
DrawOpaqueRenderables_DrawStaticProps( pProps[- 1 - iBucket][0], pProps[- 1 - iBucket][1], bShadowDepth );
}
}
else
#endif
for ( int bucket = 0; bucket < RENDER_GROUP_CFG_NUM_OPAQUE_ENT_BUCKETS; ++ bucket )
{
if ( bDrawopaquestaticpropslast )
{
DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], DepthMode );
DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], DepthMode );
}
else
{
DrawOpaqueRenderables_Range( pEnts[bucket][0], pEnts[bucket][1], DepthMode );
DrawOpaqueRenderables_DrawStaticProps( pProps[bucket][0], pProps[bucket][1], DepthMode );
}
}
}
//
// Draw NPCs now
//
DrawOpaqueRenderables_Range( arrRenderEntsNpcsFirst.Base(), arrRenderEntsNpcsFirst.Base() + numNpcs, DepthMode );
//
// Ropes and particles
//
RopeManager()->DrawRenderCache( DepthMode );
g_pParticleSystemMgr->DrawRenderCache( DepthMode );
}
//-----------------------------------------------------------------------------
// Renders all translucent world + detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CRendering3dView::DrawTranslucentWorldInLeaves( bool bShadowDepth )
{
VPROF_BUDGET( "CViewRender::DrawTranslucentWorldInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING );
const ClientWorldListInfo_t& info = *m_pWorldListInfo;
for( int iCurLeafIndex = info.m_LeafCount - 1; iCurLeafIndex >= 0; iCurLeafIndex-- )
{
int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex;
Assert( nActualLeafIndex != INVALID_LEAF_INDEX );
if ( render->LeafContainsTranslucentSurfaces( m_pWorldRenderList, nActualLeafIndex, m_DrawFlags ) )
{
// Now draw the surfaces in this leaf
render->DrawTranslucentSurfaces( m_pWorldRenderList, nActualLeafIndex, m_DrawFlags, bShadowDepth );
}
}
}
//-----------------------------------------------------------------------------
// Renders all translucent world + detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CRendering3dView::DrawTranslucentWorldAndDetailPropsInLeaves( int iCurLeafIndex, int iFinalLeafIndex, int nEngineDrawFlags, int &nDetailLeafCount, LeafIndex_t* pDetailLeafList, bool bShadowDepth )
{
VPROF_BUDGET( "CViewRender::DrawTranslucentWorldAndDetailPropsInLeaves", VPROF_BUDGETGROUP_WORLD_RENDERING );
const ClientWorldListInfo_t& info = *m_pWorldListInfo;
for( ; iCurLeafIndex >= iFinalLeafIndex; iCurLeafIndex-- )
{
int nActualLeafIndex = info.m_pActualLeafIndex ? info.m_pActualLeafIndex[ iCurLeafIndex ] : iCurLeafIndex;
Assert( nActualLeafIndex != INVALID_LEAF_INDEX );
if ( render->LeafContainsTranslucentSurfaces( m_pWorldRenderList, nActualLeafIndex, nEngineDrawFlags ) )
{
// First draw any queued-up detail props from previously visited leaves
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), CurrentViewRight(), CurrentViewUp(), nDetailLeafCount, pDetailLeafList );
nDetailLeafCount = 0;
// Now draw the surfaces in this leaf
render->DrawTranslucentSurfaces( m_pWorldRenderList, nActualLeafIndex, nEngineDrawFlags, bShadowDepth );
}
// Queue up detail props that existed in this leaf
if ( ClientLeafSystem()->ShouldDrawDetailObjectsInLeaf( info.m_pLeafList[iCurLeafIndex], m_pMainView->BuildWorldListsNumber() ) )
{
pDetailLeafList[nDetailLeafCount] = info.m_pLeafList[iCurLeafIndex];
++nDetailLeafCount;
}
}
}
//-----------------------------------------------------------------------------
// Renders all translucent entities in the render list
//-----------------------------------------------------------------------------
static inline void DrawTranslucentRenderable( IClientRenderable *pEnt, bool twoPass, bool bShadowDepth, bool bIgnoreDepth )
{
// Determine blending amount and tell engine
float blend = (float)( pEnt->GetFxBlend() / 255.0f );
// Totally gone
if ( blend <= 0.0f )
return;
if ( pEnt->IgnoresZBuffer() != bIgnoreDepth )
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 ( bShadowDepth )
flags |= STUDIO_SHADOWDEPTHTEXTURE;
float *pRenderClipPlane = NULL;
if( r_entityclips.GetBool() )
pRenderClipPlane = pEnt->GetRenderClipPlane();
if( pRenderClipPlane )
{
CMatRenderContextPtr pRenderContext( materials );
if( !materials->UsingFastClipping() ) //do NOT change the fast clip plane mid-scene, depth problems result. Regular user clip planes are fine though
pRenderContext->PushCustomClipPlane( pRenderClipPlane );
else
DrawClippedDepthBox( pEnt, pRenderClipPlane );
Assert( view->GetCurrentlyDrawingEntity() == NULL );
view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() );
pEnt->DrawModel( flags );
view->SetCurrentlyDrawingEntity( NULL );
if( pRenderClipPlane && !materials->UsingFastClipping() )
pRenderContext->PopCustomClipPlane();
}
else
{
Assert( view->GetCurrentlyDrawingEntity() == NULL );
view->SetCurrentlyDrawingEntity( pEnt->GetIClientUnknown()->GetBaseEntity() );
pEnt->DrawModel( flags );
view->SetCurrentlyDrawingEntity( NULL );
}
}
//-----------------------------------------------------------------------------
// Renders all translucent entities in the render list
//-----------------------------------------------------------------------------
void CRendering3dView::DrawTranslucentRenderablesNoWorld( bool bInSkybox )
{
VPROF( "CViewRender::DrawTranslucentRenderablesNoWorld" );
if ( !m_pMainView->ShouldDrawEntities() || !r_drawtranslucentrenderables.GetBool() )
return;
// Draw the particle singletons.
DrawParticleSingletons( bInSkybox );
bool bShadowDepth = (m_DrawFlags & ( DF_SHADOW_DEPTH_MAP | DF_SSAO_DEPTH_PASS ) ) != 0;
CClientRenderablesList::CEntry *pEntities = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int iCurTranslucentEntity = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1;
while( iCurTranslucentEntity >= 0 )
{
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
if ( pRenderable->UsesPowerOfTwoFrameBufferTexture() )
{
UpdateRefractTexture();
}
if ( pRenderable->UsesFullFrameBufferTexture() )
{
UpdateScreenEffectTexture();
}
DrawTranslucentRenderable( pRenderable, pEntities[iCurTranslucentEntity].m_TwoPass != 0, bShadowDepth, false );
--iCurTranslucentEntity;
}
// Reset the blend state.
render->SetBlend( 1 );
}
//-----------------------------------------------------------------------------
// Renders all translucent entities in the render list that ignore the Z buffer
//-----------------------------------------------------------------------------
void CRendering3dView::DrawNoZBufferTranslucentRenderables( void )
{
VPROF( "CViewRender::DrawNoZBufferTranslucentRenderables" );
if ( !m_pMainView->ShouldDrawEntities() || !r_drawtranslucentrenderables.GetBool() )
return;
bool bShadowDepth = (m_DrawFlags & ( DF_SHADOW_DEPTH_MAP | DF_SSAO_DEPTH_PASS ) ) != 0;
CClientRenderablesList::CEntry *pEntities = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int iCurTranslucentEntity = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1;
while( iCurTranslucentEntity >= 0 )
{
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
if ( pRenderable->UsesPowerOfTwoFrameBufferTexture() )
{
UpdateRefractTexture();
}
if ( pRenderable->UsesFullFrameBufferTexture() )
{
UpdateScreenEffectTexture();
}
DrawTranslucentRenderable( pRenderable, pEntities[iCurTranslucentEntity].m_TwoPass != 0, bShadowDepth, true );
--iCurTranslucentEntity;
}
// Reset the blend state.
render->SetBlend( 1 );
}
//-----------------------------------------------------------------------------
// Renders all translucent world, entities, and detail objects in a particular set of leaves
//-----------------------------------------------------------------------------
void CRendering3dView::DrawTranslucentRenderables( bool bInSkybox, bool bShadowDepth )
{
const ClientWorldListInfo_t& info = *m_pWorldListInfo;
#ifdef PORTAL //if we're in the portal mod, we need to make a detour so we can render portal views using stencil areas
if( ShouldDrawPortals() ) //no recursive stencil views during skybox rendering (although we might be drawing a skybox while already in a recursive stencil view)
{
int iDrawFlagsBackup = m_DrawFlags;
if( g_pPortalRender->DrawPortalsUsingStencils( (CViewRender *)m_pMainView ) )// @MULTICORE (toml 8/10/2006): remove this hack cast
{
m_DrawFlags = iDrawFlagsBackup;
//reset visibility
unsigned int iVisFlags = 0;
m_pMainView->SetupVis( *this, iVisFlags, m_pCustomVisibility );
//recreate drawlists (since I can't find an easy way to backup the originals)
{
SafeRelease( m_pWorldRenderList );
SafeRelease( m_pWorldListInfo );
BuildWorldRenderLists( ((m_DrawFlags & DF_DRAW_ENTITITES) != 0), m_pCustomVisibility ? m_pCustomVisibility->m_iForceViewLeaf : -1, false );
AssertMsg( m_DrawFlags & DF_DRAW_ENTITITES, "It shouldn't be possible to get here if this wasn't set, needs special case investigation" );
for( int i = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY]; --i >= 0; )
{
m_pRenderablesList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY][i].m_pRenderable->ComputeFxBlend();
}
}
if( r_depthoverlay.GetBool() )
{
CMatRenderContextPtr pRenderContext( materials );
ITexture *pDepthTex = GetFullFrameDepthTexture();
IMaterial *pMaterial = materials->FindMaterial( "debug/showz", TEXTURE_GROUP_OTHER, true );
pMaterial->IncrementReferenceCount();
IMaterialVar *BaseTextureVar = pMaterial->FindVar( "$basetexture", NULL, false );
IMaterialVar *pDepthInAlpha = NULL;
if( IsPC() )
{
pDepthInAlpha = pMaterial->FindVar( "$ALPHADEPTH", NULL, false );
pDepthInAlpha->SetIntValue( 1 );
}
BaseTextureVar->SetTextureValue( pDepthTex );
pRenderContext->OverrideDepthEnable( true, false ); //don't write to depth, or else we'll never see translucents
pRenderContext->DrawScreenSpaceQuad( pMaterial );
pRenderContext->OverrideDepthEnable( false, true );
pMaterial->DecrementReferenceCount();
}
}
else
{
//done recursing in, time to go back out and do translucents
CMatRenderContextPtr pRenderContext( materials );
UpdateFullScreenDepthTexture();
}
}
#else
{
//opaques generally write depth, and translucents generally don't.
//So immediately after opaques are done is the best time to snap off the depth buffer to a texture.
switch ( g_CurrentViewID )
{
case VIEW_MAIN:
#ifdef _X360
case VIEW_INTRO_CAMERA:
case VIEW_INTRO_PLAYER:
#endif
UpdateFullScreenDepthTexture();
break;
default:
materials->GetRenderContext()->SetFullScreenDepthTextureValidityFlag( false );
break;
}
}
#endif
if ( !r_drawtranslucentworld.GetBool() )
{
DrawTranslucentRenderablesNoWorld( bInSkybox );
return;
}
VPROF_BUDGET( "CViewRender::DrawTranslucentRenderables", "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 nEngineDrawFlags = BuildEngineDrawWorldListFlags( m_DrawFlags & ~DF_DRAWSKYBOX );
DetailObjectSystem()->BeginTranslucentDetailRendering();
if ( m_pMainView->ShouldDrawEntities() && r_drawtranslucentrenderables.GetBool() )
{
MDLCACHE_CRITICAL_SECTION();
// Draw the particle singletons.
DrawParticleSingletons( bInSkybox );
CClientRenderablesList::CEntry *pEntities = m_pRenderablesList->m_RenderGroups[RENDER_GROUP_TRANSLUCENT_ENTITY];
int iCurTranslucentEntity = m_pRenderablesList->m_RenderGroupCounts[RENDER_GROUP_TRANSLUCENT_ENTITY] - 1;
bool bRenderingWaterRenderTargets = m_DrawFlags & ( 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, nEngineDrawFlags, nDetailLeafCount, pDetailLeafList, bShadowDepth );
// 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_pMainView->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(), CurrentViewRight(), CurrentViewUp(), nDetailLeafCount, pDetailLeafList );
// Draw translucent renderables in the leaf interspersed with detail props
for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity )
{
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(), CurrentViewRight(), CurrentViewUp(), nLeaf, &vecRenderOrigin );
bool bUsesPowerOfTwoFB = pRenderable->UsesPowerOfTwoFrameBufferTexture();
bool bUsesFullFB = pRenderable->UsesFullFrameBufferTexture();
if ( ( bUsesPowerOfTwoFB || bUsesFullFB )&& !bShadowDepth )
{
if( bRenderingWaterRenderTargets )
{
continue;
}
CMatRenderContextPtr pRenderContext( materials );
ITexture *rt = pRenderContext->GetRenderTarget();
if ( rt && bUsesFullFB )
{
UpdateScreenEffectTexture( 0, 0, 0, rt->GetActualWidth(), rt->GetActualHeight(), true );
}
else if ( bUsesPowerOfTwoFB )
{
UpdateRefractTexture();
}
pRenderContext.SafeRelease();
}
// Then draw the translucent renderable
DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0), bShadowDepth, false );
}
// Draw all remaining props in this leaf
DetailObjectSystem()->RenderTranslucentDetailObjectsInLeaf( CurrentViewOrigin(), CurrentViewForward(), CurrentViewRight(), CurrentViewUp(), 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(), CurrentViewRight(), CurrentViewUp(), nDetailLeafCount, pDetailLeafList );
for( ;pEntities[iCurTranslucentEntity].m_iWorldListInfoLeaf == iThisLeaf && iCurTranslucentEntity >= 0; --iCurTranslucentEntity )
{
IClientRenderable *pRenderable = pEntities[iCurTranslucentEntity].m_pRenderable;
bool bUsesPowerOfTwoFB = pRenderable->UsesPowerOfTwoFrameBufferTexture();
bool bUsesFullFB = pRenderable->UsesFullFrameBufferTexture();
if ( ( bUsesPowerOfTwoFB || bUsesFullFB )&& !bShadowDepth )
{
if( bRenderingWaterRenderTargets )
{
continue;
}
CMatRenderContextPtr pRenderContext( materials );
ITexture *rt = pRenderContext->GetRenderTarget();
if ( rt )
{
if ( bUsesFullFB )
{
UpdateScreenEffectTexture( 0, 0, 0, rt->GetActualWidth(), rt->GetActualHeight(), true );
}
else if ( bUsesPowerOfTwoFB )
{
UpdateRefractTexture(0, 0, rt->GetActualWidth(), rt->GetActualHeight());
}
}
else
{
if ( bUsesPowerOfTwoFB )
{
UpdateRefractTexture();
}
}
pRenderContext.SafeRelease();
}
DrawTranslucentRenderable( pRenderable, (pEntities[iCurTranslucentEntity].m_TwoPass != 0), bShadowDepth, false );
}
}
nDetailLeafCount = 0;
}
}
// Draw the rest of the surfaces in world leaves
DrawTranslucentWorldAndDetailPropsInLeaves( iPrevLeaf, 0, nEngineDrawFlags, nDetailLeafCount, pDetailLeafList, bShadowDepth );
// Draw any queued-up detail props from previously visited leaves
DetailObjectSystem()->RenderTranslucentDetailObjects( CurrentViewOrigin(), CurrentViewForward(), CurrentViewRight(), CurrentViewUp(), nDetailLeafCount, pDetailLeafList );
// Reset the blend state.
render->SetBlend( 1 );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CRendering3dView::EnableWorldFog( void )
{
VPROF("CViewRender::EnableWorldFog");
CMatRenderContextPtr pRenderContext( materials );
fogparams_t *pFogParams = NULL;
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if ( pbp )
{
pFogParams = pbp->GetFogParams();
}
if( GetFogEnable( pFogParams ) )
{
float fogColor[3];
GetFogColor( pFogParams, fogColor );
pRenderContext->FogMode( MATERIAL_FOG_LINEAR );
pRenderContext->FogColor3fv( fogColor );
pRenderContext->FogStart( GetFogStart( pFogParams ) );
pRenderContext->FogEnd( GetFogEnd( pFogParams ) );
pRenderContext->FogMaxDensity( GetFogMaxDensity( pFogParams ) );
}
else
{
pRenderContext->FogMode( MATERIAL_FOG_NONE );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CRendering3dView::GetDrawFlags()
{
return m_DrawFlags;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CRendering3dView::SetFogVolumeState( const VisibleFogVolumeInfo_t &fogInfo, bool bUseHeightFog )
{
render->SetFogVolumeState( fogInfo.m_nVisibleFogVolume, bUseHeightFog );
#ifdef PORTAL
//the idea behind fog shifting is this...
//Normal fog simulates the effect of countless tiny particles between your viewpoint and whatever geometry is rendering.
//But, when rendering to a portal view, there's a large space between the virtual camera and the portal exit surface.
//This space isn't supposed to exist, and therefore has none of the tiny particles that make up fog.
//So, we have to shift fog start/end out to align the distances with the portal exit surface instead of the virtual camera to eliminate fog simulation in the non-space
if( g_pPortalRender->GetViewRecursionLevel() == 0 )
return; //rendering one of the primary views, do nothing
g_pPortalRender->ShiftFogForExitPortalView();
#endif //#ifdef PORTAL
}
//-----------------------------------------------------------------------------
// Standard 3d skybox view
//-----------------------------------------------------------------------------
SkyboxVisibility_t CSkyboxView::ComputeSkyboxVisibility()
{
return engine->IsSkyboxVisibleFromPoint( origin );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CSkyboxView::GetSkyboxFogEnable()
{
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 CSkyboxView::Enable3dSkyboxFog( void )
{
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
if( !pbp )
{
return;
}
CPlayerLocalData *local = &pbp->m_Local;
CMatRenderContextPtr pRenderContext( materials );
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;
}
pRenderContext->FogMode( MATERIAL_FOG_LINEAR );
pRenderContext->FogColor3fv( fogColor );
pRenderContext->FogStart( GetSkyboxFogStart() * scale );
pRenderContext->FogEnd( GetSkyboxFogEnd() * scale );
pRenderContext->FogMaxDensity( GetSkyboxFogMaxDensity() );
}
else
{
pRenderContext->FogMode( MATERIAL_FOG_NONE );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
sky3dparams_t *CSkyboxView::PreRender3dSkyboxWorld( SkyboxVisibility_t nSkyboxVisible )
{
if ( ( nSkyboxVisible != SKYBOX_3DSKYBOX_VISIBLE ) && r_3dsky.GetInt() != 2 )
return NULL;
// render the 3D skybox
if ( !r_3dsky.GetInt() )
return NULL;
C_BasePlayer *pbp = C_BasePlayer::GetLocalPlayer();
// No local player object yet...
if ( !pbp )
return NULL;
CPlayerLocalData* local = &pbp->m_Local;
if ( local->m_skybox3d.area == 255 )
return NULL;
return &local->m_skybox3d;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CSkyboxView::DrawInternal( view_id_t iSkyBoxViewID, bool bInvokePreAndPostRender, ITexture *pRenderTarget, ITexture *pDepthTarget )
{
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[m_pSky3dParams->area>>3] |= 1 << (m_pSky3dParams->area&7);
*areabits = tmpbits;
// if you can get really close to the skybox geometry it's possible that you'll be able to clip into it
// with this near plane. If so, move it in a bit. It's at 2.0 to give us more precision. That means you
// need to keep the eye position at least 2 * scale away from the geometry in the skybox
zNear = 2.0;
zFar = MAX_TRACE_LENGTH;
// scale origin by sky scale
if ( m_pSky3dParams->scale > 0 )
{
float scale = 1.0f / m_pSky3dParams->scale;
VectorScale( origin, scale, origin );
}
Enable3dSkyboxFog();
VectorAdd( origin, m_pSky3dParams->origin, 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, &m_pSky3dParams->origin.Get() );
render->Push3DView( (*this), m_ClearFlags, pRenderTarget, GetFrustum(), pDepthTarget );
// Store off view origin and angles
SetupCurrentView( origin, angles, iSkyBoxViewID );
#if defined( _X360 )
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushVertexShaderGPRAllocation( 32 );
pRenderContext.SafeRelease();
#endif
// Invoke pre-render methods
if ( bInvokePreAndPostRender )
{
IGameSystem::PreRenderAllSystems();
}
render->BeginUpdateLightmaps();
BuildWorldRenderLists( true, true, -1 );
BuildRenderableRenderLists( iSkyBoxViewID );
render->EndUpdateLightmaps();
g_pClientShadowMgr->ComputeShadowTextures( (*this), m_pWorldListInfo->m_LeafCount, m_pWorldListInfo->m_pLeafList );
DrawWorld( 0.0f );
// Iterate over all leaves and render objects in those leaves
DrawOpaqueRenderables( DEPTH_MODE_NORMAL );
// Iterate over all leaves and render objects in those leaves
DrawTranslucentRenderables( true, false );
DrawNoZBufferTranslucentRenderables();
m_pMainView->DisableFog();
CGlowOverlay::UpdateSkyOverlays( zFar, m_bCacheFullSceneState );
PixelVisibility_EndCurrentView();
// restore old area bits
*areabits = savebits;
// Invoke post-render methods
if( bInvokePreAndPostRender )
{
IGameSystem::PostRenderAllSystems();
FinishCurrentView();
}
render->PopView( GetFrustum() );
#if defined( _X360 )
pRenderContext.GetFrom( materials );
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CSkyboxView::Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible )
{
BaseClass::Setup( view );
// The skybox might not be visible from here
*pSkyboxVisible = ComputeSkyboxVisibility();
m_pSky3dParams = PreRender3dSkyboxWorld( *pSkyboxVisible );
if ( !m_pSky3dParams )
{
return false;
}
// At this point, we've cleared everything we need to clear
// The next path will need to clear depth, though.
m_ClearFlags = *pClearFlags;
*pClearFlags &= ~( VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH | VIEW_CLEAR_STENCIL | VIEW_CLEAR_FULL_TARGET );
*pClearFlags |= VIEW_CLEAR_DEPTH; // Need to clear depth after rednering the skybox
m_DrawFlags = DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_RENDER_WATER;
if( r_skybox.GetBool() )
{
m_DrawFlags |= DF_DRAWSKYBOX;
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CSkyboxView::Draw()
{
VPROF_BUDGET( "CViewRender::Draw3dSkyboxworld", "3D Skybox" );
ITexture *pRTColor = NULL;
ITexture *pRTDepth = NULL;
if( m_eStereoEye != STEREO_EYE_MONO )
{
pRTColor = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(m_eStereoEye-1), ISourceVirtualReality::RT_Color );
pRTDepth = g_pSourceVR->GetRenderTarget( (ISourceVirtualReality::VREye)(m_eStereoEye-1), ISourceVirtualReality::RT_Depth );
}
DrawInternal(VIEW_3DSKY, true, pRTColor, pRTDepth );
}
#ifdef PORTAL
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CPortalSkyboxView::Setup( const CViewSetup &view, int *pClearFlags, SkyboxVisibility_t *pSkyboxVisible, ITexture *pRenderTarget )
{
if ( !BaseClass::Setup( view, pClearFlags, pSkyboxVisible ) )
return false;
m_pRenderTarget = pRenderTarget;
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
SkyboxVisibility_t CPortalSkyboxView::ComputeSkyboxVisibility()
{
return g_pPortalRender->IsSkyboxVisibleFromExitPortal();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CPortalSkyboxView::Draw()
{
AssertMsg( (g_pPortalRender->GetViewRecursionLevel() != 0) && g_pPortalRender->IsRenderingPortal(), "This is designed for through-portal views. Use the regular skybox drawing code for primary views" );
VPROF_BUDGET( "CViewRender::Draw3dSkyboxworld_Portal", "3D Skybox (portal view)" );
int iCurrentViewID = g_CurrentViewID;
Frustum FrustumBackup;
memcpy( FrustumBackup, GetFrustum(), sizeof( Frustum ) );
CMatRenderContextPtr pRenderContext( materials );
bool bClippingEnabled = pRenderContext->EnableClipping( false );
//NOTE: doesn't magically map to VIEW_3DSKY at (0,0) like PORTAL_VIEWID maps to VIEW_MAIN
view_id_t iSkyBoxViewID = (view_id_t)g_pPortalRender->GetCurrentSkyboxViewId();
bool bInvokePreAndPostRender = ( g_pPortalRender->ShouldUseStencilsToRenderPortals() == false );
DrawInternal( iSkyBoxViewID, bInvokePreAndPostRender, m_pRenderTarget, NULL );
pRenderContext->EnableClipping( bClippingEnabled );
memcpy( GetFrustum(), FrustumBackup, sizeof( Frustum ) );
render->OverrideViewFrustum( FrustumBackup );
g_CurrentViewID = iCurrentViewID;
}
#endif // PORTAL
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CShadowDepthView::Setup( const CViewSetup &shadowViewIn, ITexture *pRenderTarget, ITexture *pDepthTexture )
{
BaseClass::Setup( shadowViewIn );
m_pRenderTarget = pRenderTarget;
m_pDepthTexture = pDepthTexture;
}
bool DrawingShadowDepthView( void ) //for easy externing
{
return (CurrentViewID() == VIEW_SHADOW_DEPTH_TEXTURE);
}
bool DrawingMainView() //for easy externing
{
return (CurrentViewID() == VIEW_MAIN);
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CShadowDepthView::Draw()
{
VPROF_BUDGET( "CShadowDepthView::Draw", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
// Start view
unsigned int visFlags;
m_pMainView->SetupVis( (*this), visFlags ); // @MULTICORE (toml 8/9/2006): Portal problem, not sending custom vis down
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->ClearColor3ub(0xFF, 0xFF, 0xFF);
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 112 ); //almost all work is done in vertex shaders for depth rendering, max out their threads
#endif
pRenderContext.SafeRelease();
if( IsPC() )
{
render->Push3DView( (*this), VIEW_CLEAR_DEPTH, m_pRenderTarget, GetFrustum(), m_pDepthTexture );
}
else if( IsX360() )
{
//for the 360, the dummy render target has a separate depth buffer which we Resolve() from afterward
render->Push3DView( (*this), VIEW_CLEAR_DEPTH, m_pRenderTarget, GetFrustum() );
}
SetupCurrentView( origin, angles, VIEW_SHADOW_DEPTH_TEXTURE );
MDLCACHE_CRITICAL_SECTION();
{
VPROF_BUDGET( "BuildWorldRenderLists", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
BuildWorldRenderLists( true, -1, true, true ); // @MULTICORE (toml 8/9/2006): Portal problem, not sending custom vis down
}
{
VPROF_BUDGET( "BuildRenderableRenderLists", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
BuildRenderableRenderLists( CurrentViewID() );
}
engine->Sound_ExtraUpdate(); // Make sure sound doesn't stutter
m_DrawFlags = m_pMainView->GetBaseDrawFlags() | DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER | DF_SHADOW_DEPTH_MAP; // Don't draw water surface...
{
VPROF_BUDGET( "DrawWorld", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DrawWorld( 0.0f );
}
// Draw opaque and translucent renderables with appropriate override materials
// OVERRIDE_DEPTH_WRITE is OK with a NULL material pointer
modelrender->ForcedMaterialOverride( NULL, OVERRIDE_DEPTH_WRITE );
{
VPROF_BUDGET( "DrawOpaqueRenderables", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DrawOpaqueRenderables( DEPTH_MODE_SHADOW );
}
modelrender->ForcedMaterialOverride( 0 );
m_DrawFlags = 0;
pRenderContext.GetFrom( materials );
if( IsX360() )
{
//Resolve() the depth texture here. Before the pop so the copy will recognize that the resolutions are the same
pRenderContext->CopyRenderTargetToTextureEx( m_pDepthTexture, -1, NULL, NULL );
}
render->PopView( GetFrustum() );
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CFreezeFrameView::Setup( const CViewSetup &shadowViewIn )
{
BaseClass::Setup( shadowViewIn );
KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
pVMTKeyValues->SetString( "$basetexture", IsX360() ? "_rt_FullFrameFB1" : "_rt_FullScreen" );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$nofog", 1 );
pVMTKeyValues->SetInt( "$ignorez", 1 );
m_pFreezeFrame.Init( "FreezeFrame_FullScreen", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pFreezeFrame->Refresh();
m_TranslucentSingleColor.Init( "debug/debugtranslucentsinglecolor", TEXTURE_GROUP_OTHER );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFreezeFrameView::Draw( void )
{
CMatRenderContextPtr pRenderContext( materials );
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 16 ); //max out pixel shader threads
#endif
// we might only need half of the texture if we're rendering in stereo
int nTexX0 = 0, nTexY0 = 0;
int nTexX1 = width, nTexY1 = height;
int nTexWidth = width, nTexHeight = height;
switch( m_eStereoEye )
{
case STEREO_EYE_LEFT:
nTexX1 = width;
nTexWidth *= 2;
break;
case STEREO_EYE_RIGHT:
nTexX0 = width;
nTexX1 = width*2;
nTexWidth *= 2;
break;
}
pRenderContext->DrawScreenSpaceRectangle( m_pFreezeFrame, x, y, width, height,
nTexX0, nTexY0, nTexX1-1, nTexY1-1, nTexWidth, nTexHeight );
//Fake a fade during freezeframe view.
if ( g_flFreezeFlash >= gpGlobals->curtime && engine->IsTakingScreenshot() == false )
{
// Overlay screen fade on entire screen
IMaterial* pMaterial = m_TranslucentSingleColor;
int iFadeAlpha = FREEZECAM_SNAPSHOT_FADE_SPEED * ( g_flFreezeFlash - gpGlobals->curtime );
iFadeAlpha = MIN( iFadeAlpha, 255 );
iFadeAlpha = MAX( 0, iFadeAlpha );
pMaterial->AlphaModulate( iFadeAlpha * ( 1.0f / 255.0f ) );
pMaterial->ColorModulate( 1.0f, 1.0f, 1.0f );
pMaterial->SetMaterialVarFlag( MATERIAL_VAR_IGNOREZ, true );
pRenderContext->DrawScreenSpaceRectangle( pMaterial, x, y, width, height, 0, 0, width-1, height-1, width, height );
}
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
//-----------------------------------------------------------------------------
// Pops a water render target
//-----------------------------------------------------------------------------
bool CBaseWorldView::AdjustView( float waterHeight )
{
if( m_DrawFlags & DF_RENDER_REFRACTION )
{
ITexture *pTexture = GetWaterRefractionTexture();
// Use the aspect ratio of the main view! So, don't recompute it here
x = y = 0;
width = pTexture->GetActualWidth();
height = pTexture->GetActualHeight();
return true;
}
if( m_DrawFlags & DF_RENDER_REFLECTION )
{
ITexture *pTexture = GetWaterReflectionTexture();
// If the main view is overriding the projection matrix (for Stereo or
// some other nefarious purpose) make sure to include any Y offset in
// the custom projection matrix in our reflected overridden projection
// matrix.
if( m_bViewToProjectionOverride )
{
m_ViewToProjection[1][2] = -m_ViewToProjection[1][2];
}
// Use the aspect ratio of the main view! So, don't recompute it here
x = y = 0;
width = pTexture->GetActualWidth();
height = pTexture->GetActualHeight();
angles[0] = -angles[0];
angles[2] = -angles[2];
origin[2] -= 2.0f * ( origin[2] - (waterHeight));
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Pops a water render target
//-----------------------------------------------------------------------------
void CBaseWorldView::PushView( float waterHeight )
{
float spread = 2.0f;
if( m_DrawFlags & DF_FUDGE_UP )
{
waterHeight += spread;
}
else
{
waterHeight -= spread;
}
MaterialHeightClipMode_t clipMode = MATERIAL_HEIGHTCLIPMODE_DISABLE;
if ( ( m_DrawFlags & DF_CLIP_Z ) && mat_clipz.GetBool() )
{
if( m_DrawFlags & DF_CLIP_BELOW )
{
clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT;
}
else
{
clipMode = MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT;
}
}
CMatRenderContextPtr pRenderContext( materials );
if( m_DrawFlags & DF_RENDER_REFRACTION )
{
pRenderContext->SetFogZ( waterHeight );
pRenderContext->SetHeightClipZ( waterHeight );
pRenderContext->SetHeightClipMode( clipMode );
// Have to re-set up the view since we reset the size
render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() );
return;
}
if( m_DrawFlags & DF_RENDER_REFLECTION )
{
ITexture *pTexture = GetWaterReflectionTexture();
pRenderContext->SetFogZ( waterHeight );
bool bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
if( bSoftwareUserClipPlane && ( origin[2] > waterHeight - r_eyewaterepsilon.GetFloat() ) )
{
waterHeight = origin[2] + r_eyewaterepsilon.GetFloat();
}
pRenderContext->SetHeightClipZ( waterHeight );
pRenderContext->SetHeightClipMode( clipMode );
render->Push3DView( *this, m_ClearFlags, pTexture, GetFrustum() );
SetLightmapScaleForWater();
return;
}
if ( m_ClearFlags & ( VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_STENCIL ) )
{
if ( m_ClearFlags & VIEW_CLEAR_OBEY_STENCIL )
{
pRenderContext->ClearBuffersObeyStencil( m_ClearFlags & VIEW_CLEAR_COLOR, m_ClearFlags & VIEW_CLEAR_DEPTH );
}
else
{
pRenderContext->ClearBuffers( m_ClearFlags & VIEW_CLEAR_COLOR, m_ClearFlags & VIEW_CLEAR_DEPTH, m_ClearFlags & VIEW_CLEAR_STENCIL );
}
}
pRenderContext->SetHeightClipMode( clipMode );
if ( clipMode != MATERIAL_HEIGHTCLIPMODE_DISABLE )
{
pRenderContext->SetHeightClipZ( waterHeight );
}
}
//-----------------------------------------------------------------------------
// Pops a water render target
//-----------------------------------------------------------------------------
void CBaseWorldView::PopView()
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
if( m_DrawFlags & (DF_RENDER_REFRACTION | DF_RENDER_REFLECTION) )
{
if ( IsX360() )
{
// these renders paths used their surfaces, so blit their results
if ( m_DrawFlags & DF_RENDER_REFRACTION )
{
pRenderContext->CopyRenderTargetToTextureEx( GetWaterRefractionTexture(), NULL, NULL );
}
if ( m_DrawFlags & DF_RENDER_REFLECTION )
{
pRenderContext->CopyRenderTargetToTextureEx( GetWaterReflectionTexture(), NULL, NULL );
}
}
render->PopView( GetFrustum() );
if (SavedLinearLightMapScale.x>=0)
{
pRenderContext->SetToneMappingScaleLinear(SavedLinearLightMapScale);
SavedLinearLightMapScale.x=-1;
}
}
}
//-----------------------------------------------------------------------------
// Draws the world + entities
//-----------------------------------------------------------------------------
void CBaseWorldView::DrawSetup( float waterHeight, int nSetupFlags, float waterZAdjust, int iForceViewLeaf )
{
int savedViewID = g_CurrentViewID;
g_CurrentViewID = VIEW_ILLEGAL;
bool bViewChanged = AdjustView( waterHeight );
if ( bViewChanged )
{
render->Push3DView( *this, 0, NULL, GetFrustum() );
}
render->BeginUpdateLightmaps();
bool bDrawEntities = ( nSetupFlags & DF_DRAW_ENTITITES ) != 0;
bool bDrawReflection = ( nSetupFlags & DF_RENDER_REFLECTION ) != 0;
BuildWorldRenderLists( bDrawEntities, iForceViewLeaf, true, false, bDrawReflection ? &waterHeight : NULL );
PruneWorldListInfo();
if ( bDrawEntities )
{
BuildRenderableRenderLists( savedViewID );
}
render->EndUpdateLightmaps();
if ( bViewChanged )
{
render->PopView( GetFrustum() );
}
#ifdef TF_CLIENT_DLL
bool bVisionOverride = ( localplayer_visionflags.GetInt() & ( 0x01 ) ); // Pyro-vision Goggles
if ( savedViewID == VIEW_MAIN && bVisionOverride && pyro_dof.GetBool() )
{
SSAO_DepthPass();
}
#endif
g_CurrentViewID = savedViewID;
}
void MaybeInvalidateLocalPlayerAnimation()
{
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
if ( ( pPlayer != NULL ) && pPlayer->InFirstPersonView() )
{
// We sometimes need different animation for the main view versus the shadow rendering,
// so we need to reset the cache to ensure this actually happens.
pPlayer->InvalidateBoneCache();
C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
if ( pWeapon != NULL )
{
pWeapon->InvalidateBoneCache();
}
#if defined USES_ECON_ITEMS
// ...and all the things you're wearing/holding/etc
int NumWearables = pPlayer->GetNumWearables();
for ( int i = 0; i < NumWearables; ++i )
{
CEconWearable* pItem = pPlayer->GetWearable ( i );
if ( pItem != NULL )
{
pItem->InvalidateBoneCache();
}
}
#endif // USES_ECON_ITEMS
}
}
void CBaseWorldView::DrawExecute( float waterHeight, view_id_t viewID, float waterZAdjust )
{
int savedViewID = g_CurrentViewID;
// @MULTICORE (toml 8/16/2006): rethink how, where, and when this is done...
g_CurrentViewID = VIEW_SHADOW_DEPTH_TEXTURE;
MaybeInvalidateLocalPlayerAnimation();
g_pClientShadowMgr->ComputeShadowTextures( *this, m_pWorldListInfo->m_LeafCount, m_pWorldListInfo->m_pLeafList );
MaybeInvalidateLocalPlayerAnimation();
// Make sure sound doesn't stutter
engine->Sound_ExtraUpdate();
g_CurrentViewID = viewID;
// Update our render view flags.
int iDrawFlagsBackup = m_DrawFlags;
m_DrawFlags |= m_pMainView->GetBaseDrawFlags();
PushView( waterHeight );
CMatRenderContextPtr pRenderContext( materials );
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 32 );
#endif
ITexture *pSaveFrameBufferCopyTexture = pRenderContext->GetFrameBufferCopyTexture( 0 );
if ( engine->GetDXSupportLevel() >= 80 )
{
pRenderContext->SetFrameBufferCopyTexture( GetPowerOfTwoFrameBufferTexture() );
}
pRenderContext.SafeRelease();
ERenderDepthMode DepthMode = DEPTH_MODE_NORMAL;
if ( m_DrawFlags & DF_DRAW_ENTITITES )
{
DrawWorld( waterZAdjust );
DrawOpaqueRenderables( DepthMode );
#ifdef TF_CLIENT_DLL
bool bVisionOverride = ( localplayer_visionflags.GetInt() & ( 0x01 ) ); // Pyro-vision Goggles
if ( g_CurrentViewID == VIEW_MAIN && bVisionOverride && pyro_dof.GetBool() ) // Pyro-vision Goggles
{
DrawDepthOfField();
}
#endif
DrawTranslucentRenderables( false, false );
DrawNoZBufferTranslucentRenderables();
}
else
{
DrawWorld( waterZAdjust );
#ifdef TF_CLIENT_DLL
bool bVisionOverride = ( localplayer_visionflags.GetInt() & ( 0x01 ) ); // Pyro-vision Goggles
if ( g_CurrentViewID == VIEW_MAIN && bVisionOverride && pyro_dof.GetBool() ) // Pyro-vision Goggles
{
DrawDepthOfField();
}
#endif
// Draw translucent world brushes only, no entities
DrawTranslucentWorldInLeaves( false );
}
// issue the pixel visibility tests for sub-views
if ( !IsMainView( CurrentViewID() ) && CurrentViewID() != VIEW_INTRO_CAMERA )
{
PixelVisibility_EndCurrentView();
}
pRenderContext.GetFrom( materials );
pRenderContext->SetFrameBufferCopyTexture( pSaveFrameBufferCopyTexture );
PopView();
m_DrawFlags = iDrawFlagsBackup;
g_CurrentViewID = savedViewID;
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
void CBaseWorldView::SSAO_DepthPass()
{
if ( !g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() )
{
return;
}
#if 1
VPROF_BUDGET( "CSimpleWorldView::SSAO_DepthPass", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
int savedViewID = g_CurrentViewID;
g_CurrentViewID = VIEW_SSAO;
ITexture *pSSAO = materials->FindTexture( "_rt_ResolvedFullFrameDepth", TEXTURE_GROUP_RENDER_TARGET );
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->ClearColor4ub( 255, 255, 255, 255 );
#if defined( _X360 )
Assert(0); // rebalance this if we ever use this on 360
pRenderContext->PushVertexShaderGPRAllocation( 112 ); //almost all work is done in vertex shaders for depth rendering, max out their threads
#endif
pRenderContext.SafeRelease();
if( IsPC() )
{
render->Push3DView( (*this), VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pSSAO, GetFrustum() );
}
else if( IsX360() )
{
render->Push3DView( (*this), VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pSSAO, GetFrustum() );
}
MDLCACHE_CRITICAL_SECTION();
engine->Sound_ExtraUpdate(); // Make sure sound doesn't stutter
m_DrawFlags |= DF_SSAO_DEPTH_PASS;
{
VPROF_BUDGET( "DrawWorld", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DrawWorld( 0.0f );
}
// Draw opaque and translucent renderables with appropriate override materials
// OVERRIDE_SSAO_DEPTH_WRITE is OK with a NULL material pointer
modelrender->ForcedMaterialOverride( NULL, OVERRIDE_SSAO_DEPTH_WRITE );
{
VPROF_BUDGET( "DrawOpaqueRenderables", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DrawOpaqueRenderables( DEPTH_MODE_SSA0 );
}
#if 0
if ( m_bRenderFlashlightDepthTranslucents || r_flashlightdepth_drawtranslucents.GetBool() )
{
VPROF_BUDGET( "DrawTranslucentRenderables", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
DrawTranslucentRenderables( false, true );
}
#endif
modelrender->ForcedMaterialOverride( 0 );
m_DrawFlags &= ~DF_SSAO_DEPTH_PASS;
pRenderContext.GetFrom( materials );
if( IsX360() )
{
//Resolve() the depth texture here. Before the pop so the copy will recognize that the resolutions are the same
pRenderContext->CopyRenderTargetToTextureEx( NULL, -1, NULL, NULL );
}
render->PopView( GetFrustum() );
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
pRenderContext.SafeRelease();
g_CurrentViewID = savedViewID;
#endif
}
void CBaseWorldView::DrawDepthOfField( )
{
if ( !g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() )
{
return;
}
CMatRenderContextPtr pRenderContext( materials );
ITexture *pSmallFB0 = materials->FindTexture( "_rt_smallfb0", TEXTURE_GROUP_RENDER_TARGET );
ITexture *pSmallFB1 = materials->FindTexture( "_rt_smallfb1", TEXTURE_GROUP_RENDER_TARGET );
Rect_t DestRect;
int w = pSmallFB0->GetActualWidth();
int h = pSmallFB0->GetActualHeight();
DestRect.x = 0;
DestRect.y = 0;
DestRect.width = w;
DestRect.height = h;
pRenderContext->CopyRenderTargetToTextureEx( pSmallFB0, 0, NULL, &DestRect );
IMaterial *pPyroBlurXMaterial = materials->FindMaterial( "dev/pyro_blur_filter_x", TEXTURE_GROUP_OTHER );
IMaterial *pPyroBlurYMaterial = materials->FindMaterial( "dev/pyro_blur_filter_y", TEXTURE_GROUP_OTHER );
pRenderContext->PushRenderTargetAndViewport( pSmallFB1, 0, 0, w, h );
pRenderContext->DrawScreenSpaceRectangle( pPyroBlurYMaterial, 0, 0, w, h, 0, 0, w - 1, h - 1, w, h );
pRenderContext->PopRenderTargetAndViewport();
pRenderContext->PushRenderTargetAndViewport( pSmallFB0, 0, 0, w, h );
pRenderContext->DrawScreenSpaceRectangle( pPyroBlurXMaterial, 0, 0, w, h, 0, 0, w - 1, h - 1, w, h );
pRenderContext->PopRenderTargetAndViewport();
IMaterial *pPyroDepthOfFieldMaterial = materials->FindMaterial( "dev/pyro_dof", TEXTURE_GROUP_OTHER );
pRenderContext->DrawScreenSpaceRectangle( pPyroDepthOfFieldMaterial, x, y, width, height, 0, 0, width-1, height-1, width, height );
}
//-----------------------------------------------------------------------------
// Draws the scene when there's no water or only cheap water
//-----------------------------------------------------------------------------
void CSimpleWorldView::Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t &waterInfo, ViewCustomVisibility_t *pCustomVisibility )
{
BaseClass::Setup( view );
m_ClearFlags = nClearFlags;
m_DrawFlags = DF_DRAW_ENTITITES;
if ( !waterInfo.m_bOpaqueWater )
{
m_DrawFlags |= 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.
m_DrawFlags |= DF_RENDER_UNDERWATER | DF_RENDER_ABOVEWATER;
}
else if ( fogInfo.m_bEyeInFogVolume )
{
m_DrawFlags |= DF_RENDER_UNDERWATER;
}
else
{
m_DrawFlags |= DF_RENDER_ABOVEWATER;
}
}
if ( waterInfo.m_bDrawWaterSurface )
{
m_DrawFlags |= DF_RENDER_WATER;
}
if ( !fogInfo.m_bEyeInFogVolume && bDrawSkybox )
{
m_DrawFlags |= DF_DRAWSKYBOX;
}
m_pCustomVisibility = pCustomVisibility;
m_fogInfo = fogInfo;
}
//-----------------------------------------------------------------------------
// Draws the scene when there's no water or only cheap water
//-----------------------------------------------------------------------------
void CSimpleWorldView::Draw()
{
VPROF( "CViewRender::ViewDrawScene_NoWater" );
CMatRenderContextPtr pRenderContext( materials );
PIXEVENT( pRenderContext, "CSimpleWorldView::Draw" );
#if defined( _X360 )
pRenderContext->PushVertexShaderGPRAllocation( 32 ); //lean toward pixel shader threads
#endif
pRenderContext.SafeRelease();
DrawSetup( 0, m_DrawFlags, 0 );
if ( !m_fogInfo.m_bEyeInFogVolume )
{
EnableWorldFog();
}
else
{
m_ClearFlags |= VIEW_CLEAR_COLOR;
SetFogVolumeState( m_fogInfo, false );
pRenderContext.GetFrom( materials );
unsigned char ucFogColor[3];
pRenderContext->GetFogColor( ucFogColor );
pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
}
pRenderContext.SafeRelease();
DrawExecute( 0, CurrentViewID(), 0 );
pRenderContext.GetFrom( materials );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
#if defined( _X360 )
pRenderContext->PopVertexShaderGPRAllocation();
#endif
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CBaseWaterView::CalcWaterEyeAdjustments( 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 = origin[2] - fogInfo.m_flWaterHeight;
float epsilon = r_eyewaterepsilon.GetFloat();
waterZAdjust = 0.0f;
if( fabs( eyeToWaterZDelta ) < epsilon )
{
if( eyeToWaterZDelta > 0 )
{
newWaterHeight = origin[2] - epsilon;
}
else
{
newWaterHeight = 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 );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CBaseWaterView::CSoftwareIntersectionView::Setup( bool bAboveWater )
{
BaseClass::Setup( *GetOuter() );
m_DrawFlags = 0;
m_DrawFlags = ( bAboveWater ) ? DF_RENDER_UNDERWATER : DF_RENDER_ABOVEWATER;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CBaseWaterView::CSoftwareIntersectionView::Draw()
{
DrawSetup( GetOuter()->m_waterHeight, m_DrawFlags, GetOuter()->m_waterZAdjust );
DrawExecute( GetOuter()->m_waterHeight, CurrentViewID(), GetOuter()->m_waterZAdjust );
}
//-----------------------------------------------------------------------------
// Draws the scene when the view point is above the level of the water
//-----------------------------------------------------------------------------
void CAboveWaterView::Setup( const CViewSetup &view, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo )
{
BaseClass::Setup( view );
m_bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
CalcWaterEyeAdjustments( fogInfo, m_waterHeight, m_waterZAdjust, m_bSoftwareUserClipPlane );
// BROKEN STUFF!
if ( m_waterZAdjust == 0.0f )
{
m_bSoftwareUserClipPlane = false;
}
m_DrawFlags = DF_RENDER_ABOVEWATER | DF_DRAW_ENTITITES;
m_ClearFlags = VIEW_CLEAR_DEPTH;
#ifdef PORTAL
if( g_pPortalRender->ShouldObeyStencilForClears() )
m_ClearFlags |= VIEW_CLEAR_OBEY_STENCIL;
#endif
if ( bDrawSkybox )
{
m_DrawFlags |= DF_DRAWSKYBOX;
}
if ( waterInfo.m_bDrawWaterSurface )
{
m_DrawFlags |= DF_RENDER_WATER;
}
if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater )
{
m_DrawFlags |= DF_RENDER_UNDERWATER;
}
m_fogInfo = fogInfo;
m_waterInfo = waterInfo;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::Draw()
{
VPROF( "CViewRender::ViewDrawScene_EyeAboveWater" );
// eye is outside of water
CMatRenderContextPtr pRenderContext( materials );
// render the reflection
if( m_waterInfo.m_bReflect )
{
m_ReflectionView.Setup( m_waterInfo.m_bReflectEntities );
m_pMainView->AddViewToScene( &m_ReflectionView );
}
bool bViewIntersectsWater = false;
// render refraction
if ( m_waterInfo.m_bRefract )
{
m_RefractionView.Setup();
m_pMainView->AddViewToScene( &m_RefractionView );
if( !m_bSoftwareUserClipPlane )
{
bViewIntersectsWater = DoesViewPlaneIntersectWater( m_fogInfo.m_flWaterHeight, m_fogInfo.m_nVisibleFogVolume );
}
}
else if ( !( m_DrawFlags & DF_DRAWSKYBOX ) )
{
m_ClearFlags |= VIEW_CLEAR_COLOR;
}
#ifdef PORTAL
if( g_pPortalRender->ShouldObeyStencilForClears() )
m_ClearFlags |= VIEW_CLEAR_OBEY_STENCIL;
#endif
// NOTE!!!!! YOU CAN ONLY DO THIS IF YOU HAVE HARDWARE USER CLIP PLANES!!!!!!
bool bHardwareUserClipPlanes = !g_pMaterialSystemHardwareConfig->UseFastClipping();
if( bViewIntersectsWater && bHardwareUserClipPlanes )
{
// 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.
m_DrawFlags |= DF_CLIP_Z | DF_CLIP_BELOW;
}
// render the world
DrawSetup( m_waterHeight, m_DrawFlags, m_waterZAdjust );
EnableWorldFog();
DrawExecute( m_waterHeight, CurrentViewID(), m_waterZAdjust );
if ( m_waterInfo.m_bRefract )
{
if ( m_bSoftwareUserClipPlane )
{
m_SoftwareIntersectionView.Setup( true );
m_SoftwareIntersectionView.Draw( );
}
else if ( bViewIntersectsWater )
{
m_IntersectionView.Setup();
m_pMainView->AddViewToScene( &m_IntersectionView );
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CReflectionView::Setup( bool bReflectEntities )
{
BaseClass::Setup( *GetOuter() );
m_ClearFlags = VIEW_CLEAR_DEPTH;
// NOTE: Clearing the color is unnecessary since we're drawing the skybox
// and dest-alpha is never used in the reflection
m_DrawFlags = DF_RENDER_REFLECTION | DF_CLIP_Z | DF_CLIP_BELOW |
DF_RENDER_ABOVEWATER;
// NOTE: This will cause us to draw the 2d skybox in the reflection
// (which we want to do instead of drawing the 3d skybox)
m_DrawFlags |= DF_DRAWSKYBOX;
if( bReflectEntities )
{
m_DrawFlags |= DF_DRAW_ENTITITES;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CReflectionView::Draw()
{
#ifdef PORTAL
g_pPortalRender->WaterRenderingHandler_PreReflection();
#endif
// Store off view origin and angles and set the new view
int nSaveViewID = CurrentViewID();
SetupCurrentView( origin, angles, VIEW_REFLECTION );
// Disable occlusion visualization in reflection
bool bVisOcclusion = r_visocclusion.GetInt();
r_visocclusion.SetValue( 0 );
DrawSetup( GetOuter()->m_fogInfo.m_flWaterHeight, m_DrawFlags, 0.0f, GetOuter()->m_fogInfo.m_nVisibleFogVolumeLeaf );
EnableWorldFog();
DrawExecute( GetOuter()->m_fogInfo.m_flWaterHeight, VIEW_REFLECTION, 0.0f );
r_visocclusion.SetValue( bVisOcclusion );
#ifdef PORTAL
// deal with stencil
g_pPortalRender->WaterRenderingHandler_PostReflection();
#endif
// finish off the view and restore the previous view.
SetupCurrentView( origin, angles, ( view_id_t )nSaveViewID );
// This is here for multithreading
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Flush();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CRefractionView::Setup()
{
BaseClass::Setup( *GetOuter() );
m_ClearFlags = VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH;
m_DrawFlags = DF_RENDER_REFRACTION | DF_CLIP_Z |
DF_RENDER_UNDERWATER | DF_FUDGE_UP |
DF_DRAW_ENTITITES ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CRefractionView::Draw()
{
#ifdef PORTAL
g_pPortalRender->WaterRenderingHandler_PreRefraction();
#endif
// Store off view origin and angles and set the new view
int nSaveViewID = CurrentViewID();
SetupCurrentView( origin, angles, VIEW_REFRACTION );
DrawSetup( GetOuter()->m_waterHeight, m_DrawFlags, GetOuter()->m_waterZAdjust );
SetFogVolumeState( GetOuter()->m_fogInfo, true );
SetClearColorToFogColor();
DrawExecute( GetOuter()->m_waterHeight, VIEW_REFRACTION, GetOuter()->m_waterZAdjust );
#ifdef PORTAL
// deal with stencil
g_pPortalRender->WaterRenderingHandler_PostRefraction();
#endif
// finish off the view. restore the previous view.
SetupCurrentView( origin, angles, ( view_id_t )nSaveViewID );
// This is here for multithreading
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
pRenderContext->Flush();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CIntersectionView::Setup()
{
BaseClass::Setup( *GetOuter() );
m_DrawFlags = DF_RENDER_UNDERWATER | DF_CLIP_Z | DF_DRAW_ENTITITES;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAboveWaterView::CIntersectionView::Draw()
{
DrawSetup( GetOuter()->m_fogInfo.m_flWaterHeight, m_DrawFlags, 0 );
SetFogVolumeState( GetOuter()->m_fogInfo, true );
SetClearColorToFogColor( );
DrawExecute( GetOuter()->m_fogInfo.m_flWaterHeight, VIEW_NONE, 0 );
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
}
//-----------------------------------------------------------------------------
// Draws the scene when the view point is under the level of the water
//-----------------------------------------------------------------------------
void CUnderWaterView::Setup( const CViewSetup &view, bool bDrawSkybox, const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t& waterInfo )
{
BaseClass::Setup( view );
m_bSoftwareUserClipPlane = g_pMaterialSystemHardwareConfig->UseFastClipping();
CalcWaterEyeAdjustments( fogInfo, m_waterHeight, m_waterZAdjust, m_bSoftwareUserClipPlane );
IMaterial *pWaterMaterial = fogInfo.m_pFogVolumeMaterial;
if (engine->GetDXSupportLevel() >= 90 ) // screen voerlays underwater are a dx9 feature
{
IMaterialVar *pScreenOverlayVar = pWaterMaterial->FindVar( "$underwateroverlay", NULL, false );
if ( pScreenOverlayVar && ( pScreenOverlayVar->IsDefined() ) )
{
char const *pOverlayName = pScreenOverlayVar->GetStringValue();
if ( pOverlayName[0] != '0' ) // fixme!!!
{
IMaterial *pOverlayMaterial = materials->FindMaterial( pOverlayName, TEXTURE_GROUP_OTHER );
m_pMainView->SetWaterOverlayMaterial( pOverlayMaterial );
}
}
}
// 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
m_DrawFlags = DF_FUDGE_UP | DF_RENDER_UNDERWATER | DF_DRAW_ENTITITES;
m_ClearFlags = VIEW_CLEAR_DEPTH;
if( !m_bSoftwareUserClipPlane )
{
m_DrawFlags |= DF_CLIP_Z;
}
if ( waterInfo.m_bDrawWaterSurface )
{
m_DrawFlags |= DF_RENDER_WATER;
}
if ( !waterInfo.m_bRefract && !waterInfo.m_bOpaqueWater )
{
m_DrawFlags |= DF_RENDER_ABOVEWATER;
}
m_fogInfo = fogInfo;
m_waterInfo = waterInfo;
m_bDrawSkybox = bDrawSkybox;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CUnderWaterView::Draw()
{
// FIXME: The 3d skybox shouldn't be drawn when the eye is under water
VPROF( "CViewRender::ViewDrawScene_EyeUnderWater" );
CMatRenderContextPtr pRenderContext( materials );
// render refraction (out of water)
if ( m_waterInfo.m_bRefract )
{
m_RefractionView.Setup( );
m_pMainView->AddViewToScene( &m_RefractionView );
}
if ( !m_waterInfo.m_bRefract )
{
SetFogVolumeState( m_fogInfo, true );
unsigned char ucFogColor[3];
pRenderContext->GetFogColor( ucFogColor );
pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
}
DrawSetup( m_waterHeight, m_DrawFlags, m_waterZAdjust );
SetFogVolumeState( m_fogInfo, false );
DrawExecute( m_waterHeight, CurrentViewID(), m_waterZAdjust );
m_ClearFlags = 0;
if( m_waterZAdjust != 0.0f && m_bSoftwareUserClipPlane && m_waterInfo.m_bRefract )
{
m_SoftwareIntersectionView.Setup( false );
m_SoftwareIntersectionView.Draw( );
}
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CUnderWaterView::CRefractionView::Setup()
{
BaseClass::Setup( *GetOuter() );
// 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!
m_DrawFlags = DF_CLIP_Z |
DF_CLIP_BELOW | DF_RENDER_ABOVEWATER |
DF_DRAW_ENTITITES;
m_ClearFlags = VIEW_CLEAR_DEPTH;
if ( GetOuter()->m_bDrawSkybox )
{
m_ClearFlags |= VIEW_CLEAR_COLOR;
m_DrawFlags |= DF_DRAWSKYBOX | DF_CLIP_SKYBOX;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CUnderWaterView::CRefractionView::Draw()
{
CMatRenderContextPtr pRenderContext( materials );
SetFogVolumeState( GetOuter()->m_fogInfo, true );
unsigned char ucFogColor[3];
pRenderContext->GetFogColor( ucFogColor );
pRenderContext->ClearColor4ub( ucFogColor[0], ucFogColor[1], ucFogColor[2], 255 );
DrawSetup( GetOuter()->m_waterHeight, m_DrawFlags, GetOuter()->m_waterZAdjust );
EnableWorldFog();
DrawExecute( GetOuter()->m_waterHeight, VIEW_REFRACTION, GetOuter()->m_waterZAdjust );
Rect_t srcRect;
srcRect.x = x;
srcRect.y = y;
srcRect.width = width;
srcRect.height = height;
// Optionally write the rendered image to a debug texture
if ( g_bDumpRenderTargets )
{
DumpTGAofRenderTarget( width, height, "WaterRefract" );
}
ITexture *pTexture = GetWaterRefractionTexture();
pRenderContext->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL );
}
//-----------------------------------------------------------------------------
//
// Reflective glass view starts here
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Draws the scene when the view contains reflective glass
//-----------------------------------------------------------------------------
void CReflectiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox,
const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t &waterInfo, const cplane_t &reflectionPlane )
{
BaseClass::Setup( view, nClearFlags, bDrawSkybox, fogInfo, waterInfo, NULL );
m_ReflectionPlane = reflectionPlane;
}
bool CReflectiveGlassView::AdjustView( float flWaterHeight )
{
ITexture *pTexture = GetWaterReflectionTexture();
// Use the aspect ratio of the main view! So, don't recompute it here
x = y = 0;
width = pTexture->GetActualWidth();
height = pTexture->GetActualHeight();
// Reflect the camera origin + vectors around the reflection plane
float flDist = DotProduct( origin, m_ReflectionPlane.normal ) - m_ReflectionPlane.dist;
VectorMA( origin, - 2.0f * flDist, m_ReflectionPlane.normal, origin );
Vector vecForward, vecUp;
AngleVectors( angles, &vecForward, NULL, &vecUp );
float flDot = DotProduct( vecForward, m_ReflectionPlane.normal );
VectorMA( vecForward, - 2.0f * flDot, m_ReflectionPlane.normal, vecForward );
flDot = DotProduct( vecUp, m_ReflectionPlane.normal );
VectorMA( vecUp, - 2.0f * flDot, m_ReflectionPlane.normal, vecUp );
VectorAngles( vecForward, vecUp, angles );
return true;
}
void CReflectiveGlassView::PushView( float waterHeight )
{
render->Push3DView( *this, m_ClearFlags, GetWaterReflectionTexture(), GetFrustum() );
Vector4D plane;
VectorCopy( m_ReflectionPlane.normal, plane.AsVector3D() );
plane.w = m_ReflectionPlane.dist + 0.1f;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushCustomClipPlane( plane.Base() );
}
void CReflectiveGlassView::PopView( )
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PopCustomClipPlane( );
render->PopView( GetFrustum() );
}
//-----------------------------------------------------------------------------
// Renders reflective or refractive parts of glass
//-----------------------------------------------------------------------------
void CReflectiveGlassView::Draw()
{
VPROF( "CReflectiveGlassView::Draw" );
CMatRenderContextPtr pRenderContext( materials );
PIXEVENT( pRenderContext, "CReflectiveGlassView::Draw" );
// Disable occlusion visualization in reflection
bool bVisOcclusion = r_visocclusion.GetInt();
r_visocclusion.SetValue( 0 );
BaseClass::Draw();
r_visocclusion.SetValue( bVisOcclusion );
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
pRenderContext->Flush();
}
//-----------------------------------------------------------------------------
// Draws the scene when the view contains reflective glass
//-----------------------------------------------------------------------------
void CRefractiveGlassView::Setup( const CViewSetup &view, int nClearFlags, bool bDrawSkybox,
const VisibleFogVolumeInfo_t &fogInfo, const WaterRenderInfo_t &waterInfo, const cplane_t &reflectionPlane )
{
BaseClass::Setup( view, nClearFlags, bDrawSkybox, fogInfo, waterInfo, NULL );
m_ReflectionPlane = reflectionPlane;
}
bool CRefractiveGlassView::AdjustView( float flWaterHeight )
{
ITexture *pTexture = GetWaterRefractionTexture();
// Use the aspect ratio of the main view! So, don't recompute it here
x = y = 0;
width = pTexture->GetActualWidth();
height = pTexture->GetActualHeight();
return true;
}
void CRefractiveGlassView::PushView( float waterHeight )
{
render->Push3DView( *this, m_ClearFlags, GetWaterRefractionTexture(), GetFrustum() );
Vector4D plane;
VectorMultiply( m_ReflectionPlane.normal, -1, plane.AsVector3D() );
plane.w = -m_ReflectionPlane.dist + 0.1f;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PushCustomClipPlane( plane.Base() );
}
void CRefractiveGlassView::PopView( )
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->PopCustomClipPlane( );
render->PopView( GetFrustum() );
}
//-----------------------------------------------------------------------------
// Renders reflective or refractive parts of glass
//-----------------------------------------------------------------------------
void CRefractiveGlassView::Draw()
{
VPROF( "CRefractiveGlassView::Draw" );
CMatRenderContextPtr pRenderContext( materials );
PIXEVENT( pRenderContext, "CRefractiveGlassView::Draw" );
BaseClass::Draw();
pRenderContext->ClearColor4ub( 0, 0, 0, 255 );
pRenderContext->Flush();
}