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

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "hud.h"
  8. #include "hudelement.h"
  9. #include "hud_macros.h"
  10. #include "hud_numericdisplay.h"
  11. #include "iclientmode.h"
  12. #include "c_cs_player.h"
  13. #include "VGuiMatSurface/IMatSystemSurface.h"
  14. #include "materialsystem/imaterial.h"
  15. #include "materialsystem/imesh.h"
  16. #include "materialsystem/imaterialvar.h"
  17. #include <vgui/IScheme.h>
  18. #include <vgui/ISurface.h>
  19. #include <keyvalues.h>
  20. #include <vgui_controls/AnimationController.h>
  21. #include "predicted_viewmodel.h"
  22. #include "HUD/sfhudreticle.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. extern ConVar cl_flinch_compensate_crosshair;
  26. extern ConVar cl_crosshair_sniper_width;
  27. 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" );
  28. //extern ConVar cl_flinch_scale;
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Draws the zoom screen
  31. //-----------------------------------------------------------------------------
  32. class CHudScope : public vgui::Panel, public CHudElement
  33. {
  34. DECLARE_CLASS_SIMPLE( CHudScope, vgui::Panel );
  35. public:
  36. explicit CHudScope( const char *pElementName );
  37. void Init( void );
  38. void LevelInit( void );
  39. protected:
  40. virtual void ApplySchemeSettings(vgui::IScheme *scheme);
  41. virtual void Paint( void );
  42. private:
  43. CMaterialReference m_ScopeMaterial;
  44. CMaterialReference m_ScopeLineBlurMaterial;
  45. CMaterialReference m_DustOverlayMaterial;
  46. int m_iScopeArcTexture;
  47. int m_iScopeLineBlurTexture;
  48. int m_iScopeDustTexture;
  49. float m_fAnimInset;
  50. float m_fLineSpreadDistance;
  51. };
  52. DECLARE_HUDELEMENT_DEPTH( CHudScope, 70 );
  53. using namespace vgui;
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Constructor
  56. //-----------------------------------------------------------------------------
  57. CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudScope")
  58. {
  59. vgui::Panel *pParent = GetClientMode()->GetViewport();
  60. SetParent( pParent );
  61. SetHiddenBits( HIDEHUD_PLAYERDEAD );
  62. SetIgnoreGlobalHudDisable( true );
  63. m_fAnimInset = 1;
  64. m_fLineSpreadDistance = 1;
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: standard hud element init function
  68. //-----------------------------------------------------------------------------
  69. void CHudScope::Init( void )
  70. {
  71. m_iScopeArcTexture = vgui::surface()->CreateNewTextureID();
  72. vgui::surface()->DrawSetTextureFile(m_iScopeArcTexture, "sprites/scope_arc", true, false);
  73. m_iScopeLineBlurTexture = vgui::surface()->CreateNewTextureID();
  74. vgui::surface()->DrawSetTextureFile(m_iScopeLineBlurTexture, "sprites/scope_line_blur", true, false);
  75. m_iScopeDustTexture = vgui::surface()->CreateNewTextureID();
  76. vgui::surface()->DrawSetTextureFile(m_iScopeDustTexture, "overlays/scope_lens", true, false);
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose: standard hud element init function
  80. //-----------------------------------------------------------------------------
  81. void CHudScope::LevelInit( void )
  82. {
  83. Init();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: sets scheme colors
  87. //-----------------------------------------------------------------------------
  88. void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme )
  89. {
  90. BaseClass::ApplySchemeSettings(scheme);
  91. SetPaintBackgroundEnabled(false);
  92. SetPaintBorderEnabled(false);
  93. int screenWide, screenTall;
  94. GetHudSize(screenWide, screenTall);
  95. SetBounds(0, 0, screenWide, screenTall);
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: draws the zoom effect
  99. //-----------------------------------------------------------------------------
  100. void CHudScope::Paint( void )
  101. {
  102. C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
  103. if ( pPlayer == NULL )
  104. return;
  105. if ( pPlayer && pPlayer->GetObserverInterpState() == C_CSPlayer::OBSERVER_INTERP_TRAVELING )
  106. return;
  107. switch ( pPlayer->GetObserverMode() )
  108. {
  109. case OBS_MODE_NONE:
  110. break;
  111. case OBS_MODE_IN_EYE:
  112. pPlayer = ToCSPlayer( pPlayer->GetObserverTarget() );
  113. if ( pPlayer == NULL )
  114. return;
  115. break;
  116. default:
  117. return; // no scope for other observer modes
  118. }
  119. CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon();
  120. if( !pWeapon || pWeapon->GetWeaponType() != WEAPONTYPE_SNIPER_RIFLE )
  121. return;
  122. Assert( m_iScopeArcTexture );
  123. Assert( m_iScopeLineBlurTexture );
  124. Assert( m_iScopeDustTexture );
  125. const float kScopeMinFOV = 25.0f; // Clamp scope FOV to this value to prevent blur from getting too big when double-scoped
  126. float flTargetFOVForZoom = MAX( pWeapon->GetZoomFOV( pWeapon->GetCSZoomLevel() ), kScopeMinFOV );
  127. if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() && pPlayer->m_bIsScoped == false )
  128. {
  129. m_fAnimInset = 2;
  130. m_fLineSpreadDistance = 20;
  131. }
  132. // see if we're zoomed with a sniper rifle
  133. if( flTargetFOVForZoom != pPlayer->GetDefaultFOV() && pPlayer->m_bIsScoped )
  134. {
  135. int screenWide, screenTall;
  136. GetHudSize( screenWide, screenTall );
  137. CBaseViewModel *baseViewModel = pPlayer->GetViewModel( 0 );
  138. if ( baseViewModel == NULL )
  139. return;
  140. CPredictedViewModel *viewModel = dynamic_cast<CPredictedViewModel *>(baseViewModel);
  141. if ( viewModel == NULL )
  142. return;
  143. float fHalfFov = DEG2RAD( flTargetFOVForZoom ) * 0.5f;
  144. float fInaccuracyIn640x480Pixels = 320.0f / tanf( fHalfFov ); // 640 = "reference screen width"
  145. // Get the weapon's inaccuracy
  146. float fWeaponInaccuracy = pWeapon->GetInaccuracy() + pWeapon->GetSpread();
  147. // Optional: Ignore "default" inaccuracy
  148. if ( !cl_crosshair_sniper_show_normal_inaccuracy.GetBool() )
  149. fWeaponInaccuracy -= pWeapon->GetInaccuracyStand( Secondary_Mode ) + pWeapon->GetSpread();
  150. fWeaponInaccuracy = MAX( fWeaponInaccuracy, 0 );
  151. float fRawSpreadDistance = fWeaponInaccuracy * fInaccuracyIn640x480Pixels; //
  152. float fSpreadDistance = clamp( fRawSpreadDistance, 0, 100 );
  153. #if 0
  154. // This lets you verify inaccuracy vs screenshots of cl_weapon_debug_show_accuracy 2;
  155. // the number after screen= should max the radius (in pixels) of the drawn circle.
  156. float fInaccuracyInScreenPixels = fRawSpreadDistance * screenTall / 480; // 480 = "reference screen width"
  157. Msg( "fWeaponInaccuracy = %8.5f, referenceScreen = %8.5f, screen = %8.5f, fov = %8.5f\n", fWeaponInaccuracy, fRawSpreadDistance, fInaccuracyInScreenPixels, flTargetFOVForZoom );
  158. #endif
  159. // reduce the goal (* 0.4 / 30.0f)
  160. // then animate towards it at speed 19.0f
  161. // (where did these numbers come from?)
  162. float flInsetGoal = fSpreadDistance * (0.4f / 30.0f);
  163. m_fAnimInset = Approach( flInsetGoal, m_fAnimInset, abs( ( ( flInsetGoal )-m_fAnimInset )*gpGlobals->frametime ) * 19.0f );
  164. // Approach speed chosen so we get 90% there in 3 frames if we are running at 192 fps vs a 64tick client/server.
  165. // If our fps is lower we will reach the target faster, if higher it is slightly slower
  166. // (since this is a framerate-dependent approach function).
  167. m_fLineSpreadDistance = RemapValClamped( gpGlobals->frametime * 140.0f, 0.0f, 1.0f, m_fLineSpreadDistance, fRawSpreadDistance );
  168. float flAccuracyFishtail = pWeapon->GetAccuracyFishtail();
  169. int offsetX = viewModel->GetBobState().m_flRawLateralBob * (screenTall/14) + flAccuracyFishtail;
  170. int offsetY = viewModel->GetBobState().m_flRawVerticalBob * (screenTall/14);
  171. float flInacDisplayBlur = m_fAnimInset * 0.04f;
  172. if ( flInacDisplayBlur > 0.22 )
  173. flInacDisplayBlur = 0.22;
  174. // calculate the bounds in which we should draw the scope
  175. int inset = (screenTall / 14) + (flInacDisplayBlur * (screenTall*0.5));
  176. int y1 = inset;
  177. int x1 = (screenWide - screenTall) / 2 + inset;
  178. int y2 = screenTall - inset;
  179. int x2 = screenWide - x1;
  180. y1 += offsetY;
  181. y2 += offsetY;
  182. x1 += offsetX;
  183. x2 += offsetX;
  184. int x = (screenWide / 2) + offsetX;
  185. int y = (screenTall / 2) + offsetY;
  186. float uv1 = 0.5f / 256.0f, uv2 = 1.0f - uv1;
  187. vgui::Vertex_t vert[4];
  188. Vector2D uv11( uv1, uv1 );
  189. Vector2D uv12( uv1, uv2 );
  190. Vector2D uv21( uv2, uv1 );
  191. Vector2D uv22( uv2, uv2 );
  192. //Msg( "flRawInacc = %f, flAnimInset = %f\n", flRawInacc, m_fAnimInset );
  193. int xMod = ( screenWide / 2 ) + offsetX + (flInacDisplayBlur * screenTall);
  194. int yMod = ( screenTall / 2 ) + offsetY + (flInacDisplayBlur * screenTall);
  195. int iMiddleX = (screenWide / 2 ) + offsetX;
  196. int iMiddleY = (screenTall / 2 ) + offsetY;
  197. vgui::surface()->DrawSetTexture( m_iScopeDustTexture );
  198. vgui::surface()->DrawSetColor( 255, 255, 255, 200 );
  199. vert[0].Init( Vector2D( iMiddleX + xMod, iMiddleY + yMod ), uv21 );
  200. vert[1].Init( Vector2D( iMiddleX - xMod, iMiddleY + yMod ), uv11 );
  201. vert[2].Init( Vector2D( iMiddleX - xMod, iMiddleY - yMod ), uv12 );
  202. vert[3].Init( Vector2D( iMiddleX + xMod, iMiddleY - yMod ), uv22 );
  203. vgui::surface()->DrawTexturedPolygon( 4, vert );
  204. //Only sniper rifles use this style of vgui hud scope
  205. //if (pWeapon->GetWeaponType() == WEAPONTYPE_SNIPER_RIFLE)
  206. {
  207. // The powf here makes the blur not quite spread out quite as much as the actual inaccuracy;
  208. // doing so is a bit too sudden and also leads to just a huge blur because the snipers are
  209. // *extremely* inaccurate while scoped and moving. This way we get a slightly smoother animation
  210. // as well as not quite blowing up the blurred area by such a large amount.
  211. float fBlurWidth = powf(m_fLineSpreadDistance, 0.75f);
  212. float fScreenBlurWidth = fBlurWidth * screenTall / 640.0f; // scale from 'reference screen size' to actual screen
  213. int nSniperCrosshairThickness = cl_crosshair_sniper_width.GetInt();
  214. if ( nSniperCrosshairThickness < 1 )
  215. nSniperCrosshairThickness = 1;
  216. float kMaxVarianceWithFullAlpha = 1.8f; // Tuned to look good
  217. float fBlurAlpha;
  218. if ( fScreenBlurWidth <= nSniperCrosshairThickness + 0.5f )
  219. fBlurAlpha = ( fBlurWidth < 1.0f ) ? 1.0f : 1.0f / fBlurWidth;
  220. else
  221. fBlurAlpha = ( fBlurWidth < kMaxVarianceWithFullAlpha ) ? 1.0f : kMaxVarianceWithFullAlpha / fBlurWidth;
  222. // This is a break from physical reality to make the look a bit better. An actual Gaussian
  223. // blur spreads the energy out over the entire blurred area, dropping the total opacity by the amount
  224. // of the spread. However, this leads to not being able to see the effect at all. We solve this in
  225. // 2 ways:
  226. // (1) use sqrt on the alpha to bring it closer to 1, kind of like a gamma curve.
  227. // (2) clamp the alpha at the lower end to 55% to make sure you can see *something* no matter
  228. // how spread out it gets.
  229. fBlurAlpha = sqrtf( fBlurAlpha );
  230. int iBlurAlpha = Clamp( ( int )( fBlurAlpha * 255.0f ), 140, 255 );
  231. //DevMsg( "blur: %8.5f fov: %8.5f alpha: %8.5f\n", fBlurWidth, flTargetFOVForZoom, fBlurAlpha );
  232. if ( fScreenBlurWidth <= nSniperCrosshairThickness + 0.5f )
  233. {
  234. vgui::surface()->DrawSetColor( 0, 0, 0, iBlurAlpha );
  235. //Draw the reticle with primitives
  236. if ( nSniperCrosshairThickness <= 1 )
  237. {
  238. vgui::surface()->DrawLine( 0, y, screenWide + offsetX, y );
  239. vgui::surface()->DrawLine( x, 0, x, screenTall + offsetY );
  240. }
  241. else
  242. {
  243. int nStep = nSniperCrosshairThickness / 2;
  244. vgui::surface()->DrawFilledRect( 0, y - nStep, screenWide + offsetX, y + nSniperCrosshairThickness - nStep );
  245. vgui::surface()->DrawFilledRect( x - nStep, 0, x + nSniperCrosshairThickness - nStep, screenTall + offsetY );
  246. }
  247. }
  248. else
  249. {
  250. // Draw blurred line
  251. vgui::surface()->DrawSetColor( 0, 0, 0, iBlurAlpha );
  252. vgui::surface()->DrawSetTexture( m_iScopeLineBlurTexture );
  253. // vertical blurred line
  254. vert[0].Init( Vector2D( iMiddleX - fScreenBlurWidth, offsetY ), uv11 );
  255. vert[1].Init( Vector2D( iMiddleX + fScreenBlurWidth, offsetY ), uv21 );
  256. vert[2].Init( Vector2D( iMiddleX + fScreenBlurWidth, screenTall + offsetY ), uv22 );
  257. vert[3].Init( Vector2D( iMiddleX - fScreenBlurWidth, screenTall + offsetY ), uv12 );
  258. vgui::surface()->DrawTexturedPolygon( 4, vert );
  259. // horizontal blurred line
  260. vert[0].Init( Vector2D( screenWide + offsetX, iMiddleY - fScreenBlurWidth ), uv12 );
  261. vert[1].Init( Vector2D ( screenWide + offsetX, iMiddleY + fScreenBlurWidth ), uv22 );
  262. vert[2].Init( Vector2D( offsetX, iMiddleY + fScreenBlurWidth ), uv21 );
  263. vert[3].Init( Vector2D( offsetX, iMiddleY - fScreenBlurWidth ), uv11 );
  264. vgui::surface()->DrawTexturedPolygon(4, vert);
  265. }
  266. //vgui::surface()->DrawSetColor(0,0,0,MAX( 128, 255 - (int)(m_flOldInacc*3000)));
  267. vgui::surface()->DrawSetColor(0,0,0,255);
  268. //Draw the outline
  269. vgui::surface()->DrawSetTexture(m_iScopeArcTexture);
  270. // bottom right
  271. vert[0].Init( Vector2D( x, y ), uv11 );
  272. vert[1].Init( Vector2D( x2, y ), uv21 );
  273. vert[2].Init( Vector2D( x2, y2 ), uv22 );
  274. vert[3].Init( Vector2D( x, y2 ), uv12 );
  275. vgui::surface()->DrawTexturedPolygon( 4, vert );
  276. // top right
  277. vert[0].Init( Vector2D( x - 1, y1 ), uv12 );
  278. vert[1].Init( Vector2D ( x2, y1 ), uv22 );
  279. vert[2].Init( Vector2D( x2, y + 1 ), uv21 );
  280. vert[3].Init( Vector2D( x - 1, y + 1 ), uv11 );
  281. vgui::surface()->DrawTexturedPolygon(4, vert);
  282. // bottom left
  283. vert[0].Init( Vector2D( x1, y ), uv21 );
  284. vert[1].Init( Vector2D( x, y ), uv11 );
  285. vert[2].Init( Vector2D( x, y2 ), uv12 );
  286. vert[3].Init( Vector2D( x1, y2), uv22 );
  287. vgui::surface()->DrawTexturedPolygon(4, vert);
  288. // top left
  289. vert[0].Init( Vector2D( x1, y1 ), uv22 );
  290. vert[1].Init( Vector2D( x, y1 ), uv12 );
  291. vert[2].Init( Vector2D( x, y ), uv11 );
  292. vert[3].Init( Vector2D( x1, y ), uv21 );
  293. vgui::surface()->DrawTexturedPolygon(4, vert);
  294. vgui::surface()->DrawFilledRect(0, 0, screenWide, y1); // top
  295. vgui::surface()->DrawFilledRect(0, y2, screenWide, screenTall); // bottom
  296. vgui::surface()->DrawFilledRect(0, y1, x1, screenTall); // left
  297. vgui::surface()->DrawFilledRect(x2, y1, screenWide, screenTall); // right
  298. }
  299. }
  300. }