Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

374 lines
14 KiB

//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "hudelement.h"
#include "hud_macros.h"
#include "hud_numericdisplay.h"
#include "iclientmode.h"
#include "c_cs_player.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imesh.h"
#include "materialsystem/imaterialvar.h"
#include <vgui/IScheme.h>
#include <vgui/ISurface.h>
#include <keyvalues.h>
#include <vgui_controls/AnimationController.h>
#include "predicted_viewmodel.h"
#include "HUD/sfhudreticle.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar cl_flinch_compensate_crosshair;
extern ConVar cl_crosshair_sniper_width;
ConVar cl_crosshair_sniper_show_normal_inaccuracy( "cl_crosshair_sniper_show_normal_inaccuracy", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE | FCVAR_SS, "Include standing inaccuracy when determining sniper crosshair blur" );
//extern ConVar cl_flinch_scale;
//-----------------------------------------------------------------------------
// Purpose: Draws the zoom screen
//-----------------------------------------------------------------------------
class CHudScope : public vgui::Panel, public CHudElement
{
DECLARE_CLASS_SIMPLE( CHudScope, vgui::Panel );
public:
explicit CHudScope( const char *pElementName );
void Init( void );
void LevelInit( void );
protected:
virtual void ApplySchemeSettings(vgui::IScheme *scheme);
virtual void Paint( void );
private:
CMaterialReference m_ScopeMaterial;
CMaterialReference m_ScopeLineBlurMaterial;
CMaterialReference m_DustOverlayMaterial;
int m_iScopeArcTexture;
int m_iScopeLineBlurTexture;
int m_iScopeDustTexture;
float m_fAnimInset;
float m_fLineSpreadDistance;
};
DECLARE_HUDELEMENT_DEPTH( CHudScope, 70 );
using namespace vgui;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudScope")
{
vgui::Panel *pParent = GetClientMode()->GetViewport();
SetParent( pParent );
SetHiddenBits( HIDEHUD_PLAYERDEAD );
SetIgnoreGlobalHudDisable( true );
m_fAnimInset = 1;
m_fLineSpreadDistance = 1;
}
//-----------------------------------------------------------------------------
// Purpose: standard hud element init function
//-----------------------------------------------------------------------------
void CHudScope::Init( void )
{
m_iScopeArcTexture = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile(m_iScopeArcTexture, "sprites/scope_arc", true, false);
m_iScopeLineBlurTexture = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile(m_iScopeLineBlurTexture, "sprites/scope_line_blur", true, false);
m_iScopeDustTexture = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile(m_iScopeDustTexture, "overlays/scope_lens", true, false);
}
//-----------------------------------------------------------------------------
// Purpose: standard hud element init function
//-----------------------------------------------------------------------------
void CHudScope::LevelInit( void )
{
Init();
}
//-----------------------------------------------------------------------------
// Purpose: sets scheme colors
//-----------------------------------------------------------------------------
void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme )
{
BaseClass::ApplySchemeSettings(scheme);
SetPaintBackgroundEnabled(false);
SetPaintBorderEnabled(false);
int screenWide, screenTall;
GetHudSize(screenWide, screenTall);
SetBounds(0, 0, screenWide, screenTall);
}
//-----------------------------------------------------------------------------
// Purpose: draws the zoom effect
//-----------------------------------------------------------------------------
void CHudScope::Paint( void )
{
C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
if ( pPlayer == NULL )
return;
if ( pPlayer && pPlayer->GetObserverInterpState() == C_CSPlayer::OBSERVER_INTERP_TRAVELING )
return;
switch ( pPlayer->GetObserverMode() )
{
case OBS_MODE_NONE:
break;
case OBS_MODE_IN_EYE:
pPlayer = ToCSPlayer( pPlayer->GetObserverTarget() );
if ( pPlayer == NULL )
return;
break;
default:
return; // no scope for other observer modes
}
CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();
if( !pWeapon || pWeapon->GetWeaponType() != WEAPONTYPE_SNIPER_RIFLE )
return;
Assert( m_iScopeArcTexture );
Assert( m_iScopeLineBlurTexture );
Assert( m_iScopeDustTexture );
const float kScopeMinFOV = 25.0f; // Clamp scope FOV to this value to prevent blur from getting too big when double-scoped
float flTargetFOVForZoom = MAX( pWeapon->GetZoomFOV( pWeapon->GetCSZoomLevel() ), kScopeMinFOV );
if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() && pPlayer->m_bIsScoped == false )
{
m_fAnimInset = 2;
m_fLineSpreadDistance = 20;
}
// see if we're zoomed with a sniper rifle
if( flTargetFOVForZoom != pPlayer->GetDefaultFOV() && pPlayer->m_bIsScoped )
{
int screenWide, screenTall;
GetHudSize( screenWide, screenTall );
CBaseViewModel *baseViewModel = pPlayer->GetViewModel( 0 );
if ( baseViewModel == NULL )
return;
CPredictedViewModel *viewModel = dynamic_cast<CPredictedViewModel *>(baseViewModel);
if ( viewModel == NULL )
return;
float fHalfFov = DEG2RAD( flTargetFOVForZoom ) * 0.5f;
float fInaccuracyIn640x480Pixels = 320.0f / tanf( fHalfFov ); // 640 = "reference screen width"
// Get the weapon's inaccuracy
float fWeaponInaccuracy = pWeapon->GetInaccuracy() + pWeapon->GetSpread();
// Optional: Ignore "default" inaccuracy
if ( !cl_crosshair_sniper_show_normal_inaccuracy.GetBool() )
fWeaponInaccuracy -= pWeapon->GetInaccuracyStand( Secondary_Mode ) + pWeapon->GetSpread();
fWeaponInaccuracy = MAX( fWeaponInaccuracy, 0 );
float fRawSpreadDistance = fWeaponInaccuracy * fInaccuracyIn640x480Pixels; //
float fSpreadDistance = clamp( fRawSpreadDistance, 0, 100 );
#if 0
// This lets you verify inaccuracy vs screenshots of cl_weapon_debug_show_accuracy 2;
// the number after screen= should max the radius (in pixels) of the drawn circle.
float fInaccuracyInScreenPixels = fRawSpreadDistance * screenTall / 480; // 480 = "reference screen width"
Msg( "fWeaponInaccuracy = %8.5f, referenceScreen = %8.5f, screen = %8.5f, fov = %8.5f\n", fWeaponInaccuracy, fRawSpreadDistance, fInaccuracyInScreenPixels, flTargetFOVForZoom );
#endif
// reduce the goal (* 0.4 / 30.0f)
// then animate towards it at speed 19.0f
// (where did these numbers come from?)
float flInsetGoal = fSpreadDistance * (0.4f / 30.0f);
m_fAnimInset = Approach( flInsetGoal, m_fAnimInset, abs( ( ( flInsetGoal )-m_fAnimInset )*gpGlobals->frametime ) * 19.0f );
// Approach speed chosen so we get 90% there in 3 frames if we are running at 192 fps vs a 64tick client/server.
// If our fps is lower we will reach the target faster, if higher it is slightly slower
// (since this is a framerate-dependent approach function).
m_fLineSpreadDistance = RemapValClamped( gpGlobals->frametime * 140.0f, 0.0f, 1.0f, m_fLineSpreadDistance, fRawSpreadDistance );
float flAccuracyFishtail = pWeapon->GetAccuracyFishtail();
int offsetX = viewModel->GetBobState().m_flRawLateralBob * (screenTall/14) + flAccuracyFishtail;
int offsetY = viewModel->GetBobState().m_flRawVerticalBob * (screenTall/14);
float flInacDisplayBlur = m_fAnimInset * 0.04f;
if ( flInacDisplayBlur > 0.22 )
flInacDisplayBlur = 0.22;
// calculate the bounds in which we should draw the scope
int inset = (screenTall / 14) + (flInacDisplayBlur * (screenTall*0.5));
int y1 = inset;
int x1 = (screenWide - screenTall) / 2 + inset;
int y2 = screenTall - inset;
int x2 = screenWide - x1;
y1 += offsetY;
y2 += offsetY;
x1 += offsetX;
x2 += offsetX;
int x = (screenWide / 2) + offsetX;
int y = (screenTall / 2) + offsetY;
float uv1 = 0.5f / 256.0f, uv2 = 1.0f - uv1;
vgui::Vertex_t vert[4];
Vector2D uv11( uv1, uv1 );
Vector2D uv12( uv1, uv2 );
Vector2D uv21( uv2, uv1 );
Vector2D uv22( uv2, uv2 );
//Msg( "flRawInacc = %f, flAnimInset = %f\n", flRawInacc, m_fAnimInset );
int xMod = ( screenWide / 2 ) + offsetX + (flInacDisplayBlur * screenTall);
int yMod = ( screenTall / 2 ) + offsetY + (flInacDisplayBlur * screenTall);
int iMiddleX = (screenWide / 2 ) + offsetX;
int iMiddleY = (screenTall / 2 ) + offsetY;
vgui::surface()->DrawSetTexture( m_iScopeDustTexture );
vgui::surface()->DrawSetColor( 255, 255, 255, 200 );
vert[0].Init( Vector2D( iMiddleX + xMod, iMiddleY + yMod ), uv21 );
vert[1].Init( Vector2D( iMiddleX - xMod, iMiddleY + yMod ), uv11 );
vert[2].Init( Vector2D( iMiddleX - xMod, iMiddleY - yMod ), uv12 );
vert[3].Init( Vector2D( iMiddleX + xMod, iMiddleY - yMod ), uv22 );
vgui::surface()->DrawTexturedPolygon( 4, vert );
//Only sniper rifles use this style of vgui hud scope
//if (pWeapon->GetWeaponType() == WEAPONTYPE_SNIPER_RIFLE)
{
// The powf here makes the blur not quite spread out quite as much as the actual inaccuracy;
// doing so is a bit too sudden and also leads to just a huge blur because the snipers are
// *extremely* inaccurate while scoped and moving. This way we get a slightly smoother animation
// as well as not quite blowing up the blurred area by such a large amount.
float fBlurWidth = powf(m_fLineSpreadDistance, 0.75f);
float fScreenBlurWidth = fBlurWidth * screenTall / 640.0f; // scale from 'reference screen size' to actual screen
int nSniperCrosshairThickness = cl_crosshair_sniper_width.GetInt();
if ( nSniperCrosshairThickness < 1 )
nSniperCrosshairThickness = 1;
float kMaxVarianceWithFullAlpha = 1.8f; // Tuned to look good
float fBlurAlpha;
if ( fScreenBlurWidth <= nSniperCrosshairThickness + 0.5f )
fBlurAlpha = ( fBlurWidth < 1.0f ) ? 1.0f : 1.0f / fBlurWidth;
else
fBlurAlpha = ( fBlurWidth < kMaxVarianceWithFullAlpha ) ? 1.0f : kMaxVarianceWithFullAlpha / fBlurWidth;
// This is a break from physical reality to make the look a bit better. An actual Gaussian
// blur spreads the energy out over the entire blurred area, dropping the total opacity by the amount
// of the spread. However, this leads to not being able to see the effect at all. We solve this in
// 2 ways:
// (1) use sqrt on the alpha to bring it closer to 1, kind of like a gamma curve.
// (2) clamp the alpha at the lower end to 55% to make sure you can see *something* no matter
// how spread out it gets.
fBlurAlpha = sqrtf( fBlurAlpha );
int iBlurAlpha = Clamp( ( int )( fBlurAlpha * 255.0f ), 140, 255 );
//DevMsg( "blur: %8.5f fov: %8.5f alpha: %8.5f\n", fBlurWidth, flTargetFOVForZoom, fBlurAlpha );
if ( fScreenBlurWidth <= nSniperCrosshairThickness + 0.5f )
{
vgui::surface()->DrawSetColor( 0, 0, 0, iBlurAlpha );
//Draw the reticle with primitives
if ( nSniperCrosshairThickness <= 1 )
{
vgui::surface()->DrawLine( 0, y, screenWide + offsetX, y );
vgui::surface()->DrawLine( x, 0, x, screenTall + offsetY );
}
else
{
int nStep = nSniperCrosshairThickness / 2;
vgui::surface()->DrawFilledRect( 0, y - nStep, screenWide + offsetX, y + nSniperCrosshairThickness - nStep );
vgui::surface()->DrawFilledRect( x - nStep, 0, x + nSniperCrosshairThickness - nStep, screenTall + offsetY );
}
}
else
{
// Draw blurred line
vgui::surface()->DrawSetColor( 0, 0, 0, iBlurAlpha );
vgui::surface()->DrawSetTexture( m_iScopeLineBlurTexture );
// vertical blurred line
vert[0].Init( Vector2D( iMiddleX - fScreenBlurWidth, offsetY ), uv11 );
vert[1].Init( Vector2D( iMiddleX + fScreenBlurWidth, offsetY ), uv21 );
vert[2].Init( Vector2D( iMiddleX + fScreenBlurWidth, screenTall + offsetY ), uv22 );
vert[3].Init( Vector2D( iMiddleX - fScreenBlurWidth, screenTall + offsetY ), uv12 );
vgui::surface()->DrawTexturedPolygon( 4, vert );
// horizontal blurred line
vert[0].Init( Vector2D( screenWide + offsetX, iMiddleY - fScreenBlurWidth ), uv12 );
vert[1].Init( Vector2D ( screenWide + offsetX, iMiddleY + fScreenBlurWidth ), uv22 );
vert[2].Init( Vector2D( offsetX, iMiddleY + fScreenBlurWidth ), uv21 );
vert[3].Init( Vector2D( offsetX, iMiddleY - fScreenBlurWidth ), uv11 );
vgui::surface()->DrawTexturedPolygon(4, vert);
}
//vgui::surface()->DrawSetColor(0,0,0,MAX( 128, 255 - (int)(m_flOldInacc*3000)));
vgui::surface()->DrawSetColor(0,0,0,255);
//Draw the outline
vgui::surface()->DrawSetTexture(m_iScopeArcTexture);
// bottom right
vert[0].Init( Vector2D( x, y ), uv11 );
vert[1].Init( Vector2D( x2, y ), uv21 );
vert[2].Init( Vector2D( x2, y2 ), uv22 );
vert[3].Init( Vector2D( x, y2 ), uv12 );
vgui::surface()->DrawTexturedPolygon( 4, vert );
// top right
vert[0].Init( Vector2D( x - 1, y1 ), uv12 );
vert[1].Init( Vector2D ( x2, y1 ), uv22 );
vert[2].Init( Vector2D( x2, y + 1 ), uv21 );
vert[3].Init( Vector2D( x - 1, y + 1 ), uv11 );
vgui::surface()->DrawTexturedPolygon(4, vert);
// bottom left
vert[0].Init( Vector2D( x1, y ), uv21 );
vert[1].Init( Vector2D( x, y ), uv11 );
vert[2].Init( Vector2D( x, y2 ), uv12 );
vert[3].Init( Vector2D( x1, y2), uv22 );
vgui::surface()->DrawTexturedPolygon(4, vert);
// top left
vert[0].Init( Vector2D( x1, y1 ), uv22 );
vert[1].Init( Vector2D( x, y1 ), uv12 );
vert[2].Init( Vector2D( x, y ), uv11 );
vert[3].Init( Vector2D( x1, y ), uv21 );
vgui::surface()->DrawTexturedPolygon(4, vert);
vgui::surface()->DrawFilledRect(0, 0, screenWide, y1); // top
vgui::surface()->DrawFilledRect(0, y2, screenWide, screenTall); // bottom
vgui::surface()->DrawFilledRect(0, y1, x1, screenTall); // left
vgui::surface()->DrawFilledRect(x2, y1, screenWide, screenTall); // right
}
}
}