Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "cl_screenshotmanager.h"
  5. #include "replay/screenshot.h"
  6. #include "replaysystem.h"
  7. #include "cl_replaymanager.h"
  8. #include "replay/replayutils.h"
  9. #include "replay/ireplayscreenshotsystem.h"
  10. #include "gametrace.h"
  11. #include "icliententity.h"
  12. #include "imageutils.h"
  13. #include "filesystem.h"
  14. #include "fmtstr.h"
  15. #include "vprof.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. //----------------------------------------------------------------------------------------
  19. #define SCREENSHOTS_SUBDIR "screenshots"
  20. //----------------------------------------------------------------------------------------
  21. CScreenshotManager::CScreenshotManager()
  22. : m_flLastScreenshotTime( 0.0f ),
  23. m_hScreenshotReplay( REPLAY_HANDLE_INVALID )
  24. {
  25. }
  26. CScreenshotManager::~CScreenshotManager()
  27. {
  28. }
  29. bool CScreenshotManager::Init()
  30. {
  31. // Create thumbnails directory
  32. CFmtStr fmtThumbnailsPath(
  33. "%s%cmaterials%cvgui%creplay%cthumbnails",
  34. g_pEngine->GetGameDir(), CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR,
  35. CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR
  36. );
  37. g_pFullFileSystem->CreateDirHierarchy( fmtThumbnailsPath.Access() );
  38. // Compute all possible resolutions if first time we're running this function
  39. for ( int iAspect = 0; iAspect < 3; ++iAspect )
  40. {
  41. for ( int iRes = 0; iRes < 2; ++iRes )
  42. {
  43. int nWidth = (int)FastPow2( 9 + iRes );
  44. m_aScreenshotWidths[ iAspect ][ iRes ] = nWidth;
  45. }
  46. }
  47. // Set current screenshot dims to 0 - screenshot cache will be created first time we see
  48. // proper screen dimensions
  49. m_nPrevScreenDims[0] = 0;
  50. m_nPrevScreenDims[1] = 0;
  51. // Initialize screenshot taker in client
  52. g_pClient->GetReplayScreenshotSystem()->UpdateReplayScreenshotCache();
  53. return true;
  54. }
  55. float CScreenshotManager::GetNextThinkTime() const
  56. {
  57. return 0.0f;
  58. }
  59. void CScreenshotManager::Think()
  60. {
  61. VPROF_BUDGET( "CScreenshotManager::Think", VPROF_BUDGETGROUP_REPLAY );
  62. //
  63. // NOTE:DoCaptureScreenshot() gets called from CReplaySystem::CL_Render()
  64. //
  65. IReplayScreenshotSystem *pScreenshotSystem = g_pClient->GetReplayScreenshotSystem(); Assert( pScreenshotSystem );
  66. // Check to see if screen resolution changed, and if so, update the client-side screenshot cache.
  67. int nScreenWidth = g_pEngineClient->GetScreenWidth();
  68. int nScreenHeight = g_pEngineClient->GetScreenHeight();
  69. if ( !CL_GetMovieManager()->IsRendering() && ( m_nPrevScreenDims[0] != nScreenWidth || m_nPrevScreenDims[1] != nScreenHeight ) )
  70. {
  71. if ( m_nPrevScreenDims[0] != 0 ) // If this is not the first update
  72. {
  73. pScreenshotSystem->UpdateReplayScreenshotCache();
  74. }
  75. m_nPrevScreenDims[0] = nScreenWidth;
  76. m_nPrevScreenDims[1] = nScreenHeight;
  77. }
  78. }
  79. bool CScreenshotManager::ShouldCaptureScreenshot()
  80. {
  81. // Record a screenshot if its been setup
  82. return ( m_flScreenshotCaptureTime >= 0.0f && m_flScreenshotCaptureTime <= g_pEngine->GetHostTime() );
  83. }
  84. void CScreenshotManager::CaptureScreenshot( CaptureScreenshotParams_t& params )
  85. {
  86. extern ConVar replay_enableeventbasedscreenshots;
  87. if ( !replay_enableeventbasedscreenshots.GetBool() && !params.m_bPrimary )
  88. return;
  89. // Schedule screenshot
  90. m_flScreenshotCaptureTime = g_pEngine->GetHostTime() + params.m_flDelay;
  91. // Cache parameters for when we take the screenshot
  92. V_memcpy( &m_screenshotParams, &params, sizeof( params ) );
  93. }
  94. void CScreenshotManager::DoCaptureScreenshot()
  95. {
  96. // Reset screenshot capture schedule time, even if we don't end up actually taking the screenshot
  97. m_flScreenshotCaptureTime = -1.0f;
  98. // Make sure we're in-game
  99. if ( !g_pEngineClient->IsConnected() )
  100. return;
  101. // Get a pointer to the replay
  102. CReplay *pReplay = ::GetReplay( m_hScreenshotReplay );
  103. if ( !pReplay )
  104. {
  105. AssertMsg( 0, "Failed to take screenshot!\n" );
  106. return;
  107. }
  108. // Max # of screenshots already taken?
  109. extern ConVar replay_maxscreenshotsperreplay;
  110. int nScreenshotLimit = replay_maxscreenshotsperreplay.GetInt();
  111. if ( nScreenshotLimit && pReplay->GetScreenshotCount() >= nScreenshotLimit )
  112. return;
  113. // If not enough time has passed since the last screenshot was taken, get out
  114. extern ConVar replay_mintimebetweenscreenshots;
  115. if ( !m_screenshotParams.m_bIgnoreMinTimeBetweenScreenshots &&
  116. ( g_pEngine->GetHostTime() - m_flLastScreenshotTime < replay_mintimebetweenscreenshots.GetInt() ) )
  117. return;
  118. // Update last screenshot taken time
  119. m_flLastScreenshotTime = g_pEngine->GetHostTime();
  120. // Setup screenshot base filename as <.dem base filename>_<N>
  121. char szBaseFilename[ MAX_OSPATH ];
  122. char szIdealBaseFilename[ MAX_OSPATH ];
  123. V_strcpy_safe( szIdealBaseFilename, CL_GetRecordingSessionManager()->m_ServerRecordingState.m_strSessionName.Get() );
  124. Replay_GetFirstAvailableFilename( szBaseFilename, sizeof( szBaseFilename ), szIdealBaseFilename, ".vtf",
  125. "materials\\vgui\\replay\\thumbnails", pReplay->GetScreenshotCount() );
  126. // Remove extension
  127. int i = V_strlen( szBaseFilename ) - 1;
  128. while ( i >= 0 )
  129. {
  130. if ( szBaseFilename[ i ] == '.' )
  131. break;
  132. --i;
  133. }
  134. szBaseFilename[ i ] = '\0';
  135. // Get destination file
  136. char szScreenshotPath[ MAX_OSPATH ];
  137. V_snprintf( szScreenshotPath, sizeof( szScreenshotPath ), "materials\\vgui\\replay\\thumbnails\\%s.vtf", szBaseFilename );
  138. // Make sure we're using the correct path separator
  139. V_FixSlashes( szScreenshotPath );
  140. // Setup screenshot dimensions
  141. int nScreenshotDims[2];
  142. GetUnpaddedScreenshotSize( nScreenshotDims[0], nScreenshotDims[1] );
  143. // Setup parameters for screenshot
  144. WriteReplayScreenshotParams_t params;
  145. V_memset( &params, 0, sizeof( params ) );
  146. // Setup the camera
  147. Vector origin;
  148. QAngle angles;
  149. if ( m_screenshotParams.m_nEntity > 0 )
  150. {
  151. IClientEntity *pEntity = entitylist->GetClientEntity( m_screenshotParams.m_nEntity );
  152. if ( pEntity )
  153. {
  154. // Clip the camera position if any world geometry is in the way
  155. trace_t trace;
  156. Ray_t ray;
  157. CTraceFilterWorldAndPropsOnly traceFilter;
  158. Vector vStartPos = pEntity->GetAbsOrigin();
  159. ray.Init( vStartPos, pEntity->GetAbsOrigin() + m_screenshotParams.m_posCamera );
  160. g_pEngineTraceClient->TraceRay( ray, MASK_PLAYERSOLID, &traceFilter, &trace );
  161. // Setup world position and angles for camera
  162. origin = trace.endpos;
  163. if ( trace.DidHit() )
  164. {
  165. float d = 5; // The distance to push in if we
  166. Vector dir = trace.endpos - vStartPos;
  167. VectorNormalize( dir );
  168. origin -= Vector( d * dir.x, d * dir.y, d * dir.z );
  169. }
  170. // Use the new camera origin
  171. params.m_pOrigin = &origin;
  172. // Use angles too if appropriate
  173. if ( m_screenshotParams.m_bUseCameraAngles )
  174. {
  175. angles = m_screenshotParams.m_angCamera;
  176. params.m_pAngles = &angles;
  177. }
  178. }
  179. }
  180. // Write the screenshot to disk
  181. params.m_nWidth = nScreenshotDims[0];
  182. params.m_nHeight = nScreenshotDims[1];
  183. params.m_pFilename = szScreenshotPath;
  184. g_pClient->GetReplayScreenshotSystem()->WriteReplayScreenshot( params );
  185. // Write a generic VMT
  186. char szVTFFullPath[ MAX_OSPATH ];
  187. V_snprintf( szVTFFullPath, sizeof( szVTFFullPath ), "%s\\materials\\vgui\\replay\\thumbnails\\%s.vtf", g_pEngine->GetGameDir(), szBaseFilename );
  188. V_FixSlashes( szVTFFullPath );
  189. ImgUtl_WriteGenericVMT( szVTFFullPath, "vgui/replay/thumbnails" );
  190. // Create the new screenshot info
  191. pReplay->AddScreenshot( nScreenshotDims[0], nScreenshotDims[1], szBaseFilename );
  192. }
  193. void CScreenshotManager::DeleteScreenshotsForReplay( CReplay *pReplay )
  194. {
  195. char szFilename[ MAX_OSPATH ];
  196. for ( int i = 0; i < pReplay->GetScreenshotCount(); ++i )
  197. {
  198. const CReplayScreenshot *pScreenshot = pReplay->GetScreenshot( i );
  199. // Delete the VGUI thumbnail VTF
  200. V_snprintf( szFilename, sizeof( szFilename ), "materials\\vgui\\replay\\thumbnails\\%s.vtf", pScreenshot->m_szBaseFilename );
  201. V_FixSlashes( szFilename );
  202. g_pFullFileSystem->RemoveFile( szFilename );
  203. // Delete the VGUI thumbnail VMT
  204. V_snprintf( szFilename, sizeof( szFilename ), "materials\\vgui\\replay\\thumbnails\\%s.vmt", pScreenshot->m_szBaseFilename );
  205. V_FixSlashes( szFilename );
  206. g_pFullFileSystem->RemoveFile( szFilename );
  207. }
  208. }
  209. void CScreenshotManager::GetUnpaddedScreenshotSize( int &nOutWidth, int &nOutHeight )
  210. {
  211. // Figure out the proper screenshot size to use based on the aspect ratio
  212. int nScreenWidth = g_pEngineClient->GetScreenWidth();
  213. int nScreenHeight = g_pEngineClient->GetScreenHeight();
  214. float flAspectRatio = (float)nScreenWidth / nScreenHeight;
  215. // Get the screenshot res
  216. extern ConVar replay_screenshotresolution;
  217. int iRes = clamp( replay_screenshotresolution.GetInt(), 0, 1 );
  218. int iAspect;
  219. if ( flAspectRatio == 16.0f/9 )
  220. {
  221. iAspect = 0;
  222. }
  223. else if ( flAspectRatio == 16.0f/10 )
  224. {
  225. iAspect = 1;
  226. }
  227. else
  228. {
  229. iAspect = 2; // 4:3
  230. }
  231. static float s_flInvAspectRatios[3] = { 9.0f/16.0f, 10.0f/16, 3.0f/4 };
  232. nOutWidth = min( nScreenWidth, m_aScreenshotWidths[ iAspect ][ iRes ] );
  233. nOutHeight = m_aScreenshotWidths[ iAspect ][ iRes ] * s_flInvAspectRatios[ iAspect ];
  234. }
  235. void CScreenshotManager::SetScreenshotReplay( ReplayHandle_t hReplay )
  236. {
  237. m_hScreenshotReplay = hReplay;
  238. }
  239. //----------------------------------------------------------------------------------------