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 37: | Line 37: | ||
==VGUI code modifications== | ==VGUI code modifications== | ||
There is a problem with VGUI screens receiving input. Unless it is fixed, the game | There is a problem with VGUI screens receiving input. Unless it is fixed, the game will crash when the cursor points at the screen. | ||
< | <pre>//========= 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" );</pre> | |||
== Example Screenshots == | == Example Screenshots == | ||
Revision as of 15:20, 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 thescriptsdirectory). - 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.resis a good starting point for creating a VGUI screen file; it can be extracted fromsource engine.gcfat pathhl2/scripts/screens/using GCFScape.- The materials used in the .res file can be found in
source materials.gfc. 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_screenatscripts/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