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