Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

841 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "networkstringtable_clientdll.h"
  9. #include <KeyValues.h>
  10. #include "panelmetaclassmgr.h"
  11. #include <vgui_controls/Controls.h>
  12. #include "mathlib/vmatrix.h"
  13. #include "VGuiMatSurface/IMatSystemSurface.h"
  14. #include "view.h"
  15. #include "collisionutils.h"
  16. #include <vgui/IInput.h>
  17. #include <vgui/IPanel.h>
  18. #include <vgui/IVGui.h>
  19. #include "ienginevgui.h"
  20. #include "in_buttons.h"
  21. #include <vgui/MouseCode.h>
  22. #include "materialsystem/imesh.h"
  23. #include "clienteffectprecachesystem.h"
  24. #include "c_vguiscreen.h"
  25. #include "iclientmode.h"
  26. #include "vgui_bitmapbutton.h"
  27. #include "vgui_bitmappanel.h"
  28. #include "filesystem.h"
  29. #include "iinput.h"
  30. #include <vgui/IInputInternal.h>
  31. extern vgui::IInputInternal *g_InputInternal;
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. #define VGUI_SCREEN_MODE_RADIUS 80
  35. //Precache the materials
  36. CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectVGuiScreen )
  37. CLIENTEFFECT_MATERIAL( "engine/writez" )
  38. CLIENTEFFECT_REGISTER_END()
  39. IMPLEMENT_CLIENTCLASS_DT(C_VGuiScreen, DT_VGuiScreen, CVGuiScreen)
  40. RecvPropFloat( RECVINFO(m_flWidth) ),
  41. RecvPropFloat( RECVINFO(m_flHeight) ),
  42. RecvPropInt( RECVINFO(m_fScreenFlags) ),
  43. RecvPropInt( RECVINFO(m_nPanelName) ),
  44. RecvPropInt( RECVINFO(m_nAttachmentIndex) ),
  45. RecvPropInt( RECVINFO(m_nOverlayMaterial) ),
  46. RecvPropEHandle( RECVINFO(m_hPlayerOwner) ),
  47. END_RECV_TABLE()
  48. //-----------------------------------------------------------------------------
  49. // Constructor
  50. //-----------------------------------------------------------------------------
  51. C_VGuiScreen::C_VGuiScreen()
  52. {
  53. m_nOldPanelName = m_nPanelName = -1;
  54. m_nOldOverlayMaterial = m_nOverlayMaterial = -1;
  55. m_nOldPx = m_nOldPy = -1;
  56. m_nButtonState = 0;
  57. m_bLoseThinkNextFrame = false;
  58. m_bAcceptsInput = true;
  59. m_WriteZMaterial.Init( "engine/writez", TEXTURE_GROUP_VGUI );
  60. m_OverlayMaterial.Init( m_WriteZMaterial );
  61. }
  62. C_VGuiScreen::~C_VGuiScreen()
  63. {
  64. DestroyVguiScreen();
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Network updates
  68. //-----------------------------------------------------------------------------
  69. void C_VGuiScreen::PreDataUpdate( DataUpdateType_t updateType )
  70. {
  71. BaseClass::PreDataUpdate( updateType );
  72. m_nOldPanelName = m_nPanelName;
  73. m_nOldOverlayMaterial = m_nOverlayMaterial;
  74. }
  75. void C_VGuiScreen::OnDataChanged( DataUpdateType_t type )
  76. {
  77. BaseClass::OnDataChanged( type );
  78. if ((type == DATA_UPDATE_CREATED) || (m_nPanelName != m_nOldPanelName))
  79. {
  80. CreateVguiScreen( PanelName() );
  81. m_nButtonState = 0;
  82. }
  83. // Set up the overlay material
  84. if (m_nOldOverlayMaterial != m_nOverlayMaterial)
  85. {
  86. m_OverlayMaterial.Shutdown();
  87. const char *pMaterialName = GetMaterialNameFromIndex(m_nOverlayMaterial);
  88. if (pMaterialName)
  89. {
  90. m_OverlayMaterial.Init( pMaterialName, TEXTURE_GROUP_VGUI );
  91. }
  92. else
  93. {
  94. m_OverlayMaterial.Init( m_WriteZMaterial );
  95. }
  96. }
  97. }
  98. void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
  99. //-----------------------------------------------------------------------------
  100. // Returns the attachment render origin + origin
  101. //-----------------------------------------------------------------------------
  102. void C_VGuiScreen::GetAimEntOrigin( IClientEntity *pAttachedTo, Vector *pOrigin, QAngle *pAngles )
  103. {
  104. C_BaseEntity *pEnt = pAttachedTo->GetBaseEntity();
  105. if (pEnt && (m_nAttachmentIndex > 0))
  106. {
  107. {
  108. C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
  109. pEnt->GetAttachment( m_nAttachmentIndex, *pOrigin, *pAngles );
  110. }
  111. if ( IsAttachedToViewModel() )
  112. {
  113. FormatViewModelAttachment( *pOrigin, true );
  114. }
  115. }
  116. else
  117. {
  118. BaseClass::GetAimEntOrigin( pAttachedTo, pOrigin, pAngles );
  119. }
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Create, destroy vgui panels...
  123. //-----------------------------------------------------------------------------
  124. void C_VGuiScreen::CreateVguiScreen( const char *pTypeName )
  125. {
  126. // Clear out any old screens.
  127. DestroyVguiScreen();
  128. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  129. // Create the new screen...
  130. VGuiScreenInitData_t initData( this );
  131. m_PanelWrapper.Activate( pTypeName, NULL, 0, &initData );
  132. // Retrieve the panel dimensions
  133. vgui::Panel *pPanel = m_PanelWrapper.GetPanel();
  134. if (pPanel)
  135. {
  136. int x, y;
  137. pPanel->GetBounds( x, y, m_nPixelWidth, m_nPixelHeight );
  138. }
  139. else
  140. {
  141. m_nPixelWidth = m_nPixelHeight = 0;
  142. }
  143. }
  144. void C_VGuiScreen::DestroyVguiScreen( )
  145. {
  146. m_PanelWrapper.Deactivate();
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Is the screen active?
  150. //-----------------------------------------------------------------------------
  151. bool C_VGuiScreen::IsActive() const
  152. {
  153. return (m_fScreenFlags & VGUI_SCREEN_ACTIVE) != 0;
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. // Output : Returns true on success, false on failure.
  158. //-----------------------------------------------------------------------------
  159. bool C_VGuiScreen::IsAttachedToViewModel() const
  160. {
  161. return (m_fScreenFlags & VGUI_SCREEN_ATTACHED_TO_VIEWMODEL) != 0;
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. // Output : Returns true on success, false on failure.
  166. //-----------------------------------------------------------------------------
  167. bool C_VGuiScreen::AcceptsInput() const
  168. {
  169. return m_bAcceptsInput;
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. // Input : acceptsinput -
  174. //-----------------------------------------------------------------------------
  175. void C_VGuiScreen::SetAcceptsInput( bool acceptsinput )
  176. {
  177. m_bAcceptsInput = acceptsinput;
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. // Output : RenderGroup_t
  182. //-----------------------------------------------------------------------------
  183. RenderGroup_t C_VGuiScreen::GetRenderGroup()
  184. {
  185. if ( IsAttachedToViewModel() )
  186. return RENDER_GROUP_VIEW_MODEL_TRANSLUCENT;
  187. return BaseClass::GetRenderGroup();
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Are we only visible to teammates?
  191. //-----------------------------------------------------------------------------
  192. bool C_VGuiScreen::IsVisibleOnlyToTeammates() const
  193. {
  194. return (m_fScreenFlags & VGUI_SCREEN_VISIBLE_TO_TEAMMATES) != 0;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Are we visible to someone on this team?
  198. //-----------------------------------------------------------------------------
  199. bool C_VGuiScreen::IsVisibleToTeam( int nTeam )
  200. {
  201. // FIXME: Should this maybe go into a derived class of some sort?
  202. // Don't bother with screens on the wrong team
  203. if (IsVisibleOnlyToTeammates() && (nTeam > 0))
  204. {
  205. // Hmmm... sort of a hack...
  206. C_BaseEntity *pOwner = GetOwnerEntity();
  207. if ( pOwner && (nTeam != pOwner->GetTeamNumber()) )
  208. return false;
  209. }
  210. return true;
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Activate, deactivate the view screen
  214. //-----------------------------------------------------------------------------
  215. void C_VGuiScreen::GainFocus( )
  216. {
  217. SetNextClientThink( CLIENT_THINK_ALWAYS );
  218. m_bLoseThinkNextFrame = false;
  219. m_nOldButtonState = 0;
  220. }
  221. void C_VGuiScreen::LoseFocus()
  222. {
  223. m_bLoseThinkNextFrame = true;
  224. m_nOldButtonState = 0;
  225. }
  226. void C_VGuiScreen::SetButtonState( int nButtonState )
  227. {
  228. m_nButtonState = nButtonState;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Returns the panel name
  232. //-----------------------------------------------------------------------------
  233. const char *C_VGuiScreen::PanelName() const
  234. {
  235. return g_StringTableVguiScreen->GetString( m_nPanelName );
  236. }
  237. //--------------------------------------------------------------------------
  238. // Purpose:
  239. // Given a field of view and mouse/screen positions as well as the current
  240. // render origin and angles, returns a unit vector through the mouse position
  241. // that can be used to trace into the world under the mouse click pixel.
  242. // Input :
  243. // mousex -
  244. // mousey -
  245. // fov -
  246. // vecRenderOrigin -
  247. // vecRenderAngles -
  248. // Output :
  249. // vecPickingRay
  250. //--------------------------------------------------------------------------
  251. void ScreenToWorld( int mousex, int mousey, float fov,
  252. const Vector& vecRenderOrigin,
  253. const QAngle& vecRenderAngles,
  254. Vector& vecPickingRay )
  255. {
  256. float dx, dy;
  257. float c_x, c_y;
  258. float dist;
  259. Vector vpn, vup, vright;
  260. float scaled_fov = ScaleFOVByWidthRatio( fov, engine->GetScreenAspectRatio() * 0.75f );
  261. c_x = ScreenWidth() / 2;
  262. c_y = ScreenHeight() / 2;
  263. dx = (float)mousex - c_x;
  264. // Invert Y
  265. dy = c_y - (float)mousey;
  266. // Convert view plane distance
  267. dist = c_x / tan( M_PI * scaled_fov / 360.0 );
  268. // Decompose view angles
  269. AngleVectors( vecRenderAngles, &vpn, &vright, &vup );
  270. // Offset forward by view plane distance, and then by pixel offsets
  271. vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy );
  272. // Convert to unit vector
  273. VectorNormalize( vecPickingRay );
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Deal with input
  277. //-----------------------------------------------------------------------------
  278. void C_VGuiScreen::ClientThink( void )
  279. {
  280. int nButtonsChanged = m_nOldButtonState ^ m_nButtonState;
  281. m_nOldButtonState = m_nButtonState;
  282. // Debounced button codes for pressed/released
  283. // UNDONE: Do we need auto-repeat?
  284. m_nButtonPressed = nButtonsChanged & m_nButtonState; // The changed ones still down are "pressed"
  285. m_nButtonReleased = nButtonsChanged & (~m_nButtonState); // The ones not down are "released"
  286. BaseClass::ClientThink();
  287. // FIXME: We should really be taking bob, shake, and roll into account
  288. // but if we did, then all the inputs would be generated multiple times
  289. // if the world was rendered multiple times (for things like water, etc.)
  290. vgui::Panel *pPanel = m_PanelWrapper.GetPanel();
  291. if (!pPanel)
  292. return;
  293. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  294. if (!pLocalPlayer)
  295. return;
  296. // Generate a ray along the view direction
  297. Vector vecEyePosition = pLocalPlayer->EyePosition();
  298. QAngle viewAngles = pLocalPlayer->EyeAngles( );
  299. // Compute cursor position...
  300. Ray_t lookDir;
  301. Vector endPos;
  302. float u, v;
  303. // Viewmodel attached screens that take input need to have a moving cursor
  304. // Do a pick under the cursor as our selection
  305. Vector viewDir;
  306. AngleVectors( viewAngles, &viewDir );
  307. VectorMA( vecEyePosition, 1000.0f, viewDir, endPos );
  308. lookDir.Init( vecEyePosition, endPos );
  309. if (!IntersectWithRay( lookDir, &u, &v, NULL ))
  310. return;
  311. if ( ((u < 0) || (v < 0) || (u > 1) || (v > 1)) && !m_bLoseThinkNextFrame)
  312. return;
  313. // This will cause our panel to grab all input!
  314. g_pClientMode->ActivateInGameVGuiContext( pPanel );
  315. // Convert (u,v) into (px,py)
  316. int px = (int)(u * m_nPixelWidth + 0.5f);
  317. int py = (int)(v * m_nPixelHeight + 0.5f);
  318. // Generate mouse input commands
  319. if ((px != m_nOldPx) || (py != m_nOldPy))
  320. {
  321. g_InputInternal->InternalCursorMoved( px, py );
  322. m_nOldPx = px;
  323. m_nOldPy = py;
  324. }
  325. if (m_nButtonPressed & IN_ATTACK)
  326. {
  327. g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED );
  328. g_InputInternal->InternalMousePressed(MOUSE_LEFT);
  329. }
  330. if (m_nButtonPressed & IN_ATTACK2)
  331. {
  332. g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_PRESSED );
  333. g_InputInternal->InternalMousePressed( MOUSE_RIGHT );
  334. }
  335. if ( (m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus
  336. {
  337. g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED );
  338. g_InputInternal->InternalMouseReleased( MOUSE_LEFT );
  339. }
  340. if (m_nButtonReleased & IN_ATTACK2)
  341. {
  342. g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_RELEASED );
  343. g_InputInternal->InternalMouseReleased( MOUSE_RIGHT );
  344. }
  345. if ( m_bLoseThinkNextFrame == true )
  346. {
  347. m_bLoseThinkNextFrame = false;
  348. SetNextClientThink( CLIENT_THINK_NEVER );
  349. }
  350. g_pClientMode->DeactivateInGameVGuiContext( );
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Computes control points of the quad describing the screen
  354. //-----------------------------------------------------------------------------
  355. void C_VGuiScreen::ComputeEdges( Vector *pUpperLeft, Vector *pUpperRight, Vector *pLowerLeft )
  356. {
  357. Vector vecOrigin = GetAbsOrigin();
  358. Vector xaxis, yaxis;
  359. AngleVectors( GetAbsAngles(), &xaxis, &yaxis, NULL );
  360. // NOTE: Have to multiply by -1 here because yaxis goes out the -y axis in AngleVectors actually...
  361. yaxis *= -1.0f;
  362. VectorCopy( vecOrigin, *pLowerLeft );
  363. VectorMA( vecOrigin, m_flHeight, yaxis, *pUpperLeft );
  364. VectorMA( *pUpperLeft, m_flWidth, xaxis, *pUpperRight );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Return intersection point of ray with screen in barycentric coords
  368. //-----------------------------------------------------------------------------
  369. bool C_VGuiScreen::IntersectWithRay( const Ray_t &ray, float *u, float *v, float *t )
  370. {
  371. // Perform a raycast to see where in barycentric coordinates the ray hits
  372. // the viewscreen; if it doesn't hit it, you're not in the mode
  373. Vector origin, upt, vpt;
  374. ComputeEdges( &origin, &upt, &vpt );
  375. return ComputeIntersectionBarycentricCoordinates( ray, origin, upt, vpt, *u, *v, t );
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Is the vgui screen backfacing?
  379. //-----------------------------------------------------------------------------
  380. bool C_VGuiScreen::IsBackfacing( const Vector &viewOrigin )
  381. {
  382. // Compute a ray from camera to center of the screen..
  383. Vector cameraToScreen;
  384. VectorSubtract( GetAbsOrigin(), viewOrigin, cameraToScreen );
  385. // Figure out the face normal
  386. Vector zaxis;
  387. GetVectors( NULL, NULL, &zaxis );
  388. // The actual backface cull
  389. return (DotProduct( zaxis, cameraToScreen ) > 0.0f);
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Computes the panel center to world transform
  393. //-----------------------------------------------------------------------------
  394. void C_VGuiScreen::ComputePanelToWorld()
  395. {
  396. // The origin is at the upper-left corner of the screen
  397. Vector vecOrigin, vecUR, vecLL;
  398. ComputeEdges( &vecOrigin, &vecUR, &vecLL );
  399. m_PanelToWorld.SetupMatrixOrgAngles( vecOrigin, GetAbsAngles() );
  400. }
  401. //-----------------------------------------------------------------------------
  402. // a pass to set the z buffer...
  403. //-----------------------------------------------------------------------------
  404. void C_VGuiScreen::DrawScreenOverlay()
  405. {
  406. CMatRenderContextPtr pRenderContext( materials );
  407. pRenderContext->MatrixMode( MATERIAL_MODEL );
  408. pRenderContext->PushMatrix();
  409. pRenderContext->LoadMatrix( m_PanelToWorld );
  410. unsigned char pColor[4] = {255, 255, 255, 255};
  411. CMeshBuilder meshBuilder;
  412. IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_OverlayMaterial );
  413. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  414. meshBuilder.Position3f( 0.0f, 0.0f, 0 );
  415. meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
  416. meshBuilder.Color4ubv( pColor );
  417. meshBuilder.AdvanceVertex();
  418. meshBuilder.Position3f( m_flWidth, 0.0f, 0 );
  419. meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
  420. meshBuilder.Color4ubv( pColor );
  421. meshBuilder.AdvanceVertex();
  422. meshBuilder.Position3f( m_flWidth, -m_flHeight, 0 );
  423. meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
  424. meshBuilder.Color4ubv( pColor );
  425. meshBuilder.AdvanceVertex();
  426. meshBuilder.Position3f( 0.0f, -m_flHeight, 0 );
  427. meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
  428. meshBuilder.Color4ubv( pColor );
  429. meshBuilder.AdvanceVertex();
  430. meshBuilder.End();
  431. pMesh->Draw();
  432. pRenderContext->PopMatrix();
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Draws the panel using a 3D transform...
  436. //-----------------------------------------------------------------------------
  437. int C_VGuiScreen::DrawModel( int flags )
  438. {
  439. vgui::Panel *pPanel = m_PanelWrapper.GetPanel();
  440. if (!pPanel || !IsActive())
  441. return 0;
  442. // Don't bother drawing stuff not visible to me...
  443. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  444. if (!pLocalPlayer || !IsVisibleToTeam(pLocalPlayer->GetTeamNumber()) )
  445. return 0;
  446. if ( !IsVisibleToPlayer( pLocalPlayer ) )
  447. {
  448. return 0;
  449. }
  450. // Backface cull the entire panel here...
  451. if (IsBackfacing(CurrentViewOrigin()))
  452. return 0;
  453. // Recompute the panel-to-world center
  454. // FIXME: Can this be cached off?
  455. ComputePanelToWorld();
  456. g_pMatSystemSurface->DrawPanelIn3DSpace( pPanel->GetVPanel(), m_PanelToWorld,
  457. m_nPixelWidth, m_nPixelHeight, m_flWidth, m_flHeight );
  458. // Finally, a pass to set the z buffer...
  459. DrawScreenOverlay();
  460. return 1;
  461. }
  462. bool C_VGuiScreen::ShouldDraw( void )
  463. {
  464. return !IsEffectActive(EF_NODRAW);
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: Hook for vgui screens to determine visibility
  468. //-----------------------------------------------------------------------------
  469. bool C_VGuiScreen::IsVisibleToPlayer( C_BasePlayer *pViewingPlayer )
  470. {
  471. return true;
  472. }
  473. bool C_VGuiScreen::IsTransparent( void )
  474. {
  475. return (m_fScreenFlags & VGUI_SCREEN_TRANSPARENT) != 0;
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Sometimes we only want a specific player to be able to input to a panel
  479. //-----------------------------------------------------------------------------
  480. C_BasePlayer *C_VGuiScreen::GetPlayerOwner( void )
  481. {
  482. return ( C_BasePlayer * )( m_hPlayerOwner.Get() );
  483. }
  484. bool C_VGuiScreen::IsInputOnlyToOwner( void )
  485. {
  486. return (m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) != 0;
  487. }
  488. //-----------------------------------------------------------------------------
  489. //
  490. // Enumator class for finding vgui screens close to the local player
  491. //
  492. //-----------------------------------------------------------------------------
  493. class CVGuiScreenEnumerator : public IPartitionEnumerator
  494. {
  495. DECLARE_CLASS_GAMEROOT( CVGuiScreenEnumerator, IPartitionEnumerator );
  496. public:
  497. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
  498. int GetScreenCount();
  499. C_VGuiScreen *GetVGuiScreen( int index );
  500. private:
  501. CUtlVector< CHandle< C_VGuiScreen > > m_VguiScreens;
  502. };
  503. IterationRetval_t CVGuiScreenEnumerator::EnumElement( IHandleEntity *pHandleEntity )
  504. {
  505. C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() );
  506. if ( pEnt == NULL )
  507. return ITERATION_CONTINUE;
  508. // FIXME.. pretty expensive...
  509. C_VGuiScreen *pScreen = dynamic_cast<C_VGuiScreen*>(pEnt);
  510. if ( pScreen )
  511. {
  512. int i = m_VguiScreens.AddToTail( );
  513. m_VguiScreens[i].Set( pScreen );
  514. }
  515. return ITERATION_CONTINUE;
  516. }
  517. int CVGuiScreenEnumerator::GetScreenCount()
  518. {
  519. return m_VguiScreens.Count();
  520. }
  521. C_VGuiScreen *CVGuiScreenEnumerator::GetVGuiScreen( int index )
  522. {
  523. return m_VguiScreens[index].Get();
  524. }
  525. //-----------------------------------------------------------------------------
  526. //
  527. // Look for vgui screens, returns true if it found one ...
  528. //
  529. //-----------------------------------------------------------------------------
  530. C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam )
  531. {
  532. if ( IsX360() )
  533. {
  534. // X360TBD: Turn this on if feature actually used
  535. return NULL;
  536. }
  537. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  538. Assert( pLocalPlayer );
  539. if ( !pLocalPlayer )
  540. return NULL;
  541. // Get the view direction...
  542. Vector lookDir;
  543. AngleVectors( viewAngle, &lookDir );
  544. // Create a ray used for raytracing
  545. Vector lookEnd;
  546. VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd );
  547. Ray_t lookRay;
  548. lookRay.Init( viewPosition, lookEnd );
  549. // Look for vgui screens that are close to the player
  550. CVGuiScreenEnumerator localScreens;
  551. ::partition->EnumerateElementsInSphere( PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens );
  552. Vector vecOut, vecViewDelta;
  553. float flBestDist = 2.0f;
  554. C_VGuiScreen *pBestScreen = NULL;
  555. for (int i = localScreens.GetScreenCount(); --i >= 0; )
  556. {
  557. C_VGuiScreen *pScreen = localScreens.GetVGuiScreen(i);
  558. if ( pScreen->IsAttachedToViewModel() )
  559. continue;
  560. // Don't bother with screens I'm behind...
  561. // Hax - don't cancel backfacing with viewmodel attached screens.
  562. // we can get prediction bugs that make us backfacing for one frame and
  563. // it resets the mouse position if we lose focus.
  564. if ( pScreen->IsBackfacing(viewPosition) )
  565. continue;
  566. // Don't bother with screens that are turned off
  567. if (!pScreen->IsActive())
  568. continue;
  569. // FIXME: Should this maybe go into a derived class of some sort?
  570. // Don't bother with screens on the wrong team
  571. if (!pScreen->IsVisibleToTeam(nTeam))
  572. continue;
  573. if ( !pScreen->AcceptsInput() )
  574. continue;
  575. if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer )
  576. continue;
  577. // Test perpendicular distance from the screen...
  578. pScreen->GetVectors( NULL, NULL, &vecOut );
  579. VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta );
  580. float flPerpDist = DotProduct(vecViewDelta, vecOut);
  581. if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) )
  582. continue;
  583. // Perform a raycast to see where in barycentric coordinates the ray hits
  584. // the viewscreen; if it doesn't hit it, you're not in the mode
  585. float u, v, t;
  586. if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t ))
  587. continue;
  588. // Barycentric test
  589. if ((u < 0) || (v < 0) || (u > 1) || (v > 1))
  590. continue;
  591. if ( t < flBestDist )
  592. {
  593. flBestDist = t;
  594. pBestScreen = pScreen;
  595. }
  596. }
  597. return pBestScreen;
  598. }
  599. void ActivateVguiScreen( C_BaseEntity *pVguiScreenEnt )
  600. {
  601. if (pVguiScreenEnt)
  602. {
  603. Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) );
  604. C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt);
  605. pVguiScreen->GainFocus( );
  606. }
  607. }
  608. void SetVGuiScreenButtonState( C_BaseEntity *pVguiScreenEnt, int nButtonState )
  609. {
  610. if (pVguiScreenEnt)
  611. {
  612. Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) );
  613. C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt);
  614. pVguiScreen->SetButtonState( nButtonState );
  615. }
  616. }
  617. void DeactivateVguiScreen( C_BaseEntity *pVguiScreenEnt )
  618. {
  619. if (pVguiScreenEnt)
  620. {
  621. Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) );
  622. C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt);
  623. pVguiScreen->LoseFocus( );
  624. }
  625. }
  626. CVGuiScreenPanel::CVGuiScreenPanel( vgui::Panel *parent, const char *panelName )
  627. : BaseClass( parent, panelName )
  628. {
  629. m_hEntity = NULL;
  630. }
  631. CVGuiScreenPanel::CVGuiScreenPanel( vgui::Panel *parent, const char *panelName, vgui::HScheme hScheme )
  632. : BaseClass( parent, panelName, hScheme )
  633. {
  634. m_hEntity = NULL;
  635. }
  636. bool CVGuiScreenPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
  637. {
  638. const char *pResFile = pKeyValues->GetString( "resfile" );
  639. if (pResFile[0] != 0)
  640. {
  641. LoadControlSettings( pResFile, NULL, NULL );
  642. }
  643. // Dimensions in pixels
  644. int nWidth, nHeight;
  645. nWidth = pKeyValues->GetInt( "pixelswide", 240 );
  646. nHeight = pKeyValues->GetInt( "pixelshigh", 160 );
  647. if ((nWidth <= 0) || (nHeight <= 0))
  648. return false;
  649. // If init data isn't specified, then we're just precaching.
  650. if ( pInitData )
  651. {
  652. m_hEntity.Set( pInitData->m_pEntity );
  653. C_VGuiScreen *screen = dynamic_cast< C_VGuiScreen * >( pInitData->m_pEntity );
  654. if ( screen )
  655. {
  656. bool acceptsInput = pKeyValues->GetInt( "acceptsinput", 1 ) ? true : false;
  657. screen->SetAcceptsInput( acceptsInput );
  658. }
  659. }
  660. SetBounds( 0, 0, nWidth, nHeight );
  661. return true;
  662. }
  663. vgui::Panel *CVGuiScreenPanel::CreateControlByName(const char *controlName)
  664. {
  665. // Check the panel metaclass manager to make these controls...
  666. if (!Q_strncmp(controlName, "MaterialImage", 20))
  667. {
  668. return new CBitmapPanel(NULL, "BitmapPanel");
  669. }
  670. if (!Q_strncmp(controlName, "MaterialButton", 20))
  671. {
  672. return new CBitmapButton(NULL, "BitmapButton", "");
  673. }
  674. // Didn't find it? Just use the default stuff
  675. return BaseClass::CreateControlByName( controlName );
  676. }
  677. //-----------------------------------------------------------------------------
  678. // Purpose: Called when the user presses a button
  679. //-----------------------------------------------------------------------------
  680. void CVGuiScreenPanel::OnCommand( const char *command)
  681. {
  682. if ( Q_stricmp( command, "vguicancel" ) )
  683. {
  684. engine->ClientCmd( const_cast<char *>( command ) );
  685. }
  686. BaseClass::OnCommand(command);
  687. }
  688. DECLARE_VGUI_SCREEN_FACTORY( CVGuiScreenPanel, "vgui_screen_panel" );