VGUI Screen Creation: Difference between revisions
Jump to navigation
Jump to search
Note:This section assumes a mod where VGUI screens have been fixed (see the code modifications section below) but otherwise left the same. VGUI screens do not work in HL2 or HL2:DM; they crash the game. (CS:S not tested.)
Line 17: | Line 17: | ||
# <code>vgui_test_screen.res</code> is a good starting point for creating a VGUI screen file; it can be extracted from <code>source engine.gcf</code> at path <code>hl2/scripts/screens/</code> using [[GCFScape]]. | # <code>vgui_test_screen.res</code> is a good starting point for creating a VGUI screen file; it can be extracted from <code>source engine.gcf</code> at path <code>hl2/scripts/screens/</code> using [[GCFScape]]. | ||
# The materials used in the ''.res'' file can be found in <code>source materials. | # The materials used in the ''.res'' file can be found in <code>source materials.gcf</code>. Extract the material files mentioned in the ''.res'' file to <code>materials\vgui\screens\</code>. | ||
# Add the screen to <code>scripts\vgui_screens.txt</code>. If it does not exist, create it. Here is an example which describes screen <code>vgui_test_screen</code> at <code>scripts/screens/vgui_test_screen.res</code>. Adjust it for your screen. | # Add the screen to <code>scripts\vgui_screens.txt</code>. If it does not exist, create it. Here is an example which describes screen <code>vgui_test_screen</code> at <code>scripts/screens/vgui_test_screen.res</code>. Adjust it for your screen. | ||
Revision as of 16:21, 11 March 2006
Adding a VGUI screen to a map

- Create a vgui_screen entity. This is a point entity.
- Set its Panel Name to the name of the screen that should be shown. (This is not the filename of the screen.) The available screens are listed in
vgui_screens.txt
(which should be in thescripts
directory). - Set Panel Width in World and Panel Height in World to match the size of the brush. 64 wide by 32 tall would be a reasonable starting size.
- Compile the map and test.
The position of the entity in the map marks the bottom-left corner of the panel. The panel's direction (the normal of its face) is set by the entity's angle (Yaw).
Creating a VGUI screen
VGUI screen files have a .res extension and should be placed in scripts\screens\
.
vgui_test_screen.res
is a good starting point for creating a VGUI screen file; it can be extracted fromsource engine.gcf
at pathhl2/scripts/screens/
using GCFScape.- The materials used in the .res file can be found in
source materials.gcf
. Extract the material files mentioned in the .res file tomaterials\vgui\screens\
. - Add the screen to
scripts\vgui_screens.txt
. If it does not exist, create it. Here is an example which describes screenvgui_test_screen
atscripts/screens/vgui_test_screen.res
. Adjust it for your screen.
"VGUI_Screens" { "vgui_test_screen" { // This is our example screen "type" "vgui_screen_panel" "pixelswide" 480 "pixelshigh" 240 // This must be the file you created in step 1 "resfile" "scripts/screens/vgui_test_screen.res" } }
An example vgui_screen.txt
file can be extracted from source engine.gcf
at root/hl2/scripts
.
VGUI code modifications
There is a problem with VGUI screens receiving input. Unless it is fixed, the game will crash when the cursor points at the screen.
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "networkstringtable_clientdll.h" #include <KeyValues.h> #include "PanelMetaClassMgr.h" #include <vgui_controls/Controls.h> #include "VMatrix.h" #include "VGUIMatSurface/IMatSystemSurface.h" #include "view.h" #include "CollisionUtils.h" #include <vgui/IInput.h> #include <vgui/IPanel.h> #include <vgui/IVGui.h> #include "ienginevgui.h" #include <vgui/Mousecode.h> #include "materialsystem/IMesh.h" #include "ClientEffectPrecacheSystem.h" #include "C_VGuiScreen.h" #include "IClientMode.h" #include "vgui_bitmapbutton.h" #include "vgui_bitmappanel.h" #include "filesystem.h" #include <vgui/IInputInternal.h> extern vgui::IInputInternal *g_InputInternal; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define VGUI_SCREEN_MODE_RADIUS 80 //Precache the materials CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectVGuiScreen ) CLIENTEFFECT_MATERIAL( "engine/writez" ) CLIENTEFFECT_REGISTER_END() using namespace vgui; // ----------------------------------------------------------------------------- // // This is a cache of preloaded keyvalues. // ----------------------------------------------------------------------------- // CUtlDict<KeyValues*, int> g_KeyValuesCache; KeyValues* CacheKeyValuesForFile( const char *pFilename ) { int i = g_KeyValuesCache.Find( pFilename ); if ( i == g_KeyValuesCache.InvalidIndex() ) { KeyValues *rDat = new KeyValues( pFilename ); rDat->LoadFromFile( vgui::filesystem(), pFilename, NULL ); g_KeyValuesCache.Insert( pFilename, rDat ); return rDat; } else { return g_KeyValuesCache[i]; } } void ClearKeyValuesCache() { for ( int i=g_KeyValuesCache.First(); i != g_KeyValuesCache.InvalidIndex(); i=g_KeyValuesCache.Next( i ) ) { g_KeyValuesCache[i]->deleteThis(); } g_KeyValuesCache.Purge(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class C_VGuiScreen : public C_BaseEntity { DECLARE_CLASS( C_VGuiScreen, C_BaseEntity ); public: DECLARE_CLIENTCLASS(); C_VGuiScreen(); virtual void PreDataUpdate( DataUpdateType_t updateType ); virtual void OnDataChanged( DataUpdateType_t type ); virtual int DrawModel( int flags ); virtual bool ShouldDraw() { return !IsEffectActive(EF_NODRAW); } virtual void ClientThink( ); virtual void GetAimEntOrigin( IClientEntity *pAttachedTo, Vector *pOrigin, QAngle *pAngles ); const char *PanelName() const; // The view screen has the cursor pointing at it void GainFocus( ); void LoseFocus(); // Is the screen backfaced given a view position? bool IsBackfacing( const Vector &viewOrigin ); // Return intersection point of ray with screen in barycentric coords bool IntersectWithRay( const Ray_t &ray, float *u, float *v, float *t ); // Is the screen turned on? bool IsActive() const; // Are we only visible to teammates? bool IsVisibleOnlyToTeammates() const; // Are we visible to someone on this team? bool IsVisibleToTeam( int nTeam ); bool IsAttachedToViewModel() const; virtual RenderGroup_t GetRenderGroup(); bool AcceptsInput() const; void SetAcceptsInput( bool acceptsinput ); private: // Vgui screen management void CreateVguiScreen( const char *pTypeName ); void DestroyVguiScreen( ); // Computes the panel to world transform void ComputePanelToWorld(); // Computes control points of the quad describing the screen void ComputeEdges( Vector *pUpperLeft, Vector *pUpperRight, Vector *pLowerLeft ); // Writes the z buffer void DrawScreenOverlay(); private: int m_nPixelWidth; int m_nPixelHeight; float m_flWidth; float m_flHeight; int m_nPanelName; // The name of the panel int m_nOldPx; int m_nOldPy; int m_nAttachmentIndex; int m_nOverlayMaterial; int m_fScreenFlags; int m_nOldPanelName; int m_nOldOverlayMaterial; bool m_bAcceptsInput; CMaterialReference m_WriteZMaterial; CMaterialReference m_OverlayMaterial; VMatrix m_PanelToWorld; CPanelWrapper m_PanelWrapper; VPANEL _focus; bool m_bRightMouseDown; bool m_bOldRightMouseDown; bool m_bLeftMouseDown; bool m_bOldLeftMouseDown; }; void SetVGuiScreenButtonState( C_BaseEntity *pVguiScreen, int nButtonState ) { } IMPLEMENT_CLIENTCLASS_DT(C_VGuiScreen, DT_VGuiScreen, CVGuiScreen) RecvPropFloat( RECVINFO(m_flWidth) ), RecvPropFloat( RECVINFO(m_flHeight) ), RecvPropInt( RECVINFO(m_fScreenFlags) ), RecvPropInt( RECVINFO(m_nPanelName) ), RecvPropInt( RECVINFO(m_nAttachmentIndex) ), RecvPropInt( RECVINFO(m_nOverlayMaterial) ), END_RECV_TABLE() //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- C_VGuiScreen::C_VGuiScreen() { m_nOldPanelName = m_nPanelName = -1; m_nOldOverlayMaterial = m_nOverlayMaterial = -1; m_nOldPx = m_nOldPy = -1; m_bAcceptsInput = true; _focus = NULL; m_bRightMouseDown = m_bLeftMouseDown = m_bOldRightMouseDown = m_bOldLeftMouseDown = false; m_WriteZMaterial.Init( "engine/writez", TEXTURE_GROUP_VGUI ); m_OverlayMaterial.Init( m_WriteZMaterial ); } //----------------------------------------------------------------------------- // Network updates //----------------------------------------------------------------------------- void C_VGuiScreen::PreDataUpdate( DataUpdateType_t updateType ) { BaseClass::PreDataUpdate( updateType ); m_nOldPanelName = m_nPanelName; m_nOldOverlayMaterial = m_nOverlayMaterial; } void C_VGuiScreen::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ((type == DATA_UPDATE_CREATED) || (m_nPanelName != m_nOldPanelName)) { CreateVguiScreen( PanelName() ); } // Set up the overlay material if (m_nOldOverlayMaterial != m_nOverlayMaterial) { m_OverlayMaterial.Shutdown(); const char *pMaterialName = GetMaterialNameFromIndex(m_nOverlayMaterial); if (pMaterialName) { m_OverlayMaterial.Init( pMaterialName, TEXTURE_GROUP_VGUI ); } else { m_OverlayMaterial.Init( m_WriteZMaterial ); } } } void FormatViewModelAttachment( Vector &vOrigin, bool bInverse ); //----------------------------------------------------------------------------- // Returns the attachment render origin + origin //----------------------------------------------------------------------------- void C_VGuiScreen::GetAimEntOrigin( IClientEntity *pAttachedTo, Vector *pOrigin, QAngle *pAngles ) { C_BaseEntity *pEnt = pAttachedTo->GetBaseEntity(); if (pEnt && (m_nAttachmentIndex > 0)) { C_BaseAnimating::PushAllowBoneAccess( true, true ); pEnt->GetAttachment( m_nAttachmentIndex, *pOrigin, *pAngles ); C_BaseAnimating::PopBoneAccess(); if ( IsAttachedToViewModel() ) { FormatViewModelAttachment( *pOrigin, true ); } } else { BaseClass::GetAimEntOrigin( pAttachedTo, pOrigin, pAngles ); } } //----------------------------------------------------------------------------- // Create, destroy vgui panels... //----------------------------------------------------------------------------- void C_VGuiScreen::CreateVguiScreen( const char *pTypeName ) { // Clear out any old screens. DestroyVguiScreen(); // Create the new screen... VGuiScreenInitData_t initData( this ); m_PanelWrapper.Activate( pTypeName, NULL, 0, &initData ); // Retrieve the panel dimensions vgui::Panel *pPanel = m_PanelWrapper.GetPanel(); if (pPanel) { int x, y; pPanel->GetBounds( x, y, m_nPixelWidth, m_nPixelHeight ); } else { m_nPixelWidth = m_nPixelHeight = 0; } } void C_VGuiScreen::DestroyVguiScreen( ) { m_PanelWrapper.Deactivate(); } //----------------------------------------------------------------------------- // Is the screen active? //----------------------------------------------------------------------------- bool C_VGuiScreen::IsActive() const { return (m_fScreenFlags & VGUI_SCREEN_ACTIVE) != 0; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool C_VGuiScreen::IsAttachedToViewModel() const { return (m_fScreenFlags & VGUI_SCREEN_ATTACHED_TO_VIEWMODEL) != 0; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool C_VGuiScreen::AcceptsInput() const { return m_bAcceptsInput; } //----------------------------------------------------------------------------- // Purpose: // Input : acceptsinput - //----------------------------------------------------------------------------- void C_VGuiScreen::SetAcceptsInput( bool acceptsinput ) { m_bAcceptsInput = acceptsinput; } //----------------------------------------------------------------------------- // Purpose: // Output : RenderGroup_t //----------------------------------------------------------------------------- RenderGroup_t C_VGuiScreen::GetRenderGroup() { if ( IsAttachedToViewModel() ) return RENDER_GROUP_VIEW_MODEL_TRANSLUCENT; return BaseClass::GetRenderGroup(); } //----------------------------------------------------------------------------- // Are we only visible to teammates? //----------------------------------------------------------------------------- bool C_VGuiScreen::IsVisibleOnlyToTeammates() const { return (m_fScreenFlags & VGUI_SCREEN_VISIBLE_TO_TEAMMATES) != 0; } //----------------------------------------------------------------------------- // Are we visible to someone on this team? //----------------------------------------------------------------------------- bool C_VGuiScreen::IsVisibleToTeam( int nTeam ) { // FIXME: Should this maybe go into a derived class of some sort? // Don't bother with screens on the wrong team if (IsVisibleOnlyToTeammates() && (nTeam > 0)) { // Hmmm... sort of a hack... C_BaseEntity *pOwner = GetOwnerEntity(); if ( pOwner && (nTeam != pOwner->GetTeamNumber()) ) return false; } return true; } //----------------------------------------------------------------------------- // Activate, deactivate the view screen //----------------------------------------------------------------------------- void C_VGuiScreen::GainFocus( ) { SetNextClientThink( CLIENT_THINK_ALWAYS ); ClientThink(); } void C_VGuiScreen::LoseFocus() { if(_focus) { ivgui()->PostMessage(_focus, new KeyValues("CursorExited"), NULL); _focus = NULL; } SetNextClientThink( CLIENT_THINK_NEVER ); } //----------------------------------------------------------------------------- // Returns the panel name //----------------------------------------------------------------------------- const char *C_VGuiScreen::PanelName() const { return g_StringTableVguiScreen->GetString( m_nPanelName ); } //----------------------------------------------------------------------------- // Purpose: Deal with input //----------------------------------------------------------------------------- void C_VGuiScreen::ClientThink( void ) { m_bOldLeftMouseDown = m_bLeftMouseDown; m_bLeftMouseDown = input()->IsMouseDown(MOUSE_LEFT); m_bOldRightMouseDown = m_bRightMouseDown; m_bRightMouseDown = input()->IsMouseDown(MOUSE_LEFT); BaseClass::ClientThink(); // FIXME: We should really be taking bob, shake, and roll into account // but if we did, then all the inputs would be generated multiple times // if the world was rendered multiple times (for things like water, etc.) vgui::Panel *pPanel = m_PanelWrapper.GetPanel(); if (!pPanel) return; C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if (!pLocalPlayer) return; // Generate a ray along the view direction Vector vecEyePosition = pLocalPlayer->EyePosition(); QAngle viewAngles = pLocalPlayer->EyeAngles( ); Vector viewDir, endPos; AngleVectors( viewAngles, &viewDir ); VectorMA( vecEyePosition, 1000.0f, viewDir, endPos ); // Compute cursor position... Ray_t lookDir; lookDir.Init( vecEyePosition, endPos ); float u, v; if (!IntersectWithRay( lookDir, &u, &v, NULL )) return; if ( (u < 0) || (v < 0) || (u > 1) || (v > 1)) return; // This will cause our panel to grab all input! g_pClientMode->ActivateInGameVGuiContext( pPanel ); // Convert (u,v) into (px,py) int px = (int)(u * m_nPixelWidth + 0.5f); int py = (int)(v * m_nPixelHeight + 0.5f); // Generate mouse input commands if ((px != m_nOldPx) || (py != m_nOldPy)) { //cursor has moved, so make sure the mouseFocus is current VPANEL oldFocus = _focus; _focus = m_PanelWrapper.GetPanel()->IsWithinTraverse(px,py,true); if(oldFocus!=_focus) { if(oldFocus) { ivgui()->PostMessage(oldFocus, new KeyValues("CursorExited"), NULL); } if(_focus) { ivgui()->PostMessage(_focus, new KeyValues("CursorEntered"), NULL); ivgui()->PostMessage(_focus, new KeyValues("OnCursorMoved", "xpos", px, "ypos", py), NULL); } } else if(_focus) { ivgui()->PostMessage(_focus, new KeyValues("OnCursorMoved", "xpos", px, "ypos", py), NULL); } m_nOldPx = px; m_nOldPy = py; } if (m_bLeftMouseDown!=m_bOldLeftMouseDown) { ivgui()->PostMessage(_focus,new KeyValues(m_bLeftMouseDown?"MousePressed":"MouseReleased", "code", MOUSE_LEFT),NULL); } if (m_bRightMouseDown!=m_bOldRightMouseDown) { ivgui()->PostMessage(_focus,new KeyValues(m_bRightMouseDown?"MousePressed":"MouseReleased", "code", MOUSE_RIGHT),NULL); } g_pClientMode->DeactivateInGameVGuiContext( ); } //----------------------------------------------------------------------------- // Computes control points of the quad describing the screen //----------------------------------------------------------------------------- void C_VGuiScreen::ComputeEdges( Vector *pUpperLeft, Vector *pUpperRight, Vector *pLowerLeft ) { Vector vecOrigin = GetAbsOrigin(); Vector xaxis, yaxis; AngleVectors( GetAbsAngles(), &xaxis, &yaxis, NULL ); // NOTE: Have to multiply by -1 here because yaxis goes out the -y axis in AngleVectors actually... yaxis *= -1.0f; VectorCopy( vecOrigin, *pLowerLeft ); VectorMA( vecOrigin, m_flHeight, yaxis, *pUpperLeft ); VectorMA( *pUpperLeft, m_flWidth, xaxis, *pUpperRight ); } //----------------------------------------------------------------------------- // Return intersection point of ray with screen in barycentric coords //----------------------------------------------------------------------------- bool C_VGuiScreen::IntersectWithRay( const Ray_t &ray, float *u, float *v, float *t ) { // Perform a raycast to see where in barycentric coordinates the ray hits // the viewscreen; if it doesn't hit it, you're not in the mode Vector origin, upt, vpt; ComputeEdges( &origin, &upt, &vpt ); return ComputeIntersectionBarycentricCoordinates( ray, origin, upt, vpt, *u, *v, t ); } //----------------------------------------------------------------------------- // Is the vgui screen backfacing? //----------------------------------------------------------------------------- bool C_VGuiScreen::IsBackfacing( const Vector &viewOrigin ) { // Compute a ray from camera to center of the screen.. Vector cameraToScreen; VectorSubtract( GetAbsOrigin(), viewOrigin, cameraToScreen ); // Figure out the face normal Vector zaxis; GetVectors( NULL, NULL, &zaxis ); // The actual backface cull return (DotProduct( zaxis, cameraToScreen ) > 0.0f); } //----------------------------------------------------------------------------- // Computes the panel center to world transform //----------------------------------------------------------------------------- void C_VGuiScreen::ComputePanelToWorld() { // The origin is at the upper-left corner of the screen Vector vecOrigin, vecUR, vecLL; ComputeEdges( &vecOrigin, &vecUR, &vecLL ); m_PanelToWorld.SetupMatrixOrgAngles( vecOrigin, GetAbsAngles() ); } //----------------------------------------------------------------------------- // a pass to set the z buffer... //----------------------------------------------------------------------------- void C_VGuiScreen::DrawScreenOverlay() { materials->MatrixMode( MATERIAL_MODEL ); materials->PushMatrix(); materials->LoadMatrix( m_PanelToWorld ); unsigned char pColor[4] = {255, 255, 255, 255}; CMeshBuilder meshBuilder; IMesh *pMesh = materials->GetDynamicMesh( true, NULL, NULL, m_OverlayMaterial ); meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); meshBuilder.Position3f( 0.0f, 0.0f, 0 ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Color4ubv( pColor ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( m_flWidth, 0.0f, 0 ); meshBuilder.TexCoord2f( 0, 1.0f, 0.0f ); meshBuilder.Color4ubv( pColor ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( m_flWidth, -m_flHeight, 0 ); meshBuilder.TexCoord2f( 0, 1.0f, 1.0f ); meshBuilder.Color4ubv( pColor ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( 0.0f, -m_flHeight, 0 ); meshBuilder.TexCoord2f( 0, 0.0f, 1.0f ); meshBuilder.Color4ubv( pColor ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); materials->PopMatrix(); } //----------------------------------------------------------------------------- // Draws the panel using a 3D transform... //----------------------------------------------------------------------------- int C_VGuiScreen::DrawModel( int flags ) { vgui::Panel *pPanel = m_PanelWrapper.GetPanel(); if (!pPanel || !IsActive()) return 0; // Don't bother drawing stuff not visible to me... C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if (!pLocalPlayer || !IsVisibleToTeam(pLocalPlayer->GetTeamNumber()) ) return 0; // Backface cull the entire panel here... if (IsBackfacing(CurrentViewOrigin())) return 0; // Recompute the panel-to-world center // FIXME: Can this be cached off? ComputePanelToWorld(); g_pMatSystemSurface->DrawPanelIn3DSpace( pPanel->GetVPanel(), m_PanelToWorld, m_nPixelWidth, m_nPixelHeight, m_flWidth, m_flHeight ); // Finally, a pass to set the z buffer... DrawScreenOverlay(); return 1; } //----------------------------------------------------------------------------- // // Enumator class for finding vgui screens close to the local player // //----------------------------------------------------------------------------- class CVGuiScreenEnumerator : public IPartitionEnumerator { DECLARE_CLASS_GAMEROOT( CVGuiScreenEnumerator, IPartitionEnumerator ); public: virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); int GetScreenCount(); C_VGuiScreen *GetVGuiScreen( int index ); private: CUtlVector< CHandle< C_VGuiScreen > > m_VguiScreens; }; IterationRetval_t CVGuiScreenEnumerator::EnumElement( IHandleEntity *pHandleEntity ) { C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() ); if ( pEnt == NULL ) return ITERATION_CONTINUE; // FIXME.. pretty expensive... C_VGuiScreen *pScreen = dynamic_cast<C_VGuiScreen*>(pEnt); if ( pScreen ) { int i = m_VguiScreens.AddToTail( ); m_VguiScreens[i].Set( pScreen ); } return ITERATION_CONTINUE; } int CVGuiScreenEnumerator::GetScreenCount() { return m_VguiScreens.Count(); } C_VGuiScreen *CVGuiScreenEnumerator::GetVGuiScreen( int index ) { return m_VguiScreens[index].Get(); } //----------------------------------------------------------------------------- // // Look for vgui screens, returns true if it found one ... // //----------------------------------------------------------------------------- C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam ) { // Get the view direction... Vector lookDir; AngleVectors( viewAngle, &lookDir ); // Create a ray used for raytracing Vector lookEnd; VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd ); Ray_t lookRay; lookRay.Init( viewPosition, lookEnd ); // Look for vgui screens that are close to the player CVGuiScreenEnumerator localScreens; partition->EnumerateElementsInSphere( PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens ); Vector vecOut, vecViewDelta; float flBestDist = 2.0f; C_VGuiScreen *pBestScreen = NULL; for (int i = localScreens.GetScreenCount(); --i >= 0; ) { C_VGuiScreen *pScreen = localScreens.GetVGuiScreen(i); // Don't bother with screens I'm behind... if (pScreen->IsBackfacing(viewPosition)) continue; // Don't bother with screens that are turned off if (!pScreen->IsActive()) continue; // FIXME: Should this maybe go into a derived class of some sort? // Don't bother with screens on the wrong team if (!pScreen->IsVisibleToTeam(nTeam)) continue; if ( !pScreen->AcceptsInput() ) continue; // Test perpendicular distance from the screen... pScreen->GetVectors( NULL, NULL, &vecOut ); VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta ); float flPerpDist = DotProduct(vecViewDelta, vecOut); if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) ) continue; // Perform a raycast to see where in barycentric coordinates the ray hits // the viewscreen; if it doesn't hit it, you're not in the mode float u, v, t; if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t )) continue; // Barycentric test if ((u < 0) || (v < 0) || (u > 1) || (v > 1)) continue; if ( t < flBestDist ) { flBestDist = t; pBestScreen = pScreen; } } return pBestScreen; } void ActivateVguiScreen( C_BaseEntity *pVguiScreenEnt ) { if (pVguiScreenEnt) { Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) ); C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt); pVguiScreen->GainFocus( ); } } void DeactivateVguiScreen( C_BaseEntity *pVguiScreenEnt ) { if (pVguiScreenEnt) { Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) ); C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt); pVguiScreen->LoseFocus( ); } } CVGuiScreenPanel::CVGuiScreenPanel( vgui::Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) { m_hEntity = NULL; } CVGuiScreenPanel::CVGuiScreenPanel( vgui::Panel *parent, const char *panelName, vgui::HScheme hScheme ) : BaseClass( parent, panelName, hScheme ) { m_hEntity = NULL; } bool CVGuiScreenPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData ) { const char *pResFile = pKeyValues->GetString( "resfile" ); if (pResFile[0] != 0) { KeyValues *pCachedKeyValues = CacheKeyValuesForFile( pResFile ); LoadControlSettings( pResFile, NULL, pCachedKeyValues ); } // Dimensions in pixels int nWidth, nHeight; nWidth = pKeyValues->GetInt( "pixelswide", 240 ); nHeight = pKeyValues->GetInt( "pixelshigh", 160 ); if ((nWidth <= 0) || (nHeight <= 0)) return false; // If init data isn't specified, then we're just precaching. if ( pInitData ) { m_hEntity.Set( pInitData->m_pEntity ); C_VGuiScreen *screen = dynamic_cast< C_VGuiScreen * >( pInitData->m_pEntity ); if ( screen ) { bool acceptsInput = pKeyValues->GetInt( "acceptsinput", 1 ) ? true : false; screen->SetAcceptsInput( acceptsInput ); } } SetBounds( 0, 0, nWidth, nHeight ); return true; } vgui::Panel *CVGuiScreenPanel::CreateControlByName(const char *controlName) { // Check the panel metaclass manager to make these controls... if (!Q_strncmp(controlName, "MaterialImage", 20)) { return new CBitmapPanel(NULL, "BitmapPanel"); } if (!Q_strncmp(controlName, "MaterialButton", 20)) { return new CBitmapButton(NULL, "BitmapButton", ""); } // Didn't find it? Just use the default stuff return BaseClass::CreateControlByName( controlName ); } DECLARE_VGUI_SCREEN_FACTORY( CVGuiScreenPanel, "vgui_screen_panel" );
Example Screenshots
VGUI screen used in mod
VGUI screen used to display dynamically drawn EKG data