You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
878 lines
25 KiB
878 lines
25 KiB
//========= Copyright 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 "mathlib/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 "in_buttons.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 "iinput.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() |
|
|
|
|
|
// ----------------------------------------------------------------------------- // |
|
// This is a cache of preloaded keyvalues. |
|
// ----------------------------------------------------------------------------- // |
|
|
|
CUtlDict<KeyValues*, int> g_KeyValuesCache; |
|
|
|
KeyValues* CacheKeyValuesForFile( const char *pFilename ) |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
int i = g_KeyValuesCache.Find( pFilename ); |
|
if ( i == g_KeyValuesCache.InvalidIndex() ) |
|
{ |
|
KeyValues *rDat = new KeyValues( pFilename ); |
|
rDat->LoadFromFile( filesystem, pFilename, NULL ); |
|
g_KeyValuesCache.Insert( pFilename, rDat ); |
|
return rDat; |
|
} |
|
else |
|
{ |
|
return g_KeyValuesCache[i]; |
|
} |
|
} |
|
|
|
void ClearKeyValuesCache() |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
for ( int i=g_KeyValuesCache.First(); i != g_KeyValuesCache.InvalidIndex(); i=g_KeyValuesCache.Next( i ) ) |
|
{ |
|
g_KeyValuesCache[i]->deleteThis(); |
|
} |
|
g_KeyValuesCache.Purge(); |
|
} |
|
|
|
|
|
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) ), |
|
RecvPropEHandle( RECVINFO(m_hPlayerOwner) ), |
|
END_RECV_TABLE() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor |
|
//----------------------------------------------------------------------------- |
|
C_VGuiScreen::C_VGuiScreen() |
|
{ |
|
m_nOldPanelName = m_nPanelName = -1; |
|
m_nOldOverlayMaterial = m_nOverlayMaterial = -1; |
|
m_nOldPx = m_nOldPy = -1; |
|
m_nButtonState = 0; |
|
m_bLoseThinkNextFrame = false; |
|
m_bAcceptsInput = true; |
|
|
|
m_WriteZMaterial.Init( "engine/writez", TEXTURE_GROUP_VGUI ); |
|
m_OverlayMaterial.Init( m_WriteZMaterial ); |
|
} |
|
|
|
C_VGuiScreen::~C_VGuiScreen() |
|
{ |
|
DestroyVguiScreen(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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() ); |
|
m_nButtonState = 0; |
|
} |
|
|
|
// 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::AutoAllowBoneAccess boneaccess( true, true ); |
|
pEnt->GetAttachment( m_nAttachmentIndex, *pOrigin, *pAngles ); |
|
} |
|
|
|
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(); |
|
|
|
AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); |
|
|
|
// 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 ); |
|
m_bLoseThinkNextFrame = false; |
|
m_nOldButtonState = 0; |
|
} |
|
|
|
void C_VGuiScreen::LoseFocus() |
|
{ |
|
m_bLoseThinkNextFrame = true; |
|
m_nOldButtonState = 0; |
|
} |
|
|
|
void C_VGuiScreen::SetButtonState( int nButtonState ) |
|
{ |
|
m_nButtonState = nButtonState; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the panel name |
|
//----------------------------------------------------------------------------- |
|
const char *C_VGuiScreen::PanelName() const |
|
{ |
|
return g_StringTableVguiScreen->GetString( m_nPanelName ); |
|
} |
|
|
|
//-------------------------------------------------------------------------- |
|
// Purpose: |
|
// Given a field of view and mouse/screen positions as well as the current |
|
// render origin and angles, returns a unit vector through the mouse position |
|
// that can be used to trace into the world under the mouse click pixel. |
|
// Input : |
|
// mousex - |
|
// mousey - |
|
// fov - |
|
// vecRenderOrigin - |
|
// vecRenderAngles - |
|
// Output : |
|
// vecPickingRay |
|
//-------------------------------------------------------------------------- |
|
void ScreenToWorld( int mousex, int mousey, float fov, |
|
const Vector& vecRenderOrigin, |
|
const QAngle& vecRenderAngles, |
|
Vector& vecPickingRay ) |
|
{ |
|
float dx, dy; |
|
float c_x, c_y; |
|
float dist; |
|
Vector vpn, vup, vright; |
|
|
|
float scaled_fov = ScaleFOVByWidthRatio( fov, engine->GetScreenAspectRatio() * 0.75f ); |
|
|
|
c_x = ScreenWidth() / 2; |
|
c_y = ScreenHeight() / 2; |
|
|
|
dx = (float)mousex - c_x; |
|
// Invert Y |
|
dy = c_y - (float)mousey; |
|
|
|
// Convert view plane distance |
|
dist = c_x / tan( M_PI * scaled_fov / 360.0 ); |
|
|
|
// Decompose view angles |
|
AngleVectors( vecRenderAngles, &vpn, &vright, &vup ); |
|
|
|
// Offset forward by view plane distance, and then by pixel offsets |
|
vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy ); |
|
|
|
// Convert to unit vector |
|
VectorNormalize( vecPickingRay ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Deal with input |
|
//----------------------------------------------------------------------------- |
|
void C_VGuiScreen::ClientThink( void ) |
|
{ |
|
int nButtonsChanged = m_nOldButtonState ^ m_nButtonState; |
|
|
|
m_nOldButtonState = m_nButtonState; |
|
|
|
// Debounced button codes for pressed/released |
|
// UNDONE: Do we need auto-repeat? |
|
m_nButtonPressed = nButtonsChanged & m_nButtonState; // The changed ones still down are "pressed" |
|
m_nButtonReleased = nButtonsChanged & (~m_nButtonState); // The ones not down are "released" |
|
|
|
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( ); |
|
|
|
// Compute cursor position... |
|
Ray_t lookDir; |
|
Vector endPos; |
|
|
|
float u, v; |
|
|
|
// Viewmodel attached screens that take input need to have a moving cursor |
|
// Do a pick under the cursor as our selection |
|
Vector viewDir; |
|
AngleVectors( viewAngles, &viewDir ); |
|
VectorMA( vecEyePosition, 1000.0f, viewDir, endPos ); |
|
lookDir.Init( vecEyePosition, endPos ); |
|
|
|
if (!IntersectWithRay( lookDir, &u, &v, NULL )) |
|
return; |
|
|
|
if ( ((u < 0) || (v < 0) || (u > 1) || (v > 1)) && !m_bLoseThinkNextFrame) |
|
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)) |
|
{ |
|
g_InputInternal->InternalCursorMoved( px, py ); |
|
m_nOldPx = px; |
|
m_nOldPy = py; |
|
} |
|
|
|
if (m_nButtonPressed & IN_ATTACK) |
|
{ |
|
g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED ); |
|
g_InputInternal->InternalMousePressed(MOUSE_LEFT); |
|
} |
|
if (m_nButtonPressed & IN_ATTACK2) |
|
{ |
|
g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_PRESSED ); |
|
g_InputInternal->InternalMousePressed( MOUSE_RIGHT ); |
|
} |
|
if ( (m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus |
|
{ |
|
g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED ); |
|
g_InputInternal->InternalMouseReleased( MOUSE_LEFT ); |
|
} |
|
if (m_nButtonReleased & IN_ATTACK2) |
|
{ |
|
g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_RELEASED ); |
|
g_InputInternal->InternalMouseReleased( MOUSE_RIGHT ); |
|
} |
|
|
|
if ( m_bLoseThinkNextFrame == true ) |
|
{ |
|
m_bLoseThinkNextFrame = false; |
|
SetNextClientThink( CLIENT_THINK_NEVER ); |
|
} |
|
|
|
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() |
|
{ |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
pRenderContext->MatrixMode( MATERIAL_MODEL ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadMatrix( m_PanelToWorld ); |
|
|
|
unsigned char pColor[4] = {255, 255, 255, 255}; |
|
|
|
CMeshBuilder meshBuilder; |
|
IMesh *pMesh = pRenderContext->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(); |
|
|
|
pRenderContext->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; |
|
|
|
if ( !IsVisibleToPlayer( pLocalPlayer ) ) |
|
{ |
|
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; |
|
} |
|
|
|
bool C_VGuiScreen::ShouldDraw( void ) |
|
{ |
|
return !IsEffectActive(EF_NODRAW); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Hook for vgui screens to determine visibility |
|
//----------------------------------------------------------------------------- |
|
bool C_VGuiScreen::IsVisibleToPlayer( C_BasePlayer *pViewingPlayer ) |
|
{ |
|
return true; |
|
} |
|
|
|
bool C_VGuiScreen::IsTransparent( void ) |
|
{ |
|
return (m_fScreenFlags & VGUI_SCREEN_TRANSPARENT) != 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sometimes we only want a specific player to be able to input to a panel |
|
//----------------------------------------------------------------------------- |
|
C_BasePlayer *C_VGuiScreen::GetPlayerOwner( void ) |
|
{ |
|
return ( C_BasePlayer * )( m_hPlayerOwner.Get() ); |
|
} |
|
|
|
bool C_VGuiScreen::IsInputOnlyToOwner( void ) |
|
{ |
|
return (m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) != 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// 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 ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// X360TBD: Turn this on if feature actually used |
|
return NULL; |
|
} |
|
|
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); |
|
|
|
Assert( pLocalPlayer ); |
|
|
|
if ( !pLocalPlayer ) |
|
return NULL; |
|
|
|
// 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); |
|
|
|
if ( pScreen->IsAttachedToViewModel() ) |
|
continue; |
|
|
|
// Don't bother with screens I'm behind... |
|
// Hax - don't cancel backfacing with viewmodel attached screens. |
|
// we can get prediction bugs that make us backfacing for one frame and |
|
// it resets the mouse position if we lose focus. |
|
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; |
|
|
|
if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer ) |
|
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 SetVGuiScreenButtonState( C_BaseEntity *pVguiScreenEnt, int nButtonState ) |
|
{ |
|
if (pVguiScreenEnt) |
|
{ |
|
Assert( dynamic_cast<C_VGuiScreen*>(pVguiScreenEnt) ); |
|
C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pVguiScreenEnt); |
|
pVguiScreen->SetButtonState( nButtonState ); |
|
} |
|
} |
|
|
|
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 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when the user presses a button |
|
//----------------------------------------------------------------------------- |
|
void CVGuiScreenPanel::OnCommand( const char *command) |
|
{ |
|
if ( Q_stricmp( command, "vguicancel" ) ) |
|
{ |
|
engine->ClientCmd( const_cast<char *>( command ) ); |
|
} |
|
|
|
BaseClass::OnCommand(command); |
|
} |
|
|
|
DECLARE_VGUI_SCREEN_FACTORY( CVGuiScreenPanel, "vgui_screen_panel" ); |