You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
9.0 KiB
290 lines
9.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
//=======================================================================================// |
|
|
|
#include "cl_screenshotmanager.h" |
|
#include "replay/screenshot.h" |
|
#include "replaysystem.h" |
|
#include "cl_replaymanager.h" |
|
#include "replay/replayutils.h" |
|
#include "replay/ireplayscreenshotsystem.h" |
|
#include "gametrace.h" |
|
#include "icliententity.h" |
|
#include "imageutils.h" |
|
#include "filesystem.h" |
|
#include "fmtstr.h" |
|
#include "vprof.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
#define SCREENSHOTS_SUBDIR "screenshots" |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
CScreenshotManager::CScreenshotManager() |
|
: m_flLastScreenshotTime( 0.0f ), |
|
m_hScreenshotReplay( REPLAY_HANDLE_INVALID ) |
|
{ |
|
} |
|
|
|
CScreenshotManager::~CScreenshotManager() |
|
{ |
|
} |
|
|
|
bool CScreenshotManager::Init() |
|
{ |
|
// Create thumbnails directory |
|
CFmtStr fmtThumbnailsPath( |
|
"%s%cmaterials%cvgui%creplay%cthumbnails", |
|
g_pEngine->GetGameDir(), CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, |
|
CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR |
|
); |
|
g_pFullFileSystem->CreateDirHierarchy( fmtThumbnailsPath.Access() ); |
|
|
|
// Compute all possible resolutions if first time we're running this function |
|
for ( int iAspect = 0; iAspect < 3; ++iAspect ) |
|
{ |
|
for ( int iRes = 0; iRes < 2; ++iRes ) |
|
{ |
|
int nWidth = (int)FastPow2( 9 + iRes ); |
|
m_aScreenshotWidths[ iAspect ][ iRes ] = nWidth; |
|
} |
|
} |
|
|
|
// Set current screenshot dims to 0 - screenshot cache will be created first time we see |
|
// proper screen dimensions |
|
m_nPrevScreenDims[0] = 0; |
|
m_nPrevScreenDims[1] = 0; |
|
|
|
// Initialize screenshot taker in client |
|
g_pClient->GetReplayScreenshotSystem()->UpdateReplayScreenshotCache(); |
|
|
|
return true; |
|
} |
|
|
|
float CScreenshotManager::GetNextThinkTime() const |
|
{ |
|
return 0.0f; |
|
} |
|
|
|
void CScreenshotManager::Think() |
|
{ |
|
VPROF_BUDGET( "CScreenshotManager::Think", VPROF_BUDGETGROUP_REPLAY ); |
|
|
|
// |
|
// NOTE:DoCaptureScreenshot() gets called from CReplaySystem::CL_Render() |
|
// |
|
|
|
IReplayScreenshotSystem *pScreenshotSystem = g_pClient->GetReplayScreenshotSystem(); Assert( pScreenshotSystem ); |
|
|
|
// Check to see if screen resolution changed, and if so, update the client-side screenshot cache. |
|
int nScreenWidth = g_pEngineClient->GetScreenWidth(); |
|
int nScreenHeight = g_pEngineClient->GetScreenHeight(); |
|
if ( !CL_GetMovieManager()->IsRendering() && ( m_nPrevScreenDims[0] != nScreenWidth || m_nPrevScreenDims[1] != nScreenHeight ) ) |
|
{ |
|
if ( m_nPrevScreenDims[0] != 0 ) // If this is not the first update |
|
{ |
|
pScreenshotSystem->UpdateReplayScreenshotCache(); |
|
} |
|
|
|
m_nPrevScreenDims[0] = nScreenWidth; |
|
m_nPrevScreenDims[1] = nScreenHeight; |
|
} |
|
} |
|
|
|
bool CScreenshotManager::ShouldCaptureScreenshot() |
|
{ |
|
// Record a screenshot if its been setup |
|
return ( m_flScreenshotCaptureTime >= 0.0f && m_flScreenshotCaptureTime <= g_pEngine->GetHostTime() ); |
|
} |
|
|
|
void CScreenshotManager::CaptureScreenshot( CaptureScreenshotParams_t& params ) |
|
{ |
|
extern ConVar replay_enableeventbasedscreenshots; |
|
if ( !replay_enableeventbasedscreenshots.GetBool() && !params.m_bPrimary ) |
|
return; |
|
|
|
// Schedule screenshot |
|
m_flScreenshotCaptureTime = g_pEngine->GetHostTime() + params.m_flDelay; |
|
|
|
// Cache parameters for when we take the screenshot |
|
V_memcpy( &m_screenshotParams, ¶ms, sizeof( params ) ); |
|
} |
|
|
|
void CScreenshotManager::DoCaptureScreenshot() |
|
{ |
|
// Reset screenshot capture schedule time, even if we don't end up actually taking the screenshot |
|
m_flScreenshotCaptureTime = -1.0f; |
|
|
|
// Make sure we're in-game |
|
if ( !g_pEngineClient->IsConnected() ) |
|
return; |
|
|
|
// Get a pointer to the replay |
|
CReplay *pReplay = ::GetReplay( m_hScreenshotReplay ); |
|
if ( !pReplay ) |
|
{ |
|
AssertMsg( 0, "Failed to take screenshot!\n" ); |
|
return; |
|
} |
|
|
|
// Max # of screenshots already taken? |
|
extern ConVar replay_maxscreenshotsperreplay; |
|
int nScreenshotLimit = replay_maxscreenshotsperreplay.GetInt(); |
|
if ( nScreenshotLimit && pReplay->GetScreenshotCount() >= nScreenshotLimit ) |
|
return; |
|
|
|
// If not enough time has passed since the last screenshot was taken, get out |
|
extern ConVar replay_mintimebetweenscreenshots; |
|
if ( !m_screenshotParams.m_bIgnoreMinTimeBetweenScreenshots && |
|
( g_pEngine->GetHostTime() - m_flLastScreenshotTime < replay_mintimebetweenscreenshots.GetInt() ) ) |
|
return; |
|
|
|
// Update last screenshot taken time |
|
m_flLastScreenshotTime = g_pEngine->GetHostTime(); |
|
|
|
// Setup screenshot base filename as <.dem base filename>_<N> |
|
char szBaseFilename[ MAX_OSPATH ]; |
|
char szIdealBaseFilename[ MAX_OSPATH ]; |
|
V_strcpy_safe( szIdealBaseFilename, CL_GetRecordingSessionManager()->m_ServerRecordingState.m_strSessionName.Get() ); |
|
Replay_GetFirstAvailableFilename( szBaseFilename, sizeof( szBaseFilename ), szIdealBaseFilename, ".vtf", |
|
"materials\\vgui\\replay\\thumbnails", pReplay->GetScreenshotCount() ); |
|
|
|
// Remove extension |
|
int i = V_strlen( szBaseFilename ) - 1; |
|
while ( i >= 0 ) |
|
{ |
|
if ( szBaseFilename[ i ] == '.' ) |
|
break; |
|
--i; |
|
} |
|
szBaseFilename[ i ] = '\0'; |
|
|
|
// Get destination file |
|
char szScreenshotPath[ MAX_OSPATH ]; |
|
V_snprintf( szScreenshotPath, sizeof( szScreenshotPath ), "materials\\vgui\\replay\\thumbnails\\%s.vtf", szBaseFilename ); |
|
|
|
// Make sure we're using the correct path separator |
|
V_FixSlashes( szScreenshotPath ); |
|
|
|
// Setup screenshot dimensions |
|
int nScreenshotDims[2]; |
|
GetUnpaddedScreenshotSize( nScreenshotDims[0], nScreenshotDims[1] ); |
|
|
|
// Setup parameters for screenshot |
|
WriteReplayScreenshotParams_t params; |
|
V_memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
// Setup the camera |
|
Vector origin; |
|
QAngle angles; |
|
if ( m_screenshotParams.m_nEntity > 0 ) |
|
{ |
|
IClientEntity *pEntity = entitylist->GetClientEntity( m_screenshotParams.m_nEntity ); |
|
if ( pEntity ) |
|
{ |
|
// Clip the camera position if any world geometry is in the way |
|
trace_t trace; |
|
Ray_t ray; |
|
CTraceFilterWorldAndPropsOnly traceFilter; |
|
Vector vStartPos = pEntity->GetAbsOrigin(); |
|
ray.Init( vStartPos, pEntity->GetAbsOrigin() + m_screenshotParams.m_posCamera ); |
|
g_pEngineTraceClient->TraceRay( ray, MASK_PLAYERSOLID, &traceFilter, &trace ); |
|
|
|
// Setup world position and angles for camera |
|
origin = trace.endpos; |
|
|
|
if ( trace.DidHit() ) |
|
{ |
|
float d = 5; // The distance to push in if we |
|
Vector dir = trace.endpos - vStartPos; |
|
VectorNormalize( dir ); |
|
origin -= Vector( d * dir.x, d * dir.y, d * dir.z ); |
|
} |
|
|
|
// Use the new camera origin |
|
params.m_pOrigin = &origin; |
|
|
|
// Use angles too if appropriate |
|
if ( m_screenshotParams.m_bUseCameraAngles ) |
|
{ |
|
angles = m_screenshotParams.m_angCamera; |
|
params.m_pAngles = &angles; |
|
} |
|
} |
|
} |
|
|
|
// Write the screenshot to disk |
|
params.m_nWidth = nScreenshotDims[0]; |
|
params.m_nHeight = nScreenshotDims[1]; |
|
params.m_pFilename = szScreenshotPath; |
|
g_pClient->GetReplayScreenshotSystem()->WriteReplayScreenshot( params ); |
|
|
|
// Write a generic VMT |
|
char szVTFFullPath[ MAX_OSPATH ]; |
|
V_snprintf( szVTFFullPath, sizeof( szVTFFullPath ), "%s\\materials\\vgui\\replay\\thumbnails\\%s.vtf", g_pEngine->GetGameDir(), szBaseFilename ); |
|
V_FixSlashes( szVTFFullPath ); |
|
ImgUtl_WriteGenericVMT( szVTFFullPath, "vgui/replay/thumbnails" ); |
|
|
|
// Create the new screenshot info |
|
pReplay->AddScreenshot( nScreenshotDims[0], nScreenshotDims[1], szBaseFilename ); |
|
} |
|
|
|
void CScreenshotManager::DeleteScreenshotsForReplay( CReplay *pReplay ) |
|
{ |
|
char szFilename[ MAX_OSPATH ]; |
|
for ( int i = 0; i < pReplay->GetScreenshotCount(); ++i ) |
|
{ |
|
const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( i ); |
|
|
|
// Delete the VGUI thumbnail VTF |
|
V_snprintf( szFilename, sizeof( szFilename ), "materials\\vgui\\replay\\thumbnails\\%s.vtf", pScreenshot->m_szBaseFilename ); |
|
V_FixSlashes( szFilename ); |
|
g_pFullFileSystem->RemoveFile( szFilename ); |
|
|
|
// Delete the VGUI thumbnail VMT |
|
V_snprintf( szFilename, sizeof( szFilename ), "materials\\vgui\\replay\\thumbnails\\%s.vmt", pScreenshot->m_szBaseFilename ); |
|
V_FixSlashes( szFilename ); |
|
g_pFullFileSystem->RemoveFile( szFilename ); |
|
} |
|
} |
|
|
|
void CScreenshotManager::GetUnpaddedScreenshotSize( int &nOutWidth, int &nOutHeight ) |
|
{ |
|
// Figure out the proper screenshot size to use based on the aspect ratio |
|
int nScreenWidth = g_pEngineClient->GetScreenWidth(); |
|
int nScreenHeight = g_pEngineClient->GetScreenHeight(); |
|
float flAspectRatio = (float)nScreenWidth / nScreenHeight; |
|
|
|
// Get the screenshot res |
|
extern ConVar replay_screenshotresolution; |
|
int iRes = clamp( replay_screenshotresolution.GetInt(), 0, 1 ); |
|
|
|
int iAspect; |
|
if ( flAspectRatio == 16.0f/9 ) |
|
{ |
|
iAspect = 0; |
|
} |
|
else if ( flAspectRatio == 16.0f/10 ) |
|
{ |
|
iAspect = 1; |
|
} |
|
else |
|
{ |
|
iAspect = 2; // 4:3 |
|
} |
|
|
|
static float s_flInvAspectRatios[3] = { 9.0f/16.0f, 10.0f/16, 3.0f/4 }; |
|
nOutWidth = min( nScreenWidth, m_aScreenshotWidths[ iAspect ][ iRes ] ); |
|
nOutHeight = m_aScreenshotWidths[ iAspect ][ iRes ] * s_flInvAspectRatios[ iAspect ]; |
|
} |
|
|
|
void CScreenshotManager::SetScreenshotReplay( ReplayHandle_t hReplay ) |
|
{ |
|
m_hScreenshotReplay = hReplay; |
|
} |
|
|
|
//----------------------------------------------------------------------------------------
|
|
|