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.
559 lines
16 KiB
559 lines
16 KiB
//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======= |
|
// |
|
// Purpose: VGUI panel which can play back video, in-engine |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include <vgui/IVGui.h> |
|
#include "vgui/IInput.h" |
|
#include <vgui/ISurface.h> |
|
#include "ienginevgui.h" |
|
#include "iclientmode.h" |
|
#include "vgui_video.h" |
|
#include "engine/ienginesound.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
using namespace vgui; |
|
|
|
static CUtlVector< VideoPanel * > g_vecVideoPanels; |
|
|
|
// Thiis is a hack due to the fact that the user can type quit with the video panel up, but it's parented to the GameUI dll root panel, which is already gone so |
|
// we would crash in the destructor |
|
void VGui_ClearVideoPanels() |
|
{ |
|
for ( int i = g_vecVideoPanels.Count() - 1; i >= 0; --i ) |
|
{ |
|
if ( g_vecVideoPanels[ i ] ) |
|
{ |
|
delete g_vecVideoPanels[ i ]; |
|
} |
|
} |
|
g_vecVideoPanels.RemoveAll(); |
|
} |
|
|
|
VideoPanel::VideoPanel( unsigned int nXPos, unsigned int nYPos, unsigned int nHeight, unsigned int nWidth ) : |
|
BaseClass( NULL, "VideoPanel" ), |
|
m_BIKHandle( BIKHANDLE_INVALID ), |
|
m_nPlaybackWidth( 0 ), |
|
m_nPlaybackHeight( 0 ), |
|
m_nShutdownCount( 0 ), |
|
m_bLooping( false ), |
|
m_bStopAllSounds( true ), |
|
m_bAllowInterruption( true ), |
|
m_bStarted( false ) |
|
{ |
|
|
|
vgui::VPANEL pParent = enginevgui->GetPanel( PANEL_GAMEUIDLL ); |
|
|
|
SetParent( pParent ); |
|
SetVisible( false ); |
|
|
|
// Must be passed in, off by default |
|
m_szExitCommand[0] = '\0'; |
|
|
|
m_bBlackBackground = true; |
|
|
|
SetKeyBoardInputEnabled( true ); |
|
SetMouseInputEnabled( false ); |
|
|
|
SetProportional( false ); |
|
SetVisible( true ); |
|
SetPaintBackgroundEnabled( false ); |
|
SetPaintBorderEnabled( false ); |
|
|
|
// Set us up |
|
SetTall( nHeight ); |
|
SetWide( nWidth ); |
|
SetPos( nXPos, nYPos ); |
|
|
|
SetScheme(vgui::scheme()->LoadSchemeFromFile( "resource/VideoPanelScheme.res", "VideoPanelScheme")); |
|
LoadControlSettings("resource/UI/VideoPanel.res"); |
|
|
|
// Let us update |
|
vgui::ivgui()->AddTickSignal( GetVPanel() ); |
|
|
|
g_vecVideoPanels.AddToTail( this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Properly shutdown out materials |
|
//----------------------------------------------------------------------------- |
|
VideoPanel::~VideoPanel( void ) |
|
{ |
|
g_vecVideoPanels.FindAndRemove( this ); |
|
|
|
#if !defined( _X360 ) || defined( BINK_ENABLED_FOR_X360 ) |
|
// Shut down this video |
|
if ( m_BIKHandle != BIKHANDLE_INVALID ) |
|
{ |
|
bik->DestroyMaterial( m_BIKHandle ); |
|
m_BIKHandle = BIKHANDLE_INVALID; |
|
m_pMaterial = NULL; |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Keeps a tab on when the movie is ending and allows a frame to pass to prevent threading issues |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::OnTick( void ) |
|
{ |
|
if ( m_nShutdownCount > 0 ) |
|
{ |
|
m_nShutdownCount++; |
|
|
|
if ( m_nShutdownCount > 10 ) |
|
{ |
|
OnClose(); |
|
m_nShutdownCount = 0; |
|
} |
|
} |
|
|
|
BaseClass::OnTick(); |
|
} |
|
|
|
void VideoPanel::OnVideoOver() |
|
{ |
|
StopPlayback(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Begins playback of a movie |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool VideoPanel::BeginPlayback( const char *pFilename ) |
|
{ |
|
if ( !pFilename || pFilename[ 0 ] == '\0' ) |
|
return false; |
|
|
|
#if !defined( _X360 ) || defined( BINK_ENABLED_FOR_X360 ) |
|
#ifdef _X360 |
|
XVIDEO_MODE videoMode; |
|
XGetVideoMode( &videoMode ); |
|
|
|
// for 50Hz PAL, load a 25Hz version of the ep1_recap movie. |
|
if( ( videoMode.RefreshRate < 59.0f ) && ( Q_stricmp( pFilename, "media/ep1_recap.bik" ) == 0 ) ) |
|
{ |
|
pFilename = "media/ep1_recap_25fps.bik"; |
|
} |
|
#endif |
|
|
|
// Destroy any previously allocated video |
|
if ( m_BIKHandle != BIKHANDLE_INVALID ) |
|
{ |
|
bik->DestroyMaterial( m_BIKHandle ); |
|
m_BIKHandle = BIKHANDLE_INVALID; |
|
m_pMaterial = NULL; |
|
} |
|
|
|
// Load and create our BINK video |
|
int nFlags = ( m_bLooping ? BIK_LOOP : 0 ); |
|
|
|
char szMaterialName[ FILENAME_MAX ]; |
|
Q_snprintf( szMaterialName, sizeof( szMaterialName ), "VideoBIKMaterial%i", g_pBIK->GetGlobalMaterialAllocationNumber() ); |
|
|
|
m_BIKHandle = bik->CreateMaterial( szMaterialName, pFilename, "GAME", nFlags ); |
|
if ( m_BIKHandle == BIKHANDLE_INVALID ) |
|
{ |
|
return false; |
|
} |
|
|
|
m_bStarted = true; |
|
|
|
// We want to be the sole audio source |
|
if ( m_bStopAllSounds ) |
|
{ |
|
enginesound->NotifyBeginMoviePlayback(); |
|
} |
|
|
|
int nWidth, nHeight; |
|
bik->GetFrameSize( m_BIKHandle, &nWidth, &nHeight ); |
|
bik->GetTexCoordRange( m_BIKHandle, &m_flU, &m_flV ); |
|
m_pMaterial = bik->GetMaterial( m_BIKHandle ); |
|
|
|
float flFrameRatio = ( (float) GetWide() / (float) GetTall() ); |
|
float flVideoRatio = ( (float) nWidth / (float) nHeight ); |
|
|
|
if ( flVideoRatio > flFrameRatio ) |
|
{ |
|
m_nPlaybackWidth = GetWide(); |
|
m_nPlaybackHeight = ( GetWide() / flVideoRatio ); |
|
} |
|
else if ( flVideoRatio < flFrameRatio ) |
|
{ |
|
m_nPlaybackWidth = ( GetTall() * flVideoRatio ); |
|
m_nPlaybackHeight = GetTall(); |
|
} |
|
else |
|
{ |
|
m_nPlaybackWidth = GetWide(); |
|
m_nPlaybackHeight = GetTall(); |
|
} |
|
|
|
return true; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::Activate( void ) |
|
{ |
|
MoveToFront(); |
|
RequestFocus(); |
|
SetVisible( true ); |
|
SetEnabled( true ); |
|
|
|
vgui::surface()->SetMinimized( GetVPanel(), false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::DoModal( void ) |
|
{ |
|
MakePopup(); |
|
Activate(); |
|
|
|
vgui::input()->SetAppModalSurface( GetVPanel() ); |
|
vgui::surface()->RestrictPaintToSinglePanel( GetVPanel() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::OnKeyCodeTyped( vgui::KeyCode code ) |
|
{ |
|
bool bInterruptKeyPressed = ( code == KEY_ESCAPE ); |
|
if ( m_bAllowInterruption && bInterruptKeyPressed ) |
|
{ |
|
StopPlayback(); |
|
} |
|
else |
|
{ |
|
BaseClass::OnKeyCodeTyped( code ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handle keys that should cause us to close |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::OnKeyCodePressed( vgui::KeyCode keycode ) |
|
{ |
|
vgui::KeyCode code = GetBaseButtonCode( keycode ); |
|
|
|
// All these keys will interrupt playback |
|
bool bInterruptKeyPressed = ( code == KEY_ESCAPE || |
|
code == KEY_BACKQUOTE || |
|
code == KEY_SPACE || |
|
code == KEY_ENTER || |
|
code == KEY_XBUTTON_A || |
|
code == KEY_XBUTTON_B || |
|
code == KEY_XBUTTON_X || |
|
code == KEY_XBUTTON_Y || |
|
code == KEY_XBUTTON_START || |
|
code == KEY_XBUTTON_BACK ); |
|
|
|
// These keys cause the panel to shutdown |
|
if ( m_bAllowInterruption && bInterruptKeyPressed ) |
|
{ |
|
StopPlayback(); |
|
} |
|
else |
|
{ |
|
BaseClass::OnKeyCodePressed( keycode ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::StopPlayback( void ) |
|
{ |
|
SetVisible( false ); |
|
|
|
// Start the deferred shutdown process |
|
m_nShutdownCount = 1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::OnClose( void ) |
|
{ |
|
if ( m_bStopAllSounds ) |
|
{ |
|
enginesound->NotifyEndMoviePlayback(); |
|
} |
|
|
|
BaseClass::OnClose(); |
|
|
|
if ( vgui::input()->GetAppModalSurface() == GetVPanel() ) |
|
{ |
|
vgui::input()->ReleaseAppModalSurface(); |
|
} |
|
|
|
vgui::surface()->RestrictPaintToSinglePanel( NULL ); |
|
|
|
// Fire an exit command if we're asked to do so |
|
if ( m_szExitCommand[0] ) |
|
{ |
|
engine->ClientCmd( m_szExitCommand ); |
|
} |
|
|
|
MarkForDeletion(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::GetPanelPos( int &xpos, int &ypos ) |
|
{ |
|
xpos = ( (float) ( GetWide() - m_nPlaybackWidth ) / 2 ); |
|
ypos = ( (float) ( GetTall() - m_nPlaybackHeight ) / 2 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update and draw the frame |
|
//----------------------------------------------------------------------------- |
|
void VideoPanel::Paint( void ) |
|
{ |
|
BaseClass::Paint(); |
|
|
|
// No video to play, so do nothing |
|
if ( m_BIKHandle == BIKHANDLE_INVALID || m_pMaterial == NULL ) |
|
return; |
|
|
|
#if !defined( _X360 ) || defined( BINK_ENABLED_FOR_X360 ) |
|
// Update our frame |
|
if ( bik->Update( m_BIKHandle ) == false ) |
|
{ |
|
// Issue a close command |
|
OnVideoOver(); |
|
} |
|
#else |
|
return; |
|
#endif |
|
|
|
// Sit in the "center" |
|
int xpos, ypos; |
|
GetPanelPos( xpos, ypos ); |
|
LocalToScreen( xpos, ypos ); |
|
|
|
// Black out the background (we could omit drawing under the video surface, but this is straight-forward) |
|
if ( m_bBlackBackground ) |
|
{ |
|
vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); |
|
vgui::surface()->DrawFilledRect( 0, 0, GetWide(), GetTall() ); |
|
} |
|
|
|
// Draw the polys to draw this out |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_VIEW ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadIdentity(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadIdentity(); |
|
|
|
pRenderContext->Bind( m_pMaterial, NULL ); |
|
|
|
CMeshBuilder meshBuilder; |
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); |
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); |
|
|
|
float flLeftX = xpos; |
|
float flRightX = xpos + (m_nPlaybackWidth-1); |
|
|
|
float flTopY = ypos; |
|
float flBottomY = ypos + (m_nPlaybackHeight-1); |
|
|
|
// Map our UVs to cut out just the portion of the video we're interested in |
|
float flLeftU = 0.0f; |
|
float flTopV = 0.0f; |
|
|
|
// We need to subtract off a pixel to make sure we don't bleed |
|
float flRightU = m_flU - ( 1.0f / (float) m_nPlaybackWidth ); |
|
float flBottomV = m_flV - ( 1.0f / (float) m_nPlaybackHeight ); |
|
|
|
// Get the current viewport size |
|
int vx, vy, vw, vh; |
|
pRenderContext->GetViewport( vx, vy, vw, vh ); |
|
|
|
// map from screen pixel coords to -1..1 |
|
flRightX = FLerp( -1, 1, 0, vw, flRightX ); |
|
flLeftX = FLerp( -1, 1, 0, vw, flLeftX ); |
|
flTopY = FLerp( 1, -1, 0, vh ,flTopY ); |
|
flBottomY = FLerp( 1, -1, 0, vh, flBottomY ); |
|
|
|
float alpha = ((float)GetFgColor()[3]/255.0f); |
|
|
|
for ( int corner=0; corner<4; corner++ ) |
|
{ |
|
bool bLeft = (corner==0) || (corner==3); |
|
meshBuilder.Position3f( (bLeft) ? flLeftX : flRightX, (corner & 2) ? flBottomY : flTopY, 0.0f ); |
|
meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f ); |
|
meshBuilder.TexCoord2f( 0, (bLeft) ? flLeftU : flRightU, (corner & 2) ? flBottomV : flTopV ); |
|
meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f ); |
|
meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f ); |
|
meshBuilder.Color4f( 1.0f, 1.0f, 1.0f, alpha ); |
|
meshBuilder.AdvanceVertex(); |
|
} |
|
|
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_VIEW ); |
|
pRenderContext->PopMatrix(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); |
|
pRenderContext->PopMatrix(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create and playback a video in a panel |
|
// Input : nWidth - Width of panel (in pixels) |
|
// nHeight - Height of panel |
|
// *pVideoFilename - Name of the file to play |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool VideoPanel_Create( unsigned int nXPos, unsigned int nYPos, |
|
unsigned int nWidth, unsigned int nHeight, |
|
const char *pVideoFilename, |
|
const char *pExitCommand /*= NULL*/, |
|
bool bAllowInterruption /*= true*/ ) |
|
{ |
|
// Create the base video panel |
|
VideoPanel *pVideoPanel = new VideoPanel( nXPos, nYPos, nHeight, nWidth ); |
|
if ( pVideoPanel == NULL ) |
|
return false; |
|
|
|
// Toggle if we want the panel to allow interruption |
|
pVideoPanel->SetAllowInterrupt( bAllowInterruption ); |
|
|
|
// Set the command we'll call (if any) when the video is interrupted or completes |
|
pVideoPanel->SetExitCommand( pExitCommand ); |
|
|
|
// Start it going |
|
if ( pVideoPanel->BeginPlayback( pVideoFilename ) == false ) |
|
{ |
|
delete pVideoPanel; |
|
return false; |
|
} |
|
|
|
// Take control |
|
pVideoPanel->DoModal(); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Take a raw filename and ensure it points to the correct directory and file extension |
|
//----------------------------------------------------------------------------- |
|
void ComposeBinkFilename( const char *lpszFilename, char *lpszOut, int nOutSize ) |
|
{ |
|
Q_strncpy( lpszOut, "media/", nOutSize ); // Assume we must play out of the media directory |
|
char strFilename[MAX_PATH]; |
|
Q_StripExtension( lpszFilename, strFilename, MAX_PATH ); |
|
Q_strncat( lpszOut, strFilename, nOutSize ); |
|
Q_strncat( lpszOut, ".bik", nOutSize ); // Assume we're a .bik extension type |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create a video panel with the supplied commands |
|
//----------------------------------------------------------------------------- |
|
void CreateVideoPanel( const char *lpszFilename, const char *lpszExitCommand, int nWidth, int nHeight, bool bAllowInterruption ) |
|
{ |
|
char strFullpath[MAX_PATH]; |
|
ComposeBinkFilename( lpszFilename, strFullpath, sizeof(strFullpath) ); |
|
|
|
// Use the full screen size if they haven't specified an override |
|
unsigned int nScreenWidth = ( nWidth != 0 ) ? nWidth : ScreenWidth(); |
|
unsigned int nScreenHeight = ( nHeight != 0 ) ? nHeight : ScreenHeight(); |
|
|
|
// Create the panel and go! |
|
if ( VideoPanel_Create( 0, 0, nScreenWidth, nScreenHeight, strFullpath, lpszExitCommand, bAllowInterruption ) == false ) |
|
{ |
|
Warning( "Unable to play video: %s\n", strFullpath ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used to launch a video playback |
|
//----------------------------------------------------------------------------- |
|
|
|
CON_COMMAND( playvideo, "Plays a video: <filename> [width height]" ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
return; |
|
|
|
unsigned int nScreenWidth = Q_atoi( args[2] ); |
|
unsigned int nScreenHeight = Q_atoi( args[3] ); |
|
|
|
CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used to launch a video playback |
|
//----------------------------------------------------------------------------- |
|
|
|
CON_COMMAND( playvideo_nointerrupt, "Plays a video without ability to skip: <filename> [width height]" ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
return; |
|
|
|
unsigned int nScreenWidth = Q_atoi( args[2] ); |
|
unsigned int nScreenHeight = Q_atoi( args[3] ); |
|
|
|
CreateVideoPanel( args[1], NULL, nScreenWidth, nScreenHeight, false ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used to launch a video playback and fire a command on completion |
|
//----------------------------------------------------------------------------- |
|
|
|
CON_COMMAND( playvideo_exitcommand, "Plays a video and fires and exit command when it is stopped or finishes: <filename> <exit command>" ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
return; |
|
|
|
// Pull out the exit command we want to use |
|
char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); |
|
|
|
CreateVideoPanel( args[1], pExitCommand, 0, 0, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used to launch a video playback and fire a command on completion |
|
//----------------------------------------------------------------------------- |
|
|
|
CON_COMMAND( playvideo_exitcommand_nointerrupt, "Plays a video (without interruption) and fires and exit command when it is stopped or finishes: <filename> <exit command>" ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
return; |
|
|
|
// Pull out the exit command we want to use |
|
char *pExitCommand = Q_strstr( args.GetCommandString(), args[2] ); |
|
|
|
CreateVideoPanel( args[1], pExitCommand, 0, 0, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Cause all playback to stop |
|
//----------------------------------------------------------------------------- |
|
|
|
CON_COMMAND( stopvideos, "Stops all videos playing to the screen" ) |
|
{ |
|
FOR_EACH_VEC( g_vecVideoPanels, itr ) |
|
{ |
|
g_vecVideoPanels[itr]->StopPlayback(); |
|
} |
|
}
|
|
|