Team Fortress 2 Source Code as on 22/4/2020
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.

577 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "view.h"
  9. #include "iviewrender.h"
  10. #include "c_sun.h"
  11. #include "particles_simple.h"
  12. #include "clienteffectprecachesystem.h"
  13. #include "c_pixel_visibility.h"
  14. #include "glow_overlay.h"
  15. #include "utllinkedlist.h"
  16. #include "view_shared.h"
  17. #include "tier0/vprof.h"
  18. #include "materialsystem/imaterialvar.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectGlow )
  22. CLIENTEFFECT_MATERIAL( "sun/overlay" )
  23. CLIENTEFFECT_MATERIAL( "sprites/light_glow02_add_noz" )
  24. CLIENTEFFECT_REGISTER_END()
  25. class CGlowOverlaySystem : public CAutoGameSystem
  26. {
  27. public:
  28. CGlowOverlaySystem() : CAutoGameSystem( "CGlowOverlaySystem" )
  29. {
  30. }
  31. // Level init, shutdown
  32. virtual void LevelInitPreEntity() {}
  33. virtual void LevelShutdownPostEntity()
  34. {
  35. m_GlowOverlays.PurgeAndDeleteElements();
  36. }
  37. unsigned short AddToOverlayList( CGlowOverlay *pGlow )
  38. {
  39. return m_GlowOverlays.AddToTail( pGlow );
  40. }
  41. void RemoveFromOverlayList( unsigned short handle )
  42. {
  43. if( handle != m_GlowOverlays.InvalidIndex() )
  44. {
  45. m_GlowOverlays.Remove( handle );
  46. }
  47. }
  48. CUtlLinkedList<CGlowOverlay*, unsigned short> m_GlowOverlays;
  49. };
  50. CGlowOverlaySystem g_GlowOverlaySystem;
  51. ConVar cl_ShowSunVectors( "cl_ShowSunVectors", "0", 0 );
  52. ConVar cl_sun_decay_rate( "cl_sun_decay_rate", "0.05", FCVAR_CHEAT );
  53. // Dot product space the overlays are drawn in.
  54. // Here it's setup to allow you to see it if you're looking within 40 degrees of the source.
  55. float g_flOverlayRange = cos( DEG2RAD( 40 ) );
  56. // ----------------------------------------------------------------------------- //
  57. // ----------------------------------------------------------------------------- //
  58. void Do2DRotation( Vector vIn, Vector &vOut, float flDegrees, int i1, int i2, int i3 )
  59. {
  60. float c, s;
  61. SinCos( DEG2RAD( flDegrees ), &s, &c );
  62. vOut[i1] = vIn[i1]*c - vIn[i2]*s;
  63. vOut[i2] = vIn[i1]*s + vIn[i2]*c;
  64. vOut[i3] = vIn[i3];
  65. }
  66. // ----------------------------------------------------------------------------- //
  67. // ----------------------------------------------------------------------------- //
  68. CGlowOverlay::CGlowOverlay()
  69. {
  70. m_ListIndex = 0xFFFF;
  71. m_nSprites = 0;
  72. m_flGlowObstructionScale = 0.0f;
  73. m_bDirectional = false;
  74. m_bInSky = false;
  75. m_skyObstructionScale = 1.0f;
  76. m_bActivated = false;
  77. m_flProxyRadius = 2.0f;
  78. m_queryHandle = 0;
  79. m_flHDRColorScale = 1.0f;
  80. //Init our sprites
  81. for ( int i = 0; i < MAX_SUN_LAYERS; i++ )
  82. {
  83. m_Sprites[i].m_vColor.Init();
  84. m_Sprites[i].m_flHorzSize = 1.0f;
  85. m_Sprites[i].m_flVertSize = 1.0f;
  86. m_Sprites[i].m_pMaterial = NULL;
  87. }
  88. #ifdef PORTAL
  89. for( int i = 0; i != MAX_PORTAL_RECURSIVE_VIEWS; ++i )
  90. {
  91. m_skyObstructionScaleBackups[i] = 1.0f;
  92. }
  93. #endif
  94. }
  95. CGlowOverlay::~CGlowOverlay()
  96. {
  97. g_GlowOverlaySystem.RemoveFromOverlayList( m_ListIndex );
  98. }
  99. bool CGlowOverlay::Update()
  100. {
  101. return true;
  102. }
  103. ConVar building_cubemaps( "building_cubemaps", "0" );
  104. float CGlowOverlay::CalcGlowAspect()
  105. {
  106. if ( m_nSprites )
  107. {
  108. if ( m_Sprites[0].m_flHorzSize != 0 && m_Sprites[0].m_flVertSize != 0 )
  109. return m_Sprites[0].m_flHorzSize / m_Sprites[0].m_flVertSize;
  110. }
  111. return 1.0f;
  112. }
  113. void CGlowOverlay::UpdateSkyGlowObstruction( float zFar, bool bCacheFullSceneState )
  114. {
  115. Assert( m_bInSky );
  116. // If we already cached the sky obstruction and are still using that, early-out
  117. if ( bCacheFullSceneState && m_bCacheSkyObstruction )
  118. return;
  119. // Turning on sky obstruction caching mode
  120. if ( bCacheFullSceneState && !m_bCacheSkyObstruction )
  121. {
  122. m_bCacheSkyObstruction = true;
  123. }
  124. // Turning off sky obstruction caching mode
  125. if ( !bCacheFullSceneState && m_bCacheSkyObstruction )
  126. {
  127. m_bCacheSkyObstruction = false;
  128. }
  129. if ( PixelVisibility_IsAvailable() )
  130. {
  131. // Trace a ray at the object.
  132. Vector pos = CurrentViewOrigin() + m_vDirection * zFar * 0.999f;
  133. // UNDONE: Can probably do only the pixelvis query in this case if you can figure out where
  134. // to put it - or save the position of this trace
  135. pixelvis_queryparams_t params;
  136. params.Init( pos, m_flProxyRadius );
  137. params.bSizeInScreenspace = true;
  138. m_skyObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle );
  139. return;
  140. }
  141. // Trace a ray at the object.
  142. trace_t trace;
  143. UTIL_TraceLine( CurrentViewOrigin(), CurrentViewOrigin() + (m_vDirection*MAX_TRACE_LENGTH),
  144. CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace );
  145. // back the trace with a pixel query to occlude with models
  146. if ( trace.surface.flags & SURF_SKY )
  147. {
  148. m_skyObstructionScale = 1.0f;
  149. }
  150. else
  151. {
  152. m_skyObstructionScale = 0.0f;
  153. }
  154. }
  155. void CGlowOverlay::UpdateGlowObstruction( const Vector &vToGlow, bool bCacheFullSceneState )
  156. {
  157. // If we already cached the glow obstruction and are still using that, early-out
  158. if ( bCacheFullSceneState && m_bCacheGlowObstruction )
  159. return;
  160. if ( bCacheFullSceneState && !m_bCacheGlowObstruction ) // If turning on sky obstruction caching mode
  161. {
  162. m_bCacheGlowObstruction = true;
  163. }
  164. if ( !bCacheFullSceneState && m_bCacheGlowObstruction )
  165. {
  166. m_bCacheGlowObstruction = false;
  167. }
  168. if ( PixelVisibility_IsAvailable() )
  169. {
  170. if ( m_bInSky )
  171. {
  172. const CViewSetup *pViewSetup = view->GetViewSetup();
  173. Vector pos = CurrentViewOrigin() + m_vDirection * (pViewSetup->zFar * 0.999f);
  174. pixelvis_queryparams_t params;
  175. params.Init( pos, m_flProxyRadius, CalcGlowAspect() );
  176. params.bSizeInScreenspace = true;
  177. // use a pixel query to occlude with models
  178. m_flGlowObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle ) * m_skyObstructionScale;
  179. }
  180. else
  181. {
  182. // If it's not in the sky, then we need a valid position or else we don't
  183. // know what's in front of it.
  184. Assert( !m_bDirectional );
  185. pixelvis_queryparams_t params;
  186. params.Init( m_vPos, m_flProxyRadius, CalcGlowAspect() );
  187. m_flGlowObstructionScale = PixelVisibility_FractionVisible( params, &m_queryHandle );
  188. }
  189. return;
  190. }
  191. bool bFade = false;
  192. if ( m_bInSky )
  193. {
  194. // Trace a ray at the object.
  195. trace_t trace;
  196. UTIL_TraceLine( CurrentViewOrigin(), CurrentViewOrigin() + (vToGlow*MAX_TRACE_LENGTH),
  197. CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &trace );
  198. bFade = (trace.fraction < 1 && !(trace.surface.flags & SURF_SKY));
  199. }
  200. else
  201. {
  202. // If it's not in the sky, then we need a valid position or else we don't
  203. // know what's in front of it.
  204. Assert( !m_bDirectional );
  205. pixelvis_queryparams_t params;
  206. params.Init( m_vPos, m_flProxyRadius );
  207. bFade = PixelVisibility_FractionVisible( params, &m_queryHandle ) < 1.0f ? true : false;
  208. }
  209. if ( bFade )
  210. {
  211. if ( building_cubemaps.GetBool() )
  212. {
  213. m_flGlowObstructionScale = 0.0f;
  214. }
  215. else
  216. {
  217. m_flGlowObstructionScale -= gpGlobals->frametime / cl_sun_decay_rate.GetFloat();
  218. m_flGlowObstructionScale = MAX( m_flGlowObstructionScale, 0.0f );
  219. }
  220. }
  221. else
  222. {
  223. if ( building_cubemaps.GetBool() )
  224. {
  225. m_flGlowObstructionScale = 1.0f;
  226. }
  227. else
  228. {
  229. m_flGlowObstructionScale += gpGlobals->frametime / cl_sun_decay_rate.GetFloat();
  230. m_flGlowObstructionScale = MIN( m_flGlowObstructionScale, 1.0f );
  231. }
  232. }
  233. }
  234. void CGlowOverlay::CalcSpriteColorAndSize(
  235. float flDot,
  236. CGlowSprite *pSprite,
  237. float *flHorzSize,
  238. float *flVertSize,
  239. Vector *vColor )
  240. {
  241. // The overlay is largest and completely translucent at g_flOverlayRange.
  242. // When the dot product is 1, then it's smaller and more opaque.
  243. const float flSizeAtOverlayRangeMul = 150;
  244. const float flSizeAtOneMul = 70;
  245. const float flOpacityAtOverlayRange = 0;
  246. const float flOpacityAtOne = 1;
  247. // Figure out how big and how opaque it will be.
  248. *flHorzSize = RemapValClamped(
  249. flDot,
  250. g_flOverlayRange,
  251. 1,
  252. flSizeAtOverlayRangeMul * pSprite->m_flHorzSize,
  253. flSizeAtOneMul * pSprite->m_flHorzSize );
  254. *flVertSize = RemapValClamped(
  255. flDot,
  256. g_flOverlayRange,
  257. 1,
  258. flSizeAtOverlayRangeMul * pSprite->m_flVertSize,
  259. flSizeAtOneMul * pSprite->m_flVertSize );
  260. float flOpacity = RemapValClamped(
  261. flDot,
  262. g_flOverlayRange,
  263. 1,
  264. flOpacityAtOverlayRange,
  265. flOpacityAtOne );
  266. flOpacity = flOpacity * m_flGlowObstructionScale;
  267. *vColor = pSprite->m_vColor * flOpacity;
  268. }
  269. void CGlowOverlay::CalcBasis(
  270. const Vector &vToGlow,
  271. float flHorzSize,
  272. float flVertSize,
  273. Vector &vBasePt,
  274. Vector &vUp,
  275. Vector &vRight )
  276. {
  277. const float flOverlayDist = 100;
  278. vBasePt = CurrentViewOrigin() + vToGlow * flOverlayDist;
  279. vUp.Init( 0, 0, 1 );
  280. vRight = vToGlow.Cross( vUp );
  281. VectorNormalize( vRight );
  282. vUp = vRight.Cross( vToGlow );
  283. VectorNormalize( vUp );
  284. vRight *= flHorzSize;
  285. vUp *= flVertSize;
  286. }
  287. void CGlowOverlay::Draw( bool bCacheFullSceneState )
  288. {
  289. extern ConVar r_drawsprites;
  290. if( !r_drawsprites.GetBool() )
  291. return;
  292. // Get the vector to the sun.
  293. Vector vToGlow;
  294. if( m_bDirectional )
  295. vToGlow = m_vDirection;
  296. else
  297. vToGlow = m_vPos - CurrentViewOrigin();
  298. VectorNormalize( vToGlow );
  299. float flDot = vToGlow.Dot( CurrentViewForward() );
  300. UpdateGlowObstruction( vToGlow, bCacheFullSceneState );
  301. if( m_flGlowObstructionScale == 0 )
  302. return;
  303. bool bWireframe = ShouldDrawInWireFrameMode() || (r_drawsprites.GetInt() == 2);
  304. CMatRenderContextPtr pRenderContext( materials );
  305. for( int iSprite=0; iSprite < m_nSprites; iSprite++ )
  306. {
  307. CGlowSprite *pSprite = &m_Sprites[iSprite];
  308. // Figure out the color and size to draw it.
  309. float flHorzSize, flVertSize;
  310. Vector vColor;
  311. CalcSpriteColorAndSize( flDot, pSprite, &flHorzSize, &flVertSize, &vColor );
  312. // If we're alpha'd out, then don't bother
  313. if ( vColor.LengthSqr() < 0.00001f )
  314. continue;
  315. // Setup the basis to draw the sprite.
  316. Vector vBasePt, vUp, vRight;
  317. CalcBasis( vToGlow, flHorzSize, flVertSize, vBasePt, vUp, vRight );
  318. //Get our diagonal radius
  319. float radius = (vRight+vUp).Length();
  320. if ( R_CullSphere( view->GetFrustum(), 5, &vBasePt, radius ) )
  321. continue;
  322. // Get our material (deferred default load)
  323. if ( m_Sprites[iSprite].m_pMaterial == NULL )
  324. {
  325. m_Sprites[iSprite].m_pMaterial = materials->FindMaterial( "sprites/light_glow02_add_noz", TEXTURE_GROUP_CLIENT_EFFECTS );
  326. }
  327. Assert( m_Sprites[iSprite].m_pMaterial );
  328. static unsigned int nHDRColorScaleCache = 0;
  329. IMaterialVar *pHDRColorScaleVar = m_Sprites[iSprite].m_pMaterial->FindVarFast( "$hdrcolorscale", &nHDRColorScaleCache );
  330. if( pHDRColorScaleVar )
  331. {
  332. pHDRColorScaleVar->SetFloatValue( m_flHDRColorScale );
  333. }
  334. // Draw the sprite.
  335. IMesh *pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, m_Sprites[iSprite].m_pMaterial );
  336. CMeshBuilder builder;
  337. builder.Begin( pMesh, MATERIAL_QUADS, 1 );
  338. Vector vPt;
  339. vPt = vBasePt - vRight + vUp;
  340. builder.Position3fv( vPt.Base() );
  341. builder.Color4f( VectorExpand(vColor), 1 );
  342. builder.TexCoord2f( 0, 0, 1 );
  343. builder.AdvanceVertex();
  344. vPt = vBasePt + vRight + vUp;
  345. builder.Position3fv( vPt.Base() );
  346. builder.Color4f( VectorExpand(vColor), 1 );
  347. builder.TexCoord2f( 0, 1, 1 );
  348. builder.AdvanceVertex();
  349. vPt = vBasePt + vRight - vUp;
  350. builder.Position3fv( vPt.Base() );
  351. builder.Color4f( VectorExpand(vColor), 1 );
  352. builder.TexCoord2f( 0, 1, 0 );
  353. builder.AdvanceVertex();
  354. vPt = vBasePt - vRight - vUp;
  355. builder.Position3fv( vPt.Base() );
  356. builder.Color4f( VectorExpand(vColor), 1 );
  357. builder.TexCoord2f( 0, 0, 0 );
  358. builder.AdvanceVertex();
  359. builder.End( false, true );
  360. if( bWireframe )
  361. {
  362. IMaterial *pWireframeMaterial = materials->FindMaterial( "debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER );
  363. pRenderContext->Bind( pWireframeMaterial );
  364. // Draw the sprite.
  365. pMesh = pRenderContext->GetDynamicMesh( false, 0, 0, pWireframeMaterial );
  366. CMeshBuilder builderWireFrame;
  367. builderWireFrame.Begin( pMesh, MATERIAL_QUADS, 1 );
  368. vPt = vBasePt - vRight + vUp;
  369. builderWireFrame.Position3fv( vPt.Base() );
  370. builderWireFrame.Color3f( 1.0f, 0.0f, 0.0f );
  371. builderWireFrame.AdvanceVertex();
  372. vPt = vBasePt + vRight + vUp;
  373. builderWireFrame.Position3fv( vPt.Base() );
  374. builderWireFrame.Color3f( 1.0f, 0.0f, 0.0f );
  375. builderWireFrame.AdvanceVertex();
  376. vPt = vBasePt + vRight - vUp;
  377. builderWireFrame.Position3fv( vPt.Base() );
  378. builderWireFrame.Color3f( 1.0f, 0.0f, 0.0f );
  379. builderWireFrame.AdvanceVertex();
  380. vPt = vBasePt - vRight - vUp;
  381. builderWireFrame.Position3fv( vPt.Base() );
  382. builderWireFrame.Color3f( 1.0f, 0.0f, 0.0f );
  383. builderWireFrame.AdvanceVertex();
  384. builderWireFrame.End( false, true );
  385. }
  386. }
  387. }
  388. void CGlowOverlay::Activate()
  389. {
  390. m_bActivated = true;
  391. if( m_ListIndex == 0xFFFF )
  392. {
  393. m_ListIndex = g_GlowOverlaySystem.AddToOverlayList( this );
  394. }
  395. }
  396. void CGlowOverlay::Deactivate()
  397. {
  398. m_bActivated = false;
  399. }
  400. void CGlowOverlay::DrawOverlays( bool bCacheFullSceneState )
  401. {
  402. VPROF("CGlowOverlay::DrawOverlays()");
  403. CMatRenderContextPtr pRenderContext( materials );
  404. bool bClippingEnabled = pRenderContext->EnableClipping( true );
  405. unsigned short iNext;
  406. for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext )
  407. {
  408. iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i );
  409. CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
  410. if( !pOverlay->m_bActivated )
  411. continue;
  412. if( pOverlay->Update() )
  413. {
  414. pRenderContext->EnableClipping( ((pOverlay->m_bInSky) ? (false):(bClippingEnabled)) ); //disable clipping in skybox, restore clipping to pre-existing state when not in skybox (it may be off as well)
  415. pOverlay->Draw( bCacheFullSceneState );
  416. }
  417. else
  418. {
  419. delete pOverlay;
  420. }
  421. }
  422. pRenderContext->EnableClipping( bClippingEnabled ); //restore clipping to original state
  423. }
  424. void CGlowOverlay::UpdateSkyOverlays( float zFar, bool bCacheFullSceneState )
  425. {
  426. unsigned short iNext;
  427. for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext )
  428. {
  429. iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i );
  430. CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
  431. if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky )
  432. continue;
  433. pOverlay->UpdateSkyGlowObstruction( zFar, bCacheFullSceneState );
  434. }
  435. }
  436. #ifdef PORTAL
  437. void CGlowOverlay::BackupSkyOverlayData( int iBackupToSlot )
  438. {
  439. unsigned short iNext;
  440. for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext )
  441. {
  442. iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i );
  443. CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
  444. if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky )
  445. continue;
  446. pOverlay->m_skyObstructionScaleBackups[iBackupToSlot] = pOverlay->m_skyObstructionScale;
  447. }
  448. }
  449. void CGlowOverlay::RestoreSkyOverlayData( int iRestoreFromSlot )
  450. {
  451. unsigned short iNext;
  452. for( unsigned short i=g_GlowOverlaySystem.m_GlowOverlays.Head(); i != g_GlowOverlaySystem.m_GlowOverlays.InvalidIndex(); i = iNext )
  453. {
  454. iNext = g_GlowOverlaySystem.m_GlowOverlays.Next( i );
  455. CGlowOverlay *pOverlay = g_GlowOverlaySystem.m_GlowOverlays[i];
  456. if( !pOverlay->m_bActivated || !pOverlay->m_bDirectional || !pOverlay->m_bInSky )
  457. continue;
  458. pOverlay->m_skyObstructionScale = pOverlay->m_skyObstructionScaleBackups[iRestoreFromSlot];
  459. }
  460. }
  461. #endif //#ifdef PORTAL