diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/replay/vgui/replayrenderoverlay.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/replay/vgui/replayrenderoverlay.cpp')
| -rw-r--r-- | game/client/replay/vgui/replayrenderoverlay.cpp | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/game/client/replay/vgui/replayrenderoverlay.cpp b/game/client/replay/vgui/replayrenderoverlay.cpp new file mode 100644 index 0000000..52374ad --- /dev/null +++ b/game/client/replay/vgui/replayrenderoverlay.cpp @@ -0,0 +1,380 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "cbase.h" + +#if defined( REPLAY_ENABLED ) + +#include "replayrenderoverlay.h" +#include "vgui_controls/TextImage.h" +#include "replay/genericclassbased_replay.h" +#include "iclientmode.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "ienginevgui.h" +#include "vgui/IVGui.h" +#include "econ/confirm_dialog.h" +#include "replay/ireplaymanager.h" +#include "replay/irecordingsessionmanager.h" +#include "replay/ireplaymoviemanager.h" +#include "replay/replayrenderer.h" +#include "econ/econ_controls.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- + +extern IReplayMovieManager *g_pReplayMovieManager; + +//----------------------------------------------------------------------------- + +using namespace vgui; + +//----------------------------------------------------------------------------- + +#define TMP_ENCODED_AUDIO ".tmp.aac" +#ifdef USE_WEBM_FOR_REPLAY +#define TMP_ENCODED_VIDEO ".tmp.webm" +#else +#define TMP_ENCODED_VIDEO ".tmp.mov" +#endif + +//----------------------------------------------------------------------------- + +ConVar replay_enablerenderpreview( "replay_enablerenderpreview", "1", FCVAR_CLIENTDLL | FCVAR_DONTRECORD | FCVAR_ARCHIVE, "Enable preview during replay render." ); + +//----------------------------------------------------------------------------- + +void OnRenderCancelDialogButtonPressed( bool bConfirm, void *pContext ) +{ + if ( bConfirm ) + { + g_pReplayMovieManager->CancelRender(); + } +} + +//----------------------------------------------------------------------------- + +CReplayRenderOverlay::CReplayRenderOverlay( Panel *pParent ) +: BaseClass( pParent, "ReplayRenderOverlay" ), + m_pBottom( NULL ), + m_pCancelButton( NULL ), + m_pTitleLabel( NULL ), + m_pProgressLabel( NULL ), + m_pFilenameLabel( NULL ), + m_pRenderProgress( NULL ), + m_pRenderer( NULL ), + m_pPreviewCheckButton( NULL ), + m_unNumFrames( 0 ), + m_flStartTime( 0.0f ), + m_flPreviousTimeLeft( 0.0f ) +{ + if ( pParent == NULL ) + { + vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme"); + SetScheme(scheme); + SetProportional( true ); + } + + ivgui()->AddTickSignal( GetVPanel(), 10 ); + + m_pRenderer = new CReplayRenderer( this ); +} + +CReplayRenderOverlay::~CReplayRenderOverlay() +{ + ivgui()->RemoveTickSignal( GetVPanel() ); + + delete m_pRenderer; +} + +void CReplayRenderOverlay::Show() +{ + // Setup panel + SetVisible( true ); + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( true ); + MakePopup( true ); + MoveToFront(); + TFModalStack()->PushModal( this ); + + // Make sure game UI is hidden + engine->ClientCmd_Unrestricted( "gameui_hide" ); + + InvalidateLayout( false, true ); +} + +void CReplayRenderOverlay::Hide() +{ + SetVisible( false ); + TFModalStack()->PopModal( this ); + MarkForDeletion(); +} + +void CReplayRenderOverlay::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // Load controls + LoadControlSettings( "Resource/UI/replayrenderoverlay.res", "GAME" ); + + // Layout bottom + m_pBottom = dynamic_cast< EditablePanel * >( FindChildByName( "BottomPanel" ) ); + if ( !m_pBottom ) + return; + + // Find some controls + m_pTitleLabel = dynamic_cast< CExLabel * >( FindChildByName( "TitleLabel" ) ); + m_pProgressLabel = dynamic_cast< CExLabel * >( FindChildByName( "ProgressLabel" ) ); + m_pRenderProgress = dynamic_cast< ProgressBar * >( FindChildByName( "RenderProgress" ) ); + m_pCancelButton = dynamic_cast< CExButton * >( FindChildByName( "CancelButton" ) ); + m_pFilenameLabel = dynamic_cast< CExLabel * >( FindChildByName( "FilenameLabel" ) ); + m_pPreviewCheckButton = dynamic_cast< CheckButton * >( FindChildByName( "PreviewCheckButton" ) ); + + m_pPreviewCheckButton->SetProportional( false ); + m_pPreviewCheckButton->SetSelected( replay_enablerenderpreview.GetBool() ); + m_pPreviewCheckButton->AddActionSignalTarget( this ); + + const char *pMovieFilename = m_pRenderer->GetMovieFilename(); + if ( m_pFilenameLabel && pMovieFilename ) + { + const char *pFilename = V_UnqualifiedFileName( pMovieFilename ); + m_pFilenameLabel->SetText( pFilename ); + } +} + +void CReplayRenderOverlay::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( !m_pBottom ) + return; + + int sw, sh; + vgui::surface()->GetScreenSize( sw, sh ); + SetBounds( 0, 0, sw, sh ); + + int nBottomPanelHeight = sh * .13f; + int nBottomPanelStartY = sh - nBottomPanelHeight; + m_pBottom->SetBounds( 0, nBottomPanelStartY, sw, nBottomPanelHeight ); + + int nBottomW = sw; + int nBottomH = nBottomPanelHeight; + + // Setup progress bar + if ( !m_pRenderProgress ) + return; + + int nProgHeight = YRES(20); + int nMargin = nBottomW/5; + int nProgX = nMargin; + int nProgY = nBottomPanelStartY + ( nBottomH - nProgHeight ) / 2; + int nProgW = nBottomW - 2*nMargin; + + // Only show progress bar if replay is valid and length of render is non-zero, and the record start tick exists + CReplay *pReplay = g_pReplayManager->GetPlayingReplay(); + if ( pReplay ) + { + const float flTotalTime = pReplay->m_flLength; + const int nServerRecordStartTick = g_pClientReplayContext->GetRecordingSessionManager()->GetServerStartTickForSession( pReplay->m_hSession ); // NOTE: Returns -1 on fail + if ( flTotalTime > 0.0f && nServerRecordStartTick >= 0 ) + { + m_pRenderProgress->SetVisible( true ); + m_pRenderProgress->SetBounds( nProgX, nProgY, nProgW, nProgHeight ); + m_pRenderProgress->SetSegmentInfo( XRES(1), XRES(8) ); + } + } + + // Layout title label + const int nTitleLabelY = nBottomPanelStartY + ( m_pBottom->GetTall() - m_pTitleLabel->GetTall() ) / 2; + if ( m_pTitleLabel ) + { + m_pTitleLabel->SizeToContents(); + m_pTitleLabel->SetPos( ( nProgX - m_pTitleLabel->GetWide() ) / 2, nTitleLabelY ); + } + + // Layout preview check button + if ( m_pPreviewCheckButton ) + { + m_pPreviewCheckButton->SizeToContents(); + m_pPreviewCheckButton->SetPos( ( nProgX - m_pPreviewCheckButton->GetWide() ) / 2, nTitleLabelY + m_pTitleLabel->GetTall() + YRES(3) ); + } + + // Layout filename label + if ( m_pFilenameLabel ) + { + int nProgBottomY = nProgY + nProgHeight; + m_pFilenameLabel->SizeToContents(); + m_pFilenameLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pFilenameLabel->GetTall() ) / 2 ); + } + + // Layout progress label + if ( m_pProgressLabel ) + { + int nProgBottomY = nProgY + nProgHeight; + m_pProgressLabel->SizeToContents(); + m_pProgressLabel->SetPos( nProgX, nProgBottomY + ( sh - nProgBottomY - m_pProgressLabel->GetTall() ) / 2 ); + m_pProgressLabel->SetWide( nProgW ); + } + + // Layout cancel button + if ( !m_pCancelButton ) + return; + + // Put cancel button half way in between progress bar and screen right + int nProgRightX = nProgX + nProgW; + m_pCancelButton->SetPos( + nProgRightX + ( m_pBottom->GetWide() - nProgRightX - m_pCancelButton->GetWide() ) / 2, + nBottomPanelStartY + ( m_pBottom->GetTall() - m_pCancelButton->GetTall() ) / 2 + ); + + SetXToRed( m_pCancelButton ); + + m_pCancelButton->RequestFocus(); +} + +void CReplayRenderOverlay::OnTick() +{ +#if _DEBUG + if ( m_bReloadScheme ) + { + InvalidateLayout( true, true ); + m_bReloadScheme = false; + } +#endif + + // Update progress + if ( m_pRenderProgress ) + { + CReplay *pReplay = g_pReplayManager->GetPlayingReplay(); + if ( pReplay && m_pRenderProgress->IsVisible() ) + { + float flCurTime, flTotalTime; + g_pClientReplayContext->GetPlaybackTimes( flCurTime, flTotalTime, pReplay, m_pRenderer->GetPerformance() ); + const float flProgress = ( flTotalTime == 0.0f ) ? 1.0f : ( flCurTime / flTotalTime ); + + Assert( flTotalTime > 0.0f ); // NOTE: Progress bar will always be invisible if total time is 0, but check anyway to be safe. + m_pRenderProgress->SetProgress( MAX( m_pRenderProgress->GetProgress(), flProgress ) ); // The MAX() here keeps the progress bar from thrashing + + if ( m_pProgressLabel ) + { + // @note Tom Bui: this is a horribly ugly hack, but the first couple of frames take a really freaking long time, so that + // really blows out the estimate + float flTimePassed = 0.0f; + ++m_unNumFrames; + const uint32 kNumFramesToWait = 10; + if ( m_unNumFrames < kNumFramesToWait ) + { + m_flStartTime = gpGlobals->realtime; + } + else if ( m_unNumFrames > kNumFramesToWait ) + { + flTimePassed = gpGlobals->realtime - m_flStartTime; + float flEstimatedTimeLeft = flProgress > 0.0f ? ( flTimePassed / flProgress ) - flTimePassed : 0.0f; + // exponential moving average FIR filter + // S(t) = smoothing_factor * Y(t) + (1 - smoothing_factor)* Y(t-1) + // previous value is essentially 90% of the current value + const float kSmoothingFactor = 0.1f; + if ( m_flPreviousTimeLeft == 0.0f ) + { + m_flPreviousTimeLeft = flEstimatedTimeLeft; + } + else + { + m_flPreviousTimeLeft = kSmoothingFactor * flEstimatedTimeLeft + ( 1 - kSmoothingFactor ) * m_flPreviousTimeLeft; + } + } + + wchar_t wszTimeLeft[256]; + wchar_t wszTime[256]; + { + const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( m_flPreviousTimeLeft ) ); + g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTimeLeft, sizeof( wszTimeLeft ) ); + } + { + const char *pRenderTime = CReplayTime::FormatTimeString( RoundFloatToInt( flTimePassed ) ); + g_pVGuiLocalize->ConvertANSIToUnicode( pRenderTime, wszTime, sizeof( wszTime ) ); + } + wchar_t wszText[256]; + g_pVGuiLocalize->ConstructString_safe( wszText, g_pVGuiLocalize->Find( "#Replay_RenderOverlay_TimeLeft" ), 2, wszTime, wszTimeLeft ); + m_pProgressLabel->SetText( wszText ); + } + } + } +} + +void CReplayRenderOverlay::OnMousePressed( MouseCode nCode ) +{ +#if _DEBUG + m_bReloadScheme = true; +#endif + BaseClass::OnMousePressed( nCode ); +} + +void CReplayRenderOverlay::OnKeyCodeTyped( vgui::KeyCode nCode ) +{ + if ( nCode == KEY_ESCAPE ) + { + if ( TFModalStack()->Top() == GetVPanel() ) + { + OnCommand( "confirmcancel" ); + return; + } + } + + BaseClass::OnKeyCodeTyped( nCode ); +} + +void CReplayRenderOverlay::OnCommand( const char *pCommand ) +{ + if ( !V_stricmp( pCommand, "confirmcancel" ) ) + { + ShowConfirmDialog( "#Replay_CancelRenderTitle", "#Replay_ConfirmCancelRender", "#Replay_YesCancel", "#Replay_No", OnRenderCancelDialogButtonPressed, this, NULL, "replay\\replaydialog_warn.wav" ); + return; + } + + BaseClass::OnCommand( pCommand ); +} + +void CReplayRenderOverlay::OnCheckButtonChecked( Panel *pPanel ) +{ + replay_enablerenderpreview.SetValue( (int)m_pPreviewCheckButton->IsSelected() ); +} + +//----------------------------------------------------------------------------- + +static CReplayRenderOverlay *s_pRenderOverlay = NULL; + +void ReplayUI_OpenReplayRenderOverlay() +{ + if ( !g_pReplayMovieManager->IsRendering() ) + return; + + // Delete any existing panel + if ( s_pRenderOverlay ) + { + s_pRenderOverlay->MarkForDeletion(); + } + + // Create the panel - get the render resolution from the settings + s_pRenderOverlay = SETUP_PANEL( new CReplayRenderOverlay( NULL ) ); // Parenting to NULL allows us to turn off world rendering in engine/view.cpp (V_RenderView()) + + // Set the panel as the movie renderer, so it can receive begin/end render calls from the engine + g_pClientReplayContext->SetMovieRenderer( s_pRenderOverlay->m_pRenderer ); +} + +void ReplayUI_HideRenderOverlay() +{ + if ( s_pRenderOverlay ) + { + s_pRenderOverlay->MarkForDeletion(); + s_pRenderOverlay = NULL; + } + + g_pClientReplayContext->SetMovieRenderer( NULL ); +} + +//----------------------------------------------------------------------------- + +#endif |