//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
# include "cbase.h"
# include "view.h"
# include "iviewrender.h"
# include "iviewrender_beams.h"
# include "view_shared.h"
# include "ivieweffects.h"
# include "iinput.h"
# include "iclientmode.h"
# include "prediction.h"
# include "viewrender.h"
# include "c_te_legacytempents.h"
# include "cl_mat_stub.h"
# include "tier0/vprof.h"
# include "iclientvehicle.h"
# include "engine/IEngineTrace.h"
# include "mathlib/vmatrix.h"
# include "rendertexture.h"
# include "c_world.h"
# include <KeyValues.h>
# include "igameevents.h"
# include "smoke_fog_overlay.h"
# include "bitmap/tgawriter.h"
# include "hltvcamera.h"
# if defined( REPLAY_ENABLED )
# include "replay/replaycamera.h"
# include "replay/replay_screenshot.h"
# endif
# include "input.h"
# include "filesystem.h"
# include "materialsystem/itexture.h"
# include "materialsystem/imaterialsystem.h"
# include "materialsystem/materialsystem_config.h"
# include "VGuiMatSurface/IMatSystemSurface.h"
# include "toolframework_client.h"
# include "tier0/icommandline.h"
# include "ienginevgui.h"
# include <vgui_controls/Controls.h>
# include <vgui/ISurface.h>
# include "ScreenSpaceEffects.h"
# include "sourcevr/isourcevirtualreality.h"
# include "client_virtualreality.h"
# if defined( REPLAY_ENABLED )
# include "replay/ireplaysystem.h"
# include "replay/ienginereplay.h"
# endif
# if defined( HL2_CLIENT_DLL ) || defined( CSTRIKE_DLL )
# define USE_MONITORS
# endif
# ifdef PORTAL
# include "c_prop_portal.h" //portal surface rendering functions
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
void ToolFramework_AdjustEngineViewport ( int & x , int & y , int & width , int & height ) ;
bool ToolFramework_SetupEngineView ( Vector & origin , QAngle & angles , float & fov ) ;
bool ToolFramework_SetupEngineMicrophone ( Vector & origin , QAngle & angles ) ;
extern ConVar default_fov ;
extern bool g_bRenderingScreenshot ;
# if !defined( _X360 )
# define SAVEGAME_SCREENSHOT_WIDTH 180
# define SAVEGAME_SCREENSHOT_HEIGHT 100
# else
# define SAVEGAME_SCREENSHOT_WIDTH 128
# define SAVEGAME_SCREENSHOT_HEIGHT 128
# endif
# ifndef _XBOX
extern ConVar sensitivity ;
# endif
ConVar zoom_sensitivity_ratio ( " zoom_sensitivity_ratio " , " 1.0 " , 0 , " Additional mouse sensitivity scale factor applied when FOV is zoomed in. " ) ;
CViewRender g_DefaultViewRender ;
IViewRender * view = NULL ; // set in cldll_client_init.cpp if no mod creates their own
# if _DEBUG
bool g_bRenderingCameraView = false ;
# endif
// These are the vectors for the "main" view - the one the player is looking down.
// For stereo views, they are the vectors for the middle eye.
static Vector g_vecRenderOrigin ( 0 , 0 , 0 ) ;
static QAngle g_vecRenderAngles ( 0 , 0 , 0 ) ;
static Vector g_vecPrevRenderOrigin ( 0 , 0 , 0 ) ; // Last frame's render origin
static QAngle g_vecPrevRenderAngles ( 0 , 0 , 0 ) ; // Last frame's render angles
static Vector g_vecVForward ( 0 , 0 , 0 ) , g_vecVRight ( 0 , 0 , 0 ) , g_vecVUp ( 0 , 0 , 0 ) ;
static VMatrix g_matCamInverse ;
extern ConVar cl_forwardspeed ;
static ConVar v_centermove ( " v_centermove " , " 0.15 " ) ;
static ConVar v_centerspeed ( " v_centerspeed " , " 500 " ) ;
# ifdef TF_CLIENT_DLL
// 54 degrees approximates a 35mm camera - we determined that this makes the viewmodels
// and motions look the most natural.
ConVar v_viewmodel_fov ( " viewmodel_fov " , " 54 " , FCVAR_ARCHIVE ) ;
# else
ConVar v_viewmodel_fov ( " viewmodel_fov " , " 54 " , FCVAR_CHEAT ) ;
# endif
ConVar mat_viewportscale ( " mat_viewportscale " , " 1.0 " , FCVAR_ARCHIVE , " Scale down the main viewport (to reduce GPU impact on CPU profiling) " , true, (1.0f / 640.0f), true, 1.0f ) ;
ConVar mat_viewportupscale ( " mat_viewportupscale " , " 1 " , FCVAR_ARCHIVE , " Scale the viewport back up " ) ;
ConVar cl_leveloverview ( " cl_leveloverview " , " 0 " , FCVAR_CHEAT ) ;
# ifdef ANDROID
# define MAPEXTENTS_DEFAULT "12288" // small optimization
# else
# define MAPEXTENTS_DEFAULT "16384"
# endif
static ConVar r_mapextents ( " r_mapextents " , MAPEXTENTS_DEFAULT , FCVAR_CHEAT ,
" Set the max dimension for the map. This determines the far clipping plane " ) ;
// UNDONE: Delete this or move to the material system?
ConVar gl_clear ( " gl_clear " , " 0 " ) ;
ConVar gl_clear_randomcolor ( " gl_clear_randomcolor " , " 0 " , FCVAR_CHEAT , " Clear the back buffer to random colors every frame. Helps spot open seams in geometry. " ) ;
static ConVar r_farz ( " r_farz " , " -1 " , FCVAR_CHEAT , " Override the far clipping plane. -1 means to use the value in env_fog_controller. " ) ;
static ConVar cl_demoviewoverride ( " cl_demoviewoverride " , " 0 " , 0 , " Override view during demo playback " ) ;
void SoftwareCursorChangedCB ( IConVar * pVar , const char * pOldValue , float fOldValue )
{
ConVar * pConVar = ( ConVar * ) pVar ;
vgui : : surface ( ) - > SetSoftwareCursor ( pConVar - > GetBool ( ) | | UseVR ( ) ) ;
}
static ConVar cl_software_cursor ( " cl_software_cursor " , " 0 " , FCVAR_ARCHIVE , " Switches the game to use a larger software cursor instead of the normal OS cursor " , SoftwareCursorChangedCB ) ;
static Vector s_DemoView ;
static QAngle s_DemoAngle ;
static void CalcDemoViewOverride ( Vector & origin , QAngle & angles )
{
engine - > SetViewAngles ( s_DemoAngle ) ;
input - > ExtraMouseSample ( gpGlobals - > absoluteframetime , true ) ;
engine - > GetViewAngles ( s_DemoAngle ) ;
Vector forward , right , up ;
AngleVectors ( s_DemoAngle , & forward , & right , & up ) ;
float speed = gpGlobals - > absoluteframetime * cl_demoviewoverride . GetFloat ( ) * 320 ;
s_DemoView + = speed * input - > KeyState ( & in_forward ) * forward ;
s_DemoView - = speed * input - > KeyState ( & in_back ) * forward ;
s_DemoView + = speed * input - > KeyState ( & in_moveright ) * right ;
s_DemoView - = speed * input - > KeyState ( & in_moveleft ) * right ;
origin = s_DemoView ;
angles = s_DemoAngle ;
}
// Selects the relevant member variable to update. You could do it manually, but...
// We always set up the MONO eye, even when doing stereo, and it's set up to be mid-way between the left and right,
// so if you don't really care about L/R (e.g. culling, sound, etc), just use MONO.
CViewSetup & CViewRender : : GetView ( StereoEye_t eEye )
{
if ( eEye = = STEREO_EYE_MONO )
{
return m_View ;
}
else if ( eEye = = STEREO_EYE_RIGHT )
{
return m_ViewRight ;
}
else
{
Assert ( eEye = = STEREO_EYE_LEFT ) ;
return m_ViewLeft ;
}
}
const CViewSetup & CViewRender : : GetView ( StereoEye_t eEye ) const
{
return ( const_cast < CViewRender * > ( this ) ) - > GetView ( eEye ) ;
}
//-----------------------------------------------------------------------------
// Accessors to return the main view (where the player's looking)
//-----------------------------------------------------------------------------
const Vector & MainViewOrigin ( )
{
return g_vecRenderOrigin ;
}
const QAngle & MainViewAngles ( )
{
return g_vecRenderAngles ;
}
const Vector & MainViewForward ( )
{
return g_vecVForward ;
}
const Vector & MainViewRight ( )
{
return g_vecVRight ;
}
const Vector & MainViewUp ( )
{
return g_vecVUp ;
}
const VMatrix & MainWorldToViewMatrix ( )
{
return g_matCamInverse ;
}
const Vector & PrevMainViewOrigin ( )
{
return g_vecPrevRenderOrigin ;
}
const QAngle & PrevMainViewAngles ( )
{
return g_vecPrevRenderAngles ;
}
//-----------------------------------------------------------------------------
// Compute the world->camera transform
//-----------------------------------------------------------------------------
void ComputeCameraVariables ( const Vector & vecOrigin , const QAngle & vecAngles ,
Vector * pVecForward , Vector * pVecRight , Vector * pVecUp , VMatrix * pMatCamInverse )
{
// Compute view bases
AngleVectors ( vecAngles , pVecForward , pVecRight , pVecUp ) ;
for ( int i = 0 ; i < 3 ; + + i )
{
( * pMatCamInverse ) [ 0 ] [ i ] = ( * pVecRight ) [ i ] ;
( * pMatCamInverse ) [ 1 ] [ i ] = ( * pVecUp ) [ i ] ;
( * pMatCamInverse ) [ 2 ] [ i ] = - ( * pVecForward ) [ i ] ;
( * pMatCamInverse ) [ 3 ] [ i ] = 0.0F ;
}
( * pMatCamInverse ) [ 0 ] [ 3 ] = - DotProduct ( * pVecRight , vecOrigin ) ;
( * pMatCamInverse ) [ 1 ] [ 3 ] = - DotProduct ( * pVecUp , vecOrigin ) ;
( * pMatCamInverse ) [ 2 ] [ 3 ] = DotProduct ( * pVecForward , vecOrigin ) ;
( * pMatCamInverse ) [ 3 ] [ 3 ] = 1.0F ;
}
bool R_CullSphere (
VPlane const * pPlanes ,
int nPlanes ,
Vector const * pCenter ,
float radius )
{
for ( int i = 0 ; i < nPlanes ; i + + )
if ( pPlanes [ i ] . DistTo ( * pCenter ) < - radius )
return true ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void StartPitchDrift ( void )
{
view - > StartPitchDrift ( ) ;
}
static ConCommand centerview ( " centerview " , StartPitchDrift ) ;
extern ConVar default_fov ;
//-----------------------------------------------------------------------------
// Purpose: Initializes all view systems
//-----------------------------------------------------------------------------
void CViewRender : : Init ( void )
{
memset ( & m_PitchDrift , 0 , sizeof ( m_PitchDrift ) ) ;
m_bDrawOverlay = false ;
m_pDrawEntities = cvar - > FindVar ( " r_drawentities " ) ;
m_pDrawBrushModels = cvar - > FindVar ( " r_drawbrushmodels " ) ;
beams - > InitBeams ( ) ;
tempents - > Init ( ) ;
m_TranslucentSingleColor . Init ( " debug/debugtranslucentsinglecolor " , TEXTURE_GROUP_OTHER ) ;
m_ModulateSingleColor . Init ( " engine/modulatesinglecolor " , TEXTURE_GROUP_OTHER ) ;
extern CMaterialReference g_material_WriteZ ;
g_material_WriteZ . Init ( " engine/writez " , TEXTURE_GROUP_OTHER ) ;
// FIXME:
QAngle angles ;
engine - > GetViewAngles ( angles ) ;
AngleVectors ( angles , & m_vecLastFacing ) ;
# if defined( REPLAY_ENABLED )
m_pReplayScreenshotTaker = NULL ;
# endif
# if defined( CSTRIKE_DLL )
m_flLastFOV = default_fov . GetFloat ( ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Called once per level change
//-----------------------------------------------------------------------------
void CViewRender : : LevelInit ( void )
{
beams - > ClearBeams ( ) ;
tempents - > Clear ( ) ;
m_BuildWorldListsNumber = 0 ;
m_BuildRenderableListsNumber = 0 ;
for ( int i = 0 ; i < STEREO_EYE_MAX ; i + + )
{
m_rbTakeFreezeFrame [ i ] = false ;
}
m_flFreezeFrameUntil = 0 ;
// Clear our overlay materials
m_ScreenOverlayMaterial . Init ( NULL ) ;
// Init all IScreenSpaceEffects
g_pScreenSpaceEffects - > InitScreenSpaceEffects ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Called once per level change
//-----------------------------------------------------------------------------
void CViewRender : : LevelShutdown ( void )
{
g_pScreenSpaceEffects - > ShutdownScreenSpaceEffects ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Called at shutdown
//-----------------------------------------------------------------------------
void CViewRender : : Shutdown ( void )
{
m_TranslucentSingleColor . Shutdown ( ) ;
m_ModulateSingleColor . Shutdown ( ) ;
m_ScreenOverlayMaterial . Shutdown ( ) ;
m_UnderWaterOverlayMaterial . Shutdown ( ) ;
beams - > ShutdownBeams ( ) ;
tempents - > Shutdown ( ) ;
}
//-----------------------------------------------------------------------------
// Returns the worldlists build number
//-----------------------------------------------------------------------------
int CViewRender : : BuildWorldListsNumber ( void ) const
{
return m_BuildWorldListsNumber ;
}
//-----------------------------------------------------------------------------
// Purpose: Start moving pitch toward ideal
//-----------------------------------------------------------------------------
void CViewRender : : StartPitchDrift ( void )
{
if ( m_PitchDrift . laststop = = gpGlobals - > curtime )
{
// Something else is blocking the drift.
return ;
}
if ( m_PitchDrift . nodrift | | ! m_PitchDrift . pitchvel )
{
m_PitchDrift . pitchvel = v_centerspeed . GetFloat ( ) ;
m_PitchDrift . nodrift = false ;
m_PitchDrift . driftmove = 0 ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender : : StopPitchDrift ( void )
{
m_PitchDrift . laststop = gpGlobals - > curtime ;
m_PitchDrift . nodrift = true ;
m_PitchDrift . pitchvel = 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Moves the client pitch angle towards cl.idealpitch sent by the server.
// If the user is adjusting pitch manually, either with lookup/lookdown,
// mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
//-----------------------------------------------------------------------------
void CViewRender : : DriftPitch ( void )
{
float delta , move ;
C_BasePlayer * player = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( ! player )
return ;
# if defined( REPLAY_ENABLED )
if ( engine - > IsHLTV ( ) | | g_pEngineClientReplay - > IsPlayingReplayDemo ( ) | | ( player - > GetGroundEntity ( ) = = NULL ) | | engine - > IsPlayingDemo ( ) )
# else
if ( engine - > IsHLTV ( ) | | ( player - > GetGroundEntity ( ) = = NULL ) | | engine - > IsPlayingDemo ( ) )
# endif
{
m_PitchDrift . driftmove = 0 ;
m_PitchDrift . pitchvel = 0 ;
return ;
}
// Don't count small mouse motion
if ( m_PitchDrift . nodrift )
{
if ( fabs ( input - > GetLastForwardMove ( ) ) < cl_forwardspeed . GetFloat ( ) )
{
m_PitchDrift . driftmove = 0 ;
}
else
{
m_PitchDrift . driftmove + = gpGlobals - > frametime ;
}
if ( m_PitchDrift . driftmove > v_centermove . GetFloat ( ) )
{
StartPitchDrift ( ) ;
}
return ;
}
// How far off are we
delta = prediction - > GetIdealPitch ( ) - player - > GetAbsAngles ( ) [ PITCH ] ;
if ( ! delta )
{
m_PitchDrift . pitchvel = 0 ;
return ;
}
// Determine movement amount
move = gpGlobals - > frametime * m_PitchDrift . pitchvel ;
// Accelerate
m_PitchDrift . pitchvel + = gpGlobals - > frametime * v_centerspeed . GetFloat ( ) ;
// Move predicted pitch appropriately
if ( delta > 0 )
{
if ( move > delta )
{
m_PitchDrift . pitchvel = 0 ;
move = delta ;
}
player - > SetLocalAngles ( player - > GetLocalAngles ( ) + QAngle ( move , 0 , 0 ) ) ;
}
else if ( delta < 0 )
{
if ( move > - delta )
{
m_PitchDrift . pitchvel = 0 ;
move = - delta ;
}
player - > SetLocalAngles ( player - > GetLocalAngles ( ) - QAngle ( move , 0 , 0 ) ) ;
}
}
StereoEye_t CViewRender : : GetFirstEye ( ) const
{
if ( UseVR ( ) )
return STEREO_EYE_LEFT ;
else
return STEREO_EYE_MONO ;
}
StereoEye_t CViewRender : : GetLastEye ( ) const
{
if ( UseVR ( ) )
return STEREO_EYE_RIGHT ;
else
return STEREO_EYE_MONO ;
}
// This is called by cdll_client_int to setup view model origins. This has to be done before
// simulation so entities can access attachment points on view models during simulation.
void CViewRender : : OnRenderStart ( )
{
VPROF_ ( " CViewRender::OnRenderStart " , 2 , VPROF_BUDGETGROUP_OTHER_UNACCOUNTED , false , 0 ) ;
SetUpViews ( ) ;
// Adjust mouse sensitivity based upon the current FOV
C_BasePlayer * player = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( player )
{
default_fov . SetValue ( player - > m_iDefaultFOV ) ;
//Update our FOV, including any zooms going on
int iDefaultFOV = default_fov . GetInt ( ) ;
int localFOV = player - > GetFOV ( ) ;
int min_fov = player - > GetMinFOV ( ) ;
// Don't let it go too low
localFOV = MAX ( min_fov , localFOV ) ;
gHUD . m_flFOVSensitivityAdjust = 1.0f ;
# ifndef _XBOX
if ( gHUD . m_flMouseSensitivityFactor )
{
gHUD . m_flMouseSensitivity = sensitivity . GetFloat ( ) * gHUD . m_flMouseSensitivityFactor ;
}
else
# endif
{
// No override, don't use huge sensitivity
if ( localFOV = = iDefaultFOV )
{
# ifndef _XBOX
// reset to saved sensitivity
gHUD . m_flMouseSensitivity = 0 ;
# endif
}
else
{
// Set a new sensitivity that is proportional to the change from the FOV default and scaled
// by a separate compensating factor
if ( iDefaultFOV = = 0 )
{
Assert ( 0 ) ; // would divide by zero, something is broken with iDefatulFOV
iDefaultFOV = 1 ;
}
gHUD . m_flFOVSensitivityAdjust =
( ( float ) localFOV / ( float ) iDefaultFOV ) * // linear fov downscale
zoom_sensitivity_ratio . GetFloat ( ) ; // sensitivity scale factor
# ifndef _XBOX
gHUD . m_flMouseSensitivity = gHUD . m_flFOVSensitivityAdjust * sensitivity . GetFloat ( ) ; // regular sensitivity
# endif
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : const CViewSetup
//-----------------------------------------------------------------------------
const CViewSetup * CViewRender : : GetViewSetup ( void ) const
{
return & m_CurrentView ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : const CViewSetup
//-----------------------------------------------------------------------------
const CViewSetup * CViewRender : : GetPlayerViewSetup ( void ) const
{
const CViewSetup & view = GetView ( STEREO_EYE_MONO ) ;
return & view ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CViewRender : : DisableVis ( void )
{
m_bForceNoVis = true ;
}
# ifdef DBGFLAG_ASSERT
static Vector s_DbgSetupOrigin ;
static QAngle s_DbgSetupAngles ;
# endif
//-----------------------------------------------------------------------------
// Gets znear + zfar
//-----------------------------------------------------------------------------
float CViewRender : : GetZNear ( )
{
return VIEW_NEARZ ;
}
float CViewRender : : GetZFar ( )
{
// Initialize view structure with default values
float farZ ;
if ( r_farz . GetFloat ( ) < 1 )
{
// Use the far Z from the map's parameters.
farZ = r_mapextents . GetFloat ( ) * 1.73205080757f ;
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( pPlayer & & pPlayer - > GetFogParams ( ) )
{
if ( pPlayer - > GetFogParams ( ) - > farz > 0 )
{
farZ = pPlayer - > GetFogParams ( ) - > farz ;
}
}
}
else
{
farZ = r_farz . GetFloat ( ) ;
}
return farZ ;
}
//-----------------------------------------------------------------------------
// Sets up the view parameters
//-----------------------------------------------------------------------------
void CViewRender : : SetUpViews ( )
{
VPROF ( " CViewRender::SetUpViews " ) ;
// Initialize view structure with default values
float farZ = GetZFar ( ) ;
// Set up the mono/middle view.
CViewSetup & view = m_View ;
view . zFar = farZ ;
view . zFarViewmodel = farZ ;
// UNDONE: Make this farther out?
// closest point of approach seems to be view center to top of crouched box
view . zNear = GetZNear ( ) ;
view . zNearViewmodel = 1 ;
view . fov = default_fov . GetFloat ( ) ;
view . m_bOrtho = false ;
view . m_bViewToProjectionOverride = false ;
view . m_eStereoEye = STEREO_EYE_MONO ;
// Enable spatial partition access to edicts
partition - > SuppressLists ( PARTITION_ALL_CLIENT_EDICTS , false ) ;
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
// You in-view weapon aim.
bool bCalcViewModelView = false ;
Vector ViewModelOrigin ;
QAngle ViewModelAngles ;
if ( engine - > IsHLTV ( ) )
{
HLTVCamera ( ) - > CalcView ( view . origin , view . angles , view . fov ) ;
}
# if defined( REPLAY_ENABLED )
else if ( g_pEngineClientReplay - > IsPlayingReplayDemo ( ) )
{
ReplayCamera ( ) - > CalcView ( view . origin , view . angles , view . fov ) ;
}
# endif
else
{
// FIXME: Are there multiple views? If so, then what?
// FIXME: What happens when there's no player?
if ( pPlayer )
{
pPlayer - > CalcView ( view . origin , view . angles , view . zNear , view . zFar , view . fov ) ;
// If we are looking through another entities eyes, then override the angles/origin for view
int viewentity = render - > GetViewEntity ( ) ;
if ( ! g_nKillCamMode & & ( pPlayer - > entindex ( ) ! = viewentity ) )
{
C_BaseEntity * ve = cl_entitylist - > GetEnt ( viewentity ) ;
if ( ve )
{
VectorCopy ( ve - > GetAbsOrigin ( ) , view . origin ) ;
VectorCopy ( ve - > GetAbsAngles ( ) , view . angles ) ;
}
}
// There is a viewmodel.
bCalcViewModelView = true ;
ViewModelOrigin = view . origin ;
ViewModelAngles = view . angles ;
}
else
{
view . origin . Init ( ) ;
view . angles . Init ( ) ;
}
// Even if the engine is paused need to override the view
// for keeping the camera control during pause.
g_pClientMode - > OverrideView ( & view ) ;
}
// give the toolsystem a chance to override the view
ToolFramework_SetupEngineView ( view . origin , view . angles , view . fov ) ;
if ( engine - > IsPlayingDemo ( ) )
{
if ( cl_demoviewoverride . GetFloat ( ) > 0.0f )
{
// Retreive view angles from engine ( could have been set in IN_AdjustAngles above )
CalcDemoViewOverride ( view . origin , view . angles ) ;
}
else
{
s_DemoView = view . origin ;
s_DemoAngle = view . angles ;
}
}
//Find the offset our current FOV is from the default value
float fDefaultFov = default_fov . GetFloat ( ) ;
float flFOVOffset = fDefaultFov - view . fov ;
//Adjust the viewmodel's FOV to move with any FOV offsets on the viewer's end
view . fovViewmodel = fabs ( g_pClientMode - > GetViewModelFOV ( ) - flFOVOffset ) ;
if ( UseVR ( ) )
{
// Let the headtracking read the status of the HMD, etc.
// This call can go almost anywhere, but it needs to know the player FOV for sniper weapon zoom, etc
if ( flFOVOffset = = 0.0f )
{
g_ClientVirtualReality . ProcessCurrentTrackingState ( 0.0f ) ;
}
else
{
g_ClientVirtualReality . ProcessCurrentTrackingState ( view . fov ) ;
}
HeadtrackMovementMode_t hmmOverrideMode = g_pClientMode - > ShouldOverrideHeadtrackControl ( ) ;
g_ClientVirtualReality . OverrideView ( & m_View , & ViewModelOrigin , & ViewModelAngles , hmmOverrideMode ) ;
// left and right stereo views should default to being the same as the mono/middle view
m_ViewLeft = m_View ;
m_ViewRight = m_View ;
m_ViewLeft . m_eStereoEye = STEREO_EYE_LEFT ;
m_ViewRight . m_eStereoEye = STEREO_EYE_RIGHT ;
g_ClientVirtualReality . OverrideStereoView ( & m_View , & m_ViewLeft , & m_ViewRight ) ;
}
else
{
// left and right stereo views should default to being the same as the mono/middle view
m_ViewLeft = m_View ;
m_ViewRight = m_View ;
m_ViewLeft . m_eStereoEye = STEREO_EYE_LEFT ;
m_ViewRight . m_eStereoEye = STEREO_EYE_RIGHT ;
}
if ( bCalcViewModelView )
{
Assert ( pPlayer ! = NULL ) ;
pPlayer - > CalcViewModelView ( ViewModelOrigin , ViewModelAngles ) ;
}
// Disable spatial partition access
partition - > SuppressLists ( PARTITION_ALL_CLIENT_EDICTS , true ) ;
// Enable access to all model bones
C_BaseAnimating : : PopBoneAccess ( " OnRenderStart->CViewRender::SetUpView " ) ; // pops the (true, false) bone access set in OnRenderStart
C_BaseAnimating : : PushAllowBoneAccess ( true , true , " CViewRender::SetUpView->OnRenderEnd " ) ; // pop is in OnRenderEnd()
// Compute the world->main camera transform
// This is only done for the main "middle-eye" view, not for the various other views.
ComputeCameraVariables ( view . origin , view . angles ,
& g_vecVForward , & g_vecVRight , & g_vecVUp , & g_matCamInverse ) ;
// set up the hearing origin...
AudioState_t audioState ;
audioState . m_Origin = view . origin ;
audioState . m_Angles = view . angles ;
audioState . m_bIsUnderwater = pPlayer & & pPlayer - > AudioStateIsUnderwater ( view . origin ) ;
ToolFramework_SetupAudioState ( audioState ) ;
// TomF: I wonder when the audio tools modify this, if ever...
Assert ( view . origin = = audioState . m_Origin ) ;
Assert ( view . angles = = audioState . m_Angles ) ;
view . origin = audioState . m_Origin ;
view . angles = audioState . m_Angles ;
engine - > SetAudioState ( audioState ) ;
g_vecPrevRenderOrigin = g_vecRenderOrigin ;
g_vecPrevRenderAngles = g_vecRenderAngles ;
g_vecRenderOrigin = view . origin ;
g_vecRenderAngles = view . angles ;
# ifdef DBGFLAG_ASSERT
s_DbgSetupOrigin = view . origin ;
s_DbgSetupAngles = view . angles ;
# endif
}
void CViewRender : : WriteSaveGameScreenshotOfSize ( const char * pFilename , int width , int height , bool bCreatePowerOf2Padded /*=false*/ ,
bool bWriteVTF /*=false*/ )
{
# ifndef _X360
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > MatrixMode ( MATERIAL_PROJECTION ) ;
pRenderContext - > PushMatrix ( ) ;
pRenderContext - > MatrixMode ( MATERIAL_VIEW ) ;
pRenderContext - > PushMatrix ( ) ;
g_bRenderingScreenshot = true ;
// Push back buffer on the stack with small viewport
pRenderContext - > PushRenderTargetAndViewport ( NULL , 0 , 0 , width , height ) ;
// render out to the backbuffer
CViewSetup viewSetup = GetView ( STEREO_EYE_MONO ) ;
viewSetup . x = 0 ;
viewSetup . y = 0 ;
viewSetup . width = width ;
viewSetup . height = height ;
viewSetup . fov = ScaleFOVByWidthRatio ( viewSetup . fov , ( ( float ) width / ( float ) height ) / ( 4.0f / 3.0f ) ) ;
viewSetup . m_bRenderToSubrectOfLargerScreen = true ;
// draw out the scene
// Don't draw the HUD or the viewmodel
RenderView ( viewSetup , VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR , 0 ) ;
// get the data from the backbuffer and save to disk
// bitmap bits
unsigned char * pImage = ( unsigned char * ) malloc ( width * height * 3 ) ;
// Get Bits from the material system
pRenderContext - > ReadPixels ( 0 , 0 , width , height , pImage , IMAGE_FORMAT_RGB888 ) ;
// Some stuff to be setup dependent on padded vs. not padded
int nSrcWidth , nSrcHeight ;
unsigned char * pSrcImage ;
// Create a padded version if necessary
unsigned char * pPaddedImage = NULL ;
if ( bCreatePowerOf2Padded )
{
// Setup dimensions as needed
int nPaddedWidth = SmallestPowerOfTwoGreaterOrEqual ( width ) ;
int nPaddedHeight = SmallestPowerOfTwoGreaterOrEqual ( height ) ;
// Allocate
int nPaddedImageSize = nPaddedWidth * nPaddedHeight * 3 ;
pPaddedImage = ( unsigned char * ) malloc ( nPaddedImageSize ) ;
// Zero out the entire thing
V_memset ( pPaddedImage , 255 , nPaddedImageSize ) ;
// Copy over each row individually
for ( int nRow = 0 ; nRow < height ; + + nRow )
{
unsigned char * pDst = pPaddedImage + 3 * ( nRow * nPaddedWidth ) ;
const unsigned char * pSrc = pImage + 3 * ( nRow * width ) ;
V_memcpy ( pDst , pSrc , 3 * width ) ;
}
// Setup source data
nSrcWidth = nPaddedWidth ;
nSrcHeight = nPaddedHeight ;
pSrcImage = pPaddedImage ;
}
else
{
// Use non-padded info
nSrcWidth = width ;
nSrcHeight = height ;
pSrcImage = pImage ;
}
// allocate a buffer to write the tga into
CUtlBuffer buffer ;
bool bWriteResult ;
if ( bWriteVTF )
{
// Create and initialize a VTF texture
IVTFTexture * pVTFTexture = CreateVTFTexture ( ) ;
const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB ;
if ( pVTFTexture - > Init ( nSrcWidth , nSrcHeight , 1 , IMAGE_FORMAT_RGB888 , nFlags , 1 , 1 ) )
{
// Copy the image data over to the VTF
unsigned char * pDestBits = pVTFTexture - > ImageData ( ) ;
int nDstSize = nSrcWidth * nSrcHeight * 3 ;
V_memcpy ( pDestBits , pSrcImage , nDstSize ) ;
// Allocate output buffer
int iMaxVTFSize = 1024 + ( nSrcWidth * nSrcHeight * 3 ) ;
void * pVTF = malloc ( iMaxVTFSize ) ;
buffer . SetExternalBuffer ( pVTF , iMaxVTFSize , 0 ) ;
// Serialize to the buffer
bWriteResult = pVTFTexture - > Serialize ( buffer ) ;
// Free the VTF texture
DestroyVTFTexture ( pVTFTexture ) ;
}
else
{
bWriteResult = false ;
}
}
else
{
// Write TGA format to buffer
int iMaxTGASize = 1024 + ( nSrcWidth * nSrcHeight * 4 ) ;
void * pTGA = new char [ iMaxTGASize ] ;
buffer . SetExternalBuffer ( pTGA , iMaxTGASize , 0 ) ;
bWriteResult = TGAWriter : : WriteToBuffer ( pSrcImage , buffer , nSrcWidth , nSrcHeight , IMAGE_FORMAT_RGB888 , IMAGE_FORMAT_RGB888 ) ;
}
if ( ! bWriteResult )
{
Error ( " Couldn't write bitmap data snapshot. \n " ) ;
}
free ( pImage ) ;
free ( pPaddedImage ) ;
// async write to disk (this will take ownership of the memory)
char szPathedFileName [ _MAX_PATH ] ;
Q_snprintf ( szPathedFileName , sizeof ( szPathedFileName ) , " //MOD/%s " , pFilename ) ;
filesystem - > AsyncWrite ( szPathedFileName , buffer . Base ( ) , buffer . TellPut ( ) , true ) ;
// restore our previous state
pRenderContext - > PopRenderTargetAndViewport ( ) ;
pRenderContext - > MatrixMode ( MATERIAL_PROJECTION ) ;
pRenderContext - > PopMatrix ( ) ;
pRenderContext - > MatrixMode ( MATERIAL_VIEW ) ;
pRenderContext - > PopMatrix ( ) ;
g_bRenderingScreenshot = false ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: takes a screenshot for the replay system
//-----------------------------------------------------------------------------
void CViewRender : : WriteReplayScreenshot ( WriteReplayScreenshotParams_t & params )
{
# if defined( REPLAY_ENABLED )
if ( ! m_pReplayScreenshotTaker )
return ;
m_pReplayScreenshotTaker - > TakeScreenshot ( params ) ;
# endif
}
void CViewRender : : UpdateReplayScreenshotCache ( )
{
# if defined( REPLAY_ENABLED )
// Delete the old one
delete m_pReplayScreenshotTaker ;
// Create a new one
m_pReplayScreenshotTaker = new CReplayScreenshotTaker ( this , GetView ( STEREO_EYE_MONO ) ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: takes a screenshot of the save game
//-----------------------------------------------------------------------------
void CViewRender : : WriteSaveGameScreenshot ( const char * pFilename )
{
WriteSaveGameScreenshotOfSize ( pFilename , SAVEGAME_SCREENSHOT_WIDTH , SAVEGAME_SCREENSHOT_HEIGHT ) ;
}
float ScaleFOVByWidthRatio ( float fovDegrees , float ratio )
{
float halfAngleRadians = fovDegrees * ( 0.5f * M_PI / 180.0f ) ;
float t = tan ( halfAngleRadians ) ;
t * = ratio ;
float retDegrees = ( 180.0f / M_PI ) * atan ( t ) ;
return retDegrees * 2.0f ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets view parameters for level overview mode
// Input : *rect -
//-----------------------------------------------------------------------------
void CViewRender : : SetUpOverView ( )
{
static int oldCRC = 0 ;
CViewSetup & view = GetView ( STEREO_EYE_MONO ) ;
view . m_bOrtho = true ;
float aspect = ( float ) view . width / ( float ) view . height ;
int size_y = 1024.0f * cl_leveloverview . GetFloat ( ) ; // scale factor, 1024 = OVERVIEW_MAP_SIZE
int size_x = size_y * aspect ; // standard screen aspect
view . origin . x - = size_x / 2 ;
view . origin . y + = size_y / 2 ;
view . m_OrthoLeft = 0 ;
view . m_OrthoTop = - size_y ;
view . m_OrthoRight = size_x ;
view . m_OrthoBottom = 0 ;
view . angles = QAngle ( 90 , 90 , 0 ) ;
// simple movement detector, show position if moved
int newCRC = view . origin . x + view . origin . y + view . origin . z ;
if ( newCRC ! = oldCRC )
{
Msg ( " Overview: scale %.2f, pos_x %.0f, pos_y %.0f \n " , cl_leveloverview . GetFloat ( ) ,
view . origin . x , view . origin . y ) ;
oldCRC = newCRC ;
}
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > ClearColor4ub ( 0 , 255 , 0 , 255 ) ;
// render->DrawTopView( true );
}
//-----------------------------------------------------------------------------
// Purpose: Render current view into specified rectangle
// Input : *rect - is computed by CVideoMode_Common::GetClientViewRect()
//-----------------------------------------------------------------------------
void CViewRender : : Render ( vrect_t * rect )
{
Assert ( s_DbgSetupOrigin = = m_View . origin ) ;
Assert ( s_DbgSetupAngles = = m_View . angles ) ;
VPROF_BUDGET ( " CViewRender::Render " , " CViewRender::Render " ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
vrect_t vr = * rect ;
// Stub out the material system if necessary.
CMatStubHandler matStub ;
engine - > EngineStats_BeginFrame ( ) ;
// Assume normal vis
m_bForceNoVis = false ;
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
// Set for console commands, etc.
render - > SetMainView ( m_View . origin , m_View . angles ) ;
for ( StereoEye_t eEye = GetFirstEye ( ) ; eEye < = GetLastEye ( ) ; eEye = ( StereoEye_t ) ( eEye + 1 ) )
{
CViewSetup & view = GetView ( eEye ) ;
#if 0 && defined( CSTRIKE_DLL )
const bool bPlayingBackReplay = g_pEngineClientReplay & & g_pEngineClientReplay - > IsPlayingReplayDemo ( ) ;
if ( pPlayer & & ! bPlayingBackReplay )
{
C_BasePlayer * pViewTarget = pPlayer ;
if ( pPlayer - > IsObserver ( ) & & pPlayer - > GetObserverMode ( ) = = OBS_MODE_IN_EYE )
{
pViewTarget = dynamic_cast < C_BasePlayer * > ( pPlayer - > GetObserverTarget ( ) ) ;
}
if ( pViewTarget )
{
float targetFOV = ( float ) pViewTarget - > m_iFOV ;
if ( targetFOV = = 0 )
{
// FOV of 0 means use the default FOV
targetFOV = g_pGameRules - > DefaultFOV ( ) ;
}
float deltaFOV = view . fov - m_flLastFOV ;
float FOVDirection = targetFOV - pViewTarget - > m_iFOVStart ;
// Clamp FOV changes to stop FOV oscillation
if ( ( deltaFOV < 0.0f & & FOVDirection > 0.0f ) | |
( deltaFOV > 0.0f & & FOVDirection < 0.0f ) )
{
view . fov = m_flLastFOV ;
}
// Catch case where FOV overshoots its target FOV
if ( ( view . fov < targetFOV & & FOVDirection < = 0.0f ) | |
( view . fov > targetFOV & & FOVDirection > = 0.0f ) )
{
view . fov = targetFOV ;
}
m_flLastFOV = view . fov ;
}
}
# endif
static ConVarRef sv_restrict_aspect_ratio_fov ( " sv_restrict_aspect_ratio_fov " ) ;
float aspectRatio = engine - > GetScreenAspectRatio ( ) * 0.75f ; // / (4/3)
float limitedAspectRatio = aspectRatio ;
if ( ( sv_restrict_aspect_ratio_fov . GetInt ( ) > 0 & & engine - > IsWindowedMode ( ) & & gpGlobals - > maxClients > 1 ) | |
sv_restrict_aspect_ratio_fov . GetInt ( ) = = 2 )
{
limitedAspectRatio = MIN ( aspectRatio , 1.85f * 0.75f ) ; // cap out the FOV advantage at a 1.85:1 ratio (about the widest any legit user should be)
}
view . fov = ScaleFOVByWidthRatio ( view . fov , limitedAspectRatio ) ;
view . fovViewmodel = ScaleFOVByWidthRatio ( view . fovViewmodel , aspectRatio ) ;
// Let the client mode hook stuff.
g_pClientMode - > PreRender ( & view ) ;
g_pClientMode - > AdjustEngineViewport ( vr . x , vr . y , vr . width , vr . height ) ;
ToolFramework_AdjustEngineViewport ( vr . x , vr . y , vr . width , vr . height ) ;
float flViewportScale = mat_viewportscale . GetFloat ( ) ;
view . m_nUnscaledX = vr . x ;
view . m_nUnscaledY = vr . y ;
view . m_nUnscaledWidth = vr . width ;
view . m_nUnscaledHeight = vr . height ;
switch ( eEye )
{
case STEREO_EYE_MONO :
{
#if 0
// Good test mode for debugging viewports that are not full-size.
view . width = vr . width * flViewportScale * 0.75f ;
view . height = vr . height * flViewportScale * 0.75f ;
view . x = vr . x + view . width * 0.10f ;
view . y = vr . y + view . height * 0.20f ;
# else
view . x = vr . x * flViewportScale ;
view . y = vr . y * flViewportScale ;
view . width = vr . width * flViewportScale ;
view . height = vr . height * flViewportScale ;
# endif
float engineAspectRatio = engine - > GetScreenAspectRatio ( ) ;
view . m_flAspectRatio = ( engineAspectRatio > 0.0f ) ? engineAspectRatio : ( ( float ) view . width / ( float ) view . height ) ;
}
break ;
case STEREO_EYE_RIGHT :
case STEREO_EYE_LEFT :
{
g_pSourceVR - > GetViewportBounds ( ( ISourceVirtualReality : : VREye ) ( eEye - 1 ) , & view . x , & view . y , & view . width , & view . height ) ;
view . m_nUnscaledWidth = view . width ;
view . m_nUnscaledHeight = view . height ;
view . m_nUnscaledX = view . x ;
view . m_nUnscaledY = view . y ;
}
break ;
default :
Assert ( false ) ;
break ;
}
// if we still don't have an aspect ratio, compute it from the view size
if ( view . m_flAspectRatio < = 0.f )
view . m_flAspectRatio = ( float ) view . width / ( float ) view . height ;
int nClearFlags = VIEW_CLEAR_DEPTH | VIEW_CLEAR_STENCIL ;
if ( gl_clear_randomcolor . GetBool ( ) )
{
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > ClearColor3ub ( rand ( ) % 256 , rand ( ) % 256 , rand ( ) % 256 ) ;
pRenderContext - > ClearBuffers ( true , false , false ) ;
pRenderContext - > Release ( ) ;
}
else if ( gl_clear . GetBool ( ) )
{
nClearFlags | = VIEW_CLEAR_COLOR ;
}
else if ( IsPosix ( ) )
{
MaterialAdapterInfo_t adapterInfo ;
materials - > GetDisplayAdapterInfo ( materials - > GetCurrentAdapter ( ) , adapterInfo ) ;
// On Posix, on ATI, we always clear color if we're antialiasing
if ( adapterInfo . m_VendorID = = 0x1002 )
{
if ( g_pMaterialSystem - > GetCurrentConfigForVideoCard ( ) . m_nAASamples > 0 )
{
nClearFlags | = VIEW_CLEAR_COLOR ;
}
}
}
// Determine if we should draw view model ( client mode override )
bool drawViewModel = g_pClientMode - > ShouldDrawViewModel ( ) ;
if ( cl_leveloverview . GetFloat ( ) > 0 )
{
SetUpOverView ( ) ;
nClearFlags | = VIEW_CLEAR_COLOR ;
drawViewModel = false ;
}
// Apply any player specific overrides
if ( pPlayer )
{
// Override view model if necessary
if ( ! pPlayer - > m_Local . m_bDrawViewmodel )
{
drawViewModel = false ;
}
}
int flags = 0 ;
if ( eEye = = STEREO_EYE_MONO | | eEye = = STEREO_EYE_LEFT | | ( g_ClientVirtualReality . ShouldRenderHUDInWorld ( ) ) )
{
flags = RENDERVIEW_DRAWHUD ;
}
if ( drawViewModel )
{
flags | = RENDERVIEW_DRAWVIEWMODEL ;
}
if ( eEye = = STEREO_EYE_RIGHT )
{
// we should use the monitor view from the left eye for both eyes
flags | = RENDERVIEW_SUPPRESSMONITORRENDERING ;
}
RenderView ( view , nClearFlags , flags ) ;
if ( UseVR ( ) )
{
bool bDoUndistort = ! engine - > IsTakingScreenshot ( ) ;
if ( bDoUndistort )
{
g_ClientVirtualReality . PostProcessFrame ( eEye ) ;
}
// logic here all cloned from code in viewrender.cpp around RenderHUDQuad:
// figure out if we really want to draw the HUD based on freeze cam
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 ( ) )
{
// 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 . OverlayHUDQuadWithUndistort ( view , bDoUndistort , g_pClientMode - > ShouldBlackoutAroundHUD ( ) , bTranslucent ) ;
}
}
}
// TODO: should these be inside or outside the stereo eye stuff?
g_pClientMode - > PostRender ( ) ;
engine - > EngineStats_EndFrame ( ) ;
# if !defined( _X360 )
// Stop stubbing the material system so we can see the budget panel
matStub . End ( ) ;
# endif
// Draw all of the UI stuff "fullscreen"
// (this is not health, ammo, etc. Nor is it pre-game briefing interface stuff - this is the stuff that appears when you hit Esc in-game)
// In stereo mode this is rendered inside of RenderView so it goes into the render target
if ( ! g_ClientVirtualReality . ShouldRenderHUDInWorld ( ) )
{
CViewSetup view2d ;
view2d . x = rect - > x ;
view2d . y = rect - > y ;
view2d . width = rect - > width ;
view2d . height = rect - > height ;
render - > Push2DView ( view2d , 0 , NULL , GetFrustum ( ) ) ;
render - > VGui_Paint ( PAINT_UIPANELS | PAINT_CURSOR ) ;
render - > PopView ( GetFrustum ( ) ) ;
}
}
static void GetPos ( const CCommand & args , Vector & vecOrigin , QAngle & angles )
{
vecOrigin = MainViewOrigin ( ) ;
angles = MainViewAngles ( ) ;
if ( args . ArgC ( ) = = 2 & & atoi ( args [ 1 ] ) = = 2 )
{
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( pPlayer )
{
vecOrigin = pPlayer - > GetAbsOrigin ( ) ;
angles = pPlayer - > GetAbsAngles ( ) ;
}
}
}
CON_COMMAND ( spec_pos , " dump position and angles to the console " )
{
Vector vecOrigin ;
QAngle angles ;
GetPos ( args , vecOrigin , angles ) ;
Warning ( " spec_goto %.1f %.1f %.1f %.1f %.1f \n " , vecOrigin . x , vecOrigin . y ,
vecOrigin . z , angles . x , angles . y ) ;
}
CON_COMMAND ( getpos , " dump position and angles to the console " )
{
Vector vecOrigin ;
QAngle angles ;
GetPos ( args , vecOrigin , angles ) ;
const char * pCommand1 = " setpos " ;
const char * pCommand2 = " setang " ;
if ( args . ArgC ( ) = = 2 & & atoi ( args [ 1 ] ) = = 2 )
{
pCommand1 = " setpos_exact " ;
pCommand2 = " setang_exact " ;
}
Warning ( " %s %f %f %f; " , pCommand1 , vecOrigin . x , vecOrigin . y , vecOrigin . z ) ;
Warning ( " %s %f %f %f \n " , pCommand2 , angles . x , angles . y , angles . z ) ;
}