Counter Strike : Global Offensive Source Code
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.

1043 lines
27 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "vgui_int.h"
  9. #include "ienginevgui.h"
  10. #include "itextmessage.h"
  11. #include "vguicenterprint.h"
  12. #include "iloadingdisc.h"
  13. #include "ifpspanel.h"
  14. #include "imessagechars.h"
  15. #include "inetgraphpanel.h"
  16. #include "idebugoverlaypanel.h"
  17. #include <vgui/ISurface.h>
  18. #include <vgui/IVGui.h>
  19. #include <vgui/IInput.h>
  20. #include "tier0/vprof.h"
  21. #include "iclientmode.h"
  22. #include <vgui_controls/Panel.h>
  23. #include <keyvalues.h>
  24. #include "filesystem.h"
  25. #include "matsys_controls/matsyscontrols.h"
  26. #ifdef SIXENSE
  27. #include "sixense/in_sixense.h"
  28. #endif
  29. #ifdef _PS3
  30. #include "ps3/ps3_core.h"
  31. #endif
  32. using namespace vgui;
  33. #ifndef _GAMECONSOLE
  34. void MP3Player_Create( vgui::VPANEL parent );
  35. void MP3Player_Destroy();
  36. #endif
  37. #include <vgui/IInputInternal.h>
  38. vgui::IInputInternal *g_InputInternal = NULL;
  39. #include <vgui_controls/Controls.h>
  40. #include "cstrike15/gameui/cstrike15/steamoverlay/isteamoverlaymgr.h"
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43. bool IsWidescreen( void );
  44. void ss_pipsplit_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
  45. {
  46. VGui_OnSplitScreenStateChanged();
  47. }
  48. static ConVar ss_pipsplit( "ss_pipsplit", "1", 0, "If enabled, use PIP instead of splitscreen. (Only works for 2 players)", ss_pipsplit_changed );
  49. static ConVar ss_pipscale( "ss_pipscale", "0.3f", 0, "Scale of the PIP aspect ratio to our resolution.", ss_pipsplit_changed );
  50. static ConVar ss_pip_right_offset( "ss_pip_right_offset", "25", 0, "PIP offset vector from the right of the screen", ss_pipsplit_changed );
  51. static ConVar ss_pip_bottom_offset( "ss_pip_bottom_offset", "25", 0, "PIP offset vector from the bottom of the screen", ss_pipsplit_changed );
  52. static ConVar ss_force_primary_fullscreen( "ss_force_primary_fullscreen", "0", 0, "If enabled, all splitscreen users will only see the first user's screen full screen", ss_pipsplit_changed );
  53. bool VGui_UsePipSplit();
  54. void ss_verticalsplit_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
  55. {
  56. ConVarRef var( pConVar );
  57. if ( var.GetBool() != !!(int)flOldValue )
  58. {
  59. VGui_OnSplitScreenStateChanged();
  60. if ( GetFullscreenClientMode() )
  61. {
  62. // we have to force re-layout, because the screen dimensions haven't changed,
  63. // but our layout is going to be different.
  64. GetFullscreenClientMode()->Layout( true );
  65. }
  66. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  67. {
  68. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( i );
  69. GetClientMode()->Layout();
  70. GetHud().OnSplitScreenStateChanged();
  71. }
  72. }
  73. }
  74. static ConVar ss_verticalsplit( "ss_verticalsplit", "0", 0, "Two player split screen uses vertical split (do not set this directly, use ss_splitmode instead).", ss_verticalsplit_changed );
  75. void ss_splitmode_changed( IConVar *pConVar, const char *pOldString, float flOldValue )
  76. {
  77. ConVarRef var( pConVar );
  78. if ( !IsWidescreen() )
  79. {
  80. // Non-widescreen is alway horizontal
  81. ss_verticalsplit.SetValue( 0 );
  82. }
  83. else
  84. {
  85. if ( var.GetInt() == 1 )
  86. {
  87. // Horizontal
  88. ss_verticalsplit.SetValue( 0 );
  89. }
  90. else if ( var.GetInt() == 2 )
  91. {
  92. // Vertical
  93. ss_verticalsplit.SetValue( 1 );
  94. }
  95. else
  96. {
  97. // Vertical is default for widescreen
  98. ss_verticalsplit.SetValue( 1 );
  99. }
  100. }
  101. }
  102. static ConVar ss_splitmode( "ss_splitmode", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE, "Two player split screen mode (0 - recommended settings base on the width, 1 - horizontal, 2 - vertical (only allowed in widescreen)", ss_splitmode_changed );
  103. static ConVar ss_enable( "ss_enable", "0", FCVAR_RELEASE, "Enables Split Screen support. Play Single Player now launches into split screen mode. NO ONLINE SUPPORT" );
  104. void GetVGUICursorPos( int& x, int& y )
  105. {
  106. vgui::input()->GetCursorPos(x, y);
  107. }
  108. void SetVGUICursorPos( int x, int y )
  109. {
  110. if ( !g_bTextMode )
  111. {
  112. vgui::input()->SetCursorPos(x, y);
  113. }
  114. }
  115. class CHudTextureHandleProperty : public vgui::IPanelAnimationPropertyConverter
  116. {
  117. public:
  118. virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
  119. {
  120. void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
  121. CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
  122. // lookup texture name for id
  123. if ( pHandle->Get() )
  124. {
  125. kv->SetString( entry->name(), pHandle->Get()->szShortName );
  126. }
  127. else
  128. {
  129. kv->SetString( entry->name(), "" );
  130. }
  131. }
  132. virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry )
  133. {
  134. void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
  135. CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
  136. const char *texturename = kv->GetString( entry->name() );
  137. if ( texturename && texturename[ 0 ] )
  138. {
  139. CHudTexture *currentTexture = HudIcons().GetIcon( texturename );
  140. pHandle->Set( currentTexture );
  141. }
  142. else
  143. {
  144. pHandle->Set( NULL );
  145. }
  146. }
  147. virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry )
  148. {
  149. void *data = ( void * )( (*entry->m_pfnLookup)( panel ) );
  150. CHudTextureHandle *pHandle = ( CHudTextureHandle * )data;
  151. const char *texturename = entry->defaultvalue();
  152. if ( texturename && texturename[ 0 ] )
  153. {
  154. CHudTexture *currentTexture = HudIcons().GetIcon( texturename );
  155. pHandle->Set( currentTexture );
  156. }
  157. else
  158. {
  159. pHandle->Set( NULL );
  160. }
  161. }
  162. };
  163. class CSplitScreenLetterBox
  164. {
  165. public:
  166. enum
  167. {
  168. SPLITSCREEN_NONWIDESCREEN_HORIZONTAL_SPLIT = 0,
  169. SPLITSCREEN_WIDESCREEN_HORIZONTAL_SPLIT,
  170. SPLITSCREEN_WIDESCREEN_VERTICAL_SPLIT,
  171. NUM_SPLITSCREEN_TYPES,
  172. };
  173. void Init();
  174. void SetNumSplitScreenPlayers( int nPlayers );
  175. bool GetSettings( bool *pbInsetHud, float *pflAspect, float *pFOV, float *pViewmodelFOV );
  176. private:
  177. struct LetterBox_t
  178. {
  179. LetterBox_t() : m_flAspectRatio( 4.0f / 3.0f ), m_bInsetHud( false ) {}
  180. float m_flAspectRatio;
  181. bool m_bInsetHud;
  182. float m_flFOV;
  183. float m_flViewModelFOV;
  184. };
  185. bool m_bValid;
  186. LetterBox_t m_Settings[ NUM_SPLITSCREEN_TYPES ];
  187. int m_nSplitScreenPlayers;
  188. };
  189. void CSplitScreenLetterBox::Init()
  190. {
  191. m_nSplitScreenPlayers = 1;
  192. char const *pchSlotNames[] = { "nonwidescreen", "widescreen_horizontal_split", "widescreen_vertical_split" };
  193. char const *pchConfigFile = "splitscreen_config.txt";
  194. m_bValid = true;
  195. KeyValues *kv = new KeyValues( "splitscreen" );
  196. if ( kv->LoadFromFile( g_pFullFileSystem, pchConfigFile, "MOD" ) )
  197. {
  198. for ( int i = 0; i < NUM_SPLITSCREEN_TYPES && m_bValid; ++i )
  199. {
  200. KeyValues *settings = kv->FindKey( pchSlotNames[ i ], false );
  201. if ( settings )
  202. {
  203. // Get settings
  204. char const *pchAspect = settings->GetString( "aspect", "4 by 3" );
  205. if ( pchAspect )
  206. {
  207. // Allowable syntax is "16 by 9" or "16 x 9" or "1.77"
  208. if ( Q_stristr( pchAspect, " by " ) )
  209. {
  210. float f1, f2;
  211. if ( 2 == sscanf( pchAspect, "%f by %f", &f1, &f2 ) && f2 > 0.001f )
  212. {
  213. m_Settings[ i ].m_flAspectRatio = f1 / f2;
  214. }
  215. else
  216. {
  217. Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
  218. m_bValid = false;
  219. }
  220. }
  221. else if ( Q_stristr( pchAspect, " x " ) )
  222. {
  223. float f1, f2;
  224. if ( 2 == sscanf( pchAspect, "%f x %f", &f1, &f2 ) && f2 > 0.001f )
  225. {
  226. m_Settings[ i ].m_flAspectRatio = f1 / f2;
  227. }
  228. else
  229. {
  230. Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
  231. m_bValid = false;
  232. }
  233. }
  234. else if ( Q_atof( pchAspect ) > 0.1f )
  235. {
  236. m_Settings[ i ].m_flAspectRatio = Q_atof( pchAspect );
  237. }
  238. else
  239. {
  240. Error( "%s: Invalid aspect ratio string '%s'\n", pchConfigFile, pchAspect );
  241. m_bValid = false;
  242. }
  243. }
  244. // Get inset for hud
  245. m_Settings[ i ].m_bInsetHud = settings->GetBool( "insethud", false );
  246. // Get FOV
  247. m_Settings[ i ].m_flFOV = settings->GetFloat( "fov", 90.0f );
  248. // Get viewmodel FOVs
  249. m_Settings[ i ].m_flViewModelFOV = settings->GetFloat( "viewmodelfov", 50.0f );
  250. }
  251. else
  252. {
  253. Error( "%s: Missing settings block for split screen mode '%s'\n", pchConfigFile, pchSlotNames[ i ] );
  254. m_bValid = false;
  255. break;
  256. }
  257. }
  258. }
  259. else
  260. {
  261. Msg( "No split screen config file '%s', using defaults\n", pchConfigFile );
  262. m_bValid = false;
  263. }
  264. kv->deleteThis();
  265. }
  266. void CSplitScreenLetterBox::SetNumSplitScreenPlayers( int nPlayers )
  267. {
  268. m_nSplitScreenPlayers = nPlayers;
  269. }
  270. bool IsWidescreen( void )
  271. {
  272. const AspectRatioInfo_t &aspectRatioInfo = materials->GetAspectRatioInfo();
  273. return aspectRatioInfo.m_bIsWidescreen;
  274. }
  275. bool CSplitScreenLetterBox::GetSettings( bool *pbInsetHud, float *pflAspect, float *pFOV, float *pViewModelFOV )
  276. {
  277. Assert( pbInsetHud );
  278. Assert( pflAspect );
  279. Assert( pFOV );
  280. Assert( pViewModelFOV );
  281. static bool bUsedDefaultsLastTime = false;
  282. if ( !m_bValid || m_nSplitScreenPlayers == 1 || VGui_UsePipSplit() || ss_force_primary_fullscreen.GetBool() )
  283. {
  284. if ( !bUsedDefaultsLastTime )
  285. {
  286. bUsedDefaultsLastTime = true;
  287. }
  288. *pbInsetHud = false;
  289. *pflAspect = 4.0f / 3.0f;
  290. // FIXME: These are the non-splitscreen defaults for L4D. This code needs to be sanitized for other games.
  291. *pFOV = 90.0f;
  292. *pViewModelFOV = 50.0f;
  293. return false;
  294. }
  295. // Figure out which splitscreen mode to use based on current configuration.
  296. int slot;
  297. if ( IsWidescreen() )
  298. {
  299. if ( ss_verticalsplit.GetBool() )
  300. {
  301. slot = SPLITSCREEN_WIDESCREEN_VERTICAL_SPLIT;
  302. }
  303. else
  304. {
  305. slot = SPLITSCREEN_WIDESCREEN_HORIZONTAL_SPLIT;
  306. }
  307. }
  308. else
  309. {
  310. slot = SPLITSCREEN_NONWIDESCREEN_HORIZONTAL_SPLIT;
  311. }
  312. bUsedDefaultsLastTime = false;
  313. const LetterBox_t &lb = m_Settings[ slot ];
  314. *pbInsetHud = lb.m_bInsetHud;
  315. *pflAspect = lb.m_flAspectRatio;
  316. *pFOV = lb.m_flFOV;
  317. *pViewModelFOV = lb.m_flViewModelFOV;
  318. return true;
  319. }
  320. static CSplitScreenLetterBox g_LetterBox;
  321. CON_COMMAND( ss_reloadletterbox, "ss_reloadletterbox" )
  322. {
  323. g_LetterBox.Init();
  324. VGui_OnSplitScreenStateChanged();
  325. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  326. {
  327. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( i );
  328. GetClientMode()->Layout();
  329. GetHud().OnSplitScreenStateChanged();
  330. }
  331. }
  332. static CHudTextureHandleProperty textureHandleConverter;
  333. static void VGui_OneTimeInit()
  334. {
  335. static bool initialized = false;
  336. if ( initialized )
  337. return;
  338. initialized = true;
  339. vgui::Panel::AddPropertyConverter( "CHudTextureHandle", &textureHandleConverter );
  340. g_LetterBox.Init();
  341. }
  342. bool VGui_Startup( CreateInterfaceFn appSystemFactory )
  343. {
  344. if ( !vgui::VGui_InitInterfacesList( "CLIENT", &appSystemFactory, 1 ) )
  345. return false;
  346. if ( !vgui::VGui_InitMatSysInterfacesList( "CLIENT", &appSystemFactory, 1 ) )
  347. return false;
  348. g_InputInternal = (IInputInternal *)appSystemFactory( VGUI_INPUTINTERNAL_INTERFACE_VERSION, NULL );
  349. if ( !g_InputInternal )
  350. {
  351. return false; // c_vguiscreen.cpp needs this!
  352. }
  353. VGui_OneTimeInit();
  354. // Create any root panels for .dll
  355. VGUI_CreateClientDLLRootPanel();
  356. // Make sure we have a panel
  357. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  358. {
  359. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
  360. VPANEL root = VGui_GetClientDLLRootPanel();
  361. if ( !root )
  362. {
  363. return false;
  364. }
  365. }
  366. CUtlVector< Panel * > list;
  367. VGui_GetPanelList( list );
  368. for ( int i = 0; i < list.Count(); ++i )
  369. {
  370. list[ i ]->SetMessageContextId_R( (uint32)i );
  371. }
  372. VGui_GetFullscreenRootPanel()->SetMessageContextId_R( (uint32)0 );
  373. VGui_OnSplitScreenStateChanged();
  374. return true;
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. //-----------------------------------------------------------------------------
  379. void VGui_CreateGlobalPanels( void )
  380. {
  381. VPANEL gameToolParent = enginevgui->GetPanel( PANEL_CLIENTDLL_TOOLS );
  382. VPANEL toolParent = enginevgui->GetPanel( PANEL_TOOLS );
  383. #if defined( TRACK_BLOCKING_IO )
  384. VPANEL gameDLLPanel = enginevgui->GetPanel( PANEL_GAMEDLL );
  385. #endif
  386. // Part of game
  387. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  388. {
  389. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
  390. VPANEL root = VGui_GetClientDLLRootPanel();
  391. GetCenterPrint()->Create( root );
  392. }
  393. loadingdisc->Create( gameToolParent );
  394. messagechars->Create( gameToolParent );
  395. // Debugging or related tool
  396. fps->Create( toolParent );
  397. #if defined( TRACK_BLOCKING_IO )
  398. iopanel->Create( gameDLLPanel );
  399. #endif
  400. netgraphpanel->Create( toolParent );
  401. debugoverlaypanel->Create( gameToolParent );
  402. #ifndef _GAMECONSOLE
  403. // Create mp3 player off of tool parent panel
  404. MP3Player_Create( toolParent );
  405. #endif
  406. // Create Steam overlay
  407. if ( IsPS3() && g_pISteamOverlayMgr )
  408. g_pISteamOverlayMgr->Create( enginevgui->GetPanel( PANEL_STEAMOVERLAY ) );
  409. #ifdef SIXENSE
  410. g_pSixenseInput->CreateGUI( gameToolParent );
  411. #endif
  412. }
  413. void VGui_Shutdown()
  414. {
  415. // Destroy Steam overlay
  416. if ( IsPS3() && g_pISteamOverlayMgr )
  417. g_pISteamOverlayMgr->Destroy();
  418. #ifndef _GAMECONSOLE
  419. MP3Player_Destroy();
  420. #endif
  421. netgraphpanel->Destroy();
  422. debugoverlaypanel->Destroy();
  423. #if defined( TRACK_BLOCKING_IO )
  424. iopanel->Destroy();
  425. #endif
  426. fps->Destroy();
  427. messagechars->Destroy();
  428. loadingdisc->Destroy();
  429. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  430. {
  431. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
  432. GetCenterPrint()->Destroy();
  433. }
  434. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  435. {
  436. ACTIVE_SPLITSCREEN_PLAYER_GUARD_VGUI( hh );
  437. if ( GetClientMode() )
  438. {
  439. GetClientMode()->VGui_Shutdown();
  440. if ( hh == 0 )
  441. {
  442. GetFullscreenClientMode()->VGui_Shutdown();
  443. }
  444. }
  445. }
  446. VGUI_DestroyClientDLLRootPanel();
  447. // Make sure anything "marked for deletion"
  448. // actually gets deleted before this dll goes away
  449. vgui::ivgui()->RunFrame();
  450. }
  451. static ConVar cl_showpausedimage( "cl_showpausedimage", "1", 0, "Show the 'Paused' image when game is paused." );
  452. //-----------------------------------------------------------------------------
  453. // Things to do before rendering vgui stuff...
  454. //-----------------------------------------------------------------------------
  455. void VGui_PreRender()
  456. {
  457. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  458. VPROF( "VGui_PreRender" );
  459. // 360 does not use these plaques
  460. #if !defined( PORTAL2 )
  461. if ( IsPC() )
  462. {
  463. loadingdisc->SetLoadingVisible( engine->IsDrawingLoadingImage() && !engine->IsPlayingDemo() );
  464. loadingdisc->SetPausedVisible( !enginevgui->IsGameUIVisible() && cl_showpausedimage.GetBool() && engine->IsPaused() && !engine->IsTakingScreenshot() && !engine->IsPlayingDemo() );
  465. }
  466. #endif
  467. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  468. CUtlVector< Panel * > list;
  469. VGui_GetPanelList( list );
  470. for ( int i = 0; i < list.Count() ; ++i )
  471. {
  472. list[ i ]->SetVisible( i == nSlot );
  473. }
  474. VGui_GetFullscreenRootPanel()->SetVisible( true );
  475. }
  476. void VGui_PostRender()
  477. {
  478. int w, h;
  479. CUtlVector< Panel * > list;
  480. VGui_GetPanelList( list );
  481. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  482. {
  483. int x, y;
  484. VGui_GetHudBounds( i, x, y, w, h);
  485. list[ i ]->SetVisible( true );
  486. list[ i ]->SetBounds( x, y, w, h );
  487. surface()->SetAbsPosForContext( i, x, y );
  488. }
  489. VGui_GetTrueScreenSize( w, h );
  490. VGui_GetFullscreenRootPanel()->SetVisible( true );
  491. VGui_GetFullscreenRootPanel()->SetBounds( 0, 0, w, h );
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Purpose:
  495. // Input : cl_panelanimation -
  496. //-----------------------------------------------------------------------------
  497. CON_COMMAND( cl_panelanimation, "Shows panel animation variables: <panelname | blank for all panels>." )
  498. {
  499. if ( args.ArgC() == 2 )
  500. {
  501. PanelAnimationDumpVars( args[1] );
  502. }
  503. else
  504. {
  505. PanelAnimationDumpVars( NULL );
  506. }
  507. }
  508. void GetHudSize( int& w, int &h )
  509. {
  510. vgui::surface()->GetScreenSize( w, h );
  511. }
  512. static vrect_t g_TrueScreenSize;
  513. static vrect_t g_ScreenSpaceBounds[ MAX_SPLITSCREEN_CLIENTS ];
  514. void VGui_GetTrueScreenSize( int &w, int &h )
  515. {
  516. w = g_TrueScreenSize.width;
  517. h = g_TrueScreenSize.height;
  518. }
  519. void VGUI_SetScreenSpaceBounds( int slot, int x, int y, int w, int h )
  520. {
  521. vrect_t &r = g_ScreenSpaceBounds[ slot ];
  522. r.x = x;
  523. r.y = y;
  524. r.width = w;
  525. r.height = h;
  526. }
  527. void VGUI_UpdateScreenSpaceBounds( int nNumSplits, int sx, int sy, int sw, int sh )
  528. {
  529. g_TrueScreenSize.x = sx;
  530. g_TrueScreenSize.y = sy;
  531. g_TrueScreenSize.width = sw;
  532. g_TrueScreenSize.height = sh;
  533. CUtlVector< int > validSlots;
  534. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  535. {
  536. validSlots.AddToTail( i );
  537. }
  538. Assert( validSlots.Count() == nNumSplits );
  539. switch ( nNumSplits )
  540. {
  541. default:
  542. case 1:
  543. // Make it screen sized
  544. {
  545. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
  546. }
  547. break;
  548. case 2:
  549. {
  550. if ( ss_force_primary_fullscreen.GetBool() )
  551. {
  552. // fullscreen
  553. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], 0, 0, sw, sh );
  554. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sw, sh, 1, 1 );
  555. }
  556. else if ( VGui_UsePipSplit() )
  557. {
  558. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
  559. // scale with PIP resolution
  560. float flPIPScale = ss_pipscale.GetFloat();
  561. int pipWidth = sw * flPIPScale;
  562. int pipHeight = sh * flPIPScale;
  563. int x = sw - pipWidth - ss_pip_right_offset.GetInt();
  564. int y = sh - pipHeight - ss_pip_bottom_offset.GetInt();
  565. // round upper left corner down to the nearest multiple of 8 for X360 (resolve alignment requirements)
  566. if ( IsX360() )
  567. {
  568. x &= (~7);
  569. y &= (~7);
  570. }
  571. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], x, y, pipWidth, pipHeight );
  572. }
  573. else if ( ss_verticalsplit.GetBool() )
  574. {
  575. sw /= 2;
  576. // Stack two horiz, side by side
  577. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
  578. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx + sw, sy, sw, sh );
  579. }
  580. else
  581. {
  582. sh /= 2;
  583. // Stack two wide on top of one another
  584. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
  585. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx, sy + sh, sw, sh );
  586. }
  587. }
  588. break;
  589. case 3:
  590. {
  591. int fullw = sw;
  592. sw /= 2;
  593. sh /= 2;
  594. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx + ( fullw - sw ) / 2, sy, sw, sh );
  595. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx, sy + sh, sw, sh );
  596. VGUI_SetScreenSpaceBounds( validSlots[ 2 ], sx + sw, sy + sh, sw, sh );
  597. }
  598. break;
  599. case 4:
  600. {
  601. sw /= 2;
  602. sh /= 2;
  603. // Stack two wide on top of one another
  604. VGUI_SetScreenSpaceBounds( validSlots[ 0 ], sx, sy, sw, sh );
  605. VGUI_SetScreenSpaceBounds( validSlots[ 1 ], sx + sw, sy, sw, sh );
  606. VGUI_SetScreenSpaceBounds( validSlots[ 2 ], sx, sy + sh, sw, sh );
  607. VGUI_SetScreenSpaceBounds( validSlots[ 3 ], sx + sw, sy + sh, sw, sh );
  608. }
  609. break;
  610. }
  611. }
  612. CBitVec< MAX_SPLITSCREEN_PLAYERS > g_SplitScreenPlayers;
  613. bool g_bIterateRemoteSplitScreenPlayers = false;
  614. C_BasePlayer *g_RemoteSplitScreenPlayers[MAX_SPLITSCREEN_PLAYERS];
  615. void AddRemoteSplitScreenViewPlayer( C_BasePlayer *pPlayer )
  616. {
  617. for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
  618. {
  619. if( g_RemoteSplitScreenPlayers[i] == pPlayer )
  620. return; //don't add it twice
  621. }
  622. for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
  623. {
  624. if( !g_SplitScreenPlayers.IsBitSet( i ) && (g_RemoteSplitScreenPlayers[i] == NULL) )
  625. {
  626. g_RemoteSplitScreenPlayers[i] = pPlayer;
  627. VGui_OnSplitScreenStateChanged();
  628. return;
  629. }
  630. }
  631. }
  632. void RemoveRemoteSplitScreenViewPlayer( C_BasePlayer *pPlayer )
  633. {
  634. for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
  635. {
  636. if( g_RemoteSplitScreenPlayers[i] == pPlayer )
  637. {
  638. g_RemoteSplitScreenPlayers[i] = NULL;
  639. VGui_OnSplitScreenStateChanged();
  640. return;
  641. }
  642. }
  643. }
  644. C_BasePlayer *GetSplitScreenViewPlayer( int nSlot )
  645. {
  646. return g_SplitScreenPlayers.IsBitSet( nSlot ) ? C_BasePlayer::GetLocalPlayer( nSlot ) : g_RemoteSplitScreenPlayers[nSlot];
  647. }
  648. void cl_enable_remote_splitscreen_callback_f( IConVar *var, const char *pOldValue, float flOldValue )
  649. {
  650. VGui_OnSplitScreenStateChanged();
  651. }
  652. ConVar cl_enable_remote_splitscreen( "cl_enable_remote_splitscreen", "0", 0, "Allows viewing of nonlocal players in a split screen fashion", cl_enable_remote_splitscreen_callback_f );
  653. static CUtlVector<bool> s_IterateNetworkedSplitScreenSlotsPushedValues;
  654. void IterateRemoteSplitScreenViewSlots_Push( bool bSet )
  655. {
  656. if( !cl_enable_remote_splitscreen.GetBool() )
  657. {
  658. bSet = false;
  659. }
  660. s_IterateNetworkedSplitScreenSlotsPushedValues.AddToTail( g_bIterateRemoteSplitScreenPlayers );
  661. g_bIterateRemoteSplitScreenPlayers = bSet;
  662. }
  663. void IterateRemoteSplitScreenViewSlots_Pop( void )
  664. {
  665. Assert( s_IterateNetworkedSplitScreenSlotsPushedValues.Count() > 0 );
  666. g_bIterateRemoteSplitScreenPlayers = s_IterateNetworkedSplitScreenSlotsPushedValues.Tail();
  667. s_IterateNetworkedSplitScreenSlotsPushedValues.RemoveMultipleFromTail( 1 );
  668. }
  669. bool IsLocalSplitScreenPlayer( int nSlot )
  670. {
  671. return g_SplitScreenPlayers.IsBitSet( nSlot );
  672. }
  673. int FirstValidSplitScreenSlot()
  674. {
  675. return 0;
  676. }
  677. int NextValidSplitScreenSlot( int i )
  678. {
  679. ++i;
  680. while ( i< MAX_SPLITSCREEN_PLAYERS )
  681. {
  682. if ( g_SplitScreenPlayers.IsBitSet( i ) )
  683. return i;
  684. if( g_bIterateRemoteSplitScreenPlayers && cl_enable_remote_splitscreen.GetBool() && (g_RemoteSplitScreenPlayers[i] != NULL) )
  685. return i;
  686. ++i;
  687. }
  688. return -1;
  689. }
  690. bool IsValidSplitScreenSlot( int i )
  691. {
  692. return g_SplitScreenPlayers.IsBitSet( i ) || (g_bIterateRemoteSplitScreenPlayers && (g_RemoteSplitScreenPlayers[i] != NULL));
  693. }
  694. static int g_nCachedScreenSize[ 2 ] = { -1, -1 };
  695. void VGui_OnScreenSizeChanged()
  696. {
  697. vgui::surface()->GetScreenSize( g_nCachedScreenSize[ 0 ], g_nCachedScreenSize[ 1 ] );
  698. VGui_OnSplitScreenStateChanged();
  699. }
  700. static int g_nNumSplits = 1; //number of logical splits (local players + remote splits)
  701. static int g_nNumLocalSplits = 1; //number of local players sitting at this computer
  702. bool VGui_IsSplitScreen()
  703. {
  704. return g_nNumSplits >= 2;
  705. }
  706. bool VGui_IsSplitScreenPIP()
  707. {
  708. return VGui_IsSplitScreen() && g_nNumLocalSplits == ss_pipsplit.GetInt();
  709. }
  710. bool VGui_UsePipSplit()
  711. {
  712. return g_nNumLocalSplits <= ss_pipsplit.GetInt(); //ss_pipsplit 1 for remote splitscreen pip, ss_pipsplit 2 to use pip even with 2 local players
  713. }
  714. bool g_bSuppressConfigSystemLevelDueToPIPTransitions;
  715. void VGui_OnSplitScreenStateChanged()
  716. {
  717. CUtlVector< Panel * > list;
  718. VGui_GetPanelList( list );
  719. g_SplitScreenPlayers.ClearAll();
  720. g_nNumSplits = 0;
  721. g_nNumLocalSplits = 0;
  722. for ( int i = engine->FirstValidSplitScreenSlot();
  723. i != -1;
  724. i = engine->NextValidSplitScreenSlot( i ) )
  725. {
  726. g_SplitScreenPlayers.Set( i );
  727. g_RemoteSplitScreenPlayers[i] = NULL; //actual splitscreen players nuke networked splitscreen players
  728. ++g_nNumSplits;
  729. ++g_nNumLocalSplits;
  730. }
  731. if( cl_enable_remote_splitscreen.GetBool() )
  732. {
  733. for( int i = 0; i != MAX_SPLITSCREEN_PLAYERS; ++i )
  734. {
  735. if( g_RemoteSplitScreenPlayers[i] != NULL )
  736. {
  737. ++g_nNumSplits;
  738. }
  739. }
  740. }
  741. IterateRemoteSplitScreenViewSlots_Push( true );
  742. g_LetterBox.SetNumSplitScreenPlayers( g_nNumSplits );
  743. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  744. {
  745. list[ i ]->SetVisible( IsValidSplitScreenSlot( i ) );
  746. }
  747. // Now tile, etc. the rest of them
  748. int sw, sh;
  749. if ( g_nCachedScreenSize[ 0 ] == -1 )
  750. {
  751. vgui::surface()->GetScreenSize( g_nCachedScreenSize[ 0 ], g_nCachedScreenSize[ 1 ] );
  752. }
  753. sw = g_nCachedScreenSize[ 0 ];
  754. sh = g_nCachedScreenSize[ 1 ];
  755. VGUI_UpdateScreenSpaceBounds( g_nNumSplits, 0, 0, sw, sh );
  756. // get the current splitscreen/letterbox settings. We only care about fov and viewmodelfov.
  757. bool bDummy;
  758. float flDummy, flFOV, flViewModelFOV;
  759. g_LetterBox.GetSettings( &bDummy, &flDummy, &flFOV, &flViewModelFOV );
  760. static SplitScreenConVarRef fov_desired( "fov_desired", true );
  761. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  762. {
  763. if ( fov_desired.IsValid() )
  764. {
  765. fov_desired.SetValue( i, flFOV );
  766. }
  767. // The actual viewport panels are all at the top left of the screen, but sized appropriately
  768. int x, y, w, h;
  769. VGui_GetHudBounds( i, x, y, w, h);
  770. list[ i ]->SetBounds( x, y, w, h );
  771. surface()->SetAbsPosForContext( i, x, y );
  772. }
  773. // This is a hack to prevent changing the current system level during PIP mode transitions. Otherwise, on the next frame mat queue mode will be disabled for
  774. // a frame and then re-enabled, which causes various known rendering problems and a noticeable hitch.
  775. // I would have loved to plumb this down in a cleaner way, but this function is a convar change callback.
  776. if ( !g_bSuppressConfigSystemLevelDueToPIPTransitions )
  777. {
  778. ConfigureCurrentSystemLevel( );
  779. }
  780. IterateRemoteSplitScreenViewSlots_Pop();
  781. C_BaseEntity::UpdateVisibilityAllEntities();
  782. }
  783. void VGui_GetPanelBounds( int slot, int &x, int &y, int &w, int &h )
  784. {
  785. if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
  786. {
  787. x = y = 0;
  788. vgui::surface()->GetScreenSize( w, h );
  789. return;
  790. }
  791. vrect_t &r = g_ScreenSpaceBounds[ slot ];
  792. x = r.x;
  793. y = r.y;
  794. w = r.width;
  795. h = r.height;
  796. }
  797. void VGui_GetEngineRenderBounds( int slot, int &x, int &y, int &w, int &h, int &insetX, int &insetY )
  798. {
  799. insetX = insetY = 0;
  800. if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
  801. {
  802. x = y = 0;
  803. vgui::surface()->GetScreenSize( w, h );
  804. return;
  805. }
  806. VGui_GetPanelBounds( slot, x, y, w, h );
  807. bool bDummy = false;
  808. float flDummy = 0;
  809. float flAspect = 1.0f;
  810. if ( !g_LetterBox.GetSettings( &bDummy, &flAspect, &flDummy, &flDummy ) )
  811. {
  812. return;
  813. }
  814. // Need to convert from physical to pixel aspect ratio. These aren't the same when using non-square pixels.
  815. const AspectRatioInfo_t &aspectRatioInfo = materials->GetAspectRatioInfo();
  816. flAspect *= aspectRatioInfo.m_flPhysicalToFrameBufferScalar;
  817. // Figure out current aspect ratio
  818. float flCurrentAspect = (float)w / (float)h;
  819. float ratio = flAspect / flCurrentAspect;
  820. if ( ratio > 1.0f )
  821. {
  822. // Screen is wider, need bars at top and bottom
  823. int usetall = (float)w / flAspect;
  824. if ( IsPC() )
  825. {
  826. insetY = ( h - usetall ) / 2;
  827. y += insetY;
  828. h = usetall;
  829. }
  830. else
  831. {
  832. // hopefully it centers, but it might not
  833. usetall = AlignValue( usetall, 2 * GPU_RESOLVE_ALIGNMENT );
  834. insetY = ( h - usetall ) / 2;
  835. y += insetY;
  836. y = AlignValue( y, GPU_RESOLVE_ALIGNMENT );
  837. insetY = AlignValue( insetY, GPU_RESOLVE_ALIGNMENT );
  838. h = usetall;
  839. }
  840. }
  841. else
  842. {
  843. // Screen is narrower, need bars at left/right
  844. int usewide = (float)h * flAspect;
  845. if ( IsPC() )
  846. {
  847. insetX = ( w - usewide ) / 2;
  848. x += insetX;
  849. w = usewide;
  850. }
  851. else
  852. {
  853. // hopefully it centers, but it might not
  854. usewide = AlignValue( usewide, 2 * GPU_RESOLVE_ALIGNMENT );
  855. insetX = ( w - usewide ) / 2;
  856. x += insetX;
  857. x = AlignValue( x, GPU_RESOLVE_ALIGNMENT );
  858. insetX = AlignValue( insetX, GPU_RESOLVE_ALIGNMENT );
  859. w = usewide;
  860. }
  861. }
  862. }
  863. void VGui_GetHudBounds( int slot, int &x, int &y, int &w, int &h )
  864. {
  865. if ( !IsValidSplitScreenSlot( slot ) || g_nNumSplits == 1 )
  866. {
  867. x = y = 0;
  868. vgui::surface()->GetScreenSize( w, h );
  869. return;
  870. }
  871. bool bInset = false;
  872. float dummy = 1.0f;
  873. if ( !g_LetterBox.GetSettings( &bInset, &dummy, &dummy, &dummy ) ||
  874. !bInset )
  875. {
  876. // Use entire bounds for HUD
  877. VGui_GetPanelBounds( slot, x, y, w, h );
  878. return;
  879. }
  880. int insetX = 0, insetY = 0;
  881. VGui_GetEngineRenderBounds( slot, x, y, w, h, insetX, insetY );
  882. }
  883. int VGUI_FindSlotForRootPanel( vgui::Panel *pRoot )
  884. {
  885. CUtlVector< Panel * > list;
  886. VGui_GetPanelList( list );
  887. int slot = list.Find( pRoot ) ;
  888. if ( slot == list.InvalidIndex() )
  889. return 0;
  890. return slot;
  891. }