VGUI Screen Creation: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
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.gfc</code>. Extract the material files mentioned in the ''.res'' file to <code>materials\vgui\screens\</code>.
# 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

Note.pngNote: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.)
  1. Create a vgui_screen entity. This is a point entity.
  2. 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 the scripts directory).
  3. 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.
  4. 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\.

  1. vgui_test_screen.res is a good starting point for creating a VGUI screen file; it can be extracted from source engine.gcf at path hl2/scripts/screens/ using GCFScape.
  2. The materials used in the .res file can be found in source materials.gcf. Extract the material files mentioned in the .res file to materials\vgui\screens\.
  3. Add the screen to scripts\vgui_screens.txt. If it does not exist, create it. Here is an example which describes screen vgui_test_screen at scripts/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

Granted vgui.jpg

VGUI screen used in mod

Ekg vgui.jpg

VGUI screen used to display dynamically drawn EKG data