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.

845 lines
32 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ============
  2. //
  3. // Functionality to render a glowing outline around client renderable objects.
  4. //
  5. //===============================================================================
  6. #include "cbase.h"
  7. #include "glow_outline_effect.h"
  8. #include "model_types.h"
  9. #include "shaderapi/ishaderapi.h"
  10. #include "materialsystem/imaterialvar.h"
  11. #include "view_shared.h"
  12. #include "c_cs_player.h"
  13. #include "tier2/renderutils.h"
  14. #define FULL_FRAME_TEXTURE "_rt_FullFrameFB"
  15. #define GLOWBOX_PASS_COLOR 0
  16. #define GLOWBOX_PASS_STENCIL 1
  17. #define GLOW_PULSE_DURATION 0.2f
  18. ConVar glow_outline_effect_enable( "glow_outline_effect_enable", "1", FCVAR_CHEAT, "Enable entity outline glow effects." );
  19. ConVar glow_outline_effect_width( "glow_outline_width", "6.0f", FCVAR_CHEAT, "Width of glow outline effect in screen space." );
  20. ConVar glow_muzzle_debug( "glow_muzzle_debug", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show muzzle glow shapes outside of the glow pass." );
  21. CGlowObjectManager &GlowObjectManager()
  22. {
  23. static CGlowObjectManager s_GlowObjectManager;
  24. return s_GlowObjectManager;
  25. }
  26. void CGlowObjectManager::RenderGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot )
  27. {
  28. if ( glow_outline_effect_enable.GetBool() )
  29. {
  30. CMatRenderContextPtr pRenderContext( materials );
  31. int nX, nY, nWidth, nHeight;
  32. pRenderContext->GetViewport( nX, nY, nWidth, nHeight );
  33. PIXEvent _pixEvent( pRenderContext, "EntityGlowEffects" );
  34. ApplyEntityGlowEffects( pSetup, nSplitScreenSlot, pRenderContext, glow_outline_effect_width.GetFloat(), nX, nY, nWidth, nHeight );
  35. }
  36. }
  37. static void SetRenderTargetAndViewPort( ITexture *rt, int w, int h )
  38. {
  39. CMatRenderContextPtr pRenderContext( materials );
  40. pRenderContext->SetRenderTarget(rt);
  41. pRenderContext->Viewport(0,0,w,h);
  42. }
  43. void CGlowObjectManager::RenderGlowBoxes( int iPass, CMatRenderContextPtr &pRenderContext )
  44. {
  45. for ( int n = m_GlowBoxDefinitions.Count() - 1; n >= 0 ; n-- )
  46. {
  47. if ( m_GlowBoxDefinitions[n].m_flTerminationTimeIndex < gpGlobals->curtime )
  48. {
  49. m_GlowBoxDefinitions.FastRemove(n);
  50. }
  51. else
  52. {
  53. float flLifeLeft = (m_GlowBoxDefinitions[n].m_flTerminationTimeIndex - gpGlobals->curtime) / (m_GlowBoxDefinitions[n].m_flTerminationTimeIndex - m_GlowBoxDefinitions[n].m_flBirthTimeIndex);
  54. if ( flLifeLeft > 0.95 )
  55. flLifeLeft = (0.05f - ( flLifeLeft - 0.95f )) / 0.05f; //fade in the first 5% of lifetime
  56. else
  57. flLifeLeft = MIN( flLifeLeft * 4.0f, 1.0f ); //fade out the last 25% of lifetime
  58. m_GlowBoxDefinitions[n].m_colColor[3] = flLifeLeft * 255;
  59. if ( iPass == GLOWBOX_PASS_COLOR )
  60. {
  61. Vector vecForward;
  62. AngleVectors( m_GlowBoxDefinitions[n].m_angOrientation, &vecForward );
  63. Vector vecLineEnd = m_GlowBoxDefinitions[n].m_vPosition + ( vecForward * m_GlowBoxDefinitions[n].m_vMaxs.x );
  64. RenderLine( m_GlowBoxDefinitions[n].m_vPosition, vecLineEnd, m_GlowBoxDefinitions[n].m_colColor, false );
  65. }
  66. else if ( iPass == GLOWBOX_PASS_STENCIL )
  67. {
  68. ShaderStencilState_t stencilState;
  69. stencilState.m_bEnable = true;
  70. stencilState.m_nReferenceValue = 1;
  71. stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
  72. stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  73. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  74. stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  75. pRenderContext->SetStencilState( stencilState );
  76. RenderBox( m_GlowBoxDefinitions[n].m_vPosition, m_GlowBoxDefinitions[n].m_angOrientation, m_GlowBoxDefinitions[n].m_vMins, m_GlowBoxDefinitions[n].m_vMaxs, m_GlowBoxDefinitions[n].m_colColor, false );
  77. }
  78. }
  79. }
  80. }
  81. // *** Keep in sync with matsys_interface.cpp, where the texture is declared ***
  82. // Resolution for glow target chosen to be the largest that we can fit in EDRAM after 720p color/depth textures.
  83. #define GLOW_360_RT_WIDTH ( MIN( 1120, pSetup->width ) )
  84. #define GLOW_360_RT_HEIGHT ( MIN( 624, pSetup->height ) )
  85. void CGlowObjectManager::RenderGlowModels( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, CUtlVector<GlowObjectDefinition_t> &vecGlowObjects )
  86. {
  87. //==========================================================================================//
  88. // This renders solid pixels with the correct coloring for each object that needs the glow. //
  89. // After this function returns, this image will then be blurred and added into the frame //
  90. // buffer with the objects stenciled out. //
  91. //==========================================================================================//
  92. pRenderContext->PushRenderTargetAndViewport();
  93. // Save modulation color and blend
  94. Vector vOrigColor;
  95. render->GetColorModulation( vOrigColor.Base() );
  96. float flOrigBlend = render->GetBlend();
  97. ITexture *pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET );
  98. if ( IsX360() )
  99. {
  100. ITexture *pRtGlowTexture360 = materials->FindTexture( "_rt_Glows360", TEXTURE_GROUP_RENDER_TARGET );
  101. SetRenderTargetAndViewPort( pRtGlowTexture360, GLOW_360_RT_WIDTH, GLOW_360_RT_HEIGHT );
  102. }
  103. else
  104. {
  105. SetRenderTargetAndViewPort( pRtFullFrame, pSetup->width, pSetup->height );
  106. }
  107. pRenderContext->ClearColor3ub( 0, 0, 0 );
  108. pRenderContext->ClearBuffers( true, false, false );
  109. // Set override material for glow color
  110. IMaterial *pMatGlowColor = NULL;
  111. pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true );
  112. //==================//
  113. // Draw the objects //
  114. //==================//
  115. for ( int i = 0; i < vecGlowObjects.Count(); ++ i )
  116. {
  117. if ( vecGlowObjects[i].IsUnused() || !vecGlowObjects[i].ShouldDraw( nSplitScreenSlot ) || vecGlowObjects[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
  118. continue;
  119. g_pStudioRender->ForcedMaterialOverride( pMatGlowColor );
  120. if ( vecGlowObjects[i].m_bFullBloomRender )
  121. {
  122. // Disabled because stencil test on off-screen buffers doesn't work with MSAA on.
  123. // Also, the normal model render does not seem to work on the off-screen buffer
  124. //g_pStudioRender->ForcedMaterialOverride( NULL );
  125. // ShaderStencilState_t stencilState;
  126. // stencilState.m_bEnable = true;
  127. // stencilState.m_nReferenceValue = vecGlowObjects[i].m_nFullBloomStencilTestValue;
  128. // stencilState.m_nTestMask = 0xFF;
  129. // stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
  130. // stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  131. // stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  132. // stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
  133. //
  134. // pRenderContext->SetStencilState( stencilState );
  135. }
  136. else
  137. {
  138. // Disabled because stencil test on off-screen buffers doesn't work with MSAA on
  139. // Most features still work, but some (e.g. partial occlusion) don't
  140. // ShaderStencilState_t stencilState;
  141. // stencilState.m_bEnable = true;
  142. // stencilState.m_nReferenceValue = 1;
  143. // stencilState.m_nTestMask = 0x1;
  144. // stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
  145. // stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  146. // stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  147. // stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
  148. //
  149. // pRenderContext->SetStencilState( stencilState );
  150. }
  151. render->SetBlend( vecGlowObjects[i].m_flGlowAlpha );
  152. Vector vGlowColor = vecGlowObjects[i].m_vGlowColor * vecGlowObjects[i].m_flGlowAlpha;
  153. // if pulse overdrive is non-zero, add its contribution to render alpha
  154. if ( vecGlowObjects[i].m_flGlowPulseOverdrive > 0 )
  155. {
  156. render->SetBlend( vecGlowObjects[i].m_flGlowAlpha + vecGlowObjects[i].m_flGlowPulseOverdrive );
  157. }
  158. // if set, cap glow alpha according to alpha of the non-glowing entity
  159. float flRenderAlpha = (float)vecGlowObjects[i].m_pEntity->GetRenderAlpha() * 0.00392;
  160. if ( vecGlowObjects[i].m_bGlowAlphaCappedByRenderAlpha && vecGlowObjects[i].m_flGlowAlpha > flRenderAlpha )
  161. {
  162. render->SetBlend( flRenderAlpha );
  163. vGlowColor *= flRenderAlpha;
  164. }
  165. // if set, alpha is multiplied by the ratio of entity velocity over the given maximum (e.g. faster = more opaque)
  166. if ( vecGlowObjects[i].m_flGlowAlphaFunctionOfMaxVelocity > 0.0f )
  167. {
  168. float flVelocityToAlpha = (vecGlowObjects[i].m_pEntity->GetAbsVelocity().Length() / vecGlowObjects[i].m_flGlowAlphaFunctionOfMaxVelocity);
  169. render->SetBlend( flVelocityToAlpha );
  170. vGlowColor *= flVelocityToAlpha;
  171. }
  172. // if set, cap cumulative glow alpha to a maximum value
  173. if ( render->GetBlend() > 0 && vecGlowObjects[i].m_flGlowAlphaMax < 1.0f )
  174. {
  175. float flCappedAlpha = MIN( render->GetBlend(), vecGlowObjects[i].m_flGlowAlphaMax );
  176. render->SetBlend( flCappedAlpha );
  177. vGlowColor *= flCappedAlpha;
  178. }
  179. render->SetColorModulation( &vGlowColor[0] ); // This only sets rgb, not alpha
  180. vecGlowObjects[i].DrawModel();
  181. // align and render the glow-only muzzle flash model for glowing weapon fire
  182. if ( vecGlowObjects[i].m_flGlowPulseOverdrive >= 0.25f )
  183. {
  184. C_CSPlayer* localPlayer = GetLocalOrInEyeCSPlayer();
  185. C_CSPlayer* tempPlayer = ToCSPlayer( vecGlowObjects[i].m_pEntity );
  186. if ( tempPlayer && localPlayer && (localPlayer->GetAbsOrigin() - tempPlayer->GetAbsOrigin()).Length() > 20 )
  187. {
  188. CWeaponCSBase* tempWeapon = tempPlayer->GetActiveCSWeapon();
  189. if ( tempWeapon )
  190. {
  191. //move muzzle flash shape to muzzle location
  192. if ( tempPlayer->m_hMuzzleFlashShape && !(tempWeapon->HasSilencer() && tempWeapon->IsSilenced()) )
  193. {
  194. tempPlayer->m_hMuzzleFlashShape->SetAbsOrigin( tempPlayer->m_vecLastMuzzleFlashPos );
  195. tempPlayer->m_hMuzzleFlashShape->SetAbsAngles( tempPlayer->m_angLastMuzzleFlashAngle );
  196. //pick a random flash shape
  197. //tempPlayer->m_hMuzzleFlashShape->SetBodygroup(0, RandomInt( 0, tempPlayer->m_hMuzzleFlashShape->GetNumBodyGroups()-1 ));
  198. //unhide and render the muzzle flash shape
  199. tempPlayer->m_hMuzzleFlashShape->RemoveEffects( EF_NODRAW );
  200. RenderableInstance_t instance;
  201. instance.m_nAlpha = (uint8)( vecGlowObjects[i].m_flGlowAlpha * 255.0f );
  202. tempPlayer->m_hMuzzleFlashShape->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
  203. if ( glow_muzzle_debug.GetInt() == 0 )
  204. tempPlayer->m_hMuzzleFlashShape->AddEffects( EF_NODRAW );
  205. }
  206. }
  207. }
  208. }
  209. // dampen overdrive here. Do this at the end, otherwise our framerate may be low enough that we don't see the effect for even one frame
  210. if ( vecGlowObjects[i].m_flGlowPulseOverdrive > 0 )
  211. {
  212. vecGlowObjects[i].m_flGlowPulseOverdrive -= MAX(0, ( vecGlowObjects[i].m_flGlowPulseOverdrive * ( gpGlobals->frametime / GLOW_PULSE_DURATION ) ) ); //return to default over 1/5th a second
  213. }
  214. }
  215. RenderGlowBoxes(GLOWBOX_PASS_COLOR, pRenderContext);
  216. g_pStudioRender->ForcedMaterialOverride( NULL );
  217. render->SetColorModulation( vOrigColor.Base() );
  218. render->SetBlend( flOrigBlend );
  219. ShaderStencilState_t stencilStateDisable;
  220. stencilStateDisable.m_bEnable = false;
  221. pRenderContext->SetStencilState( stencilStateDisable );
  222. if ( IsX360() )
  223. {
  224. Rect_t rect;
  225. rect.x = rect.y = 0;
  226. rect.width = GLOW_360_RT_WIDTH;
  227. rect.height = GLOW_360_RT_HEIGHT;
  228. pRenderContext->CopyRenderTargetToTextureEx( pRtFullFrame, 0, &rect, &rect );
  229. }
  230. pRenderContext->PopRenderTargetAndViewport();
  231. }
  232. void CGlowObjectManager::DownSampleAndBlurRT( const CViewSetup *pSetup, CMatRenderContextPtr &pRenderContext, float flBloomScale, ITexture *pRtFullFrame, ITexture *pRtQuarterSize0, ITexture *pRtQuarterSize1 )
  233. {
  234. static bool s_bFirstPass = true;
  235. //===================================
  236. // Setup state for downsample/bloom
  237. //===================================
  238. #if defined( _X360 )
  239. pRenderContext->PushVertexShaderGPRAllocation( 16 ); // Max out pixel shader threads
  240. #endif
  241. pRenderContext->PushRenderTargetAndViewport();
  242. // Get viewport
  243. int nSrcWidth = pSetup->width;
  244. int nSrcHeight = pSetup->height;
  245. int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
  246. pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
  247. // Get material and texture pointers
  248. IMaterial *pMatDownsample = materials->FindMaterial( "dev/glow_downsample", TEXTURE_GROUP_OTHER, true);
  249. IMaterial *pMatBlurX = materials->FindMaterial( "dev/glow_blur_x", TEXTURE_GROUP_OTHER, true );
  250. IMaterial *pMatBlurY = materials->FindMaterial( "dev/glow_blur_y", TEXTURE_GROUP_OTHER, true );
  251. //============================================
  252. // Downsample _rt_FullFrameFB to _rt_SmallFB0
  253. //============================================
  254. // First clear the full target to black if we're not going to touch every pixel
  255. if ( ( pRtQuarterSize0->GetActualWidth() != ( pSetup->width / 4 ) ) || ( pRtQuarterSize0->GetActualHeight() != ( pSetup->height / 4 ) ) )
  256. {
  257. SetRenderTargetAndViewPort( pRtQuarterSize0, pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
  258. pRenderContext->ClearColor3ub( 0, 0, 0 );
  259. pRenderContext->ClearBuffers( true, false, false );
  260. }
  261. // Set the viewport
  262. SetRenderTargetAndViewPort( pRtQuarterSize0, pSetup->width / 4, pSetup->height / 4 );
  263. IMaterialVar *pbloomexpvar = pMatDownsample->FindVar( "$bloomexp", 0 );
  264. if ( pbloomexpvar != NULL )
  265. {
  266. pbloomexpvar->SetFloatValue( 2.5f );
  267. }
  268. IMaterialVar *pbloomsaturationvar = pMatDownsample->FindVar( "$bloomsaturation", 0 );
  269. if ( pbloomsaturationvar != NULL )
  270. {
  271. pbloomsaturationvar->SetFloatValue( 1.0f );
  272. }
  273. // note the -2's below. Thats because we are downsampling on each axis and the shader
  274. // accesses pixels on both sides of the source coord
  275. int nFullFbWidth = nSrcWidth;
  276. int nFullFbHeight = nSrcHeight;
  277. if ( IsX360() )
  278. {
  279. nFullFbWidth = GLOW_360_RT_WIDTH;
  280. nFullFbHeight = GLOW_360_RT_HEIGHT;
  281. }
  282. pRenderContext->DrawScreenSpaceRectangle( pMatDownsample, 0, 0, nSrcWidth/4, nSrcHeight/4,
  283. 0, 0, nFullFbWidth - 4, nFullFbHeight - 4,
  284. pRtFullFrame->GetActualWidth(), pRtFullFrame->GetActualHeight() );
  285. if ( IsX360() )
  286. {
  287. // Need to reset viewport to full size so we can also copy the cleared black pixels around the border
  288. SetRenderTargetAndViewPort( pRtQuarterSize0, pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
  289. pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize0, 0, NULL, NULL );
  290. }
  291. //============================//
  292. // Guassian blur x rt0 to rt1 //
  293. //============================//
  294. // First clear the full target to black if we're not going to touch every pixel
  295. if ( s_bFirstPass || ( pRtQuarterSize1->GetActualWidth() != ( pSetup->width / 4 ) ) || ( pRtQuarterSize1->GetActualHeight() != ( pSetup->height / 4 ) ) )
  296. {
  297. // On the first render, this viewport may require clearing
  298. s_bFirstPass = false;
  299. SetRenderTargetAndViewPort( pRtQuarterSize1, pRtQuarterSize1->GetActualWidth(), pRtQuarterSize1->GetActualHeight() );
  300. pRenderContext->ClearColor3ub( 0, 0, 0 );
  301. pRenderContext->ClearBuffers( true, false, false );
  302. }
  303. // Set the viewport
  304. SetRenderTargetAndViewPort( pRtQuarterSize1, pSetup->width / 4, pSetup->height / 4 );
  305. pRenderContext->DrawScreenSpaceRectangle( pMatBlurX, 0, 0, nSrcWidth/4, nSrcHeight/4,
  306. 0, 0, nSrcWidth/4-1, nSrcHeight/4-1,
  307. pRtQuarterSize0->GetActualWidth(), pRtQuarterSize0->GetActualHeight() );
  308. if ( IsX360() )
  309. {
  310. pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize1, 0, NULL, NULL );
  311. }
  312. //============================//
  313. // Gaussian blur y rt1 to rt0 //
  314. //============================//
  315. SetRenderTargetAndViewPort( pRtQuarterSize0, pSetup->width / 4, pSetup->height / 4 );
  316. IMaterialVar *pBloomAmountVar = pMatBlurY->FindVar( "$bloomamount", NULL );
  317. pBloomAmountVar->SetFloatValue( flBloomScale );
  318. pRenderContext->DrawScreenSpaceRectangle( pMatBlurY, 0, 0, nSrcWidth / 4, nSrcHeight / 4,
  319. 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
  320. pRtQuarterSize1->GetActualWidth(), pRtQuarterSize1->GetActualHeight() );
  321. if ( IsX360() )
  322. {
  323. pRenderContext->CopyRenderTargetToTextureEx( pRtQuarterSize1, 0, NULL, NULL ); // copy to rt1 instead of rt0 because rt1 has linear reads enabled and works more easily with screenspace_general to fix 360 bloom issues
  324. }
  325. // Pop RT
  326. pRenderContext->PopRenderTargetAndViewport();
  327. }
  328. void CGlowObjectManager::ApplyEntityGlowEffects( const CViewSetup *pSetup, int nSplitScreenSlot, CMatRenderContextPtr &pRenderContext, float flBloomScale, int x, int y, int w, int h )
  329. {
  330. // gather up special glow styles
  331. CUtlVector<GlowObjectDefinition_t> vecGlowObjectsRimGlow3DStyle;
  332. vecGlowObjectsRimGlow3DStyle.RemoveAll();
  333. CUtlVector<GlowObjectDefinition_t> vecGlowObjectsEdgeHighlightStyle;
  334. vecGlowObjectsEdgeHighlightStyle.RemoveAll();
  335. FOR_EACH_VEC_BACK( m_GlowObjectDefinitions, i )
  336. {
  337. if ( !m_GlowObjectDefinitions[i].IsUnused() && m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) )
  338. {
  339. if ( m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_RIMGLOW3D )
  340. {
  341. vecGlowObjectsRimGlow3DStyle.AddToTail( m_GlowObjectDefinitions[i] );
  342. }
  343. else if ( m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT || m_GlowObjectDefinitions[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT_PULSE )
  344. {
  345. vecGlowObjectsEdgeHighlightStyle.AddToTail( m_GlowObjectDefinitions[i] );
  346. }
  347. }
  348. }
  349. if ( vecGlowObjectsRimGlow3DStyle.Count() )
  350. {
  351. //todo: expose pulse width and frequency parameters
  352. float flPulse = 0.5f + 0.5f * sin( gpGlobals->curtime * 12.0f );
  353. IMaterial *pMatRim = materials->FindMaterial( "dev/glow_rim3d", TEXTURE_GROUP_OTHER, true );
  354. g_pStudioRender->ForcedMaterialOverride( pMatRim );
  355. for ( int i = 0; i < vecGlowObjectsRimGlow3DStyle.Count(); ++ i )
  356. {
  357. if ( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha <= 0 )
  358. continue;
  359. IMaterialVar *pMatVar = pMatRim->FindVar( "$envmaptint", 0 );
  360. if ( pMatVar != NULL )
  361. {
  362. pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.x, 0, 1 ), 0 );
  363. pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.y, 0, 1 ), 1 );
  364. pMatVar->SetVecComponentValue( clamp( vecGlowObjectsRimGlow3DStyle[i].m_flGlowAlpha * vecGlowObjectsRimGlow3DStyle[i].m_vGlowColor.z, 0, 1 ), 2 );
  365. }
  366. pMatVar = pMatRim->FindVar( "$envmapfresnelminmaxexp", 0 );
  367. if ( pMatVar != NULL )
  368. {
  369. pMatVar->SetVecComponentValue( 0, 0 );
  370. pMatVar->SetVecComponentValue( 1.5f, 1 );
  371. pMatVar->SetVecComponentValue( 3.0f + flPulse * 2.0f, 2 );
  372. }
  373. vecGlowObjectsRimGlow3DStyle[i].DrawModel();
  374. }
  375. g_pStudioRender->ForcedMaterialOverride( NULL );
  376. }
  377. if ( vecGlowObjectsEdgeHighlightStyle.Count() )
  378. {
  379. // render players into the fullscreen rt all white
  380. // render in-world 3d imposter players into scene using screenspace UV lookup from fullscreen RT, use glow object alpha and colorize here.
  381. // in the above step, multisample the rt to edge detect.
  382. // push the rt
  383. pRenderContext->PushRenderTargetAndViewport();
  384. // remember the color modulation so we can put it back again at the end
  385. Vector vOrigColor;
  386. render->GetColorModulation( vOrigColor.Base() );
  387. ITexture *pRtOutput = materials->FindTexture( "_rt_FullScreen", TEXTURE_GROUP_RENDER_TARGET );
  388. SetRenderTargetAndViewPort( pRtOutput, pSetup->width, pSetup->height );
  389. pRenderContext->ClearColor3ub( 0, 0, 0 );
  390. pRenderContext->ClearBuffers( true, true, true );
  391. // Set override material for drawing the shapes
  392. IMaterial *pMatTeamIdShape = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true );
  393. g_pStudioRender->ForcedMaterialOverride( pMatTeamIdShape );
  394. // we want to render fully opaque, so bash blend to 1
  395. render->SetBlend( 1 );
  396. // set override color
  397. Vector vecOverrideColor = Vector( 1, 1, 1 );
  398. render->SetColorModulation( vecOverrideColor.Base() );
  399. // draw players
  400. FOR_EACH_VEC( vecGlowObjectsEdgeHighlightStyle, i )
  401. {
  402. vecGlowObjectsEdgeHighlightStyle[i].DrawModel();
  403. }
  404. g_pStudioRender->ForcedMaterialOverride( NULL );
  405. pRenderContext->PopRenderTargetAndViewport();
  406. IMaterial *pMatTemp = materials->FindMaterial( "dev/glow_edge_highlight", TEXTURE_GROUP_OTHER, false );
  407. g_pStudioRender->ForcedMaterialOverride( pMatTemp );
  408. // draw players
  409. FOR_EACH_VEC( vecGlowObjectsEdgeHighlightStyle, i )
  410. {
  411. Vector vecTempColor = vecGlowObjectsEdgeHighlightStyle[i].m_vGlowColor * clamp( vecGlowObjectsEdgeHighlightStyle[i].m_flGlowAlpha, 0.0f, 1.0f ) * 1.4f; // boost a bit
  412. if ( vecGlowObjectsEdgeHighlightStyle[i].m_nRenderStyle == GLOWRENDERSTYLE_EDGE_HIGHLIGHT_PULSE )
  413. {
  414. float flPulse = 1.5f + 0.5f * sin( gpGlobals->curtime * 16.0f );
  415. vecTempColor *= (flPulse * 0.5f);
  416. }
  417. render->SetColorModulation( vecTempColor.Base() );
  418. vecGlowObjectsEdgeHighlightStyle[i].DrawModel();
  419. }
  420. // restore the modulation from before
  421. render->SetColorModulation( vOrigColor.Base() );
  422. }
  423. //=======================================================//
  424. // Render objects into stencil buffer //
  425. //=======================================================//
  426. // Set override shader to the same simple shader we use to render the glow models
  427. IMaterial *pMatGlowColor = materials->FindMaterial( "dev/glow_color", TEXTURE_GROUP_OTHER, true );
  428. g_pStudioRender->ForcedMaterialOverride( pMatGlowColor );
  429. ShaderStencilState_t stencilStateDisable;
  430. stencilStateDisable.m_bEnable = false;
  431. float flSavedBlend = render->GetBlend();
  432. // Set alpha to 0 so we don't touch any color pixels
  433. render->SetBlend( 0.0f );
  434. pRenderContext->OverrideDepthEnable( true, false );
  435. RenderableInstance_t instance;
  436. instance.m_nAlpha = 255;
  437. int iNumGlowObjects = 0;
  438. for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i )
  439. {
  440. if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
  441. continue;
  442. // Full bloom rendered objects should not be stenciled out here
  443. if ( m_GlowObjectDefinitions[i].m_bFullBloomRender )
  444. {
  445. ++ iNumGlowObjects;
  446. continue;
  447. }
  448. if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded || m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded )
  449. {
  450. if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded )
  451. {
  452. ShaderStencilState_t stencilState;
  453. stencilState.m_bEnable = true;
  454. stencilState.m_nReferenceValue = 1;
  455. stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
  456. stencilState.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  457. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  458. stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  459. pRenderContext->SetStencilState( stencilState );
  460. m_GlowObjectDefinitions[i].DrawModel();
  461. }
  462. else if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded )
  463. {
  464. ShaderStencilState_t stencilState;
  465. stencilState.m_bEnable = true;
  466. stencilState.m_nReferenceValue = 1;
  467. stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
  468. stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  469. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  470. stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  471. pRenderContext->SetStencilState( stencilState );
  472. m_GlowObjectDefinitions[i].DrawModel();
  473. }
  474. else if ( m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded )
  475. {
  476. ShaderStencilState_t stencilState;
  477. stencilState.m_bEnable = true;
  478. stencilState.m_nReferenceValue = 2;
  479. stencilState.m_nTestMask = 0x1;
  480. stencilState.m_nWriteMask = 0x3;
  481. stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
  482. stencilState.m_PassOp = SHADER_STENCILOP_INCREMENT_CLAMP;
  483. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  484. stencilState.m_ZFailOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  485. pRenderContext->SetStencilState( stencilState );
  486. m_GlowObjectDefinitions[i].DrawModel();
  487. }
  488. }
  489. iNumGlowObjects++;
  490. }
  491. int iTempHealthBarRenderMaskIndex = 4;
  492. // Need to do a 2nd pass to warm stencil for objects which are rendered only when occluded
  493. for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i )
  494. {
  495. if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
  496. continue;
  497. // Full bloom rendered objects should not be stenciled out here
  498. if ( m_GlowObjectDefinitions[i].m_bFullBloomRender )
  499. continue;
  500. if ( m_GlowObjectDefinitions[i].m_bRenderWhenOccluded && !m_GlowObjectDefinitions[i].m_bRenderWhenUnoccluded )
  501. {
  502. ShaderStencilState_t stencilState;
  503. stencilState.m_bEnable = true;
  504. stencilState.m_nReferenceValue = 2;
  505. C_CSPlayer* pPlayer = ToCSPlayer( m_GlowObjectDefinitions[i].m_pEntity );
  506. if ( pPlayer )
  507. {
  508. pPlayer->m_iHealthBarRenderMaskIndex = iTempHealthBarRenderMaskIndex;
  509. stencilState.m_nReferenceValue = iTempHealthBarRenderMaskIndex;
  510. iTempHealthBarRenderMaskIndex ++;
  511. }
  512. stencilState.m_CompareFunc = SHADER_STENCILFUNC_ALWAYS;
  513. stencilState.m_PassOp = SHADER_STENCILOP_SET_TO_REFERENCE;
  514. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  515. stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
  516. pRenderContext->SetStencilState( stencilState );
  517. m_GlowObjectDefinitions[i].DrawModel();
  518. }
  519. }
  520. RenderGlowBoxes(GLOWBOX_PASS_STENCIL, pRenderContext);
  521. iNumGlowObjects += m_GlowBoxDefinitions.Count();
  522. g_pStudioRender->ForcedMaterialOverride( NULL );
  523. render->SetBlend( 0.0f );
  524. pRenderContext->OverrideDepthEnable( true, false, false ); // health bars render over everything
  525. IMaterial *pMatGlowHealthColor = NULL;
  526. pMatGlowHealthColor = materials->FindMaterial( "dev/glow_health_color", TEXTURE_GROUP_OTHER, true );
  527. for ( int i = 0; i < m_GlowObjectDefinitions.Count(); ++ i )
  528. {
  529. if ( m_GlowObjectDefinitions[i].IsUnused() || !m_GlowObjectDefinitions[i].ShouldDraw( nSplitScreenSlot ) || m_GlowObjectDefinitions[i].m_nRenderStyle != GLOWRENDERSTYLE_DEFAULT )
  530. continue;
  531. C_CSPlayer* pPlayer = ToCSPlayer( m_GlowObjectDefinitions[i].m_pEntity );
  532. if ( pPlayer && pPlayer != GetLocalOrInEyeCSPlayer() && pPlayer->IsAlive() )
  533. {
  534. ShaderStencilState_t stencilState;
  535. stencilState.m_bEnable = true;
  536. stencilState.m_nWriteMask = 0x0;
  537. stencilState.m_nTestMask = 0xFFFFFF;
  538. stencilState.m_nReferenceValue = pPlayer->m_iHealthBarRenderMaskIndex;
  539. stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
  540. stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  541. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  542. stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
  543. pRenderContext->SetStencilState( stencilState );
  544. if ( pPlayer->m_flHealthFadeAlpha > 0 )
  545. pPlayer->m_flHealthFadeAlpha -= gpGlobals->frametime * 0.4f;
  546. if ( pPlayer->m_flHealthFadeValue != pPlayer->GetHealth() )
  547. pPlayer->m_flHealthFadeAlpha = 1.0f;
  548. pPlayer->m_flHealthFadeValue = (float)pPlayer->GetHealth();
  549. if ( pPlayer->m_flHealthFadeAlpha > 0 ) //only need to update the effect if we can see it
  550. {
  551. float flGlowPulseSpeed = Lerp( pPlayer->m_flHealthFadeValue/100.0f, 30.0f, 10.0f );
  552. pMatGlowHealthColor->AlphaModulate( pPlayer->m_flHealthFadeAlpha * (0.4*(sin(flGlowPulseSpeed*gpGlobals->curtime)+1.4)) );
  553. Vector vecPlayerScreenSpaceOrigin;
  554. if ( ScreenTransform( pPlayer->GetAbsOrigin(), vecPlayerScreenSpaceOrigin ) == 0 )
  555. {
  556. ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceOrigin);
  557. float flHealthLeft = (100.0f-pPlayer->m_flHealthFadeValue)/100.0f;
  558. float flHealthHeightOffset = 72.0f;
  559. if ( pPlayer->GetFlags() & FL_DUCKING )
  560. flHealthHeightOffset = 55.0f;
  561. Vector vecPlayerScreenSpaceHealthPos;
  562. if ( ScreenTransform( pPlayer->GetAbsOrigin() + Vector (0,0,flHealthLeft*flHealthHeightOffset), vecPlayerScreenSpaceHealthPos ) == 0 )
  563. {
  564. ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceHealthPos);
  565. Vector vecPlayerScreenSpaceSizeA;
  566. ScreenTransform( pPlayer->GetAbsOrigin() + Vector(0,0,100), vecPlayerScreenSpaceSizeA );
  567. ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceSizeA);
  568. Vector vecPlayerScreenSpaceSizeB;
  569. ScreenTransform( pPlayer->GetAbsOrigin() + Vector(100,0,0), vecPlayerScreenSpaceSizeB );
  570. ConvertNormalizedScreenSpaceToPixelScreenSpace(vecPlayerScreenSpaceSizeB);
  571. Vector vecPlayerScreenSpaceSizeAPixels;
  572. VectorSubtract( vecPlayerScreenSpaceOrigin, vecPlayerScreenSpaceSizeA, vecPlayerScreenSpaceSizeAPixels );
  573. Vector vecPlayerScreenSpaceSizeBPixels;
  574. VectorSubtract( vecPlayerScreenSpaceOrigin, vecPlayerScreenSpaceSizeB, vecPlayerScreenSpaceSizeBPixels );
  575. float flPlayerScreenSpaceCoverage = Max( vecPlayerScreenSpaceSizeAPixels.Length(), vecPlayerScreenSpaceSizeBPixels.Length() );
  576. if ( flPlayerScreenSpaceCoverage < ScreenHeight() * 2 )
  577. {
  578. float flHealthWidth = flPlayerScreenSpaceCoverage;
  579. float flHealthHeight = flPlayerScreenSpaceCoverage;
  580. float flHealthPosX = vecPlayerScreenSpaceHealthPos.x - (flPlayerScreenSpaceCoverage * 0.5);
  581. float flHealthPosY = vecPlayerScreenSpaceHealthPos.y;
  582. pRenderContext->DrawScreenSpaceRectangle( pMatGlowHealthColor,
  583. flHealthPosX, flHealthPosY,
  584. flHealthWidth, flHealthHeight,
  585. 0, 0, 0, 0, 1, 1 );
  586. }
  587. }
  588. }
  589. }
  590. }
  591. }
  592. //clear out custom render settings
  593. pRenderContext->OverrideDepthEnable( false, false );
  594. render->SetBlend( flSavedBlend );
  595. pRenderContext->SetStencilState( stencilStateDisable );
  596. // If there aren't any objects to glow, don't do all this other stuff
  597. // this fixes a bug where if there are glow objects in the list, but none of them are glowing,
  598. // the whole screen blooms.
  599. if ( iNumGlowObjects <= 0 )
  600. return;
  601. //=============================================
  602. // Render the glow colors to _rt_FullFrameFB
  603. //=============================================
  604. {
  605. PIXEvent pixEvent( pRenderContext, "RenderGlowModels" );
  606. RenderGlowModels( pSetup, nSplitScreenSlot, pRenderContext, m_GlowObjectDefinitions );
  607. }
  608. ITexture *pRtFullFrame = materials->FindTexture( FULL_FRAME_TEXTURE, TEXTURE_GROUP_RENDER_TARGET );
  609. ITexture *pRtQuarterSize0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
  610. ITexture *pRtQuarterSize1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
  611. DownSampleAndBlurRT( pSetup, pRenderContext, flBloomScale, pRtFullFrame, pRtQuarterSize0, pRtQuarterSize1 );
  612. {
  613. //=======================================================================================================//
  614. // At this point, pRtQuarterSize0 is filled with the fully colored glow around everything as solid glowy //
  615. // blobs. Now we need to stencil out the original objects by only writing pixels that have no //
  616. // stencil bits set in the range we care about. //
  617. //=======================================================================================================//
  618. IMaterial *pMatHaloAddToScreen = materials->FindMaterial( "dev/halo_add_to_screen", TEXTURE_GROUP_OTHER, true );
  619. // Do not fade the glows out at all (weight = 1.0)
  620. IMaterialVar *pDimVar = pMatHaloAddToScreen->FindVar( "$C0_X", NULL );
  621. pDimVar->SetFloatValue( 1.0f );
  622. ShaderStencilState_t stencilState;
  623. stencilState.m_bEnable = true;
  624. stencilState.m_nWriteMask = 0x0; // We're not changing stencil
  625. //stencilState.m_nTestMask = 0x3;
  626. stencilState.m_nReferenceValue = 0x0;
  627. stencilState.m_CompareFunc = SHADER_STENCILFUNC_EQUAL;
  628. stencilState.m_PassOp = SHADER_STENCILOP_KEEP;
  629. stencilState.m_FailOp = SHADER_STENCILOP_KEEP;
  630. stencilState.m_ZFailOp = SHADER_STENCILOP_KEEP;
  631. pRenderContext->SetStencilState( stencilState );
  632. // Get viewport
  633. int nSrcWidth = pSetup->width;
  634. int nSrcHeight = pSetup->height;
  635. int nViewportX, nViewportY, nViewportWidth, nViewportHeight;
  636. pRenderContext->GetViewport( nViewportX, nViewportY, nViewportWidth, nViewportHeight );
  637. // Draw quad
  638. pRenderContext->DrawScreenSpaceRectangle( pMatHaloAddToScreen, 0, 0, nViewportWidth, nViewportHeight,
  639. 0.0f, -0.5f, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
  640. pRtQuarterSize1->GetActualWidth(),
  641. pRtQuarterSize1->GetActualHeight() );
  642. // Disable stencil
  643. pRenderContext->SetStencilState( stencilStateDisable );
  644. }
  645. #if defined( _X360 )
  646. pRenderContext->PopVertexShaderGPRAllocation();
  647. #endif
  648. }
  649. void CGlowObjectManager::GlowObjectDefinition_t::DrawModel()
  650. {
  651. RenderableInstance_t instance;
  652. instance.m_nAlpha = (uint8)( m_flGlowAlpha * 255.0f );
  653. m_pEntity->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
  654. C_BaseEntity *pAttachment = m_pEntity->FirstMoveChild();
  655. while ( pAttachment != NULL )
  656. {
  657. if ( pAttachment->ShouldDraw() )
  658. {
  659. pAttachment->DrawModel( STUDIO_RENDER | STUDIO_SKIP_FLEXES | STUDIO_DONOTMODIFYSTENCILSTATE | STUDIO_NOLIGHTING_OR_CUBEMAP | STUDIO_SKIP_DECALS, instance );
  660. }
  661. pAttachment = pAttachment->NextMovePeer();
  662. }
  663. }