|
|
//========= 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
} } }
|