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.

3001 lines
113 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================
  6. #include "cbase.h"
  7. #include "materialsystem/imaterialsystem.h"
  8. #include "materialsystem/itexture.h"
  9. #include "materialsystem/imaterialvar.h"
  10. #include "materialsystem/imaterialsystemhardwareconfig.h"
  11. #include "materialsystem/materialsystem_config.h"
  12. #include "tier1/callqueue.h"
  13. #include "colorcorrectionmgr.h"
  14. #include "view_scene.h"
  15. #include "c_world.h"
  16. #include "bitmap/tgawriter.h"
  17. #include "filesystem.h"
  18. #include "tier0/vprof.h"
  19. #include "proxyentity.h"
  20. //-----------------------------------------------------------------------------
  21. // Globals
  22. //-----------------------------------------------------------------------------
  23. // mapmaker controlled autoexposure
  24. bool g_bUseCustomAutoExposureMin = false;
  25. bool g_bUseCustomAutoExposureMax = false;
  26. bool g_bUseCustomBloomScale = false;
  27. float g_flCustomAutoExposureMin = 0;
  28. float g_flCustomAutoExposureMax = 0;
  29. float g_flCustomBloomScale = 0.0f;
  30. float g_flCustomBloomScaleMinimum = 0.0f;
  31. bool g_bFlashlightIsOn = false;
  32. // hdr parameters
  33. ConVar mat_bloomscale( "mat_bloomscale", "1" );
  34. ConVar mat_hdr_level( "mat_hdr_level", "2", FCVAR_ARCHIVE );
  35. ConVar mat_bloomamount_rate( "mat_bloomamount_rate", "0.05f", FCVAR_CHEAT );
  36. static ConVar debug_postproc( "mat_debug_postprocessing_effects", "0", FCVAR_NONE, "0 = off, 1 = show post-processing passes in quadrants of the screen, 2 = only apply post-processing to the centre of the screen" );
  37. static ConVar split_postproc( "mat_debug_process_halfscreen", "0", FCVAR_CHEAT );
  38. static ConVar mat_postprocessing_combine( "mat_postprocessing_combine", "1", FCVAR_NONE, "Combine bloom, software anti-aliasing and color correction into one post-processing pass" );
  39. static ConVar mat_dynamic_tonemapping( "mat_dynamic_tonemapping", "1", FCVAR_CHEAT );
  40. static ConVar mat_show_ab_hdr( "mat_show_ab_hdr", "0" );
  41. static ConVar mat_tonemapping_occlusion_use_stencil( "mat_tonemapping_occlusion_use_stencil", "0" );
  42. ConVar mat_debug_autoexposure("mat_debug_autoexposure","0", FCVAR_CHEAT);
  43. static ConVar mat_autoexposure_max( "mat_autoexposure_max", "2" );
  44. static ConVar mat_autoexposure_min( "mat_autoexposure_min", "0.5" );
  45. static ConVar mat_show_histogram( "mat_show_histogram", "0" );
  46. ConVar mat_hdr_tonemapscale( "mat_hdr_tonemapscale", "1.0", FCVAR_CHEAT );
  47. ConVar mat_hdr_uncapexposure( "mat_hdr_uncapexposure", "0", FCVAR_CHEAT );
  48. ConVar mat_force_bloom("mat_force_bloom","0", FCVAR_CHEAT);
  49. ConVar mat_disable_bloom("mat_disable_bloom","0");
  50. ConVar mat_debug_bloom("mat_debug_bloom","0", FCVAR_CHEAT);
  51. ConVar mat_accelerate_adjust_exposure_down( "mat_accelerate_adjust_exposure_down", "3.0", FCVAR_CHEAT );
  52. ConVar mat_hdr_manual_tonemap_rate( "mat_hdr_manual_tonemap_rate", "1.0" );
  53. // fudge factor to make non-hdr bloom more closely match hdr bloom. Because of auto-exposure, high
  54. // bloomscales don't blow out as much in hdr. this factor was derived by comparing images in a
  55. // reference scene.
  56. ConVar mat_non_hdr_bloom_scalefactor("mat_non_hdr_bloom_scalefactor",".3");
  57. // Apply addition scale to the final bloom scale
  58. static ConVar mat_bloom_scalefactor_scalar( "mat_bloom_scalefactor_scalar", "1.0" );
  59. //ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.75", FCVAR_CHEAT );
  60. //ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.80", FCVAR_CHEAT );
  61. //ConVar mat_exposure_center_region_x_flashlight( "mat_exposure_center_region_x_flashlight","0.33", FCVAR_CHEAT );
  62. //ConVar mat_exposure_center_region_y_flashlight( "mat_exposure_center_region_y_flashlight","0.33", FCVAR_CHEAT );
  63. ConVar mat_exposure_center_region_x( "mat_exposure_center_region_x","0.9", FCVAR_CHEAT );
  64. ConVar mat_exposure_center_region_y( "mat_exposure_center_region_y","0.85", FCVAR_CHEAT );
  65. ConVar mat_exposure_center_region_x_flashlight( "mat_exposure_center_region_x_flashlight","0.9", FCVAR_CHEAT );
  66. ConVar mat_exposure_center_region_y_flashlight( "mat_exposure_center_region_y_flashlight","0.85", FCVAR_CHEAT );
  67. ConVar mat_tonemap_algorithm( "mat_tonemap_algorithm", "1", FCVAR_CHEAT, "0 = Original Algorithm 1 = New Algorithm" );
  68. ConVar mat_tonemap_percent_target( "mat_tonemap_percent_target", "60.0", FCVAR_CHEAT );
  69. ConVar mat_tonemap_percent_bright_pixels( "mat_tonemap_percent_bright_pixels", "2.0", FCVAR_CHEAT );
  70. ConVar mat_tonemap_min_avglum( "mat_tonemap_min_avglum", "3.0", FCVAR_CHEAT );
  71. ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT );
  72. extern ConVar localplayer_visionflags;
  73. enum PostProcessingCondition {
  74. PPP_ALWAYS,
  75. PPP_IF_COND_VAR,
  76. PPP_IF_NOT_COND_VAR
  77. };
  78. struct PostProcessingPass {
  79. PostProcessingCondition ppp_test;
  80. ConVar const *cvar_to_test;
  81. char const *material_name; // terminate list with null
  82. char const *dest_rendering_target;
  83. char const *src_rendering_target; // can be null. needed for source scaling
  84. int xdest_scale,ydest_scale; // allows scaling down
  85. int xsrc_scale,ysrc_scale; // allows scaling down
  86. CMaterialReference m_mat_ref; // so we don't have to keep searching
  87. };
  88. #define PPP_PROCESS_PARTIAL_SRC(srcmatname,dest_rt_name,src_tname,scale) \
  89. {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,scale,scale}
  90. #define PPP_PROCESS_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,scale) \
  91. {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,scale,scale,1,1}
  92. #define PPP_PROCESS_PARTIAL_SRC_PARTIAL_DEST(srcmatname,dest_rt_name,src_tname,srcscale,destscale) \
  93. {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,destscale,destscale,srcscale,srcscale}
  94. #define PPP_END {PPP_ALWAYS,0,NULL,NULL,0,0,0,0,0}
  95. #define PPP_PROCESS(srcmatname,dest_rt_name) {PPP_ALWAYS,0,srcmatname,dest_rt_name,0,1,1,1,1}
  96. #define PPP_PROCESS_IF_CVAR(cvarptr,srcmatname,dest_rt_name) \
  97. {PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1}
  98. #define PPP_PROCESS_IF_NOT_CVAR(cvarptr,srcmatname,dest_rt_name) \
  99. {PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,0,1,1,1,1}
  100. #define PPP_PROCESS_IF_NOT_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_tname,dest_rt_name) \
  101. {PPP_IF_NOT_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_tname,1,1,1,1}
  102. #define PPP_PROCESS_IF_CVAR_SRCTEXTURE(cvarptr,srcmatname,src_txtrname,dest_rt_name) \
  103. {PPP_IF_COND_VAR,cvarptr,srcmatname,dest_rt_name,src_txtrname,1,1,1,1}
  104. #define PPP_PROCESS_SRCTEXTURE(srcmatname,src_tname,dest_rt_name) \
  105. {PPP_ALWAYS,0,srcmatname,dest_rt_name,src_tname,1,1,1,1}
  106. struct ClipBox
  107. {
  108. int m_minx, m_miny;
  109. int m_maxx, m_maxy;
  110. };
  111. static void DrawClippedScreenSpaceRectangle(
  112. IMaterial *pMaterial,
  113. int destx, int desty,
  114. int width, int height,
  115. float src_texture_x0, float src_texture_y0, // which texel you want to appear at
  116. // destx/y
  117. float src_texture_x1, float src_texture_y1, // which texel you want to appear at
  118. // destx+width-1, desty+height-1
  119. int src_texture_width, int src_texture_height, // needed for fixup
  120. ClipBox const *clipbox,
  121. void *pClientRenderable = NULL )
  122. {
  123. if (clipbox)
  124. {
  125. if ( (destx > clipbox->m_maxx ) || ( desty > clipbox->m_maxy ))
  126. return;
  127. if ( (destx + width - 1 < clipbox->m_minx ) || ( desty + height - 1 < clipbox->m_miny ))
  128. return;
  129. // left clip
  130. if ( destx < clipbox->m_minx )
  131. {
  132. src_texture_x0 = FLerp( src_texture_x0, src_texture_x1, destx, destx + width - 1, clipbox->m_minx );
  133. width -= ( clipbox->m_minx - destx );
  134. destx = clipbox->m_minx;
  135. }
  136. // top clip
  137. if ( desty < clipbox->m_miny )
  138. {
  139. src_texture_y0 = FLerp( src_texture_y0, src_texture_y1, desty, desty + height - 1, clipbox->m_miny );
  140. height -= ( clipbox->m_miny - desty );
  141. desty = clipbox->m_miny;
  142. }
  143. // right clip
  144. if ( destx + width - 1 > clipbox->m_maxx )
  145. {
  146. src_texture_x1 = FLerp( src_texture_x0, src_texture_x1, destx, destx + width - 1, clipbox->m_maxx );
  147. width = clipbox->m_maxx - destx;
  148. }
  149. // bottom clip
  150. if ( desty + height - 1 > clipbox->m_maxy )
  151. {
  152. src_texture_y1 = FLerp( src_texture_y0, src_texture_y1, desty, desty + height - 1, clipbox->m_maxy );
  153. height = clipbox->m_maxy - desty;
  154. }
  155. }
  156. CMatRenderContextPtr pRenderContext( materials );
  157. pRenderContext->DrawScreenSpaceRectangle( pMaterial, destx, desty, width, height, src_texture_x0,
  158. src_texture_y0, src_texture_x1, src_texture_y1,
  159. src_texture_width, src_texture_height, pClientRenderable );
  160. }
  161. void ApplyPostProcessingPasses(PostProcessingPass *pass_list, // table of effects to apply
  162. ClipBox const *clipbox=0, // clipping box for these effects
  163. ClipBox *dest_coords_out=0) // receives dest coords of last blit
  164. {
  165. CMatRenderContextPtr pRenderContext( materials );
  166. ITexture *pSaveRenderTarget = pRenderContext->GetRenderTarget();
  167. int pcount=0;
  168. if ( debug_postproc.GetInt() == 1 )
  169. {
  170. pRenderContext->SetRenderTarget(NULL);
  171. int dest_width,dest_height;
  172. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  173. pRenderContext->Viewport( 0, 0, dest_width, dest_height );
  174. pRenderContext->ClearColor3ub(255,0,0);
  175. // pRenderContext->ClearBuffers(true,true);
  176. }
  177. while(pass_list->material_name)
  178. {
  179. bool do_it=true;
  180. switch(pass_list->ppp_test)
  181. {
  182. case PPP_IF_COND_VAR:
  183. do_it=(pass_list->cvar_to_test)->GetBool();
  184. break;
  185. case PPP_IF_NOT_COND_VAR:
  186. do_it=! ((pass_list->cvar_to_test)->GetBool());
  187. break;
  188. }
  189. if ((pass_list->dest_rendering_target==0) && (debug_postproc.GetInt() == 1))
  190. do_it=0;
  191. if (do_it)
  192. {
  193. ClipBox const *cb=0;
  194. if (pass_list->dest_rendering_target==0)
  195. {
  196. cb=clipbox;
  197. }
  198. IMaterial *src_mat=pass_list->m_mat_ref;
  199. if (! src_mat)
  200. {
  201. src_mat=materials->FindMaterial(pass_list->material_name,
  202. TEXTURE_GROUP_OTHER,true);
  203. if (src_mat)
  204. {
  205. pass_list->m_mat_ref.Init(src_mat);
  206. }
  207. }
  208. if (pass_list->dest_rendering_target)
  209. {
  210. ITexture *dest_rt=materials->FindTexture(pass_list->dest_rendering_target,
  211. TEXTURE_GROUP_RENDER_TARGET );
  212. pRenderContext->SetRenderTarget( dest_rt);
  213. }
  214. else
  215. {
  216. pRenderContext->SetRenderTarget( NULL );
  217. }
  218. int dest_width,dest_height;
  219. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  220. pRenderContext->Viewport( 0, 0, dest_width, dest_height );
  221. dest_width/=pass_list->xdest_scale;
  222. dest_height/=pass_list->ydest_scale;
  223. if (pass_list->src_rendering_target)
  224. {
  225. ITexture *src_rt=materials->FindTexture(pass_list->src_rendering_target,
  226. TEXTURE_GROUP_RENDER_TARGET );
  227. int src_width=src_rt->GetActualWidth();
  228. int src_height=src_rt->GetActualHeight();
  229. int ssrc_width=(src_width-1)/pass_list->xsrc_scale;
  230. int ssrc_height=(src_height-1)/pass_list->ysrc_scale;
  231. DrawClippedScreenSpaceRectangle(
  232. src_mat,0,0,dest_width,dest_height,
  233. 0,0,ssrc_width,ssrc_height,src_width,src_height,cb);
  234. if ((pass_list->dest_rendering_target) && (debug_postproc.GetInt() == 1))
  235. {
  236. pRenderContext->SetRenderTarget(NULL);
  237. int row=pcount/2;
  238. int col=pcount %2;
  239. int vdest_width,vdest_height;
  240. pRenderContext->GetRenderTargetDimensions( vdest_width, vdest_height );
  241. pRenderContext->Viewport( 0, 0, vdest_width, vdest_height );
  242. pRenderContext->DrawScreenSpaceRectangle(
  243. src_mat,col*400,200+row*300,dest_width,dest_height,
  244. 0,0,ssrc_width,ssrc_height,src_width,src_height);
  245. }
  246. }
  247. else
  248. {
  249. // just draw the whole source
  250. if ((pass_list->dest_rendering_target==0) && split_postproc.GetInt())
  251. {
  252. DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width/2,dest_height,
  253. 0,0,.5,1,1,1,cb);
  254. }
  255. else
  256. {
  257. DrawClippedScreenSpaceRectangle(src_mat,0,0,dest_width,dest_height,
  258. 0,0,1,1,1,1,cb);
  259. }
  260. if ((pass_list->dest_rendering_target) && (debug_postproc.GetInt() == 1))
  261. {
  262. pRenderContext->SetRenderTarget(NULL);
  263. int row=pcount/4;
  264. int col=pcount %4;
  265. //int dest_width,dest_height;
  266. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  267. pRenderContext->Viewport( 0, 0, dest_width, dest_height );
  268. DrawClippedScreenSpaceRectangle(src_mat,10+col*220,10+row*220,
  269. 200,200,
  270. 0,0,1,1,1,1,cb);
  271. }
  272. }
  273. if (dest_coords_out)
  274. {
  275. dest_coords_out->m_minx=0;
  276. dest_coords_out->m_maxx=dest_width-1;
  277. dest_coords_out->m_miny=0;
  278. dest_coords_out->m_maxy=dest_height-1;
  279. }
  280. }
  281. pass_list++;
  282. pcount++;
  283. }
  284. pRenderContext->SetRenderTarget(pSaveRenderTarget);
  285. }
  286. PostProcessingPass HDRFinal_Float[] =
  287. {
  288. PPP_PROCESS_SRCTEXTURE( "dev/downsample", "_rt_FullFrameFB", "_rt_SmallFB0" ),
  289. PPP_PROCESS_SRCTEXTURE( "dev/blurfilterx", "_rt_SmallFB0", "_rt_SmallFB1" ),
  290. PPP_PROCESS_SRCTEXTURE( "dev/blurfiltery", "_rt_SmallFB1", "_rt_SmallFB0" ),
  291. PPP_PROCESS_SRCTEXTURE("dev/floattoscreen_combine","_rt_FullFrameFB",NULL),
  292. PPP_END
  293. };
  294. PostProcessingPass HDRFinal_Float_NoBloom[] =
  295. {
  296. PPP_PROCESS_SRCTEXTURE("dev/copyfullframefb", "_rt_FullFrameFB",NULL),
  297. PPP_END
  298. };
  299. PostProcessingPass HDRSimulate_NonHDR[] =
  300. {
  301. PPP_PROCESS("dev/copyfullframefb_vanilla",NULL),
  302. PPP_END
  303. };
  304. static void SetRenderTargetAndViewPort(ITexture *rt)
  305. {
  306. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  307. CMatRenderContextPtr pRenderContext( materials );
  308. pRenderContext->SetRenderTarget(rt);
  309. pRenderContext->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight());
  310. }
  311. #define FILTER_KERNEL_SLOP 20
  312. // Note carefully about the downsampling: the first downsampling samples from the full rendertarget
  313. // down to a temp. When doing this sampling, the texture source clamping will take care of the out
  314. // of bounds sampling done because of the filter kernels's width. However, on any of the subsequent
  315. // sampling operations, we will be sampling from a partially filled render target. So, texture
  316. // coordinate clamping cannot help us here. So, we need to always render a few more pixels to the
  317. // destination than we actually intend to, so as to replicate the border pixels so that garbage
  318. // pixels do not get sucked into the sampling. To deal with this, we always add FILTER_KERNEL_SLOP
  319. // to our widths/heights if there is room for them in the destination.
  320. static void DrawScreenSpaceRectangleWithSlop(
  321. ITexture *dest_rt,
  322. IMaterial *pMaterial,
  323. int destx, int desty,
  324. int width, int height,
  325. float src_texture_x0, float src_texture_y0, // which texel you want to appear at
  326. // destx/y
  327. float src_texture_x1, float src_texture_y1, // which texel you want to appear at
  328. // destx+width-1, desty+height-1
  329. int src_texture_width, int src_texture_height // needed for fixup
  330. )
  331. {
  332. // add slop
  333. int slopwidth = width + FILTER_KERNEL_SLOP; //min(dest_rt->GetActualWidth()-destx,width+FILTER_KERNEL_SLOP);
  334. int slopheight = height + FILTER_KERNEL_SLOP; //min(dest_rt->GetActualHeight()-desty,height+FILTER_KERNEL_SLOP);
  335. // adjust coordinates for slop
  336. src_texture_x1 = FLerp( src_texture_x0, src_texture_x1, destx, destx + width - 1, destx + slopwidth - 1 );
  337. src_texture_y1 = FLerp( src_texture_y0, src_texture_y1, desty, desty + height - 1, desty + slopheight - 1 );
  338. width = slopwidth;
  339. height = slopheight;
  340. CMatRenderContextPtr pRenderContext( materials );
  341. pRenderContext->DrawScreenSpaceRectangle( pMaterial, destx, desty, width, height,
  342. src_texture_x0, src_texture_y0,
  343. src_texture_x1, src_texture_y1,
  344. src_texture_width, src_texture_height );
  345. }
  346. enum Histogram_entry_state_t
  347. {
  348. HESTATE_INITIAL=0,
  349. HESTATE_FIRST_QUERY_IN_FLIGHT,
  350. HESTATE_QUERY_IN_FLIGHT,
  351. HESTATE_QUERY_DONE,
  352. };
  353. #define N_LUMINANCE_RANGES 31
  354. #define N_LUMINANCE_RANGES_NEW 17
  355. #define MAX_QUERIES_PER_FRAME 1
  356. class CHistogram_entry_t
  357. {
  358. public:
  359. Histogram_entry_state_t m_state;
  360. OcclusionQueryObjectHandle_t m_occ_handle; // the occlusion query handle
  361. int m_frame_queued; // when this query was last queued
  362. int m_npixels; // # of pixels this histogram represents
  363. int m_npixels_in_range;
  364. float m_min_lum, m_max_lum; // the luminance range this entry was queried with
  365. float m_minx, m_miny, m_maxx, m_maxy; // range is 0..1 in fractions of the screen
  366. bool ContainsValidData( void )
  367. {
  368. return ( m_state == HESTATE_QUERY_DONE ) || ( m_state == HESTATE_QUERY_IN_FLIGHT );
  369. }
  370. void IssueQuery( int frm_num );
  371. };
  372. void CHistogram_entry_t::IssueQuery( int frm_num )
  373. {
  374. CMatRenderContextPtr pRenderContext( materials );
  375. if ( !m_occ_handle )
  376. {
  377. m_occ_handle = pRenderContext->CreateOcclusionQueryObject();
  378. }
  379. int xl, yl, dest_width, dest_height;
  380. pRenderContext->GetViewport( xl, yl, dest_width, dest_height );
  381. // Find min and max gamma-space text range
  382. float flTestRangeMin = m_min_lum;
  383. float flTestRangeMax = ( m_max_lum == 1.0f ) ? 10000.0f : m_max_lum; // Count all pixels >1.0 as 1.0
  384. // First, set stencil bits where the colors match
  385. IMaterial *test_mat=materials->FindMaterial( "dev/lumcompare", TEXTURE_GROUP_OTHER, true );
  386. IMaterialVar *pMinVar = test_mat->FindVar( "$C0_X", NULL );
  387. pMinVar->SetFloatValue( flTestRangeMin );
  388. IMaterialVar *pMaxVar = test_mat->FindVar( "$C0_Y", NULL );
  389. pMaxVar->SetFloatValue( flTestRangeMax );
  390. int scrx_min = FLerp( xl, ( xl + dest_width - 1 ), 0, 1, m_minx );
  391. int scrx_max = FLerp( xl, ( xl + dest_width - 1 ), 0, 1, m_maxx );
  392. int scry_min = FLerp( yl, ( yl + dest_height - 1 ), 0, 1, m_miny );
  393. int scry_max = FLerp( yl, ( yl + dest_height - 1 ), 0, 1, m_maxy );
  394. float exposure_width_scale, exposure_height_scale;
  395. // now, shrink region of interest if the flashlight is on
  396. if ( g_bFlashlightIsOn )
  397. {
  398. exposure_width_scale = ( 0.5f * ( 1.0f - mat_exposure_center_region_x_flashlight.GetFloat() ) );
  399. exposure_height_scale = ( 0.5f * ( 1.0f - mat_exposure_center_region_y_flashlight.GetFloat() ) );
  400. }
  401. else
  402. {
  403. exposure_width_scale = ( 0.5f * ( 1.0f - mat_exposure_center_region_x.GetFloat() ) );
  404. exposure_height_scale = ( 0.5f * ( 1.0f - mat_exposure_center_region_y.GetFloat() ) );
  405. }
  406. int skip_edgex = ( 1 + scrx_max - scrx_min ) * exposure_width_scale;
  407. int skip_edgey = ( 1 + scry_max - scry_min ) * exposure_height_scale;
  408. // now, do luminance compare
  409. float tscale = 1.0;
  410. if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_FLOAT )
  411. {
  412. tscale = pRenderContext->GetToneMappingScaleLinear().x;
  413. }
  414. IMaterialVar *use_t_scale = test_mat->FindVar( "$C0_Z", NULL );
  415. use_t_scale->SetFloatValue( tscale );
  416. m_npixels = ( 1 + scrx_max - scrx_min ) * ( 1 + scry_max - scry_min );
  417. if ( mat_tonemapping_occlusion_use_stencil.GetInt() )
  418. {
  419. pRenderContext->SetStencilWriteMask( 1 );
  420. // AV - We don't need to clear stencil here because it's already been cleared at the beginning of the frame
  421. //pRenderContext->ClearStencilBufferRectangle( scrx_min, scry_min, scrx_max, scry_max, 0 );
  422. pRenderContext->SetStencilEnable( true );
  423. pRenderContext->SetStencilPassOperation( STENCILOPERATION_REPLACE );
  424. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS );
  425. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  426. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  427. pRenderContext->SetStencilReferenceValue( 1 );
  428. }
  429. else
  430. {
  431. pRenderContext->BeginOcclusionQueryDrawing( m_occ_handle );
  432. }
  433. scrx_min += skip_edgex;
  434. scry_min += skip_edgey;
  435. scrx_max -= skip_edgex;
  436. scry_max -= skip_edgey;
  437. pRenderContext->DrawScreenSpaceRectangle( test_mat,
  438. scrx_min, scry_min,
  439. 1 + scrx_max - scrx_min,
  440. 1 + scry_max - scry_min,
  441. scrx_min, scry_min,
  442. scrx_max, scry_max,
  443. dest_width, dest_height);
  444. if ( mat_tonemapping_occlusion_use_stencil.GetInt() )
  445. {
  446. // now, start counting how many pixels had their stencil bit set via an occlusion query
  447. pRenderContext->BeginOcclusionQueryDrawing( m_occ_handle );
  448. // now, issue an occlusion query using stencil as the mask
  449. pRenderContext->SetStencilEnable( true );
  450. pRenderContext->SetStencilTestMask( 1 );
  451. pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
  452. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  453. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  454. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  455. pRenderContext->SetStencilReferenceValue( 1 );
  456. IMaterial *stest_mat=materials->FindMaterial( "dev/no_pixel_write", TEXTURE_GROUP_OTHER, true);
  457. pRenderContext->DrawScreenSpaceRectangle( stest_mat,
  458. scrx_min, scry_min,
  459. 1 + scrx_max - scrx_min,
  460. 1 + scry_max - scry_min,
  461. scrx_min, scry_min,
  462. scrx_max, scry_max,
  463. dest_width, dest_height);
  464. pRenderContext->SetStencilEnable( false );
  465. }
  466. pRenderContext->EndOcclusionQueryDrawing( m_occ_handle );
  467. if ( m_state == HESTATE_INITIAL )
  468. m_state = HESTATE_FIRST_QUERY_IN_FLIGHT;
  469. else
  470. m_state = HESTATE_QUERY_IN_FLIGHT;
  471. m_frame_queued = frm_num;
  472. }
  473. #define HISTOGRAM_BAR_SIZE 200
  474. class CLuminanceHistogramSystem
  475. {
  476. CHistogram_entry_t CurHistogram[N_LUMINANCE_RANGES];
  477. int cur_query_frame;
  478. public:
  479. float FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTarget );
  480. float GetTargetTonemapScalar( bool bGetIdealTargetForDebugMode );
  481. void Update( void );
  482. void DisplayHistogram( void );
  483. void UpdateLuminanceRanges( void );
  484. CLuminanceHistogramSystem(void)
  485. {
  486. UpdateLuminanceRanges();
  487. }
  488. };
  489. void CLuminanceHistogramSystem::Update( void )
  490. {
  491. UpdateLuminanceRanges();
  492. // find which histogram entries should have something done this frame
  493. int n_queries_issued_this_frame=0;
  494. cur_query_frame++;
  495. int nNumRanges = N_LUMINANCE_RANGES;
  496. if ( mat_tonemap_algorithm.GetInt() == 1 )
  497. nNumRanges = N_LUMINANCE_RANGES_NEW;
  498. for ( int i=0; i<nNumRanges; i++ )
  499. {
  500. switch ( CurHistogram[i].m_state )
  501. {
  502. case HESTATE_INITIAL:
  503. if ( n_queries_issued_this_frame<MAX_QUERIES_PER_FRAME )
  504. {
  505. CurHistogram[i].IssueQuery(cur_query_frame);
  506. n_queries_issued_this_frame++;
  507. }
  508. break;
  509. case HESTATE_FIRST_QUERY_IN_FLIGHT:
  510. case HESTATE_QUERY_IN_FLIGHT:
  511. if ( cur_query_frame > CurHistogram[i].m_frame_queued + 2 )
  512. {
  513. CMatRenderContextPtr pRenderContext( materials );
  514. int np = pRenderContext->OcclusionQuery_GetNumPixelsRendered(
  515. CurHistogram[i].m_occ_handle );
  516. if ( np !=- 1 ) // -1=query not finished. wait until
  517. // next time
  518. {
  519. CurHistogram[i].m_npixels_in_range = np;
  520. // if (mat_debug_autoexposure.GetInt())
  521. // Warning("min=%f max=%f np = %d\n",CurHistogram[i].m_min_lum,CurHistogram[i].m_max_lum,np);
  522. CurHistogram[i].m_state = HESTATE_QUERY_DONE;
  523. }
  524. }
  525. break;
  526. }
  527. }
  528. // now, issue queries for the oldest finished queries we have
  529. while( n_queries_issued_this_frame < MAX_QUERIES_PER_FRAME )
  530. {
  531. nNumRanges = N_LUMINANCE_RANGES;
  532. if ( mat_tonemap_algorithm.GetInt() == 1 )
  533. nNumRanges = N_LUMINANCE_RANGES_NEW;
  534. int oldest_so_far =- 1;
  535. for( int i = 0;i < nNumRanges;i ++ )
  536. if ( ( CurHistogram[i].m_state == HESTATE_QUERY_DONE ) &&
  537. ( ( oldest_so_far ==- 1 ) ||
  538. ( CurHistogram[i].m_frame_queued <
  539. CurHistogram[oldest_so_far].m_frame_queued ) ) )
  540. oldest_so_far = i;
  541. if ( oldest_so_far ==- 1 ) // nothing to do
  542. break;
  543. CurHistogram[oldest_so_far].IssueQuery( cur_query_frame );
  544. n_queries_issued_this_frame ++;
  545. }
  546. }
  547. float CLuminanceHistogramSystem::FindLocationOfPercentBrightPixels( float flPercentBrightPixels, float flPercentTargetToSnapToIfInSameBin = -1.0f )
  548. {
  549. if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
  550. {
  551. int nTotalValidPixels = 0;
  552. for ( int i=0; i<N_LUMINANCE_RANGES_NEW-1; i++ )
  553. {
  554. if ( CurHistogram[i].ContainsValidData() )
  555. {
  556. nTotalValidPixels += CurHistogram[i].m_npixels_in_range;
  557. }
  558. }
  559. if ( nTotalValidPixels == 0 )
  560. {
  561. return -1.0f;
  562. }
  563. // Find where percent range border is
  564. float flTotalPercentRangeTested = 0.0f;
  565. float flTotalPercentPixelsTested = 0.0f;
  566. for ( int i=N_LUMINANCE_RANGES_NEW-2; i>=0; i-- ) // Start at the bright end
  567. {
  568. if ( !CurHistogram[i].ContainsValidData() )
  569. return -1.0f;
  570. float flPixelPercentNeeded = ( flPercentBrightPixels / 100.0f ) - flTotalPercentPixelsTested;
  571. float flThisBinPercentOfTotalPixels = float( CurHistogram[i].m_npixels_in_range ) / float( nTotalValidPixels );
  572. float flThisBinLuminanceRange = CurHistogram[i].m_max_lum - CurHistogram[i].m_min_lum;
  573. if ( flThisBinPercentOfTotalPixels >= flPixelPercentNeeded ) // We found the bin needed
  574. {
  575. if ( flPercentTargetToSnapToIfInSameBin >= 0.0f )
  576. {
  577. if ( ( CurHistogram[i].m_min_lum <= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) && ( CurHistogram[i].m_max_lum >= ( flPercentTargetToSnapToIfInSameBin / 100.0f ) ) )
  578. {
  579. // Sticky bin...We're in the same bin as the target so keep the tonemap scale where it is
  580. return ( flPercentTargetToSnapToIfInSameBin / 100.0f );
  581. }
  582. }
  583. float flPercentOfThesePixelsNeeded = flPixelPercentNeeded / flThisBinPercentOfTotalPixels;
  584. float flPercentLocationOfBorder = 1.0f - ( flTotalPercentRangeTested + ( flThisBinLuminanceRange * flPercentOfThesePixelsNeeded ) );
  585. flPercentLocationOfBorder = MAX( CurHistogram[i].m_min_lum, MIN( CurHistogram[i].m_max_lum, flPercentLocationOfBorder ) ); // Clamp to this bin just in case
  586. return flPercentLocationOfBorder;
  587. }
  588. flTotalPercentPixelsTested += flThisBinPercentOfTotalPixels;
  589. flTotalPercentRangeTested += flThisBinLuminanceRange;
  590. }
  591. return -1.0f;
  592. }
  593. else
  594. {
  595. // Don't know what to do for other algorithms yet
  596. return -1.0f;
  597. }
  598. }
  599. float CLuminanceHistogramSystem::GetTargetTonemapScalar( bool bGetIdealTargetForDebugMode = false )
  600. {
  601. if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm
  602. {
  603. float flPercentLocationOfTarget;
  604. if ( bGetIdealTargetForDebugMode == true)
  605. flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat() ); // Don't pass in the second arg so the scalar doesn't snap to a bin
  606. else
  607. flPercentLocationOfTarget = FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() );
  608. if ( flPercentLocationOfTarget < 0.0f ) // This is the return error code
  609. {
  610. flPercentLocationOfTarget = mat_tonemap_percent_target.GetFloat() / 100.0f; // Pretend we're at the target
  611. }
  612. // Make sure this is > 0.0f
  613. flPercentLocationOfTarget = MAX( 0.0001f, flPercentLocationOfTarget );
  614. // Compute target scalar
  615. float flTargetScalar = ( mat_tonemap_percent_target.GetFloat() / 100.0f ) / flPercentLocationOfTarget;
  616. // Compute secondary target scalar
  617. float flAverageLuminanceLocation = FindLocationOfPercentBrightPixels( 50.0f );
  618. if ( flAverageLuminanceLocation > 0.0f )
  619. {
  620. float flTargetScalar2 = ( mat_tonemap_min_avglum.GetFloat() / 100.0f ) / flAverageLuminanceLocation;
  621. // Only override it if it's trying to brighten the image more than the primary algorithm
  622. if ( flTargetScalar2 > flTargetScalar )
  623. {
  624. flTargetScalar = flTargetScalar2;
  625. }
  626. }
  627. // Apply this against last frames scalar
  628. CMatRenderContextPtr pRenderContext( materials );
  629. float flLastScale = pRenderContext->GetToneMappingScaleLinear().x;
  630. flTargetScalar *= flLastScale;
  631. flTargetScalar = MAX( 0.001f, flTargetScalar );
  632. return flTargetScalar;
  633. }
  634. else // Original tonemapping
  635. {
  636. float average_luminance = 0.5f;
  637. float total = 0;
  638. int total_pixels = 0;
  639. float scale_value = 1.0;
  640. if ( CurHistogram[N_LUMINANCE_RANGES-1].ContainsValidData() )
  641. {
  642. scale_value = CurHistogram[N_LUMINANCE_RANGES-1].m_npixels * ( 1.0f / CurHistogram[N_LUMINANCE_RANGES-1].m_npixels_in_range );
  643. if ( mat_debug_autoexposure.GetInt() )
  644. {
  645. engine->Con_NPrintf( 20, "Scale value = %f", scale_value );
  646. //Warning( "scale value=%f\n", scale_value );
  647. }
  648. }
  649. else
  650. average_luminance = 0.5;
  651. if ( !IsFinite( scale_value ) )
  652. scale_value = 1.0f;
  653. for ( int i=0; i<N_LUMINANCE_RANGES-1; i++ )
  654. {
  655. if ( CurHistogram[i].ContainsValidData() )
  656. {
  657. total += scale_value * CurHistogram[i].m_npixels_in_range * AVG( CurHistogram[i].m_min_lum, CurHistogram[i].m_max_lum );
  658. total_pixels += CurHistogram[i].m_npixels;
  659. }
  660. else
  661. average_luminance = 0.5; // always return 0.5 until we've queried a whole frame
  662. }
  663. if ( total_pixels > 0 )
  664. average_luminance = total * ( 1.0 / total_pixels );
  665. else
  666. average_luminance = 0.5;
  667. // Make sure this is > 0.0f
  668. average_luminance = MAX( 0.0001f, average_luminance );
  669. // Compute target scalar
  670. float flTargetScalar = 0.005 / average_luminance;
  671. return flTargetScalar;
  672. }
  673. }
  674. static float GetCurrentBloomScale( void )
  675. {
  676. // Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
  677. float flCurrentBloomScale = 1.0f;
  678. if ( g_bUseCustomBloomScale )
  679. {
  680. flCurrentBloomScale = g_flCustomBloomScale;
  681. }
  682. else
  683. {
  684. flCurrentBloomScale = mat_bloomscale.GetFloat();
  685. }
  686. return flCurrentBloomScale;
  687. }
  688. static void GetExposureRange( float *flAutoExposureMin, float *flAutoExposureMax )
  689. {
  690. // Get min
  691. if ( ( g_bUseCustomAutoExposureMin ) && ( g_flCustomAutoExposureMin > 0.0f ) )
  692. {
  693. *flAutoExposureMin = g_flCustomAutoExposureMin;
  694. }
  695. else
  696. {
  697. *flAutoExposureMin = mat_autoexposure_min.GetFloat();
  698. }
  699. // Get max
  700. if ( ( g_bUseCustomAutoExposureMax ) && ( g_flCustomAutoExposureMax > 0.0f ) )
  701. {
  702. *flAutoExposureMax = g_flCustomAutoExposureMax;
  703. }
  704. else
  705. {
  706. *flAutoExposureMax = mat_autoexposure_max.GetFloat();
  707. }
  708. // Override
  709. if ( mat_hdr_uncapexposure.GetInt() )
  710. {
  711. *flAutoExposureMax = 20.0f;
  712. *flAutoExposureMin = 0.0f;
  713. }
  714. // Make sure min <= max
  715. if ( *flAutoExposureMin > *flAutoExposureMax )
  716. {
  717. *flAutoExposureMax = *flAutoExposureMin;
  718. }
  719. }
  720. void CLuminanceHistogramSystem::UpdateLuminanceRanges( void )
  721. {
  722. // Only update if our mode changed
  723. static int s_nCurrentBucketAlgorithm = -1;
  724. if ( s_nCurrentBucketAlgorithm == mat_tonemap_algorithm.GetInt() )
  725. return;
  726. s_nCurrentBucketAlgorithm = mat_tonemap_algorithm.GetInt();
  727. //==================================================================//
  728. // Force fallback to original tone mapping algorithm for these mods //
  729. //==================================================================//
  730. static bool s_bFirstTime = true;
  731. if ( engine == NULL )
  732. {
  733. // Force this code to get hit again so we can change algorithm based on the client
  734. s_nCurrentBucketAlgorithm = -1;
  735. }
  736. else if ( s_bFirstTime == true )
  737. {
  738. s_bFirstTime = false;
  739. // This seems like a bad idea but it's fine for now
  740. const char *sModsForOriginalAlgorithm[] = { "dod", "cstrike", "lostcoast", "hl1" };
  741. for ( int i=0; i<3; i++ )
  742. {
  743. if ( strlen( engine->GetGameDirectory() ) >= strlen( sModsForOriginalAlgorithm[i] ) )
  744. {
  745. if ( stricmp( &( engine->GetGameDirectory()[strlen( engine->GetGameDirectory() ) - strlen( sModsForOriginalAlgorithm[i] )] ), sModsForOriginalAlgorithm[i] ) == 0 )
  746. {
  747. mat_tonemap_algorithm.SetValue( 0 ); // Original algorithm
  748. s_nCurrentBucketAlgorithm = mat_tonemap_algorithm.GetInt();
  749. break;
  750. }
  751. }
  752. }
  753. }
  754. int nNumRanges = N_LUMINANCE_RANGES;
  755. if ( mat_tonemap_algorithm.GetInt() == 1 )
  756. nNumRanges = N_LUMINANCE_RANGES_NEW;
  757. cur_query_frame=0;
  758. for ( int bucket = 0; bucket < nNumRanges; bucket ++ )
  759. {
  760. int idx = bucket;
  761. CHistogram_entry_t & e = CurHistogram[idx];
  762. e.m_state = HESTATE_INITIAL;
  763. e.m_minx = 0;
  764. e.m_maxx = 1;
  765. e.m_miny = 0;
  766. e.m_maxy = 1;
  767. if ( bucket != nNumRanges-1 ) // Last bucket is special
  768. {
  769. if ( mat_tonemap_algorithm.GetInt() == 0 ) // Original algorithm
  770. {
  771. // Use a logarithmic ramp for high range in the low range
  772. e.m_min_lum = - 0.01 + exp( FLerp( log( .01 ), log( .01 + 1 ), 0, nNumRanges - 1, bucket ) );
  773. e.m_max_lum = - 0.01 + exp( FLerp( log( .01 ), log( .01 + 1 ), 0, nNumRanges - 1, bucket + 1 ) );
  774. }
  775. else
  776. {
  777. // Use even distribution
  778. e.m_min_lum = float( bucket ) / float( nNumRanges - 1 );
  779. e.m_max_lum = float( bucket + 1 ) / float( nNumRanges - 1 );
  780. // Use a distribution with slightly more bins in the low range
  781. e.m_min_lum = e.m_min_lum > 0.0f ? powf( e.m_min_lum, 1.5f ) : e.m_min_lum;
  782. e.m_max_lum = e.m_max_lum > 0.0f ? powf( e.m_max_lum, 1.5f ) : e.m_max_lum;
  783. }
  784. }
  785. else
  786. {
  787. // The last bucket is used as a test to determine the return range for occlusion
  788. // queries to use as a scale factor. some boards (nvidia) have their occlusion
  789. // query return values larger when using AA.
  790. e.m_min_lum = 0;
  791. e.m_max_lum = 100000.0;
  792. }
  793. //Warning( "Bucket %d: min/max %f / %f ", bucket, e.m_min_lum, e.m_max_lum );
  794. }
  795. }
  796. void CLuminanceHistogramSystem::DisplayHistogram( void )
  797. {
  798. bool bDrawTextThisFrame = true;
  799. if ( IsX360() )
  800. {
  801. static float s_flLastTimeUpdate = 0.0f;
  802. if ( int( gpGlobals->curtime ) - int( s_flLastTimeUpdate ) >= 2 )
  803. {
  804. s_flLastTimeUpdate = gpGlobals->curtime;
  805. bDrawTextThisFrame = true;
  806. }
  807. else
  808. {
  809. bDrawTextThisFrame = false;
  810. }
  811. }
  812. CMatRenderContextPtr pRenderContext( materials );
  813. pRenderContext->PushRenderTargetAndViewport();
  814. int nNumRanges = N_LUMINANCE_RANGES-1;
  815. if ( mat_tonemap_algorithm.GetInt() == 1 )
  816. nNumRanges = N_LUMINANCE_RANGES_NEW-1;
  817. int nMaxValidPixels = 0;
  818. int nTotalValidPixels = 0;
  819. int nTotalGraphPixelsWide = 0;
  820. for ( int l=0; l<nNumRanges; l++ )
  821. {
  822. CHistogram_entry_t &e = CurHistogram[l];
  823. if ( e.ContainsValidData() )
  824. {
  825. nTotalValidPixels += e.m_npixels_in_range;
  826. if ( e.m_npixels_in_range > nMaxValidPixels )
  827. {
  828. nMaxValidPixels = e.m_npixels_in_range;
  829. }
  830. }
  831. int width = MAX( 1, 500 * ( e.m_max_lum - e.m_min_lum ) );
  832. nTotalGraphPixelsWide += width + 2;
  833. }
  834. int xl, yl, dest_width, dest_height;
  835. pRenderContext->GetViewport( xl, yl, dest_width, dest_height );
  836. if ( bDrawTextThisFrame == true )
  837. {
  838. engine->Con_NPrintf( 17, "(All values in linear space)" );
  839. engine->Con_NPrintf( 21, "AvgLum @ %4.2f%% mat_tonemap_min_avglum = %4.2f%% Using %d pixels of %d pixels on screen (%3d%%)",
  840. MAX( 0.0f, FindLocationOfPercentBrightPixels( 50.0f ) ) * 100.0f, mat_tonemap_min_avglum.GetFloat(),
  841. nTotalValidPixels, ( dest_width * dest_height ), int( float( nTotalValidPixels ) * 100.0f / float( dest_width * dest_height ) ) );
  842. engine->Con_NPrintf( 23, "BloomScale = %4.2f mat_hdr_manual_tonemap_rate = %4.2f mat_accelerate_adjust_exposure_down = %4.2f",
  843. GetCurrentBloomScale(), mat_hdr_manual_tonemap_rate.GetFloat(), mat_accelerate_adjust_exposure_down.GetFloat() );
  844. }
  845. if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm only
  846. {
  847. float vTotalPixelsAndHigher[N_LUMINANCE_RANGES];
  848. for ( int i=0; i<nNumRanges; i++ )
  849. {
  850. vTotalPixelsAndHigher[i] = CurHistogram[nNumRanges-1-i].m_npixels_in_range;
  851. if ( i > 0 )
  852. {
  853. vTotalPixelsAndHigher[i] += vTotalPixelsAndHigher[i-1];
  854. }
  855. }
  856. /* // This code works when N_LUMINANCE_RANGES_NEW = 11
  857. if ( bDrawTextThisFrame == true )
  858. {
  859. engine->Con_NPrintf( 17, "%04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f ",
  860. 100.0f * float( vTotalPixelsAndHigher[9] ) / float( nTotalValidPixels ),
  861. 100.0f * float( vTotalPixelsAndHigher[8] ) / float( nTotalValidPixels ),
  862. 100.0f * float( vTotalPixelsAndHigher[7] ) / float( nTotalValidPixels ),
  863. 100.0f * float( vTotalPixelsAndHigher[6] ) / float( nTotalValidPixels ),
  864. 100.0f * float( vTotalPixelsAndHigher[5] ) / float( nTotalValidPixels ),
  865. 100.0f * float( vTotalPixelsAndHigher[4] ) / float( nTotalValidPixels ),
  866. 100.0f * float( vTotalPixelsAndHigher[3] ) / float( nTotalValidPixels ),
  867. 100.0f * float( vTotalPixelsAndHigher[2] ) / float( nTotalValidPixels ),
  868. 100.0f * float( vTotalPixelsAndHigher[1] ) / float( nTotalValidPixels ),
  869. 100.0f * float( vTotalPixelsAndHigher[0] ) / float( nTotalValidPixels ) );
  870. engine->Con_NPrintf( 15, "%04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f %04.2f ",
  871. 100.0f * float( CurHistogram[nNumRanges-1-9].m_npixels_in_range ) / float( nTotalValidPixels ),
  872. 100.0f * float( CurHistogram[nNumRanges-1-8].m_npixels_in_range ) / float( nTotalValidPixels ),
  873. 100.0f * float( CurHistogram[nNumRanges-1-7].m_npixels_in_range ) / float( nTotalValidPixels ),
  874. 100.0f * float( CurHistogram[nNumRanges-1-6].m_npixels_in_range ) / float( nTotalValidPixels ),
  875. 100.0f * float( CurHistogram[nNumRanges-1-5].m_npixels_in_range ) / float( nTotalValidPixels ),
  876. 100.0f * float( CurHistogram[nNumRanges-1-4].m_npixels_in_range ) / float( nTotalValidPixels ),
  877. 100.0f * float( CurHistogram[nNumRanges-1-3].m_npixels_in_range ) / float( nTotalValidPixels ),
  878. 100.0f * float( CurHistogram[nNumRanges-1-2].m_npixels_in_range ) / float( nTotalValidPixels ),
  879. 100.0f * float( CurHistogram[nNumRanges-1-1].m_npixels_in_range ) / float( nTotalValidPixels ),
  880. 100.0f * float( CurHistogram[nNumRanges-1-0].m_npixels_in_range ) / float( nTotalValidPixels ) );
  881. }
  882. //*/
  883. }
  884. else
  885. {
  886. if ( bDrawTextThisFrame == true )
  887. {
  888. engine->Con_NPrintf( 17, "" );
  889. engine->Con_NPrintf( 15, "" );
  890. }
  891. }
  892. int xpStart = dest_width - nTotalGraphPixelsWide - 10;
  893. if ( IsX360() )
  894. {
  895. xpStart -= 50;
  896. }
  897. int xp = xpStart;
  898. for ( int l=0; l<nNumRanges; l++ )
  899. {
  900. int np = 0;
  901. CHistogram_entry_t &e = CurHistogram[l];
  902. if ( e.ContainsValidData() )
  903. np += e.m_npixels_in_range;
  904. int width = MAX( 1, 500 * ( e.m_max_lum - e.m_min_lum ) );
  905. //Warning( "Bucket %d: min/max %f / %f. m_npixels_in_range=%d m_npixels=%d\n", l, e.m_min_lum, e.m_max_lum, e.m_npixels_in_range, e.m_npixels );
  906. if ( np )
  907. {
  908. int height = MAX( 1, MIN( HISTOGRAM_BAR_SIZE, ( (float)np / (float)nMaxValidPixels ) * HISTOGRAM_BAR_SIZE ) );
  909. pRenderContext->ClearColor3ub( 255, 0, 0 );
  910. pRenderContext->Viewport( xp, 4 + HISTOGRAM_BAR_SIZE - height, width, height );
  911. pRenderContext->ClearBuffers( true, true );
  912. }
  913. else
  914. {
  915. int height = 1;
  916. pRenderContext->ClearColor3ub( 0, 0, 255 );
  917. pRenderContext->Viewport( xp, 4 + HISTOGRAM_BAR_SIZE - height, width, height );
  918. pRenderContext->ClearBuffers( true, true );
  919. }
  920. xp += width + 2;
  921. }
  922. if ( mat_tonemap_algorithm.GetInt() == 1 ) // New algorithm only
  923. {
  924. float flYellowTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * mat_tonemap_percent_target.GetFloat() / 100.0f ) );
  925. float flYellowAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * mat_tonemap_min_avglum.GetFloat() / 100.0f ) );
  926. float flTargetPixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() ) ) );
  927. float flAveragePixelStart = ( xpStart + ( float( nTotalGraphPixelsWide ) * FindLocationOfPercentBrightPixels( 50.0f ) ) );
  928. // Draw target yellow border bar
  929. int height = HISTOGRAM_BAR_SIZE;
  930. // Green is current percent target location
  931. pRenderContext->Viewport( flYellowTargetPixelStart, 4 + HISTOGRAM_BAR_SIZE - height, 4, height );
  932. pRenderContext->ClearColor3ub( 200, 200, 0 );
  933. pRenderContext->ClearBuffers( true, true );
  934. pRenderContext->Viewport( flTargetPixelStart, 4 + HISTOGRAM_BAR_SIZE - height, 4, height );
  935. pRenderContext->ClearColor3ub( 0, 255, 0 );
  936. pRenderContext->ClearBuffers( true, true );
  937. // Blue is average luminance location
  938. pRenderContext->Viewport( flYellowAveragePixelStart, 4 + HISTOGRAM_BAR_SIZE - height, 4, height );
  939. pRenderContext->ClearColor3ub( 200, 200, 0 );
  940. pRenderContext->ClearBuffers( true, true );
  941. pRenderContext->Viewport( flAveragePixelStart, 4 + HISTOGRAM_BAR_SIZE - height, 4, height );
  942. pRenderContext->ClearColor3ub( 0, 200, 200 );
  943. pRenderContext->ClearBuffers( true, true );
  944. }
  945. // Show actual tonemap value
  946. if ( 1 )
  947. {
  948. float flAutoExposureMin;
  949. float flAutoExposureMax;
  950. GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
  951. float flBarWidth = 600.0f;
  952. float flBarStart = dest_width - flBarWidth - 10.0f;
  953. if ( IsX360() )
  954. {
  955. flBarStart -= 50;
  956. }
  957. pRenderContext->Viewport( flBarStart, 4 + HISTOGRAM_BAR_SIZE - 4 + 75, flBarWidth, 4 );
  958. pRenderContext->ClearColor3ub( 200, 200, 200 );
  959. pRenderContext->ClearBuffers( true, true );
  960. pRenderContext->Viewport( flBarStart, 4 + HISTOGRAM_BAR_SIZE - 4 + 75 + 1, flBarWidth, 2 );
  961. pRenderContext->ClearColor3ub( 0, 0, 0 );
  962. pRenderContext->ClearBuffers( true, true );
  963. pRenderContext->Viewport( flBarStart + ( flBarWidth * ( ( pRenderContext->GetToneMappingScaleLinear().x - flAutoExposureMin ) / ( flAutoExposureMax - flAutoExposureMin ) ) ),
  964. 4 + HISTOGRAM_BAR_SIZE - 4 + 75 - 6, 4, 16 );
  965. pRenderContext->ClearColor3ub( 255, 0, 0 );
  966. pRenderContext->ClearBuffers( true, true );
  967. if ( bDrawTextThisFrame == true )
  968. {
  969. if ( IsX360() )
  970. engine->Con_NPrintf( 26, "Min: %.2f Max: %.2f", flAutoExposureMin, flAutoExposureMax );
  971. else
  972. engine->Con_NPrintf( 26, "%.2f %.2f %.2f", flAutoExposureMin, ( flAutoExposureMax + flAutoExposureMin ) / 2.0f, flAutoExposureMax );
  973. }
  974. }
  975. // Last bar doesn't clear properly so draw an extra pixel
  976. pRenderContext->Viewport( 0, 0, 1, 1 );
  977. pRenderContext->ClearColor3ub( 0, 0, 0 );
  978. pRenderContext->ClearBuffers( true, true );
  979. pRenderContext->PopRenderTargetAndViewport();
  980. }
  981. static CLuminanceHistogramSystem g_HDR_HistogramSystem;
  982. static float s_MovingAverageToneMapScale[10] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
  983. static int s_nInAverage = 0;
  984. void ResetToneMapping(float value)
  985. {
  986. CMatRenderContextPtr pRenderContext( materials );
  987. s_nInAverage = 0;
  988. pRenderContext->ResetToneMappingScale(value);
  989. }
  990. static ConVar mat_force_tonemap_scale( "mat_force_tonemap_scale", "0.0", FCVAR_CHEAT );
  991. static void SetToneMapScale(IMatRenderContext *pRenderContext, float newvalue, float minvalue, float maxvalue)
  992. {
  993. Assert( IsFinite( newvalue ) );
  994. if( !IsFinite( newvalue ) )
  995. return;
  996. float flForcedTonemapScale = mat_force_tonemap_scale.GetFloat();
  997. if( mat_fullbright.GetInt() == 1 )
  998. {
  999. flForcedTonemapScale = 1.0f;
  1000. }
  1001. if( flForcedTonemapScale > 0.0f )
  1002. {
  1003. mat_hdr_tonemapscale.SetValue( flForcedTonemapScale );
  1004. pRenderContext->ResetToneMappingScale( flForcedTonemapScale );
  1005. return;
  1006. }
  1007. mat_hdr_tonemapscale.SetValue( newvalue );
  1008. pRenderContext->SetGoalToneMappingScale( newvalue );
  1009. if ( s_nInAverage < ARRAYSIZE( s_MovingAverageToneMapScale ))
  1010. {
  1011. s_MovingAverageToneMapScale[s_nInAverage ++]= newvalue;
  1012. }
  1013. else
  1014. {
  1015. // scroll, losing oldest
  1016. for( int i = 0;i < ARRAYSIZE( s_MovingAverageToneMapScale ) - 1;i ++ )
  1017. s_MovingAverageToneMapScale[i]= s_MovingAverageToneMapScale[i + 1];
  1018. s_MovingAverageToneMapScale[ARRAYSIZE( s_MovingAverageToneMapScale ) - 1]= newvalue;
  1019. }
  1020. // now, use the average of the last tonemap calculations as our goal scale
  1021. if ( s_nInAverage == ARRAYSIZE( s_MovingAverageToneMapScale )) // got full buffer yet?
  1022. {
  1023. float avg = 0.;
  1024. float sumweights = 0;
  1025. int sample_pt = ARRAYSIZE( s_MovingAverageToneMapScale ) / 2;
  1026. for( int i = 0;i < ARRAYSIZE( s_MovingAverageToneMapScale );i ++ )
  1027. {
  1028. float weight = abs( i - sample_pt ) * ( 1.0 / ( ARRAYSIZE( s_MovingAverageToneMapScale ) / 2 ));
  1029. sumweights += weight;
  1030. avg += weight * s_MovingAverageToneMapScale[i];
  1031. }
  1032. avg *= ( 1.0 / sumweights );
  1033. avg = MIN( maxvalue, MAX( minvalue, avg ));
  1034. pRenderContext->SetGoalToneMappingScale( avg );
  1035. mat_hdr_tonemapscale.SetValue( avg );
  1036. }
  1037. }
  1038. //=====================================================================================================================
  1039. // Engine_Post material proxy ============================================================================================
  1040. //=====================================================================================================================
  1041. static ConVar mat_software_aa_strength( "mat_software_aa_strength", "-1.0", FCVAR_ARCHIVE, "Software AA - perform a software anti-aliasing post-process (an alternative/supplement to MSAA). This value sets the strength of the effect: (0.0 - off), (1.0 - full)" );
  1042. static ConVar mat_software_aa_quality( "mat_software_aa_quality", "0", FCVAR_ARCHIVE, "Software AA quality mode: (0 - 5-tap filter), (1 - 9-tap filter)" );
  1043. static ConVar mat_software_aa_edge_threshold( "mat_software_aa_edge_threshold", "1.0", FCVAR_ARCHIVE, "Software AA - adjusts the sensitivity of the software AA shader's edge detection (default 1.0 - a lower value will soften more edges, a higher value will soften fewer)" );
  1044. static ConVar mat_software_aa_blur_one_pixel_lines( "mat_software_aa_blur_one_pixel_lines", "0.5", FCVAR_ARCHIVE, "How much software AA should blur one-pixel thick lines: (0.0 - none), (1.0 - lots)" );
  1045. static ConVar mat_software_aa_tap_offset( "mat_software_aa_tap_offset", "1.0", FCVAR_ARCHIVE, "Software AA - adjusts the displacement of the taps used by the software AA shader (default 1.0 - a lower value will make the image sharper, higher will make it blurrier)" );
  1046. static ConVar mat_software_aa_debug( "mat_software_aa_debug", "0", FCVAR_NONE, "Software AA debug mode: (0 - off), (1 - show number of 'unlike' samples: 0->black, 1->red, 2->green, 3->blue), (2 - show anti-alias blend strength), (3 - show averaged 'unlike' colour)" );
  1047. static ConVar mat_software_aa_strength_vgui( "mat_software_aa_strength_vgui", "-1.0", FCVAR_ARCHIVE, "Same as mat_software_aa_strength, but forced to this value when called by the post vgui AA pass." );
  1048. class CEnginePostMaterialProxy : public CEntityMaterialProxy
  1049. {
  1050. public:
  1051. CEnginePostMaterialProxy();
  1052. virtual ~CEnginePostMaterialProxy();
  1053. virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  1054. virtual void OnBind( C_BaseEntity *pEntity );
  1055. virtual IMaterial *GetMaterial();
  1056. private:
  1057. IMaterialVar *m_pMaterialParam_AAValues;
  1058. IMaterialVar *m_pMaterialParam_AAValues2;
  1059. IMaterialVar *m_pMaterialParam_BloomEnable;
  1060. IMaterialVar *m_pMaterialParam_BloomUVTransform;
  1061. IMaterialVar *m_pMaterialParam_ColCorrectEnable;
  1062. IMaterialVar *m_pMaterialParam_ColCorrectNumLookups;
  1063. IMaterialVar *m_pMaterialParam_ColCorrectDefaultWeight;
  1064. IMaterialVar *m_pMaterialParam_ColCorrectLookupWeights;
  1065. public:
  1066. static IMaterial * SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize,
  1067. bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength );
  1068. static void SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength );
  1069. static void SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D destTexSize );
  1070. private:
  1071. static float s_vBloomAAValues[4];
  1072. static float s_vBloomAAValues2[4];
  1073. static float s_vBloomUVTransform[4];
  1074. static int s_PostBloomEnable;
  1075. };
  1076. float CEnginePostMaterialProxy::s_vBloomAAValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  1077. float CEnginePostMaterialProxy::s_vBloomAAValues2[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  1078. float CEnginePostMaterialProxy::s_vBloomUVTransform[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  1079. int CEnginePostMaterialProxy::s_PostBloomEnable = 1;
  1080. CEnginePostMaterialProxy::CEnginePostMaterialProxy()
  1081. {
  1082. m_pMaterialParam_AAValues = NULL;
  1083. m_pMaterialParam_AAValues2 = NULL;
  1084. m_pMaterialParam_BloomUVTransform = NULL;
  1085. m_pMaterialParam_BloomEnable = NULL;
  1086. m_pMaterialParam_ColCorrectEnable = NULL;
  1087. m_pMaterialParam_ColCorrectNumLookups = NULL;
  1088. m_pMaterialParam_ColCorrectDefaultWeight = NULL;
  1089. m_pMaterialParam_ColCorrectLookupWeights = NULL;
  1090. }
  1091. CEnginePostMaterialProxy::~CEnginePostMaterialProxy()
  1092. {
  1093. // Do nothing
  1094. }
  1095. bool CEnginePostMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  1096. {
  1097. bool bFoundVar = false;
  1098. m_pMaterialParam_AAValues = pMaterial->FindVar( "$AAInternal1", &bFoundVar, false );
  1099. m_pMaterialParam_AAValues2 = pMaterial->FindVar( "$AAInternal3", &bFoundVar, false );
  1100. m_pMaterialParam_BloomUVTransform = pMaterial->FindVar( "$AAInternal2", &bFoundVar, false );
  1101. m_pMaterialParam_BloomEnable = pMaterial->FindVar( "$bloomEnable", &bFoundVar, false );
  1102. m_pMaterialParam_ColCorrectEnable = pMaterial->FindVar( "$colCorrectEnable", &bFoundVar, false );
  1103. m_pMaterialParam_ColCorrectNumLookups = pMaterial->FindVar( "$colCorrect_NumLookups", &bFoundVar, false );
  1104. m_pMaterialParam_ColCorrectDefaultWeight = pMaterial->FindVar( "$colCorrect_DefaultWeight", &bFoundVar, false );
  1105. m_pMaterialParam_ColCorrectLookupWeights = pMaterial->FindVar( "$colCorrect_LookupWeights", &bFoundVar, false );
  1106. return true;
  1107. }
  1108. void CEnginePostMaterialProxy::OnBind( C_BaseEntity *pEnt )
  1109. {
  1110. if ( m_pMaterialParam_AAValues )
  1111. m_pMaterialParam_AAValues->SetVecValue( s_vBloomAAValues, 4 );
  1112. if ( m_pMaterialParam_AAValues2 )
  1113. m_pMaterialParam_AAValues2->SetVecValue( s_vBloomAAValues2, 4 );
  1114. if ( m_pMaterialParam_BloomUVTransform )
  1115. m_pMaterialParam_BloomUVTransform->SetVecValue( s_vBloomUVTransform, 4 );
  1116. if ( m_pMaterialParam_BloomEnable )
  1117. m_pMaterialParam_BloomEnable->SetIntValue( s_PostBloomEnable );
  1118. }
  1119. IMaterial *CEnginePostMaterialProxy::GetMaterial()
  1120. {
  1121. if ( m_pMaterialParam_AAValues == NULL)
  1122. return NULL;
  1123. return m_pMaterialParam_AAValues->GetOwningMaterial();
  1124. }
  1125. void CEnginePostMaterialProxy::SetupEnginePostMaterialAA( bool bPerformSoftwareAA, float flAAStrength )
  1126. {
  1127. if ( bPerformSoftwareAA )
  1128. {
  1129. // Pass ConVars to the material by proxy
  1130. // - the strength of the AA effect (from 0 to 1)
  1131. // - how much to allow 1-pixel lines to be blurred (from 0 to 1)
  1132. // - pick one of the two quality modes (5-tap or 9-tap filter)
  1133. // - optionally enable one of several debug modes (via dynamic combos)
  1134. // NOTE: this order matches pixel shader constants in Engine_Post_ps2x.fxc
  1135. s_vBloomAAValues[0] = flAAStrength;
  1136. s_vBloomAAValues[1] = 1.0f - mat_software_aa_blur_one_pixel_lines.GetFloat();
  1137. s_vBloomAAValues[2] = mat_software_aa_quality.GetInt();
  1138. s_vBloomAAValues[3] = mat_software_aa_debug.GetInt();
  1139. s_vBloomAAValues2[0] = mat_software_aa_edge_threshold.GetFloat();
  1140. s_vBloomAAValues2[1] = mat_software_aa_tap_offset.GetFloat();
  1141. //s_vBloomAAValues2[2] = unused;
  1142. //s_vBloomAAValues2[3] = unused;
  1143. }
  1144. else
  1145. {
  1146. // Zero-strength AA is interpreted as "AA disabled"
  1147. s_vBloomAAValues[0] = 0.0f;
  1148. }
  1149. }
  1150. void CEnginePostMaterialProxy::SetupEnginePostMaterialTextureTransform( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, Vector2D fbSize )
  1151. {
  1152. // Engine_Post uses a UV transform (from (quarter-res) bloom texture coords ('1')
  1153. // to (full-res) framebuffer texture coords ('2')).
  1154. //
  1155. // We compute the UV transform as an offset and a scale, using the texture coordinates
  1156. // of the top-left corner of the screen to compute the offset and the coordinate
  1157. // change from the top-left to the bottom-right of the quad to compute the scale.
  1158. // Take texel coordinates (start = top-left, end = bottom-right):
  1159. Vector2D texelStart1 = Vector2D( fullViewportBloomUVs.x, fullViewportBloomUVs.y );
  1160. Vector2D texelStart2 = Vector2D( fullViewportFBUVs.x, fullViewportFBUVs.y );
  1161. Vector2D texelEnd1 = Vector2D( fullViewportBloomUVs.z, fullViewportBloomUVs.w );
  1162. Vector2D texelEnd2 = Vector2D( fullViewportFBUVs.z, fullViewportFBUVs.w );
  1163. // ...and transform to UV coordinates:
  1164. Vector2D texRes1 = fbSize / 4;
  1165. Vector2D texRes2 = fbSize;
  1166. Vector2D uvStart1 = ( texelStart1 + Vector2D(0.5,0.5) ) / texRes1;
  1167. Vector2D uvStart2 = ( texelStart2 + Vector2D(0.5,0.5) ) / texRes2;
  1168. Vector2D dUV1 = ( texelEnd1 - texelStart1 ) / texRes1;
  1169. Vector2D dUV2 = ( texelEnd2 - texelStart2 ) / texRes2;
  1170. // We scale about the rect's top-left pixel centre (not the origin) in UV-space:
  1171. // uv' = ((uv - uvStart1)*uvScale + uvStart1) + uvOffset
  1172. // = uvScale*uv + uvOffset + uvStart1*(1 - uvScale)
  1173. Vector2D uvOffset = uvStart2 - uvStart1;
  1174. Vector2D uvScale = dUV2 / dUV1;
  1175. uvOffset = uvOffset + uvStart1*(Vector2D(1,1) - uvScale);
  1176. s_vBloomUVTransform[0] = uvOffset.x;
  1177. s_vBloomUVTransform[1] = uvOffset.y;
  1178. s_vBloomUVTransform[2] = uvScale.x;
  1179. s_vBloomUVTransform[3] = uvScale.y;
  1180. }
  1181. IMaterial * CEnginePostMaterialProxy::SetupEnginePostMaterial( const Vector4D & fullViewportBloomUVs, const Vector4D & fullViewportFBUVs, const Vector2D & destTexSize,
  1182. bool bPerformSoftwareAA, bool bPerformBloom, bool bPerformColCorrect, float flAAStrength )
  1183. {
  1184. // Shouldn't get here if none of the effects are enabled
  1185. Assert( bPerformSoftwareAA || bPerformBloom || bPerformColCorrect );
  1186. s_PostBloomEnable = bPerformBloom ? 1 : 0;
  1187. SetupEnginePostMaterialAA( bPerformSoftwareAA, flAAStrength );
  1188. if ( bPerformSoftwareAA || bPerformColCorrect )
  1189. {
  1190. SetupEnginePostMaterialTextureTransform( fullViewportBloomUVs, fullViewportFBUVs, destTexSize );
  1191. return materials->FindMaterial( "dev/engine_post", TEXTURE_GROUP_OTHER, true);
  1192. }
  1193. else
  1194. {
  1195. // Just use the old bloomadd material (which uses additive blending, unlike engine_post)
  1196. // NOTE: this path is what gets used for DX8 (which cannot enable AA or col-correction)
  1197. return materials->FindMaterial( "dev/bloomadd", TEXTURE_GROUP_OTHER, true);
  1198. }
  1199. }
  1200. EXPOSE_INTERFACE( CEnginePostMaterialProxy, IMaterialProxy, "engine_post" IMATERIAL_PROXY_INTERFACE_VERSION );
  1201. static void DrawBloomDebugBoxes( IMatRenderContext *pRenderContext )
  1202. {
  1203. // draw inset rects which should have a centered bloom
  1204. pRenderContext->SetRenderTarget(NULL);
  1205. int dest_width, dest_height;
  1206. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  1207. // full screen clear
  1208. pRenderContext->Viewport( 0, 0, dest_width, dest_height );
  1209. pRenderContext->ClearColor3ub( 0, 0, 0 );
  1210. pRenderContext->ClearBuffers( true, true );
  1211. // inset for screensafe
  1212. int inset = 64;
  1213. int size = 32;
  1214. // centerish, translating
  1215. static int wx = 0;
  1216. wx = ( wx + 1 ) & 63;
  1217. pRenderContext->Viewport( dest_width / 2 + wx, dest_height / 2, size, size );
  1218. pRenderContext->ClearColor3ub( 255, 255, 255 );
  1219. pRenderContext->ClearBuffers( true, true );
  1220. // upper left
  1221. pRenderContext->Viewport( inset, inset, size, size );
  1222. pRenderContext->ClearBuffers( true, true );
  1223. // upper right
  1224. pRenderContext->Viewport( dest_width - inset - size, inset, size, size );
  1225. pRenderContext->ClearBuffers( true, true );
  1226. // lower right
  1227. pRenderContext->Viewport( dest_width - inset - size, dest_height - inset - size, size, size );
  1228. pRenderContext->ClearBuffers( true, true );
  1229. // lower left
  1230. pRenderContext->Viewport( inset, dest_height - inset - size, size, size );
  1231. pRenderContext->ClearBuffers( true, true );
  1232. // restore
  1233. pRenderContext->Viewport( 0, 0, dest_width, dest_height );
  1234. }
  1235. static float GetBloomAmount( void )
  1236. {
  1237. // return bloom amount ( 0.0 if disabled or otherwise turned off )
  1238. if ( engine->GetDXSupportLevel() < 80 )
  1239. return 0.0;
  1240. HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
  1241. bool bBloomEnabled = (mat_hdr_level.GetInt() >= 1);
  1242. if ( !engine->MapHasHDRLighting() )
  1243. bBloomEnabled = false;
  1244. if ( mat_force_bloom.GetInt() )
  1245. bBloomEnabled = true;
  1246. if ( mat_disable_bloom.GetInt() )
  1247. bBloomEnabled = false;
  1248. if ( building_cubemaps.GetBool() )
  1249. bBloomEnabled = false;
  1250. if ( mat_fullbright.GetInt() == 1 )
  1251. {
  1252. bBloomEnabled = false;
  1253. }
  1254. if( !g_pMaterialSystemHardwareConfig->CanDoSRGBReadFromRTs() && g_pMaterialSystemHardwareConfig->FakeSRGBWrite() )
  1255. {
  1256. bBloomEnabled = false;
  1257. }
  1258. float flBloomAmount=0.0;
  1259. if (bBloomEnabled)
  1260. {
  1261. static float currentBloomAmount = 1.0f;
  1262. float rate = mat_bloomamount_rate.GetFloat();
  1263. // Use the appropriate bloom scale settings. Mapmakers's overrides the convar settings.
  1264. currentBloomAmount = GetCurrentBloomScale() * rate + ( 1.0f - rate ) * currentBloomAmount;
  1265. flBloomAmount = currentBloomAmount;
  1266. }
  1267. if ( hdrType == HDR_TYPE_NONE )
  1268. {
  1269. flBloomAmount *= mat_non_hdr_bloom_scalefactor.GetFloat();
  1270. }
  1271. flBloomAmount *= mat_bloom_scalefactor_scalar.GetFloat();
  1272. return flBloomAmount;
  1273. }
  1274. // Control for dumping render targets to files for debugging
  1275. static ConVar mat_dump_rts( "mat_dump_rts", "0" );
  1276. static int s_nRTIndex = 0;
  1277. bool g_bDumpRenderTargets = false;
  1278. // Dump a rendertarget to a TGA. Useful for looking at intermediate render target results.
  1279. void DumpTGAofRenderTarget( const int width, const int height, const char *pFilename )
  1280. {
  1281. // Ensure that mat_queue_mode is zero
  1282. static ConVarRef mat_queue_mode( "mat_queue_mode" );
  1283. if ( mat_queue_mode.GetInt() != 0 )
  1284. {
  1285. DevMsg( "Error: mat_queue_mode must be 0 to dump debug rendertargets\n" );
  1286. mat_dump_rts.SetValue( 0 ); // Just report this error once and stop trying to dump images
  1287. return;
  1288. }
  1289. CMatRenderContextPtr pRenderContext( materials );
  1290. // Get the data from the render target and save to disk bitmap bits
  1291. unsigned char *pImage = ( unsigned char * )malloc( width * 4 * height );
  1292. // Get Bits from the material system
  1293. pRenderContext->ReadPixels( 0, 0, width, height, pImage, IMAGE_FORMAT_RGBA8888 );
  1294. // allocate a buffer to write the tga into
  1295. int iMaxTGASize = 1024 + (width * height * 4);
  1296. void *pTGA = malloc( iMaxTGASize );
  1297. CUtlBuffer buffer( pTGA, iMaxTGASize );
  1298. if( !TGAWriter::WriteToBuffer( pImage, buffer, width, height, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 ) )
  1299. {
  1300. Error( "Couldn't write bitmap data snapshot.\n" );
  1301. }
  1302. free( pImage );
  1303. // async write to disk (this will take ownership of the memory)
  1304. char szPathedFileName[_MAX_PATH];
  1305. Q_snprintf( szPathedFileName, sizeof(szPathedFileName), "//MOD/%d_%s_%s.tga", s_nRTIndex++, pFilename, IsOSX() ? "OSX" : "PC" );
  1306. FileHandle_t fileTGA = filesystem->Open( szPathedFileName, "wb" );
  1307. filesystem->Write( buffer.Base(), buffer.TellPut(), fileTGA );
  1308. filesystem->Close( fileTGA );
  1309. free( pTGA );
  1310. }
  1311. static bool s_bScreenEffectTextureIsUpdated = false;
  1312. static void Generate8BitBloomTexture( IMatRenderContext *pRenderContext, float flBloomScale,
  1313. int x, int y, int w, int h )
  1314. {
  1315. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1316. pRenderContext->PushRenderTargetAndViewport();
  1317. ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
  1318. int nSrcWidth = pSrc->GetActualWidth();
  1319. int nSrcHeight = pSrc->GetActualHeight(); //,dest_height;
  1320. // Counter-Strike: Source uses a different downsample algorithm than other games
  1321. #ifdef CSTRIKE_DLL
  1322. IMaterial *downsample_mat = materials->FindMaterial( "dev/downsample_non_hdr_cstrike", TEXTURE_GROUP_OTHER, true);
  1323. #else
  1324. IMaterial *downsample_mat = materials->FindMaterial( "dev/downsample_non_hdr", TEXTURE_GROUP_OTHER, true);
  1325. #endif
  1326. IMaterial *xblur_mat = materials->FindMaterial( "dev/blurfilterx_nohdr", TEXTURE_GROUP_OTHER, true );
  1327. IMaterial *yblur_mat = materials->FindMaterial( "dev/blurfiltery_nohdr", TEXTURE_GROUP_OTHER, true );
  1328. ITexture *dest_rt0 = materials->FindTexture( "_rt_SmallFB0", TEXTURE_GROUP_RENDER_TARGET );
  1329. ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
  1330. // *Everything* in here relies on the small RTs being exactly 1/4 the full FB res
  1331. Assert( dest_rt0->GetActualWidth() == pSrc->GetActualWidth() / 4 );
  1332. Assert( dest_rt0->GetActualHeight() == pSrc->GetActualHeight() / 4 );
  1333. Assert( dest_rt1->GetActualWidth() == pSrc->GetActualWidth() / 4 );
  1334. Assert( dest_rt1->GetActualHeight() == pSrc->GetActualHeight() / 4 );
  1335. // Downsample fb to rt0
  1336. SetRenderTargetAndViewPort( dest_rt0 );
  1337. // note the -2's below. Thats because we are downsampling on each axis and the shader
  1338. // accesses pixels on both sides of the source coord
  1339. pRenderContext->DrawScreenSpaceRectangle( downsample_mat, 0, 0, nSrcWidth/4, nSrcHeight/4,
  1340. 0, 0, nSrcWidth-2, nSrcHeight-2,
  1341. nSrcWidth, nSrcHeight );
  1342. if ( IsX360() )
  1343. {
  1344. pRenderContext->CopyRenderTargetToTextureEx( dest_rt0, 0, NULL, NULL );
  1345. }
  1346. else if ( g_bDumpRenderTargets )
  1347. {
  1348. DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "QuarterSizeFB" );
  1349. }
  1350. // Gaussian blur x rt0 to rt1
  1351. SetRenderTargetAndViewPort( dest_rt1 );
  1352. pRenderContext->DrawScreenSpaceRectangle( xblur_mat, 0, 0, nSrcWidth/4, nSrcHeight/4,
  1353. 0, 0, nSrcWidth/4-1, nSrcHeight/4-1,
  1354. nSrcWidth/4, nSrcHeight/4 );
  1355. if ( IsX360() )
  1356. {
  1357. pRenderContext->CopyRenderTargetToTextureEx( dest_rt1, 0, NULL, NULL );
  1358. }
  1359. else if ( g_bDumpRenderTargets )
  1360. {
  1361. DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "BlurX" );
  1362. }
  1363. // Gaussian blur y rt1 to rt0
  1364. SetRenderTargetAndViewPort( dest_rt0 );
  1365. IMaterialVar *pBloomAmountVar = yblur_mat->FindVar( "$bloomamount", NULL );
  1366. pBloomAmountVar->SetFloatValue( flBloomScale );
  1367. pRenderContext->DrawScreenSpaceRectangle( yblur_mat, 0, 0, nSrcWidth / 4, nSrcHeight / 4,
  1368. 0, 0, nSrcWidth / 4 - 1, nSrcHeight / 4 - 1,
  1369. nSrcWidth / 4, nSrcHeight / 4 );
  1370. if ( IsX360() )
  1371. {
  1372. pRenderContext->CopyRenderTargetToTextureEx( dest_rt0, 0, NULL, NULL );
  1373. }
  1374. else if ( g_bDumpRenderTargets )
  1375. {
  1376. DumpTGAofRenderTarget( nSrcWidth/4, nSrcHeight/4, "BlurYAndBloom" );
  1377. }
  1378. pRenderContext->PopRenderTargetAndViewport();
  1379. }
  1380. static void DoPreBloomTonemapping( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight, float flAutoExposureMin, float flAutoExposureMax )
  1381. {
  1382. // Update HDR histogram before bloom
  1383. if ( mat_dynamic_tonemapping.GetInt() || mat_show_histogram.GetInt() )
  1384. {
  1385. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1386. if ( s_bScreenEffectTextureIsUpdated == false )
  1387. {
  1388. // FIXME: nX/nY/nWidth/nHeight are used here, but the equivalent parameters are ignored in Generate8BitBloomTexture
  1389. UpdateScreenEffectTexture( 0, nX, nY, nWidth, nHeight, true );
  1390. s_bScreenEffectTextureIsUpdated = true;
  1391. }
  1392. g_HDR_HistogramSystem.Update();
  1393. if ( mat_dynamic_tonemapping.GetInt() || mat_show_histogram.GetInt() )
  1394. {
  1395. float flTargetScalar = g_HDR_HistogramSystem.GetTargetTonemapScalar();
  1396. float flTargetScalarClamped = MAX( flAutoExposureMin, MIN( flAutoExposureMax, flTargetScalar ) );
  1397. flTargetScalarClamped = MAX( 0.001f, flTargetScalarClamped ); // Don't let this go to 0!
  1398. if ( mat_dynamic_tonemapping.GetInt() )
  1399. {
  1400. SetToneMapScale( pRenderContext, flTargetScalarClamped, flAutoExposureMin, flAutoExposureMax );
  1401. }
  1402. if ( mat_debug_autoexposure.GetInt() || mat_show_histogram.GetInt() )
  1403. {
  1404. bool bDrawTextThisFrame = true;
  1405. if ( IsX360() )
  1406. {
  1407. static float s_flLastTimeUpdate = 0.0f;
  1408. if ( int( gpGlobals->curtime ) - int( s_flLastTimeUpdate ) >= 2 )
  1409. {
  1410. s_flLastTimeUpdate = gpGlobals->curtime;
  1411. bDrawTextThisFrame = true;
  1412. }
  1413. else
  1414. {
  1415. bDrawTextThisFrame = false;
  1416. }
  1417. }
  1418. if ( bDrawTextThisFrame == true )
  1419. {
  1420. if ( mat_tonemap_algorithm.GetInt() == 0 )
  1421. {
  1422. engine->Con_NPrintf( 19, "(Original algorithm) Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f Actual: %4.2f",
  1423. flTargetScalar, flAutoExposureMin, flAutoExposureMax, mat_hdr_tonemapscale.GetFloat(), pRenderContext->GetToneMappingScaleLinear().x );
  1424. }
  1425. else
  1426. {
  1427. engine->Con_NPrintf( 19, "%.2f%% of pixels above %d%% target @ %4.2f%% Target Scalar = %4.2f Min/Max( %4.2f, %4.2f ) Final Scalar: %4.2f Actual: %4.2f",
  1428. mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetInt(),
  1429. ( g_HDR_HistogramSystem.FindLocationOfPercentBrightPixels( mat_tonemap_percent_bright_pixels.GetFloat(), mat_tonemap_percent_target.GetFloat() ) * 100.0f ),
  1430. g_HDR_HistogramSystem.GetTargetTonemapScalar( true ), flAutoExposureMin, flAutoExposureMax, mat_hdr_tonemapscale.GetFloat(), pRenderContext->GetToneMappingScaleLinear().x );
  1431. }
  1432. }
  1433. }
  1434. }
  1435. }
  1436. }
  1437. static void DoPostBloomTonemapping( IMatRenderContext *pRenderContext, int nX, int nY, int nWidth, int nHeight, float flAutoExposureMin, float flAutoExposureMax )
  1438. {
  1439. if ( mat_show_histogram.GetInt() && ( engine->GetDXSupportLevel() >= 90 ) )
  1440. {
  1441. g_HDR_HistogramSystem.DisplayHistogram();
  1442. }
  1443. }
  1444. static void CenterScaleQuadUVs( Vector4D & quadUVs, const Vector2D & uvScale )
  1445. {
  1446. Vector2D uvMid = 0.5f*Vector2D( ( quadUVs.z + quadUVs.x ), ( quadUVs.w + quadUVs.y ) );
  1447. Vector2D uvRange= 0.5f*Vector2D( ( quadUVs.z - quadUVs.x ), ( quadUVs.w - quadUVs.y ) );
  1448. quadUVs.x = uvMid.x - uvScale.x*uvRange.x;
  1449. quadUVs.y = uvMid.y - uvScale.y*uvRange.y;
  1450. quadUVs.z = uvMid.x + uvScale.x*uvRange.x;
  1451. quadUVs.w = uvMid.y + uvScale.y*uvRange.y;
  1452. }
  1453. typedef struct SPyroSide
  1454. {
  1455. float m_vCornerPos[ 2 ][ 2 ];
  1456. float m_flIntensity;
  1457. float m_flIntensityLimit;
  1458. float m_flRate;
  1459. bool m_bHorizontal;
  1460. bool m_bIncreasing;
  1461. bool m_bAlive;
  1462. } TPyroSide;
  1463. #define MAX_PYRO_SIDES 50
  1464. #define NUM_PYRO_SEGMENTS 8
  1465. #define MIN_PYRO_SIDE_LENGTH 0.5f
  1466. #define MAX_PYRO_SIDE_LENGTH 0.90f
  1467. #define MIN_PYRO_SIDE_WIDTH 0.25f
  1468. #define MAX_PYRO_SIDE_WIDTH 0.95f
  1469. static TPyroSide PyroSides[ MAX_PYRO_SIDES ];
  1470. ConVar pyro_vignette( "pyro_vignette", "2", FCVAR_ARCHIVE );
  1471. ConVar pyro_vignette_distortion( "pyro_vignette_distortion", "1", FCVAR_ARCHIVE );
  1472. ConVar pyro_min_intensity( "pyro_min_intensity", "0.1", FCVAR_ARCHIVE );
  1473. ConVar pyro_max_intensity( "pyro_max_intensity", "0.35", FCVAR_ARCHIVE );
  1474. ConVar pyro_min_rate( "pyro_min_rate", "0.05", FCVAR_ARCHIVE );
  1475. ConVar pyro_max_rate( "pyro_max_rate", "0.2", FCVAR_ARCHIVE );
  1476. ConVar pyro_min_side_length( "pyro_min_side_length", "0.3", FCVAR_ARCHIVE );
  1477. ConVar pyro_max_side_length( "pyro_max_side_length", "0.55", FCVAR_ARCHIVE );
  1478. ConVar pyro_min_side_width( "pyro_min_side_width", "0.65", FCVAR_ARCHIVE );
  1479. ConVar pyro_max_side_width( "pyro_max_side_width", "0.95", FCVAR_ARCHIVE );
  1480. static void CreatePyroSide( int nSide, Vector2D &vMaxSize )
  1481. {
  1482. int nFound = 0;
  1483. for( ; nFound < MAX_PYRO_SIDES; nFound++ )
  1484. {
  1485. if ( !PyroSides[ nFound ].m_bAlive )
  1486. {
  1487. break;
  1488. }
  1489. }
  1490. if ( nFound >= MAX_PYRO_SIDES )
  1491. {
  1492. return;
  1493. }
  1494. TPyroSide *pSide = &PyroSides[ nFound ];
  1495. pSide->m_flIntensity = 0.0f;
  1496. pSide->m_flIntensityLimit = RandomFloat( pyro_min_intensity.GetFloat(), pyro_max_intensity.GetFloat() );
  1497. pSide->m_flRate = RandomFloat( pyro_min_rate.GetFloat(), pyro_max_rate.GetFloat() );
  1498. pSide->m_bIncreasing = true;
  1499. pSide->m_bHorizontal = ( ( nSide >> 1 ) & 1 ) == 0;
  1500. pSide->m_bAlive = true;
  1501. // float flWidth = RandomFloat( MIN_PYRO_SIDE_WIDTH, MAX_PYRO_SIDE_WIDTH ) * 2.0f;
  1502. // float flLength = RandomFloat( MIN_PYRO_SIDE_LENGTH, MAX_PYRO_SIDE_LENGTH );
  1503. float flWidth = RandomFloat( pyro_min_side_width.GetFloat(), pyro_max_side_width.GetFloat() ) * 2.0f;
  1504. float flLength = RandomFloat( pyro_min_side_length.GetFloat(), pyro_max_side_length.GetFloat() );
  1505. switch( nSide )
  1506. {
  1507. case 0:
  1508. {
  1509. pSide->m_vCornerPos[ 0 ][ 0 ] = -1.0f;
  1510. pSide->m_vCornerPos[ 0 ][ 1 ] = 1.0f;
  1511. pSide->m_vCornerPos[ 1 ][ 0 ] = -1.0f + flWidth;
  1512. pSide->m_vCornerPos[ 1 ][ 1 ] = 1.0f - ( flLength * vMaxSize.y );
  1513. }
  1514. break;
  1515. case 1:
  1516. {
  1517. pSide->m_vCornerPos[ 0 ][ 0 ] = 1.0f;
  1518. pSide->m_vCornerPos[ 0 ][ 1 ] = 1.0f;
  1519. pSide->m_vCornerPos[ 1 ][ 0 ] = 1.0f - flWidth;
  1520. pSide->m_vCornerPos[ 1 ][ 1 ] = 1.0f - ( flLength * vMaxSize.y );
  1521. }
  1522. break;
  1523. case 2:
  1524. {
  1525. pSide->m_vCornerPos[ 0 ][ 0 ] = 1.0f;
  1526. pSide->m_vCornerPos[ 0 ][ 1 ] = 1.0f;
  1527. pSide->m_vCornerPos[ 1 ][ 0 ] = 1.0f - ( flLength * vMaxSize.x );
  1528. pSide->m_vCornerPos[ 1 ][ 1 ] = 1.0f - flWidth;
  1529. }
  1530. break;
  1531. case 3:
  1532. {
  1533. pSide->m_vCornerPos[ 0 ][ 0 ] = 1.0f;
  1534. pSide->m_vCornerPos[ 0 ][ 1 ] = -1.0f;
  1535. pSide->m_vCornerPos[ 1 ][ 0 ] = 1.0f - ( flLength * vMaxSize.x );
  1536. pSide->m_vCornerPos[ 1 ][ 1 ] = -1.0f + flWidth;
  1537. }
  1538. break;
  1539. case 4:
  1540. {
  1541. pSide->m_vCornerPos[ 0 ][ 0 ] = 1.0f;
  1542. pSide->m_vCornerPos[ 0 ][ 1 ] = -1.0f;
  1543. pSide->m_vCornerPos[ 1 ][ 0 ] = 1.0f - flWidth;
  1544. pSide->m_vCornerPos[ 1 ][ 1 ] = -1.0f + ( flLength * vMaxSize.y );
  1545. }
  1546. break;
  1547. case 5:
  1548. {
  1549. pSide->m_vCornerPos[ 0 ][ 0 ] = -1.0f;
  1550. pSide->m_vCornerPos[ 0 ][ 1 ] = -1.0f;
  1551. pSide->m_vCornerPos[ 1 ][ 0 ] = -1.0f + flWidth;
  1552. pSide->m_vCornerPos[ 1 ][ 1 ] = -1.0f + ( flLength * vMaxSize.y );
  1553. }
  1554. break;
  1555. case 6:
  1556. {
  1557. pSide->m_vCornerPos[ 0 ][ 0 ] = -1.0f;
  1558. pSide->m_vCornerPos[ 0 ][ 1 ] = -1.0f;
  1559. pSide->m_vCornerPos[ 1 ][ 0 ] = -1.0f + ( flLength * vMaxSize.x );
  1560. pSide->m_vCornerPos[ 1 ][ 1 ] = -1.0f + flWidth;
  1561. }
  1562. break;
  1563. case 7:
  1564. {
  1565. pSide->m_vCornerPos[ 0 ][ 0 ] = -1.0f;
  1566. pSide->m_vCornerPos[ 0 ][ 1 ] = 1.0f;
  1567. pSide->m_vCornerPos[ 1 ][ 0 ] = -1.0f + ( flLength * vMaxSize.x );
  1568. pSide->m_vCornerPos[ 1 ][ 1 ] = 1.0f - flWidth;
  1569. }
  1570. break;
  1571. }
  1572. }
  1573. static float PryoVignetteSTHorizontal[ 6 ][ 2 ] =
  1574. {
  1575. { 0.0f, 0.0f },
  1576. { 0.0f, 1.0f },
  1577. { 1.0f, 1.0f },
  1578. { 1.0f, 1.0f },
  1579. { 0.0f, 0.0f },
  1580. { 1.0f, 0.0f }
  1581. };
  1582. static float PryoVignetteSTVertical[ 6 ][ 2 ] =
  1583. {
  1584. { 0.0f, 0.0f },
  1585. { 1.0f, 0.0f },
  1586. { 1.0f, 1.0f },
  1587. { 1.0f, 1.0f },
  1588. { 0.0f, 0.0f },
  1589. { 0.0f, 1.0f }
  1590. };
  1591. static int PryoSideIndexes[ 6 ][ 2 ] =
  1592. {
  1593. { 0, 0 },
  1594. { 0, 1 },
  1595. { 1, 1 },
  1596. { 1, 1 },
  1597. { 0, 0 },
  1598. { 1, 0 }
  1599. };
  1600. static void DrawPyroVignette( int nDestX, int nDestY, int nWidth, int nHeight, // Rect to draw into in screen space
  1601. float flSrcTextureX0, float flSrcTextureY0, // which texel you want to appear at destx/y
  1602. float flSrcTextureX1, float flSrcTextureY1, // which texel you want to appear at destx+width-1, desty+height-1
  1603. void *pClientRenderable )
  1604. {
  1605. static bool bInit = false;
  1606. static int nNextSide = 0;
  1607. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1608. IMaterial *pVignetteBorder = materials->FindMaterial( "dev/pyro_vignette_border", TEXTURE_GROUP_OTHER, true );
  1609. IMaterial *pMaterial = materials->FindMaterial( "dev/pyro_vignette", TEXTURE_GROUP_OTHER, true );
  1610. ITexture *pRenderTarget = materials->FindTexture( "_rt_ResolvedFullFrameDepth", TEXTURE_GROUP_RENDER_TARGET );
  1611. pRenderContext->PushRenderTargetAndViewport( pRenderTarget );
  1612. pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
  1613. pRenderContext->ClearBuffers( true, false );
  1614. int nScreenWidth, nScreenHeight;
  1615. pRenderContext->GetRenderTargetDimensions( nScreenWidth, nScreenHeight );
  1616. pRenderContext->DrawScreenSpaceRectangle( pVignetteBorder, 0, 0, nScreenWidth, nScreenHeight, 0, 0, nScreenWidth - 1, nScreenHeight - 1, nScreenWidth, nScreenHeight, pClientRenderable );
  1617. if ( pyro_vignette.GetInt() > 1 )
  1618. {
  1619. float flPyroSegments = 2.0f / NUM_PYRO_SEGMENTS;
  1620. Vector2D vMaxSize( flPyroSegments, flPyroSegments );
  1621. if ( !bInit )
  1622. {
  1623. for( int i = 0; i < MAX_PYRO_SIDES; i++ )
  1624. {
  1625. PyroSides[ i ].m_bAlive = false;
  1626. }
  1627. CreatePyroSide( nNextSide, vMaxSize );
  1628. nNextSide = ( nNextSide + 1 ) & 7;
  1629. bInit = true;
  1630. }
  1631. int nNumAlive = 0;
  1632. TPyroSide *pSide = &PyroSides[ 0 ];
  1633. for( int nIndex = 0; nIndex < MAX_PYRO_SIDES; nIndex++, pSide++ )
  1634. {
  1635. if ( pSide->m_bAlive )
  1636. {
  1637. if ( pSide->m_bIncreasing )
  1638. {
  1639. pSide->m_flIntensity += pSide->m_flRate * gpGlobals->frametime;
  1640. if ( pSide->m_flIntensity >= pSide->m_flIntensityLimit )
  1641. {
  1642. pSide->m_bIncreasing = false;
  1643. }
  1644. }
  1645. else
  1646. {
  1647. pSide->m_flIntensity -= pSide->m_flRate * gpGlobals->frametime;
  1648. if ( pSide->m_flIntensity <= 0.0f )
  1649. {
  1650. pSide->m_bAlive = false;
  1651. }
  1652. }
  1653. }
  1654. if ( pSide->m_bAlive )
  1655. {
  1656. nNumAlive++;
  1657. }
  1658. }
  1659. if ( nNumAlive > 0 )
  1660. {
  1661. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1662. pRenderContext->PushMatrix();
  1663. pRenderContext->LoadIdentity();
  1664. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1665. pRenderContext->PushMatrix();
  1666. pRenderContext->LoadIdentity();
  1667. pRenderContext->Bind( pMaterial, pClientRenderable );
  1668. CMeshBuilder meshBuilder;
  1669. IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
  1670. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nNumAlive * 2 );
  1671. pSide = &PyroSides[ 0 ];
  1672. for( int nIndex = 0; nIndex < MAX_PYRO_SIDES; nIndex++, pSide++ )
  1673. {
  1674. if ( pSide->m_bAlive )
  1675. {
  1676. for( int i = 0; i < 6; i++ )
  1677. {
  1678. meshBuilder.Position3f( pSide->m_vCornerPos[ PryoSideIndexes[ i ][ 0 ] ][ 0 ], pSide->m_vCornerPos[ PryoSideIndexes[ i ][ 1 ] ][ 1 ], 0.0f );
  1679. meshBuilder.Color4f( pSide->m_flIntensity, pSide->m_flIntensity, pSide->m_flIntensity, 1.0f );
  1680. meshBuilder.TexCoord2fv( 0, pSide->m_bHorizontal ? PryoVignetteSTHorizontal[ i ] : PryoVignetteSTVertical[ i ] );
  1681. meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
  1682. meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
  1683. meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
  1684. meshBuilder.AdvanceVertex();
  1685. }
  1686. }
  1687. }
  1688. meshBuilder.End();
  1689. pMesh->Draw();
  1690. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1691. pRenderContext->PopMatrix();
  1692. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1693. pRenderContext->PopMatrix();
  1694. }
  1695. if ( nNumAlive < 25 )
  1696. {
  1697. CreatePyroSide( nNextSide, vMaxSize );
  1698. nNextSide = ( nNextSide + 1 ) & 7;
  1699. }
  1700. }
  1701. pRenderContext->PopRenderTargetAndViewport();
  1702. }
  1703. static void DrawPyroPost( IMaterial *pMaterial,
  1704. int nDestX, int nDestY, int nWidth, int nHeight, // Rect to draw into in screen space
  1705. float flSrcTextureX0, float flSrcTextureY0, // which texel you want to appear at destx/y
  1706. float flSrcTextureX1, float flSrcTextureY1, // which texel you want to appear at destx+width-1, desty+height-1
  1707. int nSrcTextureWidth, int nSrcTextureHeight, // needed for fixup
  1708. void *pClientRenderable ) // Used to pass to the bind proxies
  1709. {
  1710. bool bFound = false;
  1711. IMaterialVar *pVar = pMaterial->FindVar( "$disabled", &bFound, false );
  1712. if ( bFound && pVar->GetIntValue() )
  1713. {
  1714. return;
  1715. }
  1716. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  1717. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1718. pRenderContext->PushMatrix();
  1719. pRenderContext->LoadIdentity();
  1720. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1721. pRenderContext->PushMatrix();
  1722. pRenderContext->LoadIdentity();
  1723. pRenderContext->Bind( pMaterial, pClientRenderable );
  1724. int xSegments = NUM_PYRO_SEGMENTS;
  1725. int ySegments = NUM_PYRO_SEGMENTS;
  1726. CMeshBuilder meshBuilder;
  1727. IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
  1728. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 4 );
  1729. int nScreenWidth, nScreenHeight;
  1730. pRenderContext->GetRenderTargetDimensions( nScreenWidth, nScreenHeight );
  1731. float flOffset = IsPosix() ? 0.0f : 0.5f;
  1732. float flLeftX = nDestX - flOffset;
  1733. float flRightX = nDestX + nWidth - flOffset;
  1734. float flTopY = nDestY - flOffset;
  1735. float flBottomY = nDestY + nHeight - flOffset;
  1736. float flSubrectWidth = flSrcTextureX1 - flSrcTextureX0;
  1737. float flSubrectHeight = flSrcTextureY1 - flSrcTextureY0;
  1738. float flTexelsPerPixelX = ( nWidth > 1 ) ? flSubrectWidth / ( nWidth - 1 ) : 0.0f;
  1739. float flTexelsPerPixelY = ( nHeight > 1 ) ? flSubrectHeight / ( nHeight - 1 ) : 0.0f;
  1740. float flLeftU = flSrcTextureX0 + 0.5f - ( 0.5f * flTexelsPerPixelX );
  1741. float flRightU = flSrcTextureX1 + 0.5f + ( 0.5f * flTexelsPerPixelX );
  1742. float flTopV = flSrcTextureY0 + 0.5f - ( 0.5f * flTexelsPerPixelY );
  1743. float flBottomV = flSrcTextureY1 + 0.5f + ( 0.5f * flTexelsPerPixelY );
  1744. float flOOTexWidth = 1.0f / nSrcTextureWidth;
  1745. float flOOTexHeight = 1.0f / nSrcTextureHeight;
  1746. flLeftU *= flOOTexWidth;
  1747. flRightU *= flOOTexWidth;
  1748. flTopV *= flOOTexHeight;
  1749. flBottomV *= flOOTexHeight;
  1750. // Get the current viewport size
  1751. int vx, vy, vw, vh;
  1752. pRenderContext->GetViewport( vx, vy, vw, vh );
  1753. // map from screen pixel coords to -1..1
  1754. flRightX = FLerp( -1, 1, 0, vw, flRightX );
  1755. flLeftX = FLerp( -1, 1, 0, vw, flLeftX );
  1756. flTopY = FLerp( 1, -1, 0, vh ,flTopY );
  1757. flBottomY = FLerp( 1, -1, 0, vh, flBottomY );
  1758. // Screen height and width of a subrect
  1759. float flWidth = (flRightX - flLeftX) / (float) xSegments;
  1760. float flHeight = (flTopY - flBottomY) / (float) ySegments;
  1761. // UV height and width of a subrect
  1762. float flUWidth = (flRightU - flLeftU) / (float) xSegments;
  1763. float flVHeight = (flBottomV - flTopV) / (float) ySegments;
  1764. // Top Bar
  1765. // Top left
  1766. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) 0 * flHeight, 0.0f );
  1767. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float) 0 * flVHeight);
  1768. meshBuilder.AdvanceVertex();
  1769. // Top right (x+1)
  1770. meshBuilder.Position3f( flLeftX + (float) (xSegments+1) * flWidth, flTopY - (float) 0 * flHeight, 0.0f );
  1771. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments+1) * flUWidth, flTopV + (float) 0 * flVHeight);
  1772. meshBuilder.AdvanceVertex();
  1773. // Bottom right (x+1), (y+1)
  1774. meshBuilder.Position3f( flLeftX + (float) (xSegments+1) * flWidth, flTopY - (float) (0+1) * flHeight, 0.0f );
  1775. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments+1) * flUWidth, flTopV + (float)(0+1) * flVHeight);
  1776. meshBuilder.AdvanceVertex();
  1777. // Bottom left (y+1)
  1778. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) (0+1) * flHeight, 0.0f );
  1779. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float)(0+1) * flVHeight);
  1780. meshBuilder.AdvanceVertex();
  1781. // Bottom Bar
  1782. // Top left
  1783. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) ( ySegments - 1) * flHeight, 0.0f );
  1784. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float) ( ySegments - 1) * flVHeight);
  1785. meshBuilder.AdvanceVertex();
  1786. // Top right (x+1)
  1787. meshBuilder.Position3f( flLeftX + (float) (xSegments) * flWidth, flTopY - (float) ( ySegments - 1) * flHeight, 0.0f );
  1788. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments) * flUWidth, flTopV + (float) ( ySegments - 1 ) * flVHeight);
  1789. meshBuilder.AdvanceVertex();
  1790. // Bottom right (x+1), (y+1)
  1791. meshBuilder.Position3f( flLeftX + (float) (xSegments) * flWidth, flTopY - (float) ( ySegments ) * flHeight, 0.0f );
  1792. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments) * flUWidth, flTopV + (float)( ySegments ) * flVHeight);
  1793. meshBuilder.AdvanceVertex();
  1794. // Bottom left (y+1)
  1795. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) ( ySegments ) * flHeight, 0.0f );
  1796. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float)( ySegments ) * flVHeight);
  1797. meshBuilder.AdvanceVertex();
  1798. // Left Bar
  1799. // Top left
  1800. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) 1 * flHeight, 0.0f );
  1801. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float) 1 * flVHeight);
  1802. meshBuilder.AdvanceVertex();
  1803. // Top right (x+1)
  1804. meshBuilder.Position3f( flLeftX + (float) (0+1) * flWidth, flTopY - (float) 1 * flHeight, 0.0f );
  1805. meshBuilder.TexCoord2f( 0, flLeftU + (float) (0+1) * flUWidth, flTopV + (float) 1 * flVHeight);
  1806. meshBuilder.AdvanceVertex();
  1807. // Bottom right (x+1), (y+1)
  1808. meshBuilder.Position3f( flLeftX + (float) (0+1) * flWidth, flTopY - (float) (ySegments - 1) * flHeight, 0.0f );
  1809. meshBuilder.TexCoord2f( 0, flLeftU + (float) (0+1) * flUWidth, flTopV + (float)(ySegments - 1) * flVHeight);
  1810. meshBuilder.AdvanceVertex();
  1811. // Bottom left (y+1)
  1812. meshBuilder.Position3f( flLeftX + (float) 0 * flWidth, flTopY - (float) (ySegments - 1) * flHeight, 0.0f );
  1813. meshBuilder.TexCoord2f( 0, flLeftU + (float) 0 * flUWidth, flTopV + (float)(ySegments - 1) * flVHeight);
  1814. meshBuilder.AdvanceVertex();
  1815. // Right Bar
  1816. // Top left
  1817. meshBuilder.Position3f( flLeftX + (float) (xSegments - 1) * flWidth, flTopY - (float) 1 * flHeight, 0.0f );
  1818. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments - 1) * flUWidth, flTopV + (float) 1 * flVHeight);
  1819. meshBuilder.AdvanceVertex();
  1820. // Top right (x+1)
  1821. meshBuilder.Position3f( flLeftX + (float) (xSegments) * flWidth, flTopY - (float) 1 * flHeight, 0.0f );
  1822. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments) * flUWidth, flTopV + (float) 1 * flVHeight);
  1823. meshBuilder.AdvanceVertex();
  1824. // Bottom right (x+1), (y+1)
  1825. meshBuilder.Position3f( flLeftX + (float) (xSegments) * flWidth, flTopY - (float) (ySegments - 1) * flHeight, 0.0f );
  1826. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments) * flUWidth, flTopV + (float)(ySegments - 1) * flVHeight);
  1827. meshBuilder.AdvanceVertex();
  1828. // Bottom left (y+1)
  1829. meshBuilder.Position3f( flLeftX + (float) (xSegments - 1) * flWidth, flTopY - (float) (ySegments - 1) * flHeight, 0.0f );
  1830. meshBuilder.TexCoord2f( 0, flLeftU + (float) (xSegments - 1) * flUWidth, flTopV + (float)(ySegments - 1) * flVHeight);
  1831. meshBuilder.AdvanceVertex();
  1832. #if 0
  1833. for ( int x=0; x < xSegments; x++ )
  1834. {
  1835. for ( int y=0; y < ySegments; y++ )
  1836. {
  1837. if ( ( x == 1 || x == 2 ) && ( y == 1 || y == 2 ) )
  1838. { // skip the center 4 segments
  1839. continue;
  1840. }
  1841. // Top left
  1842. meshBuilder.Position3f( flLeftX + (float) x * flWidth, flTopY - (float) y * flHeight, 0.0f );
  1843. meshBuilder.TexCoord2f( 0, flLeftU + (float) x * flUWidth, flTopV + (float) y * flVHeight);
  1844. meshBuilder.AdvanceVertex();
  1845. // Top right (x+1)
  1846. meshBuilder.Position3f( flLeftX + (float) (x+1) * flWidth, flTopY - (float) y * flHeight, 0.0f );
  1847. meshBuilder.TexCoord2f( 0, flLeftU + (float) (x+1) * flUWidth, flTopV + (float) y * flVHeight);
  1848. meshBuilder.AdvanceVertex();
  1849. // Bottom right (x+1), (y+1)
  1850. meshBuilder.Position3f( flLeftX + (float) (x+1) * flWidth, flTopY - (float) (y+1) * flHeight, 0.0f );
  1851. meshBuilder.TexCoord2f( 0, flLeftU + (float) (x+1) * flUWidth, flTopV + (float)(y+1) * flVHeight);
  1852. meshBuilder.AdvanceVertex();
  1853. // Bottom left (y+1)
  1854. meshBuilder.Position3f( flLeftX + (float) x * flWidth, flTopY - (float) (y+1) * flHeight, 0.0f );
  1855. meshBuilder.TexCoord2f( 0, flLeftU + (float) x * flUWidth, flTopV + (float)(y+1) * flVHeight);
  1856. meshBuilder.AdvanceVertex();
  1857. }
  1858. }
  1859. #endif
  1860. meshBuilder.End();
  1861. pMesh->Draw();
  1862. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1863. pRenderContext->PopMatrix();
  1864. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1865. pRenderContext->PopMatrix();
  1866. }
  1867. static ConVar r_queued_post_processing( "r_queued_post_processing", "0" );
  1868. // How much to dice up the screen during post-processing on 360
  1869. // This has really marginal effects, but 4x1 does seem vaguely better for post-processing
  1870. static ConVar mat_postprocess_x( "mat_postprocess_x", "4" );
  1871. static ConVar mat_postprocess_y( "mat_postprocess_y", "1" );
  1872. void DoEnginePostProcessing( int x, int y, int w, int h, bool bFlashlightIsOn, bool bPostVGui )
  1873. {
  1874. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1875. CMatRenderContextPtr pRenderContext( materials );
  1876. if ( g_bDumpRenderTargets )
  1877. {
  1878. g_bDumpRenderTargets = false; // Turn off from previous frame
  1879. }
  1880. if ( mat_dump_rts.GetBool() )
  1881. {
  1882. g_bDumpRenderTargets = true; // Dump intermediate render targets this frame
  1883. s_nRTIndex = 0; // Used for numbering the TGA files for easy browsing
  1884. mat_dump_rts.SetValue( 0 ); // We only want to capture one frame, on rising edge of this convar
  1885. DumpTGAofRenderTarget( w, h, "BackBuffer" );
  1886. }
  1887. #if defined( _X360 )
  1888. pRenderContext->PushVertexShaderGPRAllocation( 16 ); //max out pixel shader threads
  1889. #endif
  1890. if ( r_queued_post_processing.GetInt() )
  1891. {
  1892. ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
  1893. if ( pCallQueue )
  1894. {
  1895. pCallQueue->QueueCall( DoEnginePostProcessing, x, y, w, h, bFlashlightIsOn, bPostVGui );
  1896. return;
  1897. }
  1898. }
  1899. float flBloomScale = GetBloomAmount();
  1900. HDRType_t hdrType = g_pMaterialSystemHardwareConfig->GetHDRType();
  1901. g_bFlashlightIsOn = bFlashlightIsOn;
  1902. // Use the appropriate autoexposure min / max settings.
  1903. // Mapmaker's overrides the convar settings.
  1904. float flAutoExposureMin;
  1905. float flAutoExposureMax;
  1906. GetExposureRange( &flAutoExposureMin, &flAutoExposureMax );
  1907. if ( mat_debug_bloom.GetInt() == 1 )
  1908. {
  1909. DrawBloomDebugBoxes( pRenderContext );
  1910. }
  1911. switch( hdrType )
  1912. {
  1913. case HDR_TYPE_NONE:
  1914. case HDR_TYPE_INTEGER:
  1915. {
  1916. s_bScreenEffectTextureIsUpdated = false;
  1917. if ( hdrType != HDR_TYPE_NONE )
  1918. {
  1919. DoPreBloomTonemapping( pRenderContext, x, y, w, h, flAutoExposureMin, flAutoExposureMax );
  1920. }
  1921. // Set software-AA on by default for 360
  1922. if ( mat_software_aa_strength.GetFloat() == -1.0f )
  1923. {
  1924. if ( IsX360() )
  1925. {
  1926. mat_software_aa_strength.SetValue( 1.0f );
  1927. if ( g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height > 480 )
  1928. {
  1929. mat_software_aa_quality.SetValue( 0 );
  1930. }
  1931. else
  1932. {
  1933. // For standard-def, we have fewer pixels so we can afford 'high quality' mode (5->9 taps/pixel)
  1934. mat_software_aa_quality.SetValue( 1 );
  1935. }
  1936. }
  1937. else
  1938. {
  1939. mat_software_aa_strength.SetValue( 0.0f );
  1940. }
  1941. }
  1942. // Same trick for setting up the vgui aa strength
  1943. if ( mat_software_aa_strength_vgui.GetFloat() == -1.0f )
  1944. {
  1945. if ( IsX360() && (g_pMaterialSystem->GetCurrentConfigForVideoCard().m_VideoMode.m_Height == 720) )
  1946. {
  1947. mat_software_aa_strength_vgui.SetValue( 2.0f );
  1948. }
  1949. else
  1950. {
  1951. mat_software_aa_strength_vgui.SetValue( 1.0f );
  1952. }
  1953. }
  1954. float flAAStrength;
  1955. // We do a second AA blur pass over the TF intro menus. use mat_software_aa_strength_vgui there instead
  1956. if ( IsX360() && bPostVGui )
  1957. {
  1958. flAAStrength = mat_software_aa_strength_vgui.GetFloat();
  1959. }
  1960. else
  1961. {
  1962. flAAStrength = mat_software_aa_strength.GetFloat();
  1963. }
  1964. static ConVarRef mat_colorcorrection( "mat_colorcorrection" );
  1965. // bloom, software-AA and colour-correction (applied in 1 pass, after generation of the bloom texture)
  1966. bool bPerformSoftwareAA = IsX360() && ( engine->GetDXSupportLevel() >= 90 ) && ( flAAStrength != 0.0f );
  1967. bool bPerformBloom = !bPostVGui && ( flBloomScale > 0.0f ) && ( engine->GetDXSupportLevel() >= 90 );
  1968. bool bPerformColCorrect = !bPostVGui &&
  1969. ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90) &&
  1970. ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT ) &&
  1971. g_pColorCorrectionMgr->HasNonZeroColorCorrectionWeights() &&
  1972. mat_colorcorrection.GetInt();
  1973. bool bSplitScreenHDR = mat_show_ab_hdr.GetInt();
  1974. pRenderContext->EnableColorCorrection( bPerformColCorrect );
  1975. if ( bPerformBloom || bPerformSoftwareAA || bPerformColCorrect )
  1976. {
  1977. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "ColorCorrection" );
  1978. ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
  1979. int nSrcWidth = pSrc->GetActualWidth();
  1980. int nSrcHeight = pSrc->GetActualHeight();
  1981. ITexture *dest_rt1 = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
  1982. if ( !s_bScreenEffectTextureIsUpdated )
  1983. {
  1984. // NOTE: UpdateScreenEffectTexture() uses StretchRect, so _rt_FullFrameFB is always 100%
  1985. // filled, even when the viewport is not fullscreen (e.g. with 'mat_viewportscale 0.5')
  1986. UpdateScreenEffectTexture( 0, x, y, w, h, true );
  1987. s_bScreenEffectTextureIsUpdated = true;
  1988. }
  1989. if ( bPerformBloom )
  1990. {
  1991. Generate8BitBloomTexture( pRenderContext, flBloomScale, x, y, w, h );
  1992. }
  1993. // Now add bloom (dest_rt0) to the framebuffer and perform software anti-aliasing and
  1994. // colour correction, all in one pass (improves performance, reduces quantization errors)
  1995. //
  1996. // First, set up texel coords (in the bloom and fb textures) at the centres of the outer pixel of the viewport:
  1997. Vector4D fullViewportPostSrcCorners( 0.0f, -0.5f, nSrcWidth/4-1, nSrcHeight/4-1 );
  1998. Vector4D fullViewportPostDestCorners( 0.0f, 0.0f, nSrcWidth - 1, nSrcHeight - 1 );
  1999. Rect_t fullViewportPostDestRect = { x, y, w, h };
  2000. Vector2D destTexSize( nSrcWidth, nSrcHeight );
  2001. // When the viewport is not fullscreen, the UV-space size of a pixel changes
  2002. // (due to a stretchrect blit being used in UpdateScreenEffectTexture()), so
  2003. // we need to adjust the corner-pixel UVs sent to our drawrect call:
  2004. Vector2D uvScale( ( nSrcWidth - ( nSrcWidth / (float)w ) ) / ( nSrcWidth - 1 ),
  2005. ( nSrcHeight - ( nSrcHeight / (float)h ) ) / ( nSrcHeight - 1 ) );
  2006. CenterScaleQuadUVs( fullViewportPostSrcCorners, uvScale );
  2007. CenterScaleQuadUVs( fullViewportPostDestCorners, uvScale );
  2008. Rect_t partialViewportPostDestRect = fullViewportPostDestRect;
  2009. Vector4D partialViewportPostSrcCorners = fullViewportPostSrcCorners;
  2010. if ( debug_postproc.GetInt() == 2 )
  2011. {
  2012. // Restrict the post effects to the centre quarter of the screen
  2013. // (we only use a portion of the bloom texture, so this *does* affect bloom texture UVs)
  2014. partialViewportPostDestRect.x += 0.25f*fullViewportPostDestRect.width;
  2015. partialViewportPostDestRect.y += 0.25f*fullViewportPostDestRect.height;
  2016. partialViewportPostDestRect.width -= 0.50f*fullViewportPostDestRect.width;
  2017. partialViewportPostDestRect.height -= 0.50f*fullViewportPostDestRect.height;
  2018. // This math interprets texel coords as being at corner pixel centers (*not* at corner vertices):
  2019. Vector2D uvScalePost( 1.0f - ( (w / 2) / (float)(w - 1) ),
  2020. 1.0f - ( (h / 2) / (float)(h - 1) ) );
  2021. CenterScaleQuadUVs( partialViewportPostSrcCorners, uvScalePost );
  2022. }
  2023. // Temporary hack... Color correction was crashing on the first frame
  2024. // when run outside the debugger for some mods (DoD). This forces it to skip
  2025. // a frame, ensuring we don't get the weird texture crash we otherwise would.
  2026. // FIXME: This will be removed when the true cause is found [added: Main CL 144694]
  2027. static bool bFirstFrame = !IsX360();
  2028. if( !bFirstFrame || !bPerformColCorrect )
  2029. {
  2030. bool bFBUpdated = false;
  2031. if ( mat_postprocessing_combine.GetInt() )
  2032. {
  2033. // Perform post-processing in one combined pass
  2034. IMaterial *post_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, bPerformBloom, bPerformColCorrect, flAAStrength );
  2035. if (bSplitScreenHDR)
  2036. {
  2037. pRenderContext->SetScissorRect( partialViewportPostDestRect.width / 2, 0, partialViewportPostDestRect.width, partialViewportPostDestRect.height, true );
  2038. }
  2039. pRenderContext->DrawScreenSpaceRectangle(post_mat,
  2040. // TomF - offset already done by the viewport.
  2041. 0,0, //partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2042. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2043. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2044. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2045. dest_rt1->GetActualWidth(),dest_rt1->GetActualHeight(),
  2046. GetClientWorldEntity()->GetClientRenderable(),
  2047. mat_postprocess_x.GetInt(), mat_postprocess_y.GetInt() );
  2048. if (bSplitScreenHDR)
  2049. {
  2050. pRenderContext->SetScissorRect( -1, -1, -1, -1, false );
  2051. }
  2052. bFBUpdated = true;
  2053. }
  2054. else
  2055. {
  2056. // Perform post-processing in three separate passes
  2057. if ( bPerformSoftwareAA )
  2058. {
  2059. IMaterial *aa_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, bPerformSoftwareAA, false, false, flAAStrength );
  2060. if (bSplitScreenHDR)
  2061. {
  2062. pRenderContext->SetScissorRect( partialViewportPostDestRect.width / 2, 0, partialViewportPostDestRect.width, partialViewportPostDestRect.height, true );
  2063. }
  2064. pRenderContext->DrawScreenSpaceRectangle(aa_mat,
  2065. // TODO: check if offsets should be 0,0 here, as with the combined-pass case
  2066. partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2067. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2068. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2069. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2070. dest_rt1->GetActualWidth(),dest_rt1->GetActualHeight(),
  2071. GetClientWorldEntity()->GetClientRenderable());
  2072. if (bSplitScreenHDR)
  2073. {
  2074. pRenderContext->SetScissorRect( -1, -1, -1, -1, false );
  2075. }
  2076. bFBUpdated = true;
  2077. }
  2078. if ( bPerformBloom )
  2079. {
  2080. IMaterial *bloom_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, bPerformBloom, false, flAAStrength );
  2081. if (bSplitScreenHDR)
  2082. {
  2083. pRenderContext->SetScissorRect( partialViewportPostDestRect.width / 2, 0, partialViewportPostDestRect.width, partialViewportPostDestRect.height, true );
  2084. }
  2085. pRenderContext->DrawScreenSpaceRectangle(bloom_mat,
  2086. // TODO: check if offsets should be 0,0 here, as with the combined-pass case
  2087. partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2088. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2089. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2090. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2091. dest_rt1->GetActualWidth(),dest_rt1->GetActualHeight(),
  2092. GetClientWorldEntity()->GetClientRenderable());
  2093. if (bSplitScreenHDR)
  2094. {
  2095. pRenderContext->SetScissorRect( -1, -1, -1, -1, false );
  2096. }
  2097. bFBUpdated = true;
  2098. }
  2099. if ( bPerformColCorrect )
  2100. {
  2101. if ( bFBUpdated )
  2102. {
  2103. Rect_t actualRect;
  2104. UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect );
  2105. }
  2106. IMaterial *colcorrect_mat = CEnginePostMaterialProxy::SetupEnginePostMaterial( fullViewportPostSrcCorners, fullViewportPostDestCorners, destTexSize, false, false, bPerformColCorrect, flAAStrength );
  2107. if (bSplitScreenHDR)
  2108. {
  2109. pRenderContext->SetScissorRect( partialViewportPostDestRect.width / 2, 0, partialViewportPostDestRect.width, partialViewportPostDestRect.height, true );
  2110. }
  2111. pRenderContext->DrawScreenSpaceRectangle(colcorrect_mat,
  2112. // TODO: check if offsets should be 0,0 here, as with the combined-pass case
  2113. partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2114. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2115. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2116. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2117. dest_rt1->GetActualWidth(),dest_rt1->GetActualHeight(),
  2118. GetClientWorldEntity()->GetClientRenderable());
  2119. if (bSplitScreenHDR)
  2120. {
  2121. pRenderContext->SetScissorRect( -1, -1, -1, -1, false );
  2122. }
  2123. bFBUpdated = true;
  2124. }
  2125. }
  2126. bool bVisionOverride = ( localplayer_visionflags.GetInt() & ( 0x01 ) ); // Pyro-vision Goggles
  2127. if ( bVisionOverride && g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() && pyro_vignette.GetInt() > 0 )
  2128. {
  2129. if ( bFBUpdated )
  2130. {
  2131. Rect_t actualRect;
  2132. UpdateScreenEffectTexture( 0, x, y, w, h, false, &actualRect );
  2133. }
  2134. DrawPyroVignette(
  2135. // TODO: check if offsets should be 0,0 here, as with the combined-pass case
  2136. partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2137. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2138. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2139. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2140. GetClientWorldEntity()->GetClientRenderable() );
  2141. IMaterial *pPyroVisionPostMaterial = materials->FindMaterial( "dev/pyro_post", TEXTURE_GROUP_OTHER, true);
  2142. DrawPyroPost( pPyroVisionPostMaterial,
  2143. // TODO: check if offsets should be 0,0 here, as with the combined-pass case
  2144. partialViewportPostDestRect.x, partialViewportPostDestRect.y,
  2145. partialViewportPostDestRect.width, partialViewportPostDestRect.height,
  2146. partialViewportPostSrcCorners.x, partialViewportPostSrcCorners.y,
  2147. partialViewportPostSrcCorners.z, partialViewportPostSrcCorners.w,
  2148. dest_rt1->GetActualWidth(),dest_rt1->GetActualHeight(),
  2149. GetClientWorldEntity()->GetClientRenderable() );
  2150. }
  2151. if ( g_bDumpRenderTargets )
  2152. {
  2153. DumpTGAofRenderTarget( partialViewportPostDestRect.width, partialViewportPostDestRect.height, "EnginePost" );
  2154. }
  2155. }
  2156. bFirstFrame = false;
  2157. }
  2158. if ( hdrType != HDR_TYPE_NONE )
  2159. {
  2160. DoPostBloomTonemapping( pRenderContext, x, y, w, h, flAutoExposureMin, flAutoExposureMax );
  2161. }
  2162. }
  2163. break;
  2164. case HDR_TYPE_FLOAT:
  2165. {
  2166. int dest_width,dest_height;
  2167. pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
  2168. if (mat_dynamic_tonemapping.GetInt() || mat_show_histogram.GetInt())
  2169. {
  2170. g_HDR_HistogramSystem.Update();
  2171. // Warning("avg_lum=%f\n",g_HDR_HistogramSystem.GetTargetTonemapScalar());
  2172. if ( mat_dynamic_tonemapping.GetInt() )
  2173. {
  2174. float avg_lum = MAX( 0.0001, g_HDR_HistogramSystem.GetTargetTonemapScalar() );
  2175. float scalevalue = MAX( flAutoExposureMin,
  2176. MIN( flAutoExposureMax, 0.18 / avg_lum ));
  2177. pRenderContext->SetGoalToneMappingScale( scalevalue );
  2178. mat_hdr_tonemapscale.SetValue( scalevalue );
  2179. }
  2180. }
  2181. IMaterial *pBloomMaterial;
  2182. pBloomMaterial = materials->FindMaterial( "dev/floattoscreen_combine", "" );
  2183. IMaterialVar *pBloomAmountVar = pBloomMaterial->FindVar( "$bloomamount", NULL );
  2184. pBloomAmountVar->SetFloatValue( flBloomScale );
  2185. PostProcessingPass* selectedHDR;
  2186. if ( flBloomScale > 0.0 )
  2187. {
  2188. selectedHDR = HDRFinal_Float;
  2189. }
  2190. else
  2191. {
  2192. selectedHDR = HDRFinal_Float_NoBloom;
  2193. }
  2194. if (mat_show_ab_hdr.GetInt())
  2195. {
  2196. ClipBox splitScreenClip;
  2197. splitScreenClip.m_minx = splitScreenClip.m_miny = 0;
  2198. // Left half
  2199. splitScreenClip.m_maxx = dest_width / 2;
  2200. splitScreenClip.m_maxy = dest_height - 1;
  2201. ApplyPostProcessingPasses(HDRSimulate_NonHDR, &splitScreenClip);
  2202. // Right half
  2203. splitScreenClip.m_minx = splitScreenClip.m_maxx;
  2204. splitScreenClip.m_maxx = dest_width - 1;
  2205. ApplyPostProcessingPasses(selectedHDR, &splitScreenClip);
  2206. }
  2207. else
  2208. {
  2209. ApplyPostProcessingPasses(selectedHDR);
  2210. }
  2211. pRenderContext->SetRenderTarget(NULL);
  2212. if ( mat_show_histogram.GetInt() && (engine->GetDXSupportLevel()>=90))
  2213. g_HDR_HistogramSystem.DisplayHistogram();
  2214. if ( mat_dynamic_tonemapping.GetInt() )
  2215. {
  2216. float avg_lum = MAX( 0.0001, g_HDR_HistogramSystem.GetTargetTonemapScalar() );
  2217. float scalevalue = MAX( flAutoExposureMin,
  2218. MIN( flAutoExposureMax, 0.023 / avg_lum ));
  2219. SetToneMapScale( pRenderContext, scalevalue, flAutoExposureMin, flAutoExposureMax );
  2220. }
  2221. pRenderContext->SetRenderTarget( NULL );
  2222. break;
  2223. }
  2224. }
  2225. #if defined( _X360 )
  2226. pRenderContext->PopVertexShaderGPRAllocation();
  2227. #endif
  2228. }
  2229. // Motion Blur Material Proxy =========================================================================================
  2230. static float g_vMotionBlurValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
  2231. class CMotionBlurMaterialProxy : public CEntityMaterialProxy
  2232. {
  2233. public:
  2234. CMotionBlurMaterialProxy();
  2235. virtual ~CMotionBlurMaterialProxy();
  2236. virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
  2237. virtual void OnBind( C_BaseEntity *pEntity );
  2238. virtual IMaterial *GetMaterial();
  2239. private:
  2240. IMaterialVar *m_pMaterialParam;
  2241. };
  2242. CMotionBlurMaterialProxy::CMotionBlurMaterialProxy()
  2243. {
  2244. m_pMaterialParam = NULL;
  2245. }
  2246. CMotionBlurMaterialProxy::~CMotionBlurMaterialProxy()
  2247. {
  2248. // Do nothing
  2249. }
  2250. bool CMotionBlurMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  2251. {
  2252. bool bFoundVar = false;
  2253. m_pMaterialParam = pMaterial->FindVar( "$MotionBlurInternal", &bFoundVar, false );
  2254. if ( bFoundVar == false)
  2255. return false;
  2256. return true;
  2257. }
  2258. void CMotionBlurMaterialProxy::OnBind( C_BaseEntity *pEnt )
  2259. {
  2260. if ( m_pMaterialParam != NULL )
  2261. {
  2262. m_pMaterialParam->SetVecValue( g_vMotionBlurValues, 4 );
  2263. }
  2264. }
  2265. IMaterial *CMotionBlurMaterialProxy::GetMaterial()
  2266. {
  2267. if ( m_pMaterialParam == NULL)
  2268. return NULL;
  2269. return m_pMaterialParam->GetOwningMaterial();
  2270. }
  2271. EXPOSE_INTERFACE( CMotionBlurMaterialProxy, IMaterialProxy, "MotionBlur" IMATERIAL_PROXY_INTERFACE_VERSION );
  2272. //=====================================================================================================================
  2273. // Image-space Motion Blur ============================================================================================
  2274. //=====================================================================================================================
  2275. ConVar mat_motion_blur_forward_enabled( "mat_motion_blur_forward_enabled", "0" );
  2276. ConVar mat_motion_blur_falling_min( "mat_motion_blur_falling_min", "10.0" );
  2277. ConVar mat_motion_blur_falling_max( "mat_motion_blur_falling_max", "20.0" );
  2278. ConVar mat_motion_blur_falling_intensity( "mat_motion_blur_falling_intensity", "1.0" );
  2279. //ConVar mat_motion_blur_roll_intensity( "mat_motion_blur_roll_intensity", "1.0" );
  2280. ConVar mat_motion_blur_rotation_intensity( "mat_motion_blur_rotation_intensity", "1.0" );
  2281. ConVar mat_motion_blur_strength( "mat_motion_blur_strength", "1.0" );
  2282. void DoImageSpaceMotionBlur( const CViewSetup &viewBlur, int x, int y, int w, int h )
  2283. {
  2284. #ifdef CSS_PERF_TEST
  2285. return;
  2286. #endif
  2287. static ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" );
  2288. if ( ( !mat_motion_blur_enabled.GetInt() ) || ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 ) )
  2289. {
  2290. return;
  2291. }
  2292. //======================================================================================================//
  2293. // Get these convars here to make it easier to remove them later and to default each client differently //
  2294. //======================================================================================================//
  2295. float flMotionBlurRotationIntensity = mat_motion_blur_rotation_intensity.GetFloat() * 0.15f; // The default is to not blur past 15% of the range
  2296. float flMotionBlurRollIntensity = 0.3f; // * mat_motion_blur_roll_intensity.GetFloat(); // The default is to not blur past 30% of the range
  2297. float flMotionBlurFallingIntensity = mat_motion_blur_falling_intensity.GetFloat();
  2298. float flMotionBlurFallingMin = mat_motion_blur_falling_min.GetFloat();
  2299. float flMotionBlurFallingMax = mat_motion_blur_falling_max.GetFloat();
  2300. float flMotionBlurGlobalStrength = mat_motion_blur_strength.GetFloat();
  2301. //===============================================================================//
  2302. // Set global g_vMotionBlurValues[4] values so material proxy can get the values //
  2303. //===============================================================================//
  2304. if ( true )
  2305. {
  2306. //=====================//
  2307. // Previous frame data //
  2308. //=====================//
  2309. static float s_flLastTimeUpdate = 0.0f;
  2310. static float s_flPreviousPitch = 0.0f;
  2311. static float s_flPreviousYaw = 0.0f;
  2312. static float s_vPreviousPositon[3] = { 0.0f, 0.0f, 0.0f };
  2313. static matrix3x4_t s_mPreviousFrameBasisVectors;
  2314. static float s_flNoRotationalMotionBlurUntil = 0.0f;
  2315. //float vPreviousSideVec[3] = { s_mPreviousFrameBasisVectors[0][1], s_mPreviousFrameBasisVectors[1][1], s_mPreviousFrameBasisVectors[2][1] };
  2316. //float vPreviousForwardVec[3] = { s_mPreviousFrameBasisVectors[0][0], s_mPreviousFrameBasisVectors[1][0], s_mPreviousFrameBasisVectors[2][0] };
  2317. //float vPreviousUpVec[3] = { s_mPreviousFrameBasisVectors[0][2], s_mPreviousFrameBasisVectors[1][2], s_mPreviousFrameBasisVectors[2][2] };
  2318. float flTimeElapsed = gpGlobals->realtime - s_flLastTimeUpdate;
  2319. //===================================//
  2320. // Get current pitch & wrap to +-180 //
  2321. //===================================//
  2322. float flCurrentPitch = viewBlur.angles[PITCH];
  2323. while ( flCurrentPitch > 180.0f )
  2324. flCurrentPitch -= 360.0f;
  2325. while ( flCurrentPitch < -180.0f )
  2326. flCurrentPitch += 360.0f;
  2327. //=================================//
  2328. // Get current yaw & wrap to +-180 //
  2329. //=================================//
  2330. float flCurrentYaw = viewBlur.angles[YAW];
  2331. while ( flCurrentYaw > 180.0f )
  2332. flCurrentYaw -= 360.0f;
  2333. while ( flCurrentYaw < -180.0f )
  2334. flCurrentYaw += 360.0f;
  2335. //engine->Con_NPrintf( 0, "Blur Pitch: %6.2f Yaw: %6.2f", flCurrentPitch, flCurrentYaw );
  2336. //engine->Con_NPrintf( 1, "Blur FOV: %6.2f Aspect: %6.2f Ortho: %s", view.fov, view.m_flAspectRatio, view.m_bOrtho ? "Yes" : "No" );
  2337. //===========================//
  2338. // Get current basis vectors //
  2339. //===========================//
  2340. matrix3x4_t mCurrentBasisVectors;
  2341. AngleMatrix( viewBlur.angles, mCurrentBasisVectors );
  2342. float vCurrentSideVec[3] = { mCurrentBasisVectors[0][1], mCurrentBasisVectors[1][1], mCurrentBasisVectors[2][1] };
  2343. float vCurrentForwardVec[3] = { mCurrentBasisVectors[0][0], mCurrentBasisVectors[1][0], mCurrentBasisVectors[2][0] };
  2344. //float vCurrentUpVec[3] = { mCurrentBasisVectors[0][2], mCurrentBasisVectors[1][2], mCurrentBasisVectors[2][2] };
  2345. //======================//
  2346. // Get current position //
  2347. //======================//
  2348. float vCurrentPosition[3] = { viewBlur.origin.x, viewBlur.origin.y, viewBlur.origin.z };
  2349. //===============================================================//
  2350. // Evaluate change in position to determine if we need to update //
  2351. //===============================================================//
  2352. float vPositionChange[3] = { 0.0f, 0.0f, 0.0f };
  2353. VectorSubtract( s_vPreviousPositon, vCurrentPosition, vPositionChange );
  2354. if ( ( VectorLength( vPositionChange ) > 30.0f ) && ( flTimeElapsed >= 0.5f ) )
  2355. {
  2356. //=======================================================//
  2357. // If we moved a far distance in one frame and more than //
  2358. // half a second elapsed, disable motion blur this frame //
  2359. //=======================================================//
  2360. //engine->Con_NPrintf( 8, " Pos change && time > 0.5 seconds %f ", gpGlobals->realtime );
  2361. g_vMotionBlurValues[0] = 0.0f;
  2362. g_vMotionBlurValues[1] = 0.0f;
  2363. g_vMotionBlurValues[2] = 0.0f;
  2364. g_vMotionBlurValues[3] = 0.0f;
  2365. }
  2366. else if ( flTimeElapsed > ( 1.0f / 15.0f ) )
  2367. {
  2368. //==========================================//
  2369. // If slower than 15 fps, don't motion blur //
  2370. //==========================================//
  2371. g_vMotionBlurValues[0] = 0.0f;
  2372. g_vMotionBlurValues[1] = 0.0f;
  2373. g_vMotionBlurValues[2] = 0.0f;
  2374. g_vMotionBlurValues[3] = 0.0f;
  2375. }
  2376. else if ( VectorLength( vPositionChange ) > 50.0f )
  2377. {
  2378. //================================================================================//
  2379. // We moved a far distance in a frame, use the same motion blur as last frame //
  2380. // because I think we just went through a portal (should we ifdef this behavior?) //
  2381. //================================================================================//
  2382. //engine->Con_NPrintf( 8, " Position changed %f units @ %.2f time ", VectorLength( vPositionChange ), gpGlobals->realtime );
  2383. s_flNoRotationalMotionBlurUntil = gpGlobals->realtime + 1.0f; // Wait a second until the portal craziness calms down
  2384. }
  2385. else
  2386. {
  2387. //====================//
  2388. // Normal update path //
  2389. //====================//
  2390. // Compute horizontal and vertical fov
  2391. float flHorizontalFov = viewBlur.fov;
  2392. float flVerticalFov = (viewBlur.m_flAspectRatio <= 0.0f ) ? (viewBlur.fov ) : (viewBlur.fov / viewBlur.m_flAspectRatio );
  2393. //engine->Con_NPrintf( 2, "Horizontal Fov: %6.2f Vertical Fov: %6.2f", flHorizontalFov, flVerticalFov );
  2394. //=====================//
  2395. // Forward motion blur //
  2396. //=====================//
  2397. float flViewDotMotion = DotProduct( vCurrentForwardVec, vPositionChange );
  2398. if ( mat_motion_blur_forward_enabled.GetBool() ) // Want forward and falling
  2399. g_vMotionBlurValues[2] = flViewDotMotion;
  2400. else // Falling only
  2401. g_vMotionBlurValues[2] = flViewDotMotion * fabs( vCurrentForwardVec[2] ); // Only want this if we're looking up or down;
  2402. //====================================//
  2403. // Yaw (Compensate for circle strafe) //
  2404. //====================================//
  2405. float flSideDotMotion = DotProduct( vCurrentSideVec, vPositionChange );
  2406. float flYawDiffOriginal = s_flPreviousYaw - flCurrentYaw;
  2407. if ( ( ( s_flPreviousYaw - flCurrentYaw > 180.0f ) || ( s_flPreviousYaw - flCurrentYaw < -180.0f ) ) &&
  2408. ( ( s_flPreviousYaw + flCurrentYaw > -180.0f ) && ( s_flPreviousYaw + flCurrentYaw < 180.0f ) ) )
  2409. flYawDiffOriginal = s_flPreviousYaw + flCurrentYaw;
  2410. float flYawDiffAdjusted = flYawDiffOriginal + ( flSideDotMotion / 3.0f ); // Yes, 3.0 is a magic number, sue me
  2411. // Make sure the adjustment only lessens the effect, not magnify it or reverse it
  2412. if ( flYawDiffOriginal < 0.0f )
  2413. flYawDiffAdjusted = clamp ( flYawDiffAdjusted, flYawDiffOriginal, 0.0f );
  2414. else
  2415. flYawDiffAdjusted = clamp ( flYawDiffAdjusted, 0.0f, flYawDiffOriginal );
  2416. // Use pitch to dampen yaw
  2417. float flUndampenedYaw = flYawDiffAdjusted / flHorizontalFov;
  2418. g_vMotionBlurValues[0] = flUndampenedYaw * ( 1.0f - ( fabs( flCurrentPitch ) / 90.0f ) ); // Dampen horizontal yaw blur based on pitch
  2419. //engine->Con_NPrintf( 4, "flSideDotMotion: %6.2f yaw diff: %6.2f ( %6.2f, %6.2f )", flSideDotMotion, ( s_flPreviousYaw - flCurrentYaw ), flYawDiffOriginal, flYawDiffAdjusted );
  2420. //=======================================//
  2421. // Pitch (Compensate for forward motion) //
  2422. //=======================================//
  2423. float flPitchCompensateMask = 1.0f - ( ( 1.0f - fabs( vCurrentForwardVec[2] ) ) * ( 1.0f - fabs( vCurrentForwardVec[2] ) ) );
  2424. float flPitchDiffOriginal = s_flPreviousPitch - flCurrentPitch;
  2425. float flPitchDiffAdjusted = flPitchDiffOriginal;
  2426. if ( flCurrentPitch > 0.0f )
  2427. flPitchDiffAdjusted = flPitchDiffOriginal - ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
  2428. else
  2429. flPitchDiffAdjusted = flPitchDiffOriginal + ( ( flViewDotMotion / 2.0f ) * flPitchCompensateMask ); // Yes, 2.0 is a magic number, sue me
  2430. // Make sure the adjustment only lessens the effect, not magnify it or reverse it
  2431. if ( flPitchDiffOriginal < 0.0f )
  2432. flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, flPitchDiffOriginal, 0.0f );
  2433. else
  2434. flPitchDiffAdjusted = clamp ( flPitchDiffAdjusted, 0.0f, flPitchDiffOriginal );
  2435. g_vMotionBlurValues[1] = flPitchDiffAdjusted / flVerticalFov;
  2436. //engine->Con_NPrintf( 5, "flViewDotMotion %6.2f, flPitchCompensateMask %6.2f, flPitchDiffOriginal %6.2f, flPitchDiffAdjusted %6.2f, g_vMotionBlurValues[1] %6.2f", flViewDotMotion, flPitchCompensateMask, flPitchDiffOriginal, flPitchDiffAdjusted, g_vMotionBlurValues[1]);
  2437. //========================================================//
  2438. // Roll (Enabled when we're looking down and yaw changes) //
  2439. //========================================================//
  2440. g_vMotionBlurValues[3] = flUndampenedYaw; // Roll starts out as undampened yaw intensity and is then scaled by pitch
  2441. g_vMotionBlurValues[3] *= ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ) * ( fabs( flCurrentPitch ) / 90.0f ); // Dampen roll based on pitch^3
  2442. //engine->Con_NPrintf( 4, "[2] before scale and bias: %6.2f", g_vMotionBlurValues[2] );
  2443. //engine->Con_NPrintf( 5, "[3] before scale and bias: %6.2f", g_vMotionBlurValues[3] );
  2444. //==============================================================//
  2445. // Time-adjust falling effect until we can do something smarter //
  2446. //==============================================================//
  2447. if ( flTimeElapsed > 0.0f )
  2448. g_vMotionBlurValues[2] /= flTimeElapsed * 30.0f; // 1/30th of a second?
  2449. else
  2450. g_vMotionBlurValues[2] = 0.0f;
  2451. // Scale and bias values after time adjustment
  2452. g_vMotionBlurValues[2] = clamp( ( fabs( g_vMotionBlurValues[2] ) - flMotionBlurFallingMin ) / ( flMotionBlurFallingMax - flMotionBlurFallingMin ), 0.0f, 1.0f ) * ( g_vMotionBlurValues[2] >= 0.0f ? 1.0f : -1.0f );
  2453. g_vMotionBlurValues[2] /= 30.0f; // To counter-adjust for time adjustment above
  2454. //=================//
  2455. // Apply intensity //
  2456. //=================//
  2457. g_vMotionBlurValues[0] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength;
  2458. g_vMotionBlurValues[1] *= flMotionBlurRotationIntensity * flMotionBlurGlobalStrength;
  2459. g_vMotionBlurValues[2] *= flMotionBlurFallingIntensity * flMotionBlurGlobalStrength;
  2460. g_vMotionBlurValues[3] *= flMotionBlurRollIntensity * flMotionBlurGlobalStrength;
  2461. //===============================================================//
  2462. // Dampen motion blur from 100%-0% as fps drops from 50fps-30fps //
  2463. //===============================================================//
  2464. if ( !IsX360() ) // I'm not doing this on the 360 yet since I can't test it
  2465. {
  2466. float flSlowFps = 30.0f;
  2467. float flFastFps = 50.0f;
  2468. float flCurrentFps = ( flTimeElapsed > 0.0f ) ? ( 1.0f / flTimeElapsed ) : 0.0f;
  2469. float flDampenFactor = clamp( ( ( flCurrentFps - flSlowFps ) / ( flFastFps - flSlowFps ) ), 0.0f, 1.0f );
  2470. //engine->Con_NPrintf( 4, "gpGlobals->realtime %.2f gpGlobals->curtime %.2f", gpGlobals->realtime, gpGlobals->curtime );
  2471. //engine->Con_NPrintf( 5, "flCurrentFps %.2f", flCurrentFps );
  2472. //engine->Con_NPrintf( 7, "flTimeElapsed %.2f", flTimeElapsed );
  2473. g_vMotionBlurValues[0] *= flDampenFactor;
  2474. g_vMotionBlurValues[1] *= flDampenFactor;
  2475. g_vMotionBlurValues[2] *= flDampenFactor;
  2476. g_vMotionBlurValues[3] *= flDampenFactor;
  2477. //engine->Con_NPrintf( 6, "Dampen: %.2f", flDampenFactor );
  2478. }
  2479. //engine->Con_NPrintf( 6, "Final values: { %6.2f%%, %6.2f%%, %6.2f%%, %6.2f%% }", g_vMotionBlurValues[0]*100.0f, g_vMotionBlurValues[1]*100.0f, g_vMotionBlurValues[2]*100.0f, g_vMotionBlurValues[3]*100.0f );
  2480. }
  2481. //============================================//
  2482. // Zero out blur if still in that time window //
  2483. //============================================//
  2484. if ( gpGlobals->realtime < s_flNoRotationalMotionBlurUntil )
  2485. {
  2486. //engine->Con_NPrintf( 9, " No Rotation @ %f ", gpGlobals->realtime );
  2487. // Zero out rotational blur but leave forward/falling blur alone
  2488. g_vMotionBlurValues[0] = 0.0f; // X
  2489. g_vMotionBlurValues[1] = 0.0f; // Y
  2490. g_vMotionBlurValues[3] = 0.0f; // Roll
  2491. }
  2492. else
  2493. {
  2494. s_flNoRotationalMotionBlurUntil = 0.0f;
  2495. }
  2496. //====================================//
  2497. // Store current frame for next frame //
  2498. //====================================//
  2499. VectorCopy( vCurrentPosition, s_vPreviousPositon );
  2500. s_mPreviousFrameBasisVectors = mCurrentBasisVectors;
  2501. s_flPreviousPitch = flCurrentPitch;
  2502. s_flPreviousYaw = flCurrentYaw;
  2503. s_flLastTimeUpdate = gpGlobals->realtime;
  2504. }
  2505. //=============================================================================================//
  2506. // Render quad and let material proxy pick up the g_vMotionBlurValues[4] values just set above //
  2507. //=============================================================================================//
  2508. if ( true )
  2509. {
  2510. CMatRenderContextPtr pRenderContext( materials );
  2511. //pRenderContext->PushRenderTargetAndViewport();
  2512. ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
  2513. int nSrcWidth = pSrc->GetActualWidth();
  2514. int nSrcHeight = pSrc->GetActualHeight();
  2515. int dest_width, dest_height, nDummy;
  2516. pRenderContext->GetViewport( nDummy, nDummy, dest_width, dest_height );
  2517. if ( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_FLOAT )
  2518. {
  2519. UpdateScreenEffectTexture( 0, x, y, w, h, true ); // Do we need to check if we already did this?
  2520. }
  2521. // Get material pointer
  2522. IMaterial *pMatMotionBlur = materials->FindMaterial( "dev/motion_blur", TEXTURE_GROUP_OTHER, true );
  2523. //SetRenderTargetAndViewPort( dest_rt0 );
  2524. //pRenderContext->PopRenderTargetAndViewport();
  2525. if ( pMatMotionBlur != NULL )
  2526. {
  2527. pRenderContext->DrawScreenSpaceRectangle(
  2528. pMatMotionBlur,
  2529. 0, 0, dest_width, dest_height,
  2530. 0, 0, nSrcWidth-1, nSrcHeight-1,
  2531. nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
  2532. if ( g_bDumpRenderTargets )
  2533. {
  2534. DumpTGAofRenderTarget( dest_width, dest_height, "MotionBlur" );
  2535. }
  2536. }
  2537. }
  2538. }