|
|
Line 35: |
Line 35: |
| An example <code>vgui_screen.txt</code> file can be [[GCFScape|extracted]] from <code>source engine.gcf</code> at <code>root/hl2/scripts</code>. | | An example <code>vgui_screen.txt</code> file can be [[GCFScape|extracted]] from <code>source engine.gcf</code> at <code>root/hl2/scripts</code>. |
|
| |
|
| ==VGUI code modifications== | | ==Creating a VGUI screen== |
|
| |
|
| There is a problem with VGUI screens receiving input. Unless it is fixed, the game will crash when the cursor points at the screen.
| | VGUI screen files have a ''.res'' extension and should be placed in <code>scripts\screens\</code>. |
|
| |
|
| <pre>//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// | | # <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.gfc</code>. Extract the material files mentioned in the ''.res'' file to <code>materials\vgui\screens\</code>. |
| // Purpose: | | # 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. |
| // | |
| // $NoKeywords: $ | |
| //=============================================================================// | |
|
| |
|
| #include "cbase.h"
| | "VGUI_Screens" |
| #include "networkstringtable_clientdll.h"
| | { |
| #include <KeyValues.h>
| | "vgui_test_screen" |
| #include "PanelMetaClassMgr.h"
| | { |
| #include <vgui_controls/Controls.h>
| | // This is our example screen |
| #include "VMatrix.h"
| | "type" "vgui_screen_panel" |
| #include "VGUIMatSurface/IMatSystemSurface.h"
| | "pixelswide" 480 |
| #include "view.h"
| | "pixelshigh" 240 |
| #include "CollisionUtils.h"
| | // This must be the file you created in step 1 |
| #include <vgui/IInput.h>
| | "resfile" "scripts/screens/vgui_test_screen.res" |
| #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>
| | An example <code>vgui_screen.txt</code> file can be [[GCFScape|extracted]] from <code>source engine.gcf</code> at <code>root/hl2/scripts</code>. |
| extern vgui::IInputInternal *g_InputInternal;
| |
|
| |
|
| // memdbgon must be the last include file in a .cpp file!!!
| | ==VGUI code modifications== |
| #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;
| |
| }
| |
|
| |
|
| | There is a problem with VGUI screens receiving input. Unless it is fixed, the game may crash when the cursor points at the screen. |
|
| |
|
| bool CVGuiScreenPanel::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
| | <code>CInput::ExtraMouseSample</code> calls <code>g_pClientMode->CreateMove()</code> without initializing the button flags, thus the VGui screen is updated with bad button input. Since <code>IN_VALIDVGUIINPUT</code> is only set from within <code>CInput::CreateMove</code>, the VGui screens only update when the button flags are valid. |
| {
| |
| const char *pResFile = pKeyValues->GetString( "resfile" );
| |
| if (pResFile[0] != 0)
| |
| {
| |
| KeyValues *pCachedKeyValues = CacheKeyValuesForFile( pResFile );
| |
| LoadControlSettings( pResFile, NULL, pCachedKeyValues );
| |
| }
| |
|
| |
|
| // Dimensions in pixels
| | There are two known fixes. They both involve code changes and therefore are only applicable to mods. |
| 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.
| | === Fix 1 === |
| if ( pInitData )
| |
| {
| |
| m_hEntity.Set( pInitData->m_pEntity );
| |
|
| |
|
| C_VGuiScreen *screen = dynamic_cast< C_VGuiScreen * >( pInitData->m_pEntity );
| | In '''src\game_shared\in_buttons.h''', where the other flags are defined add: |
| if ( screen )
| | #define IN_VALIDVGUIINPUT (1 << 23) //bitflag for vgui fix |
| {
| |
| bool acceptsInput = pKeyValues->GetInt( "acceptsinput", 1 ) ? true : false;
| |
| screen->SetAcceptsInput( acceptsInput );
| |
| }
| |
| }
| |
|
| |
|
| SetBounds( 0, 0, nWidth, nHeight );
| | next, in '''src\cl_dll\in_main.cpp''' inside method <code>CInput::CreateMove( ''...'' )</code> |
| | add: |
| | cmd->buttons |= IN_VALIDVGUIINPUT; |
| | right above: |
| | g_pClientMode->CreateMove( input_sample_frametime, cmd ); |
|
| |
|
| return true;
| | next, in '''src\cl_dll\c_baseplayer.cpp''' inside method <code>C_BasePlayer::CreateMove( ''...'' )</code> |
| }
| | add: |
| | if(pCmd->buttons & IN_VALIDVGUIINPUT) |
| | right above: |
| | DetermineVguiInputMode( pCmd ); |
| | ''(So it only calls <code>DetermineVguiInputMode</code> if the buttons include our flag)'' |
|
| |
|
| vgui::Panel *CVGuiScreenPanel::CreateControlByName(const char *controlName)
| | and finally, inside method <code>C_BasePlayer::DetermineVguiInputMode( ''...'' )</code> |
| {
| | change '''both''' instances of: |
| // Check the panel metaclass manager to make these controls...
| | pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); |
| if (!Q_strncmp(controlName, "MaterialImage", 20))
| | to read: |
| {
| | pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT); |
| return new CBitmapPanel(NULL, "BitmapPanel");
| |
| }
| |
|
| |
|
| if (!Q_strncmp(controlName, "MaterialButton", 20))
| | Since an [[HL2]] engine update in October VGUI Screens will crash when you put your mouse over them. This is because the g_InputInternal function used in c_vuguiscreen.cpp is no longer working. Trying to access it makes it crash. So for a workaround to get them working, i've done the following to the C_VGuiScreen::ClientThink( void ) function: |
| {
| | // Convert (u,v) into (px,py) |
| return new CBitmapButton(NULL, "BitmapButton", "");
| | int px = (int)(u * m_nPixelWidth + 0.5f); |
| }
| | int py = (int)(v * m_nPixelHeight + 0.5f); |
| | |
| | // START TEDDYS FIX |
| | for (int i = 0; i < pPanel->GetChildCount(); i++) |
| | { |
| | vgui::Button *child = dynamic_cast<vgui::Button*>(pPanel->GetChild(i)); |
| | if ( child ) |
| | { |
| | int x1, x2, y1, y2; |
| | child->GetBounds( x1, y1, x2, y2 ); |
| | |
| | // Generate mouse input commands |
| | if ( (m_nButtonState & IN_ATTACK) ) |
| | { |
| | if ( px >= x1 && px <= x1 + x2 && py >= y1 && py <= y1 + y2 ) |
| | child->FireActionSignal(); |
| | } |
| | } |
| | } |
| | // FIN TEDDYS FIX |
| | |
| | if ( m_bLooseThinkNextFrame == true ) |
| | { |
| | m_bLooseThinkNextFrame = false; |
| | SetNextClientThink( CLIENT_THINK_NEVER ); |
| | } |
|
| |
|
| // Didn't find it? Just use the default stuff
| | === Fix 2 === |
| return BaseClass::CreateControlByName( controlName );
| |
| }
| |
|
| |
|
| DECLARE_VGUI_SCREEN_FACTORY( CVGuiScreenPanel, "vgui_screen_panel" );</pre>
| | An alternative fix would be to add a new boolean variable to <code>CUserCmd</code> such as <code>bButtonFlagsValid</code> that can be set depending on the validity of the button flags. This would allow the structure to internally store this information for its whole lifetime while freeing up that extra button bit. |
|
| |
|
| == Example Screenshots == | | == Example Screenshots == |