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.
2693 lines
82 KiB
2693 lines
82 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#if defined( USE_SDL ) |
|
#undef PROTECTED_THINGS_ENABLE |
|
#include "SDL.h" |
|
#include "SDL_syswm.h" |
|
#endif |
|
|
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
#include "winlite.h" |
|
#elif defined(POSIX) |
|
typedef void *HDC; |
|
#endif |
|
|
|
#include "appframework/ilaunchermgr.h" |
|
|
|
#include "basetypes.h" |
|
#include "sysexternal.h" |
|
#include "cmd.h" |
|
#include "modelloader.h" |
|
#include "gl_matsysiface.h" |
|
#include "vmodes.h" |
|
#include "modes.h" |
|
#include "ivideomode.h" |
|
#include "igame.h" |
|
#include "iengine.h" |
|
#include "engine_launcher_api.h" |
|
#include "iregistry.h" |
|
#include "common.h" |
|
#include "tier0/icommandline.h" |
|
#include "cl_main.h" |
|
#include "filesystem_engine.h" |
|
#include "host.h" |
|
#include "gl_model_private.h" |
|
#include "bitmap/tgawriter.h" |
|
#include "vtf/vtf.h" |
|
#include "materialsystem/materialsystem_config.h" |
|
#include "materialsystem/itexture.h" |
|
#include "materialsystem/imaterialsystemhardwareconfig.h" |
|
#include "jpeglib/jpeglib.h" |
|
#include "vgui/ISurface.h" |
|
#include "vgui_controls/Controls.h" |
|
#include "gl_shader.h" |
|
#include "sys_dll.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "IHammer.h" |
|
#include "sourcevr/isourcevirtualreality.h" |
|
#include "tier2/tier2.h" |
|
#include "tier2/renderutils.h" |
|
#include "tier0/etwprof.h" |
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#else |
|
#include "xbox/xboxstubs.h" |
|
#endif |
|
#include "video/ivideoservices.h" |
|
#if !defined(NO_STEAM) |
|
#include "cl_steamauth.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
void CL_GetBackgroundLevelName(char *pszBackgroundName, int bufSize, bool bMapName); |
|
void ClientDLL_HudVidInit( void ); |
|
|
|
ConVar cl_savescreenshotstosteam( "cl_savescreenshotstosteam", "0", FCVAR_HIDDEN, "Saves screenshots to the Steam's screenshot library" ); |
|
ConVar cl_screenshotusertag( "cl_screenshotusertag", "", FCVAR_HIDDEN, "User to tag in the screenshot" ); |
|
ConVar cl_screenshotlocation( "cl_screenshotlocation", "", FCVAR_HIDDEN, "Location to tag the screenshot with" ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// HDRFIXME: move this somewhere else. |
|
//----------------------------------------------------------------------------- |
|
static void PFMWrite( float *pFloatImage, const char *pFilename, int width, int height ) |
|
{ |
|
FileHandle_t fp; |
|
fp = g_pFileSystem->Open( pFilename, "wb" ); |
|
g_pFileSystem->FPrintf( fp, "PF\n%d %d\n-1.000000\n", width, height ); |
|
int i; |
|
for( i = height-1; i >= 0; i-- ) |
|
{ |
|
float *pRow = &pFloatImage[3 * width * i]; |
|
g_pFileSystem->Write( pRow, width * sizeof( float ) * 3, fp ); |
|
} |
|
g_pFileSystem->Close( fp ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Functionality shared by all video modes |
|
//----------------------------------------------------------------------------- |
|
class CVideoMode_Common : public IVideoMode |
|
{ |
|
public: |
|
CVideoMode_Common( void ); |
|
virtual ~CVideoMode_Common( void ); |
|
|
|
// Methods of IVideoMode |
|
virtual bool Init( ); |
|
virtual void Shutdown( void ); |
|
virtual vmode_t *GetMode( int num ); |
|
virtual int GetModeCount( void ); |
|
virtual bool IsWindowedMode( void ) const; |
|
virtual void UpdateWindowPosition( void ); |
|
virtual void RestoreVideo( void ); |
|
virtual void ReleaseVideo( void ); |
|
virtual void DrawNullBackground( void *hdc, int w, int h ); |
|
virtual void InvalidateWindow(); |
|
virtual void DrawStartupGraphic(); |
|
virtual bool CreateGameWindow( int nWidth, int nHeight, bool bWindowed ); |
|
virtual int GetModeWidth( void ) const; |
|
virtual int GetModeHeight( void ) const; |
|
virtual int GetModeStereoWidth() const; |
|
virtual int GetModeStereoHeight() const; |
|
virtual int GetModeUIWidth() const OVERRIDE; |
|
virtual int GetModeUIHeight() const OVERRIDE; |
|
virtual const vrect_t &GetClientViewRect( ) const; |
|
virtual void SetClientViewRect( const vrect_t &viewRect ); |
|
virtual void MarkClientViewRectDirty(); |
|
virtual void TakeSnapshotTGA( const char *pFileName ); |
|
virtual void TakeSnapshotTGARect( const char *pFilename, int x, int y, int w, int h, int resampleWidth, int resampleHeight, bool bPFM, CubeMapFaceIndex_t faceIndex ); |
|
virtual void WriteMovieFrame( const MovieInfo_t& info ); |
|
virtual void TakeSnapshotJPEG( const char *pFileName, int quality ); |
|
virtual bool TakeSnapshotJPEGToBuffer( CUtlBuffer& buf, int quality ); |
|
protected: |
|
bool GetInitialized( ) const; |
|
void SetInitialized( bool init ); |
|
void AdjustWindow( int nWidth, int nHeight, int nBPP, bool bWindowed ); |
|
void ResetCurrentModeForNewResolution( int width, int height, bool bWindowed ); |
|
int GetModeBPP( ) const { return 32; } |
|
void DrawStartupVideo(); |
|
void ComputeStartupGraphicName( char *pBuf, int nBufLen ); |
|
void WriteScreenshotToSteam( uint8 *pImage, int cubImage, int width, int height ); |
|
void AddScreenshotToSteam( const char *pchFilenameJpeg, int width, int height ); |
|
#if !defined(NO_STEAM) |
|
void ApplySteamScreenshotTags( ScreenshotHandle hScreenshot ); |
|
#endif |
|
|
|
// Finds the video mode in the list of video modes |
|
int FindVideoMode( int nDesiredWidth, int nDesiredHeight, bool bWindowed ); |
|
|
|
// Purpose: Returns the optimal refresh rate for the specified mode |
|
int GetRefreshRateForMode( const vmode_t *pMode ); |
|
|
|
// Inline accessors |
|
vmode_t& DefaultVideoMode(); |
|
vmode_t& RequestedWindowVideoMode(); |
|
|
|
private: |
|
// Purpose: Loads the startup graphic |
|
void SetupStartupGraphic(); |
|
void CenterEngineWindow(void *hWndCenter, int width, int height); |
|
void DrawStartupGraphic( HWND window ); |
|
void BlitGraphicToHDC(HDC hdc, byte *rgba, int imageWidth, int imageHeight, int x0, int y0, int x1, int y1); |
|
void BlitGraphicToHDCWithAlpha(HDC hdc, byte *rgba, int imageWidth, int imageHeight, int x0, int y0, int x1, int y1); |
|
IVTFTexture *LoadVTF( CUtlBuffer &temp, const char *szFileName ); |
|
void RecomputeClientViewRect(); |
|
|
|
// Overridden by derived classes |
|
virtual void ReleaseFullScreen( void ); |
|
virtual void ChangeDisplaySettingsToFullscreen( int nWidth, int nHeight, int nBPP ); |
|
virtual void ReadScreenPixels( int x, int y, int w, int h, void *pBuffer, ImageFormat format ); |
|
|
|
// PFM screenshot methods |
|
ITexture *GetBuildCubemaps16BitTexture( void ); |
|
ITexture *GetFullFrameFB0( void ); |
|
|
|
void BlitHiLoScreenBuffersTo16Bit( void ); |
|
void TakeSnapshotPFMRect( const char *pFilename, int x, int y, int w, int h, int resampleWidth, int resampleHeight, CubeMapFaceIndex_t faceIndex ); |
|
|
|
protected: |
|
enum |
|
{ |
|
#if !defined( _X360 ) |
|
MAX_MODE_LIST = 512 |
|
#else |
|
MAX_MODE_LIST = 2 |
|
#endif |
|
}; |
|
|
|
enum |
|
{ |
|
VIDEO_MODE_DEFAULT = -1, |
|
VIDEO_MODE_REQUESTED_WINDOW_SIZE = -2, |
|
CUSTOM_VIDEO_MODES = 2 |
|
}; |
|
|
|
// Master mode list |
|
int m_nNumModes; |
|
vmode_t m_rgModeList[MAX_MODE_LIST]; |
|
vmode_t m_nCustomModeList[CUSTOM_VIDEO_MODES]; |
|
bool m_bInitialized; |
|
bool m_bPlayedStartupVideo; |
|
|
|
// Renderable surface information |
|
int m_nModeWidth; |
|
int m_nModeHeight; |
|
int m_nStereoWidth; |
|
int m_nStereoHeight; |
|
int m_nUIWidth; |
|
int m_nUIHeight; |
|
int m_nVROverrideX; |
|
int m_nVROverrideY; |
|
#if defined( USE_SDL ) |
|
int m_nRenderWidth; |
|
int m_nRenderHeight; |
|
#endif |
|
bool m_bWindowed; |
|
bool m_bSetModeOnce; |
|
bool m_bVROverride; |
|
|
|
// Client view rectangle |
|
vrect_t m_ClientViewRect; |
|
bool m_bClientViewRectDirty; |
|
|
|
// loading image |
|
IVTFTexture *m_pBackgroundTexture; |
|
IVTFTexture *m_pLoadingTexture; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Inline accessors |
|
//----------------------------------------------------------------------------- |
|
inline vmode_t& CVideoMode_Common::DefaultVideoMode() |
|
{ |
|
return m_nCustomModeList[ - VIDEO_MODE_DEFAULT - 1 ]; |
|
} |
|
|
|
inline vmode_t& CVideoMode_Common::RequestedWindowVideoMode() |
|
{ |
|
return m_nCustomModeList[ - VIDEO_MODE_REQUESTED_WINDOW_SIZE - 1 ]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CVideoMode_Common::CVideoMode_Common( void ) |
|
{ |
|
m_nNumModes = 0; |
|
m_bInitialized = false; |
|
|
|
DefaultVideoMode().width = 640; |
|
DefaultVideoMode().height = 480; |
|
DefaultVideoMode().bpp = 32; |
|
DefaultVideoMode().refreshRate = 0; |
|
|
|
RequestedWindowVideoMode().width = -1; |
|
RequestedWindowVideoMode().height = -1; |
|
RequestedWindowVideoMode().bpp = 32; |
|
RequestedWindowVideoMode().refreshRate = 0; |
|
|
|
m_bClientViewRectDirty = false; |
|
m_pBackgroundTexture = NULL; |
|
m_pLoadingTexture = NULL; |
|
m_bWindowed = false; |
|
m_nModeWidth = IsPC() ? 1024 : 640; |
|
m_nModeHeight = IsPC() ? 768 : 480; |
|
m_bVROverride = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CVideoMode_Common::~CVideoMode_Common( void ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_Common::GetInitialized( void ) const |
|
{ |
|
return m_bInitialized; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : init - |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::SetInitialized( bool init ) |
|
{ |
|
m_bInitialized = init; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_Common::IsWindowedMode( void ) const |
|
{ |
|
return m_bWindowed; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the video mode width + height. |
|
//----------------------------------------------------------------------------- |
|
int CVideoMode_Common::GetModeWidth( void ) const |
|
{ |
|
return m_nModeWidth; |
|
} |
|
|
|
int CVideoMode_Common::GetModeHeight( void ) const |
|
{ |
|
return m_nModeHeight; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the video mode width + height for one stereo view. |
|
//----------------------------------------------------------------------------- |
|
int CVideoMode_Common::GetModeStereoWidth( void ) const |
|
{ |
|
return m_nStereoWidth; |
|
} |
|
|
|
int CVideoMode_Common::GetModeStereoHeight( void ) const |
|
{ |
|
return m_nStereoHeight; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the video mode full screen UI width + height. |
|
//----------------------------------------------------------------------------- |
|
|
|
int CVideoMode_Common::GetModeUIWidth( void ) const |
|
{ |
|
return m_nUIWidth; |
|
} |
|
|
|
int CVideoMode_Common::GetModeUIHeight( void ) const |
|
{ |
|
return m_nUIHeight; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the enumerated video mode |
|
//----------------------------------------------------------------------------- |
|
vmode_t *CVideoMode_Common::GetMode( int num ) |
|
{ |
|
if ( num < 0 ) |
|
return &m_nCustomModeList[-num - 1]; |
|
|
|
if ( num >= m_nNumModes ) |
|
return &DefaultVideoMode(); |
|
|
|
return &m_rgModeList[num]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the number of fullscreen video modes |
|
//----------------------------------------------------------------------------- |
|
int CVideoMode_Common::GetModeCount( void ) |
|
{ |
|
return m_nNumModes; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Compares video modes so we can sort the list |
|
// Input : *arg1 - |
|
// *arg2 - |
|
// Output : static int |
|
//----------------------------------------------------------------------------- |
|
static int __cdecl VideoModeCompare( const void *arg1, const void *arg2 ) |
|
{ |
|
vmode_t *m1, *m2; |
|
|
|
m1 = (vmode_t *)arg1; |
|
m2 = (vmode_t *)arg2; |
|
|
|
if ( m1->width < m2->width ) |
|
{ |
|
return -1; |
|
} |
|
|
|
if ( m1->width == m2->width ) |
|
{ |
|
if ( m1->height < m2->height ) |
|
{ |
|
return -1; |
|
} |
|
|
|
if ( m1->height > m2->height ) |
|
{ |
|
return 1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_Common::Init( ) |
|
{ |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds the video mode in the list of video modes |
|
//----------------------------------------------------------------------------- |
|
int CVideoMode_Common::FindVideoMode( int nDesiredWidth, int nDesiredHeight, bool bWindowed ) |
|
{ |
|
#if defined( USE_SDL ) |
|
|
|
// If we want to scale the 3D portion of the game and leave the UI at the same res, then |
|
// re-enable this code. Not that on retina displays the UI will be super small and that |
|
// should probably be fixed. |
|
#if 0 |
|
static ConVarRef mat_viewportscale( "mat_viewportscale" ); |
|
|
|
if ( !bWindowed ) |
|
{ |
|
m_nRenderWidth = nDesiredWidth; |
|
m_nRenderHeight = nDesiredHeight; |
|
|
|
uint nWidth, nHeight, nRefreshHz; |
|
|
|
g_pLauncherMgr->GetNativeDisplayInfo( -1, nWidth, nHeight, nRefreshHz ); |
|
|
|
for ( int i = 0; i < m_nNumModes; i++) |
|
{ |
|
if ( m_rgModeList[i].width != ( int )nWidth ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( m_rgModeList[i].height != ( int )nHeight ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( m_rgModeList[i].refreshRate != ( int )nRefreshHz ) |
|
{ |
|
continue; |
|
} |
|
|
|
mat_viewportscale.SetValue( ( float )nDesiredWidth / ( float )nWidth ); |
|
return i; |
|
} |
|
|
|
Assert( 0 ); // we should have found our native resolution, why not??? |
|
} |
|
else |
|
{ |
|
mat_viewportscale.SetValue( 1.0f ); |
|
} |
|
#endif // 0 |
|
|
|
#endif // USE_SDL |
|
|
|
// Check the default window size.. |
|
if ( ( nDesiredWidth == DefaultVideoMode().width) && (nDesiredHeight == DefaultVideoMode().height) ) |
|
return VIDEO_MODE_DEFAULT; |
|
|
|
// Check the requested window size, but only if we're running windowed |
|
if ( bWindowed ) |
|
{ |
|
if ( ( nDesiredWidth == RequestedWindowVideoMode().width) && (nDesiredHeight == RequestedWindowVideoMode().height) ) |
|
return VIDEO_MODE_REQUESTED_WINDOW_SIZE; |
|
} |
|
|
|
int i; |
|
int iOK = VIDEO_MODE_DEFAULT; |
|
for ( i = 0; i < m_nNumModes; i++) |
|
{ |
|
// Match width first |
|
if ( m_rgModeList[i].width != nDesiredWidth ) |
|
continue; |
|
|
|
iOK = i; |
|
|
|
if ( m_rgModeList[i].height != nDesiredHeight ) |
|
continue; |
|
|
|
// Found a decent match |
|
break; |
|
} |
|
|
|
// No match, use mode 0 |
|
if ( i >= m_nNumModes ) |
|
{ |
|
if ( iOK != VIDEO_MODE_DEFAULT ) |
|
{ |
|
i = iOK; |
|
} |
|
else |
|
{ |
|
i = 0; |
|
} |
|
} |
|
|
|
return i; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Choose the actual video mode based on the available modes |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::ResetCurrentModeForNewResolution( int nWidth, int nHeight, bool bWindowed ) |
|
{ |
|
// Fill in vid structure for the mode |
|
int nGameMode = FindVideoMode( nWidth, nHeight, bWindowed ); |
|
vmode_t *pMode = GetMode( nGameMode ); |
|
|
|
// default to non-VR values |
|
m_bWindowed = bWindowed; |
|
m_nModeWidth = pMode->width; |
|
m_nModeHeight = pMode->height; |
|
m_nUIWidth = pMode->width; |
|
m_nUIHeight = pMode->height; |
|
m_nStereoWidth = pMode->width; |
|
m_nStereoHeight = pMode->height; |
|
|
|
// assume we won't be overriding the position |
|
m_bVROverride = false; |
|
|
|
if ( UseVR() || ShouldForceVRActive() ) |
|
{ |
|
g_pSourceVR->GetViewportBounds( ISourceVirtualReality::VREye_Left, NULL, NULL, &m_nStereoWidth, &m_nStereoHeight ); |
|
VRRect_t vrBounds; |
|
if( g_pSourceVR->GetDisplayBounds( &vrBounds ) ) |
|
{ |
|
ConVarRef vr_force_windowed( "vr_force_windowed" ); |
|
|
|
RequestedWindowVideoMode().width = m_nModeWidth = vrBounds.nWidth; |
|
RequestedWindowVideoMode().height = m_nModeHeight = vrBounds.nHeight; |
|
m_bVROverride = true; |
|
m_bWindowed = vr_force_windowed.GetBool(); |
|
|
|
|
|
// This is the smallest size the the UI in source games can handle. |
|
m_nUIWidth = 640; |
|
m_nUIHeight = 480; |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
m_nVROverrideX = vrBounds.nX; |
|
m_nVROverrideY = vrBounds.nY; |
|
#elif defined( USE_SDL ) |
|
for ( int i = 0; i < SDL_GetNumVideoDisplays(); i++ ) |
|
{ |
|
SDL_Rect sdlRect; |
|
SDL_GetDisplayBounds( i, &sdlRect ); |
|
|
|
if( sdlRect.x == vrBounds.nX && sdlRect.y == vrBounds.nY |
|
&& sdlRect.w == vrBounds.nWidth && sdlRect.h == vrBounds.nHeight ) |
|
{ |
|
static ConVarRef sdl_displayindex( "sdl_displayindex" ); |
|
sdl_displayindex.SetValue( i ); |
|
break; |
|
} |
|
} |
|
#endif |
|
} |
|
} |
|
else if( materials->GetCurrentConfigForVideoCard().m_nVRModeAdapter == materials->GetCurrentAdapter() ) |
|
{ |
|
// if we aren't in VR mode but we do have a VR mode adapter set, we must not be full |
|
// screen because that would show up on the HMD |
|
m_bWindowed = true; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates the game window, plays the startup movie |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_Common::CreateGameWindow( int nWidth, int nHeight, bool bWindowed ) |
|
{ |
|
COM_TimestampedLog( "CVideoMode_Common::Init CreateGameWindow" ); |
|
|
|
if ( ShouldForceVRActive() ) |
|
{ |
|
// First make sure we're running a compatible version of DirectX |
|
ConVarRef mat_dxlevel( "mat_dxlevel" ); |
|
if ( mat_dxlevel.IsValid() ) |
|
{ |
|
if ( mat_dxlevel.GetInt() < 90 ) |
|
{ |
|
Msg( "VR mode does not work with DirectX8.\nPlease use at least \"-dxlevel 90\" or higher.\n" ); |
|
return false; |
|
} |
|
} |
|
|
|
VRRect_t bounds; |
|
g_pSourceVR->GetDisplayBounds( &bounds ); |
|
|
|
nWidth = bounds.nWidth; |
|
nHeight = bounds.nHeight; |
|
|
|
m_nVROverrideX = bounds.nX; |
|
m_nVROverrideY = bounds.nY; |
|
} |
|
|
|
// This allows you to have a window of any size. |
|
// Requires you to set both width and height for the window and |
|
// that you start in windowed mode |
|
if ( bWindowed && nWidth && nHeight ) |
|
{ |
|
// FIXME: There's some ordering issues related to the config record |
|
// and reading the command-line. Would be nice for just one place where this is done. |
|
RequestedWindowVideoMode().width = nWidth; |
|
RequestedWindowVideoMode().height = nHeight; |
|
} |
|
|
|
if ( !InEditMode() ) |
|
{ |
|
// Fill in vid structure for the mode. |
|
// Note: ModeWidth/Height may *not* match requested nWidth/nHeight |
|
ResetCurrentModeForNewResolution( nWidth, nHeight, bWindowed ); |
|
|
|
COM_TimestampedLog( "CreateGameWindow - Start" ); |
|
// When running in stand-alone mode, create your own window |
|
if ( !game->CreateGameWindow() ) |
|
return false; |
|
COM_TimestampedLog( "CreateGameWindow - Finish" ); |
|
|
|
// Re-size and re-center the window |
|
AdjustWindow( GetModeWidth(), GetModeHeight(), GetModeBPP(), IsWindowedMode() ); |
|
|
|
COM_TimestampedLog( "SetMode - Start" ); |
|
// Set the mode and let the materialsystem take over |
|
if ( !SetMode( GetModeWidth(), GetModeHeight(), IsWindowedMode() ) ) |
|
return false; |
|
|
|
#if defined( USE_SDL ) && 0 |
|
static ConVarRef mat_viewportscale( "mat_viewportscale" ); |
|
|
|
if ( !bWindowed ) |
|
{ |
|
m_nRenderWidth = nWidth; |
|
m_nRenderHeight = nHeight; |
|
|
|
mat_viewportscale.SetValue( ( float )nWidth / ( float )GetModeWidth() ); |
|
} |
|
#endif |
|
|
|
COM_TimestampedLog( "SetMode - Finish" ); |
|
|
|
// Play our videos for the background after the render device has been initialized |
|
DrawStartupVideo(); |
|
|
|
COM_TimestampedLog( "DrawStartupGraphic - Start" ); |
|
// Play our videos or display our temp image for the background |
|
DrawStartupGraphic(); |
|
|
|
COM_TimestampedLog( "DrawStartupGraphic - Finish" ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: loads a vtf, through the temporary buffer |
|
//----------------------------------------------------------------------------- |
|
IVTFTexture *CVideoMode_Common::LoadVTF( CUtlBuffer &temp, const char *szFileName ) |
|
{ |
|
if ( !g_pFileSystem->ReadFile( szFileName, NULL, temp ) ) |
|
return NULL; |
|
|
|
IVTFTexture *texture = CreateVTFTexture(); |
|
if ( !texture->Unserialize( temp ) ) |
|
{ |
|
Error( "Invalid or corrupt background texture %s\n", szFileName ); |
|
return NULL; |
|
} |
|
texture->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); |
|
return texture; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes the startup graphic name |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::ComputeStartupGraphicName( char *pBuf, int nBufLen ) |
|
{ |
|
char szBackgroundName[_MAX_PATH]; |
|
CL_GetBackgroundLevelName( szBackgroundName, sizeof(szBackgroundName), false ); |
|
|
|
float aspectRatio = (float)GetModeStereoWidth() / GetModeStereoHeight(); |
|
if ( aspectRatio >= 1.6f ) |
|
{ |
|
// use the widescreen version |
|
Q_snprintf( pBuf, nBufLen, "materials/console/%s_widescreen.vtf", szBackgroundName ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( pBuf, nBufLen, "materials/console/%s.vtf", szBackgroundName ); |
|
} |
|
|
|
if ( !g_pFileSystem->FileExists( pBuf, "GAME" ) ) |
|
{ |
|
Q_strncpy( pBuf, ( aspectRatio >= 1.6f ) ? "materials/console/background01_widescreen.vtf" : "materials/console/background01.vtf", nBufLen ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Writes a screenshot to Steam screenshot library given an RGB buffer |
|
// and applies any tags that have been set for it |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::WriteScreenshotToSteam( uint8 *pImage, int cubImage, int width, int height ) |
|
{ |
|
#if !defined(NO_STEAM) |
|
if ( cl_savescreenshotstosteam.GetBool() ) |
|
{ |
|
if ( Steam3Client().SteamScreenshots() ) |
|
{ |
|
ScreenshotHandle hScreenshot = Steam3Client().SteamScreenshots()->WriteScreenshot( pImage, cubImage, width, height ); |
|
ApplySteamScreenshotTags( hScreenshot ); |
|
} |
|
} |
|
cl_screenshotusertag.SetValue(0); |
|
cl_screenshotlocation.SetValue(""); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Adds a screenshot to the Steam screenshot library from disk |
|
// and applies any tags that have been set for it |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::AddScreenshotToSteam( const char *pchFilename, int width, int height ) |
|
{ |
|
#if !defined(NO_STEAM) |
|
if ( cl_savescreenshotstosteam.GetBool() ) |
|
{ |
|
if ( Steam3Client().SteamScreenshots() ) |
|
{ |
|
ScreenshotHandle hScreenshot = Steam3Client().SteamScreenshots()->AddScreenshotToLibrary( pchFilename, NULL, width, height ); |
|
ApplySteamScreenshotTags( hScreenshot ); |
|
} |
|
} |
|
cl_screenshotusertag.SetValue(0); |
|
cl_screenshotlocation.SetValue(""); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Applies tags to a screenshot for the Steam screenshot library, which are |
|
// passed in through convars |
|
//----------------------------------------------------------------------------- |
|
#if !defined(NO_STEAM) |
|
void CVideoMode_Common::ApplySteamScreenshotTags( ScreenshotHandle hScreenshot ) |
|
{ |
|
if ( hScreenshot != INVALID_SCREENSHOT_HANDLE ) |
|
{ |
|
if ( cl_screenshotusertag.GetBool() ) |
|
{ |
|
if ( Steam3Client().SteamUtils() ) |
|
{ |
|
CSteamID steamID( cl_screenshotusertag.GetInt(), Steam3Client().SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual ); |
|
Steam3Client().SteamScreenshots()->TagUser( hScreenshot, steamID ); |
|
} |
|
} |
|
const char *pchLocation = cl_screenshotlocation.GetString(); |
|
if ( pchLocation && pchLocation[0] ) |
|
{ |
|
Steam3Client().SteamScreenshots()->SetLocation( hScreenshot, pchLocation ); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
void CVideoMode_Common::SetupStartupGraphic() |
|
{ |
|
COM_TimestampedLog( "CVideoMode_Common::Init SetupStartupGraphic" ); |
|
|
|
char szBackgroundName[_MAX_PATH]; |
|
CL_GetBackgroundLevelName( szBackgroundName, sizeof(szBackgroundName), false ); |
|
|
|
// get the image to load |
|
char material[_MAX_PATH]; |
|
CUtlBuffer buf; |
|
|
|
float aspectRatio = (float)GetModeWidth() / GetModeHeight(); |
|
if ( aspectRatio >= 1.6f ) |
|
{ |
|
// use the widescreen version |
|
Q_snprintf( material, sizeof(material), |
|
"materials/console/%s_widescreen.vtf", szBackgroundName ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( material, sizeof(material), |
|
"materials/console/%s.vtf", szBackgroundName ); |
|
} |
|
|
|
// load in the background vtf |
|
buf.Clear(); |
|
m_pBackgroundTexture = LoadVTF( buf, material ); |
|
if ( !m_pBackgroundTexture ) |
|
{ |
|
// fallback to opening just the default background |
|
m_pBackgroundTexture = LoadVTF( buf, ( aspectRatio >= 1.6f ) ? "materials/console/background01_widescreen.vtf" : "materials/console/background01.vtf" ); |
|
if ( !m_pBackgroundTexture ) |
|
{ |
|
Error( "Can't find background image '%s'\n", material ); |
|
return; |
|
} |
|
} |
|
|
|
// loading.vtf |
|
buf.Clear(); // added this Clear() because we saw cases where LoadVTF was not emptying the buf fully in the above section |
|
const char* loading = "materials/console/startup_loading.vtf"; |
|
if ( IsSteamDeck() ) |
|
loading = "materials/gamepadui/game_logo.vtf"; |
|
m_pLoadingTexture = LoadVTF( buf, loading ); |
|
if ( !m_pLoadingTexture ) |
|
{ |
|
Error( "Can't find background image '%s'\n", loading ); |
|
return; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Renders the startup video into the HWND |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::DrawStartupVideo() |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
CETWScope timer( "CVideoMode_Common::DrawStartupGraphic" ); |
|
|
|
// render an avi, if we have one |
|
if ( !m_bPlayedStartupVideo && !InEditMode() && !ShouldForceVRActive() ) |
|
{ |
|
game->PlayStartupVideos(); |
|
m_bPlayedStartupVideo = true; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Renders the startup graphic into the HWND |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::DrawStartupGraphic() |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
char debugstartup = CommandLine()->FindParm("-debugstartupscreen"); |
|
|
|
SetupStartupGraphic(); |
|
|
|
if ( !m_pBackgroundTexture || !m_pLoadingTexture ) |
|
return; |
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); |
|
|
|
char pStartupGraphicName[MAX_PATH]; |
|
ComputeStartupGraphicName( pStartupGraphicName, sizeof(pStartupGraphicName) ); |
|
|
|
if(debugstartup) |
|
{ |
|
// slam the startup graphic name for sanity - take your pick |
|
strcpy( pStartupGraphicName, "materials/console/background01.vtf"); |
|
//strcpy( pStartupGraphicName, "materials/console/testramp.vtf"); |
|
} |
|
|
|
// Allocate a white material |
|
KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); |
|
pVMTKeyValues->SetString( "$basetexture", pStartupGraphicName + 10 ); |
|
pVMTKeyValues->SetInt( "$ignorez", 1 ); |
|
pVMTKeyValues->SetInt( "$nofog", 1 ); |
|
pVMTKeyValues->SetInt( "$no_fullbright", 1 ); |
|
pVMTKeyValues->SetInt( "$nocull", 1 ); |
|
IMaterial *pMaterial = g_pMaterialSystem->CreateMaterial( "__background", pVMTKeyValues ); |
|
|
|
const char* loading = "console/startup_loading.vtf"; |
|
if ( IsSteamDeck() ) |
|
loading = "gamepadui/game_logo.vtf"; |
|
|
|
pVMTKeyValues = new KeyValues( "UnlitGeneric" ); |
|
pVMTKeyValues->SetString( "$basetexture", loading ); |
|
pVMTKeyValues->SetInt( "$translucent", 1 ); |
|
pVMTKeyValues->SetInt( "$ignorez", 1 ); |
|
pVMTKeyValues->SetInt( "$nofog", 1 ); |
|
pVMTKeyValues->SetInt( "$no_fullbright", 1 ); |
|
pVMTKeyValues->SetInt( "$nocull", 1 ); |
|
IMaterial *pLoadingMaterial = g_pMaterialSystem->CreateMaterial( "__loading", pVMTKeyValues ); |
|
|
|
int w = GetModeStereoWidth(); |
|
int h = GetModeStereoHeight(); |
|
int tw = m_pBackgroundTexture->Width(); |
|
int th = m_pBackgroundTexture->Height(); |
|
int lw = m_pLoadingTexture->Width(); |
|
int lh = m_pLoadingTexture->Height(); |
|
|
|
if (debugstartup) |
|
{ |
|
for ( int repeat = 0; repeat<100000; repeat++) |
|
{ |
|
pRenderContext->Viewport( 0, 0, w, h ); |
|
pRenderContext->DepthRange( 0, 1 ); |
|
pRenderContext->ClearColor3ub( 0, (repeat & 0x7) << 3, 0 ); |
|
pRenderContext->ClearBuffers( true, true, true ); |
|
pRenderContext->SetToneMappingScaleLinear( Vector(1,1,1) ); |
|
|
|
if(1) // draw normal BK |
|
{ |
|
float depth = 0.55f; |
|
int slide = (repeat) % 200; // 100 down and 100 up |
|
if (slide > 100) |
|
{ |
|
slide = 200-slide; // aka 100-(slide-100). |
|
} |
|
|
|
// stop sliding about |
|
slide = 0; |
|
|
|
DrawScreenSpaceRectangle( pMaterial, 0, 0+slide, w, h-50, 0, 0, tw-1, th-1, tw, th, NULL,1,1,depth ); |
|
if ( !IsSteamDeck() ) |
|
DrawScreenSpaceRectangle( pLoadingMaterial, w-lw, h-lh+slide/2, lw, lh, 0, 0, lw-1, lh-1, lw, lh, NULL,1,1,depth-0.1 ); |
|
else |
|
// TODO: Steam Deck |
|
DrawScreenSpaceRectangle( pLoadingMaterial, w-lw, h-lh+slide/2, lw, lh, 0, 0, lw-1, lh-1, lw, lh, NULL,1,1,depth-0.1 ); |
|
} |
|
|
|
if(0) |
|
{ |
|
// draw a grid too |
|
int grid_size = 8; |
|
float depthacc = 0.0; |
|
float depthinc = 1.0 / (float)((grid_size * grid_size)+1); |
|
|
|
for( int x = 0; x<grid_size; x++) |
|
{ |
|
float cornerx = ((float)x) * 20.0f; |
|
|
|
for( int y=0; y<grid_size; y++) |
|
{ |
|
float cornery = ((float)y) * 20.0f; |
|
|
|
//if (! ((x^y) & 1) ) |
|
{ |
|
DrawScreenSpaceRectangle( pMaterial, 10.0f+cornerx,10.0f+ cornery, 15, 15, 0, 0, tw-1, th-1, tw, th, NULL,1,1, depthacc ); |
|
} |
|
|
|
depthacc += depthinc; |
|
} |
|
} |
|
} |
|
|
|
g_pMaterialSystem->SwapBuffers(); |
|
} |
|
} |
|
else |
|
{ |
|
pRenderContext->Viewport( 0, 0, w, h ); |
|
pRenderContext->DepthRange( 0, 1 ); |
|
pRenderContext->SetToneMappingScaleLinear( Vector(1,1,1) ); |
|
|
|
float depth = 0.5f; |
|
|
|
// Make sure we clear both front & back buffer. |
|
for (int i = 0; i < 2; ++i) |
|
{ |
|
pRenderContext->ClearColor3ub( 0, 0, 0 ); |
|
pRenderContext->ClearBuffers( true, true, true ); |
|
DrawScreenSpaceRectangle( pMaterial, 0, 0, w, h, 0, 0, tw-1, th-1, tw, th, NULL,1,1,depth ); |
|
DrawScreenSpaceRectangle( pLoadingMaterial, w-lw, h-lh, lw, lh, 0, 0, lw-1, lh-1, lw, lh, NULL,1,1,depth ); |
|
g_pMaterialSystem->SwapBuffers(); |
|
} |
|
} |
|
|
|
#ifdef DX_TO_GL_ABSTRACTION |
|
g_pMaterialSystem->DoStartupShaderPreloading(); |
|
#endif |
|
|
|
pMaterial->Release(); |
|
pLoadingMaterial->Release(); |
|
|
|
// release graphics |
|
DestroyVTFTexture( m_pBackgroundTexture ); |
|
m_pBackgroundTexture = NULL; |
|
DestroyVTFTexture( m_pLoadingTexture ); |
|
m_pLoadingTexture = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Blits an image to the loading window hdc |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::BlitGraphicToHDCWithAlpha(HDC hdc, byte *rgba, int imageWidth, int imageHeight, int x0, int y0, int x1, int y1) |
|
{ |
|
#ifdef WIN32 |
|
if ( IsX360() ) |
|
return; |
|
|
|
int x = x0; |
|
int y = y0; |
|
int wide = x1 - x0; |
|
int tall = y1 - y0; |
|
|
|
Assert(imageWidth == wide && imageHeight == tall); |
|
|
|
int texwby4 = imageWidth << 2; |
|
|
|
for ( int v = 0; v < tall; v++ ) |
|
{ |
|
int *src = (int *)(rgba + (v * texwby4)); |
|
int xaccum = 0; |
|
|
|
for ( int u = 0; u < wide; u++ ) |
|
{ |
|
byte *xsrc = (byte *)(src + xaccum); |
|
if (xsrc[3]) |
|
{ |
|
::SetPixel(hdc, x + u, y + v, RGB(xsrc[0], xsrc[1], xsrc[2])); |
|
} |
|
xaccum += 1; |
|
} |
|
} |
|
#else |
|
Assert( !"Impl me" ); |
|
#endif |
|
} |
|
|
|
void CVideoMode_Common::InvalidateWindow() |
|
{ |
|
if ( CommandLine()->FindParm( "-noshaderapi" ) ) |
|
{ |
|
#if defined( USE_SDL ) |
|
SDL_Event fake; |
|
memset(&fake, '\0', sizeof (SDL_Event)); |
|
fake.type = SDL_WINDOWEVENT; |
|
fake.window.windowID = SDL_GetWindowID( (SDL_Window *) g_pLauncherMgr->GetWindowRef() ); |
|
fake.window.event = SDL_WINDOWEVENT_EXPOSED; |
|
SDL_PushEvent(&fake); |
|
#else |
|
InvalidateRect( (HWND)game->GetMainWindow(), NULL, FALSE ); |
|
#endif |
|
} |
|
} |
|
|
|
void CVideoMode_Common::DrawNullBackground( void *hHDC, int w, int h ) |
|
{ |
|
#ifdef WIN32 |
|
if ( IsX360() ) |
|
return; |
|
|
|
HDC hdc = (HDC)hHDC; |
|
|
|
// Show a message if running without renderer.. |
|
if ( CommandLine()->FindParm( "-noshaderapi" ) ) |
|
{ |
|
HFONT fnt = CreateFontA( -18, |
|
0, |
|
0, |
|
0, |
|
FW_NORMAL, |
|
FALSE, |
|
FALSE, |
|
FALSE, |
|
ANSI_CHARSET, |
|
OUT_TT_PRECIS, |
|
CLIP_DEFAULT_PRECIS, |
|
ANTIALIASED_QUALITY, |
|
DEFAULT_PITCH, |
|
"Arial" ); |
|
|
|
HFONT oldFont = (HFONT)SelectObject( hdc, fnt ); |
|
int oldBkMode = SetBkMode( hdc, TRANSPARENT ); |
|
COLORREF oldFgColor = SetTextColor( hdc, RGB( 255, 255, 255 ) ); |
|
|
|
HBRUSH br = CreateSolidBrush( RGB( 0, 0, 0 ) ); |
|
HBRUSH oldBr = (HBRUSH)SelectObject( hdc, br ); |
|
Rectangle( hdc, 0, 0, w, h ); |
|
|
|
RECT rc; |
|
rc.left = 0; |
|
rc.top = 0; |
|
rc.right = w; |
|
rc.bottom = h; |
|
|
|
DrawText( hdc, "Running with -noshaderapi", -1, &rc, DT_NOPREFIX | DT_VCENTER | DT_CENTER | DT_SINGLELINE ); |
|
|
|
rc.top = rc.bottom - 30; |
|
|
|
if ( host_state.worldmodel != NULL ) |
|
{ |
|
rc.left += 10; |
|
DrawText( hdc, modelloader->GetName( host_state.worldmodel ), -1, &rc, DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE ); |
|
} |
|
|
|
SetTextColor( hdc, oldFgColor ); |
|
|
|
SelectObject( hdc, oldBr ); |
|
SetBkMode( hdc, oldBkMode ); |
|
SelectObject( hdc, oldFont ); |
|
|
|
DeleteObject( br ); |
|
DeleteObject( fnt ); |
|
} |
|
#else |
|
Assert( !"Impl me" ); |
|
#endif |
|
|
|
} |
|
|
|
#ifndef _WIN32 |
|
|
|
typedef unsigned char BYTE; |
|
typedef signed long LONG; |
|
typedef unsigned long ULONG; |
|
|
|
typedef char * LPSTR; |
|
|
|
typedef struct tagBITMAPINFOHEADER{ |
|
DWORD biSize; |
|
LONG biWidth; |
|
LONG biHeight; |
|
WORD biPlanes; |
|
WORD biBitCount; |
|
DWORD biCompression; |
|
DWORD biSizeImage; |
|
LONG biXPelsPerMeter; |
|
LONG biYPelsPerMeter; |
|
DWORD biClrUsed; |
|
DWORD biClrImportant; |
|
} BITMAPINFOHEADER; |
|
|
|
typedef struct tagBITMAPFILEHEADER { |
|
WORD bfType; |
|
DWORD bfSize; |
|
WORD bfReserved1; |
|
WORD bfReserved2; |
|
DWORD bfOffBits; |
|
} BITMAPFILEHEADER; |
|
|
|
typedef struct tagRGBQUAD { |
|
BYTE rgbBlue; |
|
BYTE rgbGreen; |
|
BYTE rgbRed; |
|
BYTE rgbReserved; |
|
} RGBQUAD; |
|
|
|
/* constants for the biCompression field */ |
|
#define BI_RGB 0L |
|
#define BI_RLE8 1L |
|
#define BI_RLE4 2L |
|
#define BI_BITFIELDS 3L |
|
|
|
#if 0 |
|
typedef struct _GUID |
|
{ |
|
unsigned long Data1; |
|
unsigned short Data2; |
|
unsigned short Data3; |
|
unsigned char Data4[8]; |
|
} GUID; |
|
|
|
#endif |
|
typedef GUID UUID; |
|
|
|
#endif //WIN32 |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Blits an image to the loading window hdc |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::BlitGraphicToHDC(HDC hdc, byte *rgba, int imageWidth, int imageHeight, int x0, int y0, int x1, int y1) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
#ifdef WIN32 |
|
int x = x0; |
|
int y = y0; |
|
int wide = x1 - x0; |
|
int tall = y1 - y0; |
|
|
|
// Needs to be a multiple of 4 |
|
int dibwide = ( wide + 3 ) & ~3; |
|
|
|
Assert(rgba); |
|
int texwby4 = imageWidth << 2; |
|
|
|
double st = Plat_FloatTime(); |
|
|
|
void *destBits = NULL; |
|
|
|
HBITMAP bm; |
|
BITMAPINFO bmi; |
|
Q_memset( &bmi, 0, sizeof( bmi ) ); |
|
|
|
BITMAPINFOHEADER *hdr = &bmi.bmiHeader; |
|
|
|
hdr->biSize = sizeof( *hdr ); |
|
hdr->biWidth = dibwide; |
|
hdr->biHeight = -tall; // top down bitmap |
|
hdr->biBitCount = 24; |
|
hdr->biPlanes = 1; |
|
hdr->biCompression = BI_RGB; |
|
hdr->biSizeImage = dibwide * tall * 3; |
|
hdr->biXPelsPerMeter = 3780; |
|
hdr->biYPelsPerMeter = 3780; |
|
|
|
// Create a "source" DC |
|
HDC tempDC = CreateCompatibleDC( hdc ); |
|
|
|
// Create the dibsection bitmap |
|
bm = CreateDIBSection |
|
( |
|
tempDC, // handle to DC |
|
&bmi, // bitmap data |
|
DIB_RGB_COLORS, // data type indicator |
|
&destBits, // bit values |
|
NULL, // handle to file mapping object |
|
0 // offset to bitmap bit values |
|
); |
|
|
|
// Select it into the source DC |
|
HBITMAP oldBitmap = (HBITMAP)SelectObject( tempDC, bm ); |
|
|
|
// Setup for bilinaer filtering. If we don't do this filter here, there will be a big |
|
// annoying pop when it switches to the vguimatsurface version of the background. |
|
// We leave room for 14 bits of integer precision, so the image can be up to 16k x 16k. |
|
const int BILINEAR_FIX_SHIFT = 17; |
|
const int BILINEAR_FIX_MUL = (1 << BILINEAR_FIX_SHIFT); |
|
|
|
#define FIXED_BLEND( a, b, out, frac ) \ |
|
out[0] = (a[0]*frac + b[0]*(BILINEAR_FIX_MUL-frac)) >> BILINEAR_FIX_SHIFT; \ |
|
out[1] = (a[1]*frac + b[1]*(BILINEAR_FIX_MUL-frac)) >> BILINEAR_FIX_SHIFT; \ |
|
out[2] = (a[2]*frac + b[2]*(BILINEAR_FIX_MUL-frac)) >> BILINEAR_FIX_SHIFT; |
|
|
|
float eps = 0.001f; |
|
float uMax = imageWidth - 1 - eps; |
|
float vMax = imageHeight - 1 - eps; |
|
|
|
int fixedBilinearV = 0; |
|
int bilinearUInc = (int)( (uMax / (dibwide-1)) * BILINEAR_FIX_MUL ); |
|
int bilinearVInc = (int)( (vMax / (tall-1)) * BILINEAR_FIX_MUL ); |
|
|
|
for ( int v = 0; v < tall; v++ ) |
|
{ |
|
int iBilinearV = fixedBilinearV >> BILINEAR_FIX_SHIFT; |
|
int fixedFractionV = fixedBilinearV & (BILINEAR_FIX_MUL-1); |
|
fixedBilinearV += bilinearVInc; |
|
|
|
int fixedBilinearU = 0; |
|
byte *dest = (byte *)destBits + ( ( y + v ) * dibwide + x ) * 3; |
|
|
|
for ( int u = 0; u < dibwide; u++, dest+=3 ) |
|
{ |
|
int iBilinearU = fixedBilinearU >> BILINEAR_FIX_SHIFT; |
|
int fixedFractionU = fixedBilinearU & (BILINEAR_FIX_MUL-1); |
|
fixedBilinearU += bilinearUInc; |
|
|
|
Assert( iBilinearU >= 0 && iBilinearU+1 < imageWidth ); |
|
Assert( iBilinearV >= 0 && iBilinearV+1 < imageHeight ); |
|
|
|
byte *srcTopLine = rgba + iBilinearV * texwby4; |
|
byte *srcBottomLine = rgba + (iBilinearV+1) * texwby4; |
|
|
|
byte *xsrc[4] = { |
|
srcTopLine + (iBilinearU+0)*4, srcTopLine + (iBilinearU+1)*4, |
|
srcBottomLine + (iBilinearU+0)*4, srcBottomLine + (iBilinearU+1)*4 }; |
|
|
|
int topColor[3], bottomColor[3], finalColor[3]; |
|
FIXED_BLEND( xsrc[1], xsrc[0], topColor, fixedFractionU ); |
|
FIXED_BLEND( xsrc[3], xsrc[2], bottomColor, fixedFractionU ); |
|
FIXED_BLEND( bottomColor, topColor, finalColor, fixedFractionV ); |
|
|
|
// Windows wants the colors in reverse order. |
|
dest[0] = finalColor[2]; |
|
dest[1] = finalColor[1]; |
|
dest[2] = finalColor[0]; |
|
} |
|
} |
|
|
|
// Now do the Blt |
|
BitBlt( hdc, 0, 0, dibwide, tall, tempDC, 0, 0, SRCCOPY ); |
|
|
|
// This only draws if running -noshaderapi |
|
DrawNullBackground( hdc, dibwide, tall ); |
|
|
|
// Restore the old Bitmap |
|
SelectObject( tempDC, oldBitmap ); |
|
|
|
// Destroy the temporary DC |
|
DeleteDC( tempDC ); |
|
|
|
// Destroy the DIBSection bitmap |
|
DeleteObject( bm ); |
|
|
|
double elapsed = Plat_FloatTime() - st; |
|
|
|
COM_TimestampedLog( "BlitGraphicToHDC: new ver took %.4f", elapsed ); |
|
#else |
|
Assert( !"Impl me" ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: This is called in response to a WM_MOVE message |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::UpdateWindowPosition( void ) |
|
{ |
|
int x, y, w, h; |
|
|
|
// Get the window from the game ( right place for it? ) |
|
game->GetWindowRect( &x, &y, &w, &h ); |
|
|
|
#ifdef WIN32 |
|
RECT window_rect; |
|
window_rect.left = x; |
|
window_rect.right = x + w; |
|
window_rect.top = y; |
|
window_rect.bottom = y + h; |
|
#endif |
|
// NOTE: We need to feed this back into the video mode stuff |
|
// esp. in Resizing window mode. |
|
} |
|
|
|
void CVideoMode_Common::ChangeDisplaySettingsToFullscreen( int nWidth, int nHeight, int nBPP ) |
|
{ |
|
} |
|
|
|
void CVideoMode_Common::ReleaseFullScreen( void ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the optimal refresh rate for the specified mode |
|
//----------------------------------------------------------------------------- |
|
int CVideoMode_Common::GetRefreshRateForMode( const vmode_t *pMode ) |
|
{ |
|
int nRefreshRate = pMode->refreshRate; |
|
|
|
// FIXME: We should only read this once, at the beginning |
|
// override the refresh rate from the command-line maybe |
|
nRefreshRate = CommandLine()->ParmValue( "-freq", nRefreshRate ); |
|
nRefreshRate = CommandLine()->ParmValue( "-refresh", nRefreshRate ); |
|
nRefreshRate = CommandLine()->ParmValue( "-refreshrate", nRefreshRate ); |
|
|
|
return nRefreshRate; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *mode - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::AdjustWindow( int nWidth, int nHeight, int nBPP, bool bWindowed ) |
|
{ |
|
if ( g_bTextMode ) |
|
return; |
|
|
|
// Use Change Display Settings to go full screen |
|
ChangeDisplaySettingsToFullscreen( nWidth, nHeight, nBPP ); |
|
|
|
RECT WindowRect; |
|
WindowRect.top = 0; |
|
WindowRect.left = 0; |
|
WindowRect.right = nWidth; |
|
WindowRect.bottom = nHeight; |
|
|
|
#ifndef USE_SDL |
|
#ifndef _X360 |
|
// Get window style |
|
DWORD style = GetWindowLong( (HWND)game->GetMainWindow(), GWL_STYLE ); |
|
DWORD exStyle = GetWindowLong( (HWND)game->GetMainWindow(), GWL_EXSTYLE ); |
|
|
|
if ( bWindowed ) |
|
{ |
|
// Give it a frame (pretty much WS_OVERLAPPEDWINDOW except for we do not modify the |
|
// flags corresponding to resizing-frame and maximize-box) |
|
if( !CommandLine()->FindParm( "-noborder" ) && !m_bVROverride ) |
|
{ |
|
style |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; |
|
} |
|
else |
|
{ |
|
style &= ~WS_OVERLAPPEDWINDOW; |
|
} |
|
|
|
// remove topmost flag |
|
exStyle &= ~WS_EX_TOPMOST; |
|
SetWindowLong( (HWND)game->GetMainWindow(), GWL_EXSTYLE, exStyle ); |
|
} |
|
else |
|
{ |
|
// Remove window border going into fullscreen mode to avoid Vista sizing issues when DWM is enabled |
|
style &= ~WS_OVERLAPPEDWINDOW; |
|
} |
|
|
|
SetWindowLong( (HWND)game->GetMainWindow(), GWL_STYLE, style ); |
|
|
|
// Compute rect needed for that size client area based on window style |
|
AdjustWindowRectEx( &WindowRect, style, FALSE, exStyle ); |
|
#endif |
|
|
|
// Prepare to set window pos, which is required when toggling between topmost and not window flags |
|
HWND hWndAfter = NULL; |
|
DWORD dwSwpFlags = 0; |
|
#ifndef _X360 |
|
{ |
|
if ( bWindowed ) |
|
{ |
|
hWndAfter = HWND_NOTOPMOST; |
|
} |
|
else |
|
{ |
|
hWndAfter = HWND_TOPMOST; |
|
} |
|
dwSwpFlags = SWP_FRAMECHANGED; |
|
} |
|
#else |
|
{ |
|
dwSwpFlags = SWP_NOZORDER; |
|
} |
|
#endif |
|
|
|
// Move the window to 0, 0 and the new true size |
|
SetWindowPos( (HWND)game->GetMainWindow(), |
|
hWndAfter, |
|
0, 0, WindowRect.right - WindowRect.left, |
|
WindowRect.bottom - WindowRect.top, |
|
SWP_NOREDRAW | dwSwpFlags ); |
|
#endif // !USE_SDL |
|
|
|
// Now center |
|
CenterEngineWindow( game->GetMainWindow(), |
|
WindowRect.right - WindowRect.left, |
|
WindowRect.bottom - WindowRect.top ); |
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr->SetWindowFullScreen( !bWindowed, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top ); |
|
|
|
CenterEngineWindow( game->GetMainWindow(), |
|
WindowRect.right - WindowRect.left, |
|
WindowRect.bottom - WindowRect.top ); |
|
|
|
g_pLauncherMgr->SizeWindow( WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top ); |
|
|
|
if( bWindowed ) |
|
{ |
|
SDL_Window* win = (SDL_Window*)g_pLauncherMgr->GetWindowRef(); |
|
if ( m_bVROverride || CommandLine()->FindParm( "-noborder" ) ) |
|
SDL_SetWindowBordered( win, SDL_FALSE ); |
|
else |
|
SDL_SetWindowBordered( win, SDL_TRUE ); |
|
|
|
} |
|
#endif |
|
|
|
game->SetWindowSize( nWidth, nHeight ); |
|
|
|
// Make sure we have updated window information |
|
UpdateWindowPosition(); |
|
MarkClientViewRectDirty(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::Shutdown( void ) |
|
{ |
|
ReleaseFullScreen(); |
|
game->DestroyGameWindow(); |
|
|
|
if ( !GetInitialized() ) |
|
return; |
|
|
|
SetInitialized( false ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets/sets the client view rectangle |
|
//----------------------------------------------------------------------------- |
|
const vrect_t &CVideoMode_Common::GetClientViewRect( ) const |
|
{ |
|
const_cast<CVideoMode_Common*>(this)->RecomputeClientViewRect(); |
|
return m_ClientViewRect; |
|
} |
|
|
|
void CVideoMode_Common::SetClientViewRect( const vrect_t &viewRect ) |
|
{ |
|
m_ClientViewRect = viewRect; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Marks the client view rect dirty |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::MarkClientViewRectDirty() |
|
{ |
|
m_bClientViewRectDirty = true; |
|
} |
|
|
|
void CVideoMode_Common::RecomputeClientViewRect() |
|
{ |
|
if ( !InEditMode() ) |
|
{ |
|
if ( !m_bClientViewRectDirty ) |
|
return; |
|
} |
|
|
|
m_bClientViewRectDirty = false; |
|
|
|
int nWidth, nHeight; |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
pRenderContext->GetRenderTargetDimensions( nWidth, nHeight ); |
|
m_ClientViewRect.width = nWidth; |
|
m_ClientViewRect.height = nHeight; |
|
m_ClientViewRect.x = 0; |
|
m_ClientViewRect.y = 0; |
|
|
|
if (!nWidth || !nHeight) |
|
{ |
|
// didn't successfully get the screen size, try again next frame |
|
// window is probably minimized |
|
m_bClientViewRectDirty = true; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : hWndCenter - |
|
// width - |
|
// height - |
|
// Output : static void |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::CenterEngineWindow( void *hWndCenter, int width, int height) |
|
{ |
|
int CenterX, CenterY; |
|
|
|
#if defined(USE_SDL) |
|
// Get the displayindex, and center our window on that display. |
|
static ConVarRef sdl_displayindex( "sdl_displayindex" ); |
|
int displayindex = sdl_displayindex.IsValid() ? sdl_displayindex.GetInt() : 0; |
|
|
|
SDL_DisplayMode mode; |
|
SDL_GetCurrentDisplayMode( displayindex, &mode ); |
|
|
|
const int wide = mode.w; |
|
const int tall = mode.h; |
|
|
|
CenterX = (wide - width) / 2; |
|
CenterY = (tall - height) / 2; |
|
CenterX = (CenterX < 0) ? 0: CenterX; |
|
CenterY = (CenterY < 0) ? 0: CenterY; |
|
|
|
// tweak the x and w positions if the user species them on the command-line |
|
CenterX = CommandLine()->ParmValue( "-x", CenterX ); |
|
CenterY = CommandLine()->ParmValue( "-y", CenterY ); |
|
|
|
// also check for the negated form (since it is hard to say "-x -1000") |
|
int negx = CommandLine()->ParmValue( "-negx", 0 ); |
|
if (negx > 0) |
|
{ |
|
CenterX = -negx; |
|
} |
|
int negy = CommandLine()->ParmValue( "-negy", 0 ); |
|
if (negy > 0) |
|
{ |
|
CenterY = -negy; |
|
} |
|
|
|
SDL_Rect rect = { 0, 0, 0, 0 }; |
|
SDL_GetDisplayBounds( displayindex, &rect ); |
|
|
|
CenterX += rect.x; |
|
CenterY += rect.y; |
|
|
|
game->SetWindowXY( CenterX, CenterY ); |
|
g_pLauncherMgr->MoveWindow( CenterX, CenterY ); |
|
#else |
|
if ( IsPC() ) |
|
{ |
|
// In windowed mode go through game->GetDesktopInfo because system metrics change |
|
// when going fullscreen vs windowed. |
|
// Use system metrics for fullscreen or when game didn't have a chance to initialize. |
|
|
|
int cxScreen = 0, cyScreen = 0, refreshRate = 0; |
|
|
|
if ( !( WS_EX_TOPMOST & ::GetWindowLong( (HWND)hWndCenter, GWL_EXSTYLE ) ) && m_bWindowed ) |
|
{ |
|
game->GetDesktopInfo( cxScreen, cyScreen, refreshRate ); |
|
} |
|
|
|
if ( !cxScreen || !cyScreen ) |
|
{ |
|
cxScreen = GetSystemMetrics(SM_CXSCREEN); |
|
cyScreen = GetSystemMetrics(SM_CYSCREEN); |
|
} |
|
|
|
// Compute top-left corner offset |
|
CenterX = (cxScreen - width) / 2; |
|
CenterY = (cyScreen - height) / 2; |
|
CenterX = (CenterX < 0) ? 0: CenterX; |
|
CenterY = (CenterY < 0) ? 0: CenterY; |
|
} |
|
else |
|
{ |
|
CenterX = 0; |
|
CenterY = 0; |
|
} |
|
|
|
if( m_bVROverride ) |
|
{ |
|
CenterX = m_nVROverrideX; |
|
CenterY = m_nVROverrideY; |
|
} |
|
|
|
// tweak the x and w positions if the user species them on the command-line |
|
CenterX = CommandLine()->ParmValue( "-x", CenterX ); |
|
CenterY = CommandLine()->ParmValue( "-y", CenterY ); |
|
|
|
game->SetWindowXY( CenterX, CenterY ); |
|
|
|
SetWindowPos ( (HWND)hWndCenter, NULL, CenterX, CenterY, 0, 0, |
|
SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); |
|
#endif |
|
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Handle alt-tab |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::RestoreVideo( void ) |
|
{ |
|
} |
|
|
|
void CVideoMode_Common::ReleaseVideo( void ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Read screen pixels |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::ReadScreenPixels( int x, int y, int w, int h, void *pBuffer, ImageFormat format ) |
|
{ |
|
int nBytes = ImageLoader::GetMemRequired( w, h, 1, format, false ); |
|
memset( pBuffer, 0, nBytes ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Write vid.buffer out as a .tga file |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::TakeSnapshotTGA( const char *pFilename ) |
|
{ |
|
// bitmap bits |
|
uint8 *pImage = new uint8[ GetModeStereoWidth() * 3 * GetModeStereoHeight() ]; |
|
|
|
// Get Bits from the material system |
|
ReadScreenPixels( 0, 0, GetModeStereoWidth(), GetModeStereoHeight(), pImage, IMAGE_FORMAT_RGB888 ); |
|
|
|
CUtlBuffer outBuf; |
|
if ( TGAWriter::WriteToBuffer( pImage, outBuf, GetModeStereoWidth(), GetModeStereoHeight(), IMAGE_FORMAT_RGB888, |
|
IMAGE_FORMAT_RGB888 ) ) |
|
{ |
|
if ( !g_pFileSystem->WriteFile( pFilename, NULL, outBuf ) ) |
|
{ |
|
Warning( "Couldn't write bitmap data snapshot to file %s.\n", pFilename ); |
|
} |
|
else |
|
{ |
|
char szPath[MAX_PATH]; |
|
szPath[0] = 0; |
|
if ( g_pFileSystem->GetLocalPath( pFilename, szPath, sizeof(szPath) ) ) |
|
{ |
|
AddScreenshotToSteam( szPath, GetModeStereoWidth(), GetModeStereoHeight() ); |
|
} |
|
} |
|
} |
|
|
|
delete[] pImage; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// PFM screenshot helpers |
|
//----------------------------------------------------------------------------- |
|
ITexture *CVideoMode_Common::GetBuildCubemaps16BitTexture( void ) |
|
{ |
|
return materials->FindTexture( "_rt_BuildCubemaps16bit", TEXTURE_GROUP_RENDER_TARGET ); |
|
} |
|
|
|
ITexture *CVideoMode_Common::GetFullFrameFB0( void ) |
|
{ |
|
return materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET ); |
|
} |
|
|
|
void CVideoMode_Common::BlitHiLoScreenBuffersTo16Bit( void ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// FIXME: this breaks in 480p due to (at least) the multisampled depth buffer (need to cache, clear and restore the depth target) |
|
Assert( 0 ); |
|
return; |
|
} |
|
|
|
IMaterial *pHDRCombineMaterial = materials->FindMaterial( "dev/hdrcombineto16bit", TEXTURE_GROUP_OTHER, true ); |
|
// if( IsErrorMaterial( pHDRCombineMaterial ) ) |
|
// { |
|
// Assert( 0 ); |
|
// return; |
|
// } |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
ITexture *pSaveRenderTarget; |
|
pSaveRenderTarget = pRenderContext->GetRenderTarget(); |
|
|
|
int oldX, oldY, oldW, oldH; |
|
pRenderContext->GetViewport( oldX, oldY, oldW, oldH ); |
|
|
|
pRenderContext->SetRenderTarget( GetBuildCubemaps16BitTexture() ); |
|
int width, height; |
|
pRenderContext->GetRenderTargetDimensions( width, height ); |
|
pRenderContext->Viewport( 0, 0, width, height ); |
|
pRenderContext->DrawScreenSpaceQuad( pHDRCombineMaterial ); |
|
|
|
pRenderContext->SetRenderTarget( pSaveRenderTarget ); |
|
pRenderContext->Viewport( oldX, oldY, oldW, oldH ); |
|
} |
|
|
|
void GetCubemapOffset( CubeMapFaceIndex_t faceIndex, int &x, int &y, int &faceDim ) |
|
{ |
|
int fbWidth, fbHeight; |
|
materials->GetBackBufferDimensions( fbWidth, fbHeight ); |
|
|
|
if( fbWidth * 4 > fbHeight * 3 ) |
|
{ |
|
faceDim = fbHeight / 3; |
|
} |
|
else |
|
{ |
|
faceDim = fbWidth / 4; |
|
} |
|
|
|
switch( faceIndex ) |
|
{ |
|
case CUBEMAP_FACE_RIGHT: |
|
x = 2; |
|
y = 1; |
|
break; |
|
case CUBEMAP_FACE_LEFT: |
|
x = 0; |
|
y = 1; |
|
break; |
|
case CUBEMAP_FACE_BACK: |
|
x = 1; |
|
y = 1; |
|
break; |
|
case CUBEMAP_FACE_FRONT: |
|
x = 3; |
|
y = 1; |
|
break; |
|
case CUBEMAP_FACE_UP: |
|
x = 2; |
|
y = 0; |
|
break; |
|
case CUBEMAP_FACE_DOWN: |
|
x = 2; |
|
y = 2; |
|
break; |
|
NO_DEFAULT |
|
} |
|
x *= faceDim; |
|
y *= faceDim; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Takes a snapshot to PFM |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::TakeSnapshotPFMRect( const char *pFilename, int x, int y, int w, int h, int resampleWidth, int resampleHeight, CubeMapFaceIndex_t faceIndex ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// FIXME: this breaks in 480p due to (at least) the multisampled depth buffer (need to cache, clear and restore the depth target) |
|
Assert( 0 ); |
|
return; |
|
} |
|
|
|
if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_NONE ) |
|
{ |
|
Warning( "Unable to take PFM screenshots if HDR isn't enabled!\n" ); |
|
return; |
|
} |
|
|
|
// hack |
|
// resampleWidth = w; |
|
// resampleHeight = h; |
|
// bitmap bits |
|
float16 *pImage = ( float16 * )malloc( w * h * ImageLoader::SizeInBytes( IMAGE_FORMAT_RGBA16161616F ) ); |
|
float *pImage1 = ( float * )malloc( w * h * ImageLoader::SizeInBytes( IMAGE_FORMAT_RGB323232F ) ); |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
// Save the current render target. |
|
ITexture *pSaveRenderTarget = pRenderContext->GetRenderTarget(); |
|
|
|
// Set this as the render target so that we can read it. |
|
pRenderContext->SetRenderTarget( GetFullFrameFB0() ); |
|
|
|
// Get Bits from the material system |
|
ReadScreenPixels( x, y, w, h, pImage, IMAGE_FORMAT_RGBA16161616F ); |
|
|
|
// Draw what we just grabbed to the screen |
|
pRenderContext->SetRenderTarget( NULL); |
|
|
|
int scrw, scrh; |
|
pRenderContext->GetRenderTargetDimensions( scrw, scrh ); |
|
pRenderContext->Viewport( 0, 0, scrw,scrh ); |
|
|
|
int offsetX, offsetY, faceDim; |
|
GetCubemapOffset( faceIndex, offsetX, offsetY, faceDim ); |
|
pRenderContext->DrawScreenSpaceRectangle( materials->FindMaterial( "dev/copyfullframefb", "" ), |
|
offsetX, offsetY, faceDim, faceDim, 0, 0, w-1, h-1, scrw, scrh ); |
|
|
|
// Restore the render target. |
|
pRenderContext->SetRenderTarget( pSaveRenderTarget ); |
|
|
|
// convert from float16 to float32 |
|
ImageLoader::ConvertImageFormat( ( unsigned char * )pImage, IMAGE_FORMAT_RGBA16161616F, |
|
( unsigned char * )pImage1, IMAGE_FORMAT_RGB323232F, |
|
w, h ); |
|
|
|
Assert( w == h ); // garymcthack - this only works for square images |
|
|
|
float *pFloatImage = ( float * )malloc( resampleWidth * resampleHeight * ImageLoader::SizeInBytes( IMAGE_FORMAT_RGB323232F ) ); |
|
|
|
ImageLoader::ResampleInfo_t info; |
|
info.m_pSrc = ( unsigned char * )pImage1; |
|
info.m_pDest = ( unsigned char * )pFloatImage; |
|
info.m_nSrcWidth = w; |
|
info.m_nSrcHeight = h; |
|
info.m_nDestWidth = resampleWidth; |
|
info.m_nDestHeight = resampleHeight; |
|
info.m_flSrcGamma = 1.0f; |
|
info.m_flDestGamma = 1.0f; |
|
|
|
if( !ImageLoader::ResampleRGB323232F( info ) ) |
|
{ |
|
Sys_Error( "Can't resample\n" ); |
|
} |
|
|
|
PFMWrite( pFloatImage, pFilename, resampleWidth, resampleHeight ); |
|
|
|
free( pImage1 ); |
|
free( pImage ); |
|
free( pFloatImage ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Takes a snapshot |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::TakeSnapshotTGARect( const char *pFilename, int x, int y, int w, int h, int resampleWidth, int resampleHeight, bool bPFM, CubeMapFaceIndex_t faceIndex ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
Assert( 0 ); |
|
return; |
|
} |
|
|
|
if ( bPFM ) |
|
{ |
|
TakeSnapshotPFMRect( pFilename, x, y, w, h, resampleWidth, resampleHeight, faceIndex ); |
|
return; |
|
} |
|
|
|
// bitmap bits |
|
uint8 *pImage = new uint8[ w * h * 4 ]; |
|
uint8 *pImage1 = new uint8[ resampleWidth * resampleHeight * 4 ]; |
|
|
|
// Get Bits from the material system |
|
ReadScreenPixels( x, y, w, h, pImage, IMAGE_FORMAT_RGBA8888 ); |
|
|
|
Assert( w == h ); // garymcthack - this only works for square images |
|
|
|
ImageLoader::ResampleInfo_t info; |
|
info.m_pSrc = pImage; |
|
info.m_pDest = pImage1; |
|
info.m_nSrcWidth = w; |
|
info.m_nSrcHeight = h; |
|
info.m_nDestWidth = resampleWidth; |
|
info.m_nDestHeight = resampleHeight; |
|
info.m_flSrcGamma = 1.0f; |
|
info.m_flDestGamma = 1.0f; |
|
|
|
if( !ImageLoader::ResampleRGBA8888( info ) ) |
|
{ |
|
Sys_Error( "Can't resample\n" ); |
|
} |
|
|
|
CUtlBuffer outBuf; |
|
if ( TGAWriter::WriteToBuffer( pImage1, outBuf, resampleWidth, resampleHeight, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 ) ) |
|
{ |
|
if ( !g_pFileSystem->WriteFile( pFilename, NULL, outBuf ) ) |
|
{ |
|
Error( "Couldn't write bitmap data snapshot to file %s.\n", pFilename ); |
|
} |
|
else |
|
{ |
|
DevMsg( "Screenshot: %dx%d saved to '%s'.\n", w, h, pFilename ); |
|
} |
|
} |
|
|
|
delete[] pImage1; |
|
delete[] pImage; |
|
materials->SwapBuffers(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Writes the data in *data to the sequentially number .bmp file filename |
|
// Input : *filename - |
|
// width - |
|
// height - |
|
// depth - |
|
// *data - |
|
// Output : static void |
|
//----------------------------------------------------------------------------- |
|
static void VID_ProcessMovieFrame( const MovieInfo_t& info, bool jpeg, const char *filename, int width, int height, byte *data ) |
|
{ |
|
CUtlBuffer outBuf; |
|
bool bSuccess = false; |
|
if ( jpeg ) |
|
{ |
|
#if HAVE_JPEG |
|
bSuccess = videomode->TakeSnapshotJPEGToBuffer( outBuf, info.jpeg_quality ); |
|
#endif |
|
} |
|
else |
|
{ |
|
bSuccess = TGAWriter::WriteToBuffer( data, outBuf, width, height, IMAGE_FORMAT_BGR888, IMAGE_FORMAT_RGB888 ); |
|
} |
|
|
|
if ( bSuccess ) |
|
{ |
|
if ( !g_pFileSystem->WriteFile( filename, NULL, outBuf ) ) |
|
{ |
|
Warning( "Couldn't write movie snapshot to file %s.\n", filename ); |
|
Cbuf_AddText( "endmovie\n" ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Store current frame to numbered .bmp file |
|
// Input : *pFilename - |
|
//----------------------------------------------------------------------------- |
|
extern IVideoRecorder *g_pVideoRecorder; |
|
|
|
void CVideoMode_Common::WriteMovieFrame( const MovieInfo_t& info ) |
|
{ |
|
char const *pMovieName = info.moviename; |
|
int nMovieFrame = info.movieframe; |
|
|
|
if ( g_LostVideoMemory ) |
|
return; |
|
|
|
if ( !pMovieName[0] ) |
|
{ |
|
Cbuf_AddText( "endmovie\n" ); |
|
ConMsg( "Tried to write movie buffer with no filename set!\n" ); |
|
return; |
|
} |
|
|
|
int imagesize = GetModeStereoWidth() * GetModeStereoHeight(); |
|
BGR888_t *hp = new BGR888_t[ imagesize ]; |
|
if ( hp == NULL ) |
|
{ |
|
Sys_Error( "Couldn't allocate bitmap header to snapshot.\n" ); |
|
} |
|
|
|
// Get Bits from material system |
|
ReadScreenPixels( 0, 0, GetModeStereoWidth(), GetModeStereoHeight(), hp, IMAGE_FORMAT_BGR888 ); |
|
|
|
// Store frame to disk |
|
if ( info.DoTga() ) |
|
{ |
|
VID_ProcessMovieFrame( info, false, va( "%s%04d.tga", pMovieName, nMovieFrame ), |
|
GetModeStereoWidth(), GetModeStereoHeight(), (unsigned char*)hp ); |
|
} |
|
|
|
if ( info.DoJpg() ) |
|
{ |
|
VID_ProcessMovieFrame( info, true, va( "%s%04d.jpg", pMovieName, nMovieFrame ), |
|
GetModeStereoWidth(), GetModeStereoHeight(), (unsigned char*)hp ); |
|
} |
|
|
|
if ( info.DoVideo() ) |
|
{ |
|
g_pVideoRecorder->AppendVideoFrame( hp ); |
|
} |
|
|
|
delete[] hp; |
|
} |
|
|
|
#if HAVE_JPEG |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Expanded data destination object for CUtlBuffer output |
|
//----------------------------------------------------------------------------- |
|
struct JPEGDestinationManager_t |
|
{ |
|
struct jpeg_destination_mgr pub; // public fields |
|
|
|
CUtlBuffer *pBuffer; // target/final buffer |
|
byte *buffer; // start of temp buffer |
|
}; |
|
|
|
// choose an efficiently bufferaable size |
|
#define OUTPUT_BUF_SIZE 4096 |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize destination --- called by jpeg_start_compress |
|
// before any data is actually written. |
|
//----------------------------------------------------------------------------- |
|
METHODDEF(void) init_destination (j_compress_ptr cinfo) |
|
{ |
|
JPEGDestinationManager_t *dest = ( JPEGDestinationManager_t *) cinfo->dest; |
|
|
|
// Allocate the output buffer --- it will be released when done with image |
|
dest->buffer = (byte *) |
|
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, |
|
OUTPUT_BUF_SIZE * sizeof(byte)); |
|
|
|
dest->pub.next_output_byte = dest->buffer; |
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Empty the output buffer --- called whenever buffer fills up. |
|
// Input : boolean - |
|
//----------------------------------------------------------------------------- |
|
METHODDEF(boolean) empty_output_buffer (j_compress_ptr cinfo) |
|
{ |
|
JPEGDestinationManager_t *dest = ( JPEGDestinationManager_t * ) cinfo->dest; |
|
|
|
CUtlBuffer *buf = dest->pBuffer; |
|
|
|
// Add some data |
|
buf->Put( dest->buffer, OUTPUT_BUF_SIZE ); |
|
|
|
dest->pub.next_output_byte = dest->buffer; |
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; |
|
|
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Terminate destination --- called by jpeg_finish_compress |
|
// after all data has been written. Usually needs to flush buffer. |
|
// |
|
// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding |
|
// application must deal with any cleanup that should happen even |
|
// for error exit. |
|
//----------------------------------------------------------------------------- |
|
METHODDEF(void) term_destination (j_compress_ptr cinfo) |
|
{ |
|
JPEGDestinationManager_t *dest = (JPEGDestinationManager_t *) cinfo->dest; |
|
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; |
|
|
|
CUtlBuffer *buf = dest->pBuffer; |
|
|
|
/* Write any data remaining in the buffer */ |
|
if (datacount > 0) |
|
{ |
|
buf->Put( dest->buffer, datacount ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set up functions for writing data to a CUtlBuffer instead of FILE * |
|
//----------------------------------------------------------------------------- |
|
GLOBAL(void) jpeg_UtlBuffer_dest (j_compress_ptr cinfo, CUtlBuffer *pBuffer ) |
|
{ |
|
JPEGDestinationManager_t *dest; |
|
|
|
/* The destination object is made permanent so that multiple JPEG images |
|
* can be written to the same file without re-executing jpeg_stdio_dest. |
|
* This makes it dangerous to use this manager and a different destination |
|
* manager serially with the same JPEG object, because their private object |
|
* sizes may be different. Caveat programmer. |
|
*/ |
|
if (cinfo->dest == NULL) { /* first time for this JPEG object? */ |
|
cinfo->dest = (struct jpeg_destination_mgr *) |
|
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, |
|
sizeof(JPEGDestinationManager_t)); |
|
} |
|
|
|
dest = ( JPEGDestinationManager_t * ) cinfo->dest; |
|
|
|
dest->pub.init_destination = init_destination; |
|
dest->pub.empty_output_buffer = empty_output_buffer; |
|
dest->pub.term_destination = term_destination; |
|
dest->pBuffer = pBuffer; |
|
} |
|
#endif |
|
|
|
bool CVideoMode_Common::TakeSnapshotJPEGToBuffer( CUtlBuffer& buf, int quality ) |
|
{ |
|
#if !defined( _X360 ) && HAVE_JPEG |
|
if ( g_LostVideoMemory ) |
|
return false; |
|
|
|
// Validate quality level |
|
quality = clamp( quality, 1, 100 ); |
|
|
|
// Allocate space for bits |
|
uint8 *pImage = new uint8[ GetModeStereoWidth() * 3 * GetModeStereoHeight() ]; |
|
if ( !pImage ) |
|
{ |
|
Msg( "Unable to allocate %i bytes for image\n", GetModeStereoWidth() * 3 * GetModeStereoHeight() ); |
|
return false; |
|
} |
|
|
|
// Get Bits from the material system |
|
ReadScreenPixels( 0, 0, GetModeStereoWidth(), GetModeStereoHeight(), pImage, IMAGE_FORMAT_RGB888 ); |
|
|
|
JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] |
|
int row_stride; // physical row width in image buffer |
|
|
|
// stderr handler |
|
struct jpeg_error_mgr jerr; |
|
|
|
// compression data structure |
|
struct jpeg_compress_struct cinfo; |
|
|
|
row_stride = GetModeStereoWidth() * 3; // JSAMPLEs per row in image_buffer |
|
|
|
// point at stderr |
|
cinfo.err = jpeg_std_error(&jerr); |
|
|
|
// create compressor |
|
jpeg_create_compress(&cinfo); |
|
|
|
// Hook CUtlBuffer to compression |
|
jpeg_UtlBuffer_dest(&cinfo, &buf ); |
|
|
|
// image width and height, in pixels |
|
cinfo.image_width = GetModeStereoWidth(); |
|
cinfo.image_height = GetModeStereoHeight(); |
|
// RGB is 3 componnent |
|
cinfo.input_components = 3; |
|
// # of color components per pixel |
|
cinfo.in_color_space = JCS_RGB; |
|
|
|
// Apply settings |
|
jpeg_set_defaults(&cinfo); |
|
jpeg_set_quality(&cinfo, quality, TRUE ); |
|
|
|
// Start compressor |
|
jpeg_start_compress(&cinfo, TRUE); |
|
|
|
// Write scanlines |
|
while ( cinfo.next_scanline < cinfo.image_height ) |
|
{ |
|
row_pointer[ 0 ] = &pImage[ cinfo.next_scanline * row_stride ]; |
|
jpeg_write_scanlines( &cinfo, row_pointer, 1 ); |
|
} |
|
|
|
// Finalize image |
|
jpeg_finish_compress(&cinfo); |
|
|
|
// Cleanup |
|
jpeg_destroy_compress(&cinfo); |
|
|
|
delete[] pImage; |
|
|
|
#else |
|
// not supporting |
|
Assert( 0 ); |
|
#endif |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Write vid.buffer out as a .jpg file |
|
// Input : *pFilename - |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_Common::TakeSnapshotJPEG( const char *pFilename, int quality ) |
|
{ |
|
#if !defined( _X360 ) |
|
Assert( pFilename ); |
|
|
|
// Output buffer |
|
CUtlBuffer buf( 0, 0 ); |
|
TakeSnapshotJPEGToBuffer( buf, quality ); |
|
|
|
int finalSize = 0; |
|
FileHandle_t fh = g_pFileSystem->Open( pFilename, "wb" ); |
|
if ( FILESYSTEM_INVALID_HANDLE != fh ) |
|
{ |
|
g_pFileSystem->Write( buf.Base(), buf.TellPut(), fh ); |
|
finalSize = g_pFileSystem->Tell( fh ); |
|
g_pFileSystem->Close( fh ); |
|
} |
|
|
|
// Show info to console. |
|
char orig[ 64 ]; |
|
char final[ 64 ]; |
|
Q_strncpy( orig, Q_pretifymem( GetModeStereoWidth() * 3 * GetModeStereoHeight(), 2 ), sizeof( orig ) ); |
|
Q_strncpy( final, Q_pretifymem( finalSize, 2 ), sizeof( final ) ); |
|
|
|
Msg( "Wrote '%s': %s (%dx%d) compresssed (quality %i) to %s\n", |
|
pFilename, orig, GetModeStereoWidth(), GetModeStereoHeight(), quality, final ); |
|
|
|
if ( finalSize > 0 ) |
|
{ |
|
char szPath[MAX_PATH]; |
|
szPath[0] = 0; |
|
if ( g_pFileSystem->GetLocalPath( pFilename, szPath, sizeof(szPath) ) ) |
|
{ |
|
AddScreenshotToSteam( szPath, GetModeStereoWidth(), GetModeStereoHeight() ); |
|
} |
|
} |
|
|
|
#else |
|
Assert( 0 ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// The version of the VideoMode class for the material system |
|
//----------------------------------------------------------------------------- |
|
class CVideoMode_MaterialSystem: public CVideoMode_Common |
|
{ |
|
public: |
|
typedef CVideoMode_Common BaseClass; |
|
|
|
CVideoMode_MaterialSystem( ); |
|
|
|
virtual bool Init( ); |
|
virtual void Shutdown( void ); |
|
virtual void SetGameWindow( void *hWnd ); |
|
virtual bool SetMode( int nWidth, int nHeight, bool bWindowed ); |
|
virtual void ReleaseVideo( void ); |
|
virtual void RestoreVideo( void ); |
|
virtual void AdjustForModeChange( void ); |
|
virtual void ReadScreenPixels( int x, int y, int w, int h, void *pBuffer, ImageFormat format ); |
|
|
|
private: |
|
virtual void ReleaseFullScreen( void ); |
|
virtual void ChangeDisplaySettingsToFullscreen( int nWidth, int nHeight, int nBPP ); |
|
|
|
#ifdef WIN32 |
|
int m_nLastCDSWidth; |
|
int m_nLastCDSHeight; |
|
int m_nLastCDSBPP; |
|
int m_nLastCDSFreq; |
|
#endif |
|
}; |
|
|
|
static void VideoMode_AdjustForModeChange( void ) |
|
{ |
|
( ( CVideoMode_MaterialSystem * )videomode )->AdjustForModeChange(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CVideoMode_MaterialSystem::CVideoMode_MaterialSystem( ) |
|
{ |
|
#ifdef WIN32 |
|
m_nLastCDSWidth = 0; |
|
m_nLastCDSHeight = 0; |
|
m_nLastCDSBPP = 0; |
|
m_nLastCDSFreq = 0; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_MaterialSystem::Init( ) |
|
{ |
|
m_bSetModeOnce = false; |
|
m_bPlayedStartupVideo = false; |
|
|
|
// we only support 32-bit rendering. |
|
int bitsperpixel = 32; |
|
|
|
bool bAllowSmallModes = false; |
|
#ifndef ANDROID |
|
if ( CommandLine()->FindParm( "-small" ) ) |
|
#endif |
|
{ |
|
bAllowSmallModes = true; |
|
} |
|
|
|
int nAdapter = materials->GetCurrentAdapter(); |
|
int nModeCount = materials->GetModeCount( nAdapter ); |
|
|
|
int nDesktopWidth, nDesktopHeight, nDesktopRefresh; |
|
game->GetDesktopInfo( nDesktopWidth, nDesktopHeight, nDesktopRefresh ); |
|
|
|
for ( int i = 0; i < nModeCount; i++ ) |
|
{ |
|
MaterialVideoMode_t info; |
|
materials->GetModeInfo( nAdapter, i, info ); |
|
|
|
if ( info.m_Width < 640 || info.m_Height < 480 ) |
|
{ |
|
if ( !bAllowSmallModes ) |
|
continue; |
|
} |
|
|
|
// make sure we don't already have this mode listed |
|
bool bAlreadyInList = false; |
|
for ( int j = 0; j < m_nNumModes; j++ ) |
|
{ |
|
if ( info.m_Width == m_rgModeList[ j ].width && info.m_Height == m_rgModeList[ j ].height ) |
|
{ |
|
|
|
// in VR mode we want the highest refresh rate, without regard for the desktop refresh rate |
|
if ( UseVR() || ShouldForceVRActive() ) |
|
{ |
|
if ( info.m_RefreshRate > m_rgModeList[j].refreshRate ) |
|
{ |
|
m_rgModeList[j].refreshRate = info.m_RefreshRate; |
|
} |
|
} |
|
else |
|
{ |
|
// choose the highest refresh rate available for each mode up to the desktop rate |
|
|
|
// if the new mode is valid and current is invalid or not as high, choose the new one |
|
if ( info.m_RefreshRate <= nDesktopRefresh && (m_rgModeList[j].refreshRate > nDesktopRefresh || m_rgModeList[j].refreshRate < info.m_RefreshRate) ) |
|
{ |
|
m_rgModeList[j].refreshRate = info.m_RefreshRate; |
|
} |
|
} |
|
|
|
bAlreadyInList = true; |
|
break; |
|
} |
|
} |
|
|
|
if ( bAlreadyInList ) |
|
continue; |
|
|
|
m_rgModeList[ m_nNumModes ].width = info.m_Width; |
|
m_rgModeList[ m_nNumModes ].height = info.m_Height; |
|
m_rgModeList[ m_nNumModes ].bpp = bitsperpixel; |
|
// NOTE: Don't clamp this to the desktop rate because we want to be sure we've only added |
|
// modes that the adapter can do and maybe the desktop rate isn't available in this mode |
|
m_rgModeList[ m_nNumModes ].refreshRate = info.m_RefreshRate; |
|
|
|
if ( ++m_nNumModes >= MAX_MODE_LIST ) |
|
break; |
|
} |
|
|
|
// Sort modes for easy searching later |
|
if ( m_nNumModes > 1 ) |
|
{ |
|
qsort( (void *)&m_rgModeList[0], m_nNumModes, sizeof(vmode_t), VideoModeCompare ); |
|
} |
|
|
|
materials->AddModeChangeCallBack( &VideoMode_AdjustForModeChange ); |
|
SetInitialized( true ); |
|
return true; |
|
} |
|
|
|
|
|
void CVideoMode_MaterialSystem::Shutdown() |
|
{ |
|
materials->RemoveModeChangeCallBack( &VideoMode_AdjustForModeChange ); |
|
BaseClass::Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the video mode |
|
//----------------------------------------------------------------------------- |
|
bool CVideoMode_MaterialSystem::SetMode( int nWidth, int nHeight, bool bWindowed ) |
|
{ |
|
// Necessary for mode selection to work |
|
int nFoundMode = FindVideoMode( nWidth, nHeight, bWindowed ); |
|
vmode_t *pMode = GetMode( nFoundMode ); |
|
|
|
// update current video state |
|
MaterialSystem_Config_t config = *g_pMaterialSystemConfig; |
|
config.m_VideoMode.m_Width = pMode->width; |
|
config.m_VideoMode.m_Height = pMode->height; |
|
|
|
// make sure VR mode is up to date |
|
config.SetFlag( MATSYS_VIDCFG_FLAGS_VR_MODE, UseVR() || ShouldForceVRActive() ); |
|
|
|
if ( ShouldForceVRActive() ) |
|
{ |
|
config.m_nVRModeAdapter = materials->GetCurrentAdapter(); |
|
} |
|
|
|
#ifdef SWDS |
|
config.m_VideoMode.m_RefreshRate = 60; |
|
#else |
|
config.m_VideoMode.m_RefreshRate = GetRefreshRateForMode( pMode ); |
|
#endif |
|
|
|
config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, bWindowed ); |
|
|
|
#if defined( _X360 ) |
|
XVIDEO_MODE videoMode; |
|
XGetVideoMode( &videoMode ); |
|
if ( videoMode.fIsWideScreen ) |
|
{ |
|
extern ConVar r_aspectratio; |
|
r_aspectratio.SetValue( 16.0f/9.0f ); |
|
} |
|
config.SetFlag( MATSYS_VIDCFG_FLAGS_SCALE_TO_OUTPUT_RESOLUTION, (DWORD)nWidth != videoMode.dwDisplayWidth || (DWORD)nHeight != videoMode.dwDisplayHeight ); |
|
if ( nHeight == 480 || nWidth == 576 ) |
|
{ |
|
// Use 2xMSAA for standard def (see mat_software_aa_strength for fake hi-def aa) |
|
// FIXME: shuffle the EDRAM surfaces to allow 4xMSAA for standard def |
|
// (they would overlap & trash each other with the current arrangement) |
|
// NOTE: This should affect 640x480 and 848x480 (which is also used for 640x480 widescreen), and PAL 640x576 |
|
config.m_nAASamples = 2; |
|
} |
|
#endif |
|
|
|
// FIXME: This is trash. We have to do *different* things depending on how we're setting the mode! |
|
if ( !m_bSetModeOnce ) |
|
{ |
|
//Debugger(); |
|
|
|
if ( !materials->SetMode( (void*)game->GetMainDeviceWindow(), config ) ) |
|
return false; |
|
|
|
m_bSetModeOnce = true; |
|
|
|
InitStartupScreen(); |
|
return true; |
|
} |
|
|
|
// update the config |
|
OverrideMaterialSystemConfig( config ); |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Called by the material system when mode changes after a call to OverrideConfig |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::AdjustForModeChange( void ) |
|
{ |
|
if ( InEditMode() ) |
|
return; |
|
|
|
// get previous size |
|
int nOldUIWidth = GetModeUIWidth(); |
|
int nOldUIHeight = GetModeUIHeight(); |
|
|
|
// Get the new mode info from the config record |
|
int nNewWidth = g_pMaterialSystemConfig->m_VideoMode.m_Width; |
|
int nNewHeight = g_pMaterialSystemConfig->m_VideoMode.m_Height; |
|
bool bWindowed = g_pMaterialSystemConfig->Windowed(); |
|
|
|
// reset the window size |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
ResetCurrentModeForNewResolution( nNewWidth, nNewHeight, bWindowed ); |
|
AdjustWindow( GetModeWidth(), GetModeHeight(), GetModeBPP(), IsWindowedMode() ); |
|
MarkClientViewRectDirty(); |
|
pRenderContext->Viewport( 0, 0, GetModeStereoWidth(), GetModeStereoHeight() ); |
|
|
|
// fixup vgui |
|
vgui::surface()->OnScreenSizeChanged( nOldUIWidth, nOldUIHeight ); |
|
|
|
// Re-init the HUD |
|
ClientDLL_HudVidInit(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the game window in editor mode |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::SetGameWindow( void *hWnd ) |
|
{ |
|
if ( hWnd == NULL ) |
|
{ |
|
// No longer confine rendering into this view |
|
materials->SetView( NULL ); |
|
return; |
|
} |
|
|
|
// When running in edit mode, just use hammer's window |
|
game->SetGameWindow( (HWND)hWnd ); |
|
|
|
// FIXME: Move this code into the _MaterialSystem version of CVideoMode |
|
// In editor mode, the mode width + height is equal to the desktop width + height |
|
MaterialVideoMode_t mode; |
|
materials->GetDisplayMode( mode ); |
|
m_bWindowed = true; |
|
m_nModeWidth = mode.m_Width; |
|
m_nModeHeight = mode.m_Height; |
|
|
|
materials->SetView( game->GetMainDeviceWindow() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Called when we lose the video buffer (alt-tab) |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::ReleaseVideo( void ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
if ( IsWindowedMode() ) |
|
return; |
|
|
|
ReleaseFullScreen(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::RestoreVideo( void ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
if ( IsWindowedMode() ) |
|
return; |
|
|
|
#if defined( USE_SDL ) |
|
SDL_ShowWindow( (SDL_Window*)game->GetMainWindow() ); |
|
#else |
|
ShowWindow( (HWND)game->GetMainWindow(), SW_SHOWNORMAL ); |
|
#endif |
|
AdjustWindow( GetModeWidth(), GetModeHeight(), GetModeBPP(), IsWindowedMode() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::ReleaseFullScreen( void ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
if ( IsWindowedMode() ) |
|
return; |
|
|
|
#if !defined( USE_SDL ) |
|
// Hide the main window |
|
if ( m_nLastCDSWidth != 0 ) |
|
ChangeDisplaySettings( NULL, 0 ); |
|
ShowWindow( (HWND)game->GetMainWindow(), SW_MINIMIZE ); |
|
m_nLastCDSWidth = 0; |
|
m_nLastCDSHeight = 0; |
|
m_nLastCDSBPP = 0; |
|
m_nLastCDSFreq = 0; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Use Change Display Settings to go Full Screen |
|
//----------------------------------------------------------------------------- |
|
void CVideoMode_MaterialSystem::ChangeDisplaySettingsToFullscreen( int nWidth, int nHeight, int nBPP ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
if ( IsWindowedMode() ) |
|
return; |
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL ) |
|
DEVMODE dm; |
|
memset(&dm, 0, sizeof(dm)); |
|
|
|
dm.dmSize = sizeof( dm ); |
|
dm.dmPelsWidth = nWidth; |
|
dm.dmPelsHeight = nHeight; |
|
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; |
|
dm.dmBitsPerPel = nBPP; |
|
|
|
// FIXME: Fix direct reference of refresh rate from config record |
|
int freq = g_pMaterialSystemConfig->m_VideoMode.m_RefreshRate; |
|
if ( freq >= 60 ) |
|
{ |
|
dm.dmDisplayFrequency = freq; |
|
dm.dmFields |= DM_DISPLAYFREQUENCY; |
|
} |
|
|
|
#if defined(IS_WINDOWS_PC) |
|
DEVMODE dmCurrent; |
|
if ( EnumDisplaySettings( materials->GetDisplayDeviceName(), ENUM_CURRENT_SETTINGS, &dmCurrent ) && |
|
dmCurrent.dmBitsPerPel == dm.dmBitsPerPel && |
|
dmCurrent.dmPelsWidth == dm.dmPelsWidth && |
|
dmCurrent.dmPelsHeight == dm.dmPelsHeight && |
|
( (dm.dmFields & DM_DISPLAYFREQUENCY) == 0 || dmCurrent.dmDisplayFrequency == dm.dmDisplayFrequency ) ) |
|
{ |
|
return; |
|
} |
|
#endif |
|
|
|
if ( nWidth == m_nLastCDSWidth && nHeight == m_nLastCDSHeight && nBPP == m_nLastCDSBPP && freq == m_nLastCDSFreq ) |
|
return; |
|
|
|
m_nLastCDSWidth = nWidth; |
|
m_nLastCDSHeight = nHeight; |
|
m_nLastCDSBPP = nBPP; |
|
m_nLastCDSFreq = freq; |
|
|
|
ChangeDisplaySettingsEx( materials->GetDisplayDeviceName(), &dm, NULL, CDS_FULLSCREEN, NULL ); |
|
#elif defined( USE_SDL ) |
|
g_pLauncherMgr->SetWindowFullScreen( true, nWidth, nHeight ); |
|
#else |
|
if ( !HushAsserts() ) |
|
{ |
|
Assert( !"Impl me" ); |
|
} |
|
#endif |
|
} |
|
|
|
void CVideoMode_MaterialSystem::ReadScreenPixels( int x, int y, int w, int h, void *pBuffer, ImageFormat format ) |
|
{ |
|
if ( !g_LostVideoMemory ) |
|
{ |
|
bool bReadPixelsFromFrontBuffer = g_pMaterialSystemHardwareConfig->ReadPixelsFromFrontBuffer(); |
|
if( bReadPixelsFromFrontBuffer ) |
|
{ |
|
Shader_SwapBuffers(); |
|
} |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
Rect_t rect; |
|
rect.x = x; |
|
rect.y = y; |
|
rect.width = w; |
|
rect.height = h; |
|
|
|
pRenderContext->ReadPixelsAndStretch( &rect, &rect, (unsigned char*)pBuffer, format, w * ImageLoader::SizeInBytes( format ) ); |
|
|
|
if( bReadPixelsFromFrontBuffer ) |
|
{ |
|
Shader_SwapBuffers(); |
|
} |
|
} |
|
else |
|
{ |
|
int nBytes = ImageLoader::GetMemRequired( w, h, 1, format, false ); |
|
memset( pBuffer, 0, nBytes ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Class factory |
|
//----------------------------------------------------------------------------- |
|
|
|
IVideoMode *videomode = ( IVideoMode * )NULL; |
|
|
|
void VideoMode_Create( ) |
|
{ |
|
videomode = new CVideoMode_MaterialSystem; |
|
Assert( videomode ); |
|
} |
|
|
|
void VideoMode_Destroy() |
|
{ |
|
if ( videomode ) |
|
{ |
|
CVideoMode_MaterialSystem *pVideoMode_MS = static_cast<CVideoMode_MaterialSystem*>(videomode); |
|
delete pVideoMode_MS; |
|
videomode = NULL; |
|
} |
|
}
|
|
|