From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/client/viewrender.cpp | 6224 +++++++++++++++++++++++++++++++++++++ 1 file changed, 6224 insertions(+) create mode 100644 mp/src/game/client/viewrender.cpp (limited to 'mp/src/game/client/viewrender.cpp') diff --git a/mp/src/game/client/viewrender.cpp b/mp/src/game/client/viewrender.cpp new file mode 100644 index 00000000..d70469cb --- /dev/null +++ b/mp/src/game/client/viewrender.cpp @@ -0,0 +1,6224 @@ +//========= 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 "headtrack/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 +{ + 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 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 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 = VIEW_3DSKY, bool bInvokePreAndPostRender = true, ITexture *pRenderTarget = NULL ); + + 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(); + + render->Push3DView( viewModelSetup, 0, NULL, GetFrustum() ); + +#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 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 + { + render->Push3DView( view, nClearFlags, NULL, GetFrustum() ); + } + + // 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 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(); + } + + // 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 = view.m_eStereoEye == STEREO_EYE_RIGHT ? viewFramebufferWidth : 0; + 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 + vgui::surface()->SetFullscreenViewport( viewFramebufferX, viewFramebufferY, viewFramebufferWidth, viewFramebufferHeight ); + + // 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 pGlassReflectionView = new CReflectiveGlassView( this ); + pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + AddViewToScene( pGlassReflectionView ); + + CRefPtr pGlassRefractionView = new CRefractiveGlassView( this ); + pGlassRefractionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, bDrawSkybox, fogVolumeInfo, info, glassReflectionPlane ); + AddViewToScene( pGlassRefractionView ); + } + + CRefPtr 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 pAboveWaterView = new CAboveWaterView( this ); + pAboveWaterView->Setup( viewIn, bDrawSkybox, fogVolumeInfo, info ); + AddViewToScene( pAboveWaterView ); + } + else + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "CUnderWaterView" ); + CRefPtr 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 pGlassReflectionView = new CReflectiveGlassView( this ); + pGlassReflectionView->Setup( viewIn, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR | VIEW_CLEAR_OBEY_STENCIL, drawSkybox, fogInfo, waterInfo, glassReflectionPlane ); + AddViewToScene( pGlassReflectionView ); + + CRefPtr 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 pAboveWaterView = new CAboveWaterView( this ); + pAboveWaterView->Setup( viewIn, drawSkybox, fogInfo, waterInfo ); + AddViewToScene( pAboveWaterView ); + } + else + { + CRefPtr 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 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::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(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( 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( 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 ) +{ + 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() ); + + // 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" ); + + DrawInternal(); +} + + +#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 ); + + 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(); +} -- cgit v1.2.3