From b7d92f696edebeb716e32e2179894dea483cb3ac Mon Sep 17 00:00:00 2001
From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Sun, 27 Nov 2016 22:20:28 +0000
Subject: [PATCH] Significant rework of bouncegrid photon distribution and
 coloring.

r_editlights mode now shows render stats in the overlay.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12300 d7cf8633-e32d-0410-b094-e92efae38249
---
 client.h   |   7 +++-
 r_shadow.c | 118 +++++++++++++++++++++++++++++++++++++++--------------
 r_shadow.h |   1 +
 3 files changed, 93 insertions(+), 33 deletions(-)

diff --git a/client.h b/client.h
index 4230c6a8..aae1c868 100644
--- a/client.h
+++ b/client.h
@@ -395,8 +395,11 @@ typedef struct rtlight_s
 	rtlight_particle_t *particlecache_particles;
 
 	/// bouncegrid light info
-	float photoncolor[3];
-	float photons;
+	float bouncegrid_photoncolor[3];
+	float bouncegrid_photons;
+	int bouncegrid_hits;
+	int bouncegrid_traces;
+	float bouncegrid_effectiveradius;
 }
 rtlight_t;
 
diff --git a/r_shadow.c b/r_shadow.c
index e888fc01..caef494e 100644
--- a/r_shadow.c
+++ b/r_shadow.c
@@ -337,6 +337,7 @@ cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel texture
 cvar_t r_shadow_bouncegrid = {CVAR_SAVE, "r_shadow_bouncegrid", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity) using a 3D texture covering the scene, only active on levels with realtime lights active (r_shadow_realtime_world is usually required for these)"};
 cvar_t r_shadow_bouncegrid_blur = {CVAR_SAVE, "r_shadow_bouncegrid_blur", "1", "apply a 1-radius blur on bouncegrid to denoise it and deal with boundary issues with surfaces"};
 cvar_t r_shadow_bouncegrid_bounceanglediffuse = {CVAR_SAVE, "r_shadow_bouncegrid_bounceanglediffuse", "0", "use random bounce direction rather than true reflection, makes some corner areas dark"};
+cvar_t r_shadow_bouncegrid_dynamic_bounceminimumintensity = { CVAR_SAVE, "r_shadow_bouncegrid_dynamic_bounceminimumintensity", "0.01", "stop bouncing once intensity drops below this fraction of the original particle color" };
 cvar_t r_shadow_bouncegrid_dynamic_culllightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_culllightpaths", "1", "skip accumulating light in the bouncegrid texture where the light paths are out of view (dynamic mode only)"};
 cvar_t r_shadow_bouncegrid_dynamic_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_dlightparticlemultiplier", "1", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
 cvar_t r_shadow_bouncegrid_dynamic_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"};
@@ -359,6 +360,7 @@ cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_
 cvar_t r_shadow_bouncegrid_sortlightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_sortlightpaths", "1", "sort light paths before accumulating them into the bouncegrid texture, this reduces cpu cache misses"};
 cvar_t r_shadow_bouncegrid_lightpathsize = {CVAR_SAVE, "r_shadow_bouncegrid_lightpathsize", "1", "width of the light path for accumulation of light in the bouncegrid texture"};
 cvar_t r_shadow_bouncegrid_static = {CVAR_SAVE, "r_shadow_bouncegrid_static", "1", "use static radiosity solution (high quality) rather than dynamic (splotchy)"};
+cvar_t r_shadow_bouncegrid_static_bounceminimumintensity = { CVAR_SAVE, "r_shadow_bouncegrid_static_bounceminimumintensity", "0.01", "stop bouncing once intensity drops below this fraction of the original particle color" };
 cvar_t r_shadow_bouncegrid_static_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_static_directionalshading", "1", "whether to use directionalshading when in static mode"};
 cvar_t r_shadow_bouncegrid_static_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_static_energyperphoton", "10000", "amount of light that one photon should represent in static mode"};
 cvar_t r_shadow_bouncegrid_static_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_static_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1) when in static mode"};
@@ -802,6 +804,7 @@ void R_Shadow_Init(void)
 	Cvar_RegisterVariable(&r_shadow_bouncegrid);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_blur);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse);
+	Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_bounceminimumintensity);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_culllightpaths);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_directionalshading);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_dlightparticlemultiplier);
@@ -824,12 +827,13 @@ void R_Shadow_Init(void)
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_particleintensity);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_sortlightpaths);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_static);
-	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
+	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_bounceminimumintensity);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading);
+	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_lightradiusscale);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce);
 	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxphotons);
-	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton);
+	Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
 	Cvar_RegisterVariable(&r_coronas);
 	Cvar_RegisterVariable(&r_coronas_occlusionsizescale);
 	Cvar_RegisterVariable(&r_coronas_occlusionquery);
@@ -2547,6 +2551,7 @@ static void R_Shadow_BounceGrid_GenerateSettings(r_shadow_bouncegrid_settings_t
 {
 	qboolean s = r_shadow_bouncegrid_static.integer != 0;
 	float spacing = s ? r_shadow_bouncegrid_static_spacing.value : r_shadow_bouncegrid_dynamic_spacing.value;
+	float bounceminimumintensity = s ? r_shadow_bouncegrid_static_bounceminimumintensity.value : r_shadow_bouncegrid_dynamic_bounceminimumintensity.value;
 
 	// prevent any garbage in alignment padded areas as we'll be using memcmp
 	memset(settings, 0, sizeof(*settings)); 
@@ -2564,13 +2569,14 @@ static void R_Shadow_BounceGrid_GenerateSettings(r_shadow_bouncegrid_settings_t
 	settings->lightradiusscale              = (s ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_dynamic_lightradiusscale.value);
 	settings->maxbounce                     = (s ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_dynamic_maxbounce.integer);
 	settings->particlebounceintensity       = r_shadow_bouncegrid_particlebounceintensity.value;
-	settings->particleintensity             = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings->directionalshading ? 4.0f : 1.0f) / (spacing * spacing);
+	settings->particleintensity             = r_shadow_bouncegrid_particleintensity.value * (settings->directionalshading ? 4.0f : 1.0f) * 16384 / (spacing * spacing) / 262144.0f;
 	settings->maxphotons                    = s ? r_shadow_bouncegrid_static_maxphotons.integer : r_shadow_bouncegrid_dynamic_maxphotons.integer;
-	settings->energyperphoton            = s ? r_shadow_bouncegrid_static_energyperphoton.integer : r_shadow_bouncegrid_dynamic_energyperphoton.integer;
+	settings->energyperphoton               = s ? r_shadow_bouncegrid_static_energyperphoton.value : r_shadow_bouncegrid_dynamic_energyperphoton.value;
 	settings->spacing[0]                    = spacing;
 	settings->spacing[1]                    = spacing;
 	settings->spacing[2]                    = spacing;
 	settings->stablerandom                  = s ? 1 : r_shadow_bouncegrid_dynamic_stablerandom.integer;
+	settings->bounceminimumintensity2       = bounceminimumintensity * bounceminimumintensity;
 
 	// bound the values for sanity
 	settings->maxphotons = bound(1, settings->maxphotons, 25000000);
@@ -2721,17 +2727,16 @@ static void R_Shadow_BounceGrid_UpdateSpacing(void)
 	Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegrid_state.matrix, m);
 }
 
-#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
-
 // enumerate world rtlights and sum the overall amount of light in the world,
 // from that we can calculate a scaling factor to fairly distribute photons
 // to all the lights
 //
 // this modifies rtlight->photoncolor and rtlight->photons
-static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag, float *photonscaling)
+static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag)
 {
 	float normalphotonscaling;
-	float maxphotonscaling;
+	float photonscaling;
+	float photonintensity;
 	float photoncount = 0.0f;
 	float lightintensity;
 	float radius;
@@ -2742,16 +2747,20 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
 	unsigned int lightindex;
 	dlight_t *light;
 	rtlight_t *rtlight;
+	normalphotonscaling = 1.0f / max(0.0001f, settings->energyperphoton);
 	for (lightindex = 0;lightindex < range2;lightindex++)
 	{
 		if (lightindex < range)
 		{
-			light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+			light = (dlight_t *)Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
 			if (!light)
 				continue;
 			rtlight = &light->rtlight;
-			VectorClear(rtlight->photoncolor);
-			rtlight->photons = 0;
+			VectorClear(rtlight->bouncegrid_photoncolor);
+			rtlight->bouncegrid_photons = 0;
+			rtlight->bouncegrid_hits = 0;
+			rtlight->bouncegrid_traces = 0;
+			rtlight->bouncegrid_effectiveradius = 0;
 			if (!(light->flags & flag))
 				continue;
 			if (settings->staticmode)
@@ -2760,12 +2769,17 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
 				if (rtlight->style > 0 && r_shadow_bouncegrid.integer != 2)
 					continue;
 			}
+			else if (r_shadow_debuglight.integer >= 0 && (int)lightindex != r_shadow_debuglight.integer)
+				continue;
 		}
 		else
 		{
 			rtlight = r_refdef.scene.lights[lightindex - range];
-			VectorClear(rtlight->photoncolor);
-			rtlight->photons = 0;
+			VectorClear(rtlight->bouncegrid_photoncolor);
+			rtlight->bouncegrid_photons = 0;
+			rtlight->bouncegrid_hits = 0;
+			rtlight->bouncegrid_traces = 0;
+			rtlight->bouncegrid_effectiveradius = 0;
 		}
 		// draw only visible lights (major speedup)
 		radius = rtlight->radius * settings->lightradiusscale;
@@ -2792,9 +2806,9 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
 		if (rtlight->radius == 0.0f || VectorLength2(rtlight->color) == 0.0f)
 			continue;
 		w *= ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
-		VectorScale(rtlight->color, w, rtlight->photoncolor);
+		VectorScale(rtlight->color, w, rtlight->bouncegrid_photoncolor);
 		// skip lights that will emit no photons
-		if (!VectorLength2(rtlight->photoncolor))
+		if (!VectorLength2(rtlight->bouncegrid_photoncolor))
 			continue;
 		// shoot particles from this light
 		// use a calculation for the number of particles that will not
@@ -2805,20 +2819,41 @@ static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *se
 		lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
 		if (lightindex >= range)
 			lightintensity *= settings->dlightparticlemultiplier;
-		rtlight->photons = bound(0.0f, lightintensity * s * s, MAXBOUNCEGRIDPARTICLESPERLIGHT);
-		photoncount += rtlight->photons;
+		rtlight->bouncegrid_photons = lightintensity * s * s * normalphotonscaling;
+		photoncount += rtlight->bouncegrid_photons;
+		VectorScale(rtlight->bouncegrid_photoncolor, settings->particleintensity * settings->energyperphoton, rtlight->bouncegrid_photoncolor);
 		// if the lightstyle happens to be off right now, we can skip actually
 		// firing the photons, but we did have to count them in the total.
 		//if (VectorLength2(rtlight->photoncolor) == 0.0f)
-		//	rtlight->photons = 0;
+		//	rtlight->bouncegrid_photons = 0;
 	}
 	// the user provided an energyperphoton value which we try to use
 	// if that results in too many photons to shoot this frame, then we cap it
 	// which causes photons to appear/disappear from frame to frame, so we don't
 	// like doing that in the typical case
-	normalphotonscaling = 1.0f / max(0.0001f, settings->energyperphoton);
-	maxphotonscaling = (float)settings->maxphotons / max(1, photoncount);
-	*photonscaling = min(normalphotonscaling, maxphotonscaling);
+	photonscaling = 1.0f;
+	photonintensity = 1.0f;
+	if (photoncount > settings->maxphotons)
+	{
+		photonscaling = settings->maxphotons / photoncount;
+		photonintensity = 1.0f / photonscaling;
+	}
+
+	// modify the lights to reflect our computed scaling
+	for (lightindex = 0; lightindex < range2; lightindex++)
+	{
+		if (lightindex < range)
+		{
+			light = (dlight_t *)Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+			if (!light)
+				continue;
+			rtlight = &light->rtlight;
+		}
+		else
+			rtlight = r_refdef.scene.lights[lightindex - range];
+		rtlight->bouncegrid_photons *= photonscaling;
+		VectorScale(rtlight->bouncegrid_photoncolor, photonintensity, rtlight->bouncegrid_photoncolor);
+	}
 }
 
 static int R_Shadow_BounceGrid_SplatPathCompare(const void *pa, const void *pb)
@@ -3223,7 +3258,7 @@ static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void)
 	r_shadow_bouncegrid_state.lastupdatetime = realtime;
 }
 
-static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, float photonscaling, int flag)
+static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, int flag)
 {
 	vec3_t bouncerandom[10];
 	dlight_t *light;
@@ -3233,6 +3268,7 @@ static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t sett
 	int maxbounce;
 	int shootparticles;
 	int shotparticles;
+	float bounceminimumintensity2;
 	trace_t cliptrace;
 	//trace_t cliptrace2;
 	//trace_t cliptrace3;
@@ -3275,15 +3311,20 @@ static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t sett
 		// note that this code used to keep track of residual photons and
 		// distribute them evenly to achieve exactly a desired photon count,
 		// but that caused unwanted flickering in dynamic mode
-		shootparticles = (int)floor(rtlight->photons * photonscaling);
+		shootparticles = (int)floor(rtlight->bouncegrid_photons);
 		// skip if we won't be shooting any photons
 		if (!shootparticles)
 			continue;
 		radius = rtlight->radius * settings.lightradiusscale;
-		s = settings.particleintensity / shootparticles;
-		VectorScale(rtlight->photoncolor, s, baseshotcolor);
+		//s = settings.particleintensity / shootparticles;
+		//VectorScale(rtlight->bouncegrid_photoncolor, s, baseshotcolor);
+		VectorCopy(rtlight->bouncegrid_photoncolor, baseshotcolor);
+		if (VectorLength2(baseshotcolor) <= 0.0f)
+			continue;
 		r_refdef.stats[r_stat_bouncegrid_lights]++;
 		r_refdef.stats[r_stat_bouncegrid_particles] += shootparticles;
+		// we stop caring about bounces once the brightness goes below this fraction of the original intensity
+		bounceminimumintensity2 = VectorLength(baseshotcolor) * settings.bounceminimumintensity2;
 		switch (settings.stablerandom)
 		{
 		default:
@@ -3340,6 +3381,7 @@ static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t sett
 			for (bouncecount = 0;;bouncecount++)
 			{
 				r_refdef.stats[r_stat_bouncegrid_traces]++;
+				rtlight->bouncegrid_traces++;
 				//r_refdef.scene.worldmodel->TraceLineAgainstSurfaces(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace, clipstart, clipend, hitsupercontentsmask);
 				//r_refdef.scene.worldmodel->TraceLine(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace2, clipstart, clipend, hitsupercontentsmask);
 				if (settings.staticmode || settings.stablerandom <= 0)
@@ -3359,9 +3401,13 @@ static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t sett
 					VectorCopy(cliptrace.endpos, hitpos);
 					R_Shadow_BounceGrid_AddSplatPath(clipstart, hitpos, shotcolor);
 				}
+				s = VectorDistance(rtlight->shadoworigin, cliptrace.endpos);
+				if (rtlight->bouncegrid_effectiveradius < s)
+					rtlight->bouncegrid_effectiveradius = s;
 				if (cliptrace.fraction >= 1.0f)
 					break;
 				r_refdef.stats[r_stat_bouncegrid_hits]++;
+				rtlight->bouncegrid_hits++;
 				if (bouncecount >= maxbounce)
 					break;
 				// scale down shot color by bounce intensity and texture color (or 50% if no texture reported)
@@ -3375,7 +3421,7 @@ static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t sett
 				surfcolor[1] = min(surfcolor[1], 1.0f);
 				surfcolor[2] = min(surfcolor[2], 1.0f);
 				VectorMultiply(shotcolor, surfcolor, shotcolor);
-				if (VectorLength2(baseshotcolor) == 0.0f)
+				if (VectorLength2(shotcolor) <= bounceminimumintensity2)
 					break;
 				r_refdef.stats[r_stat_bouncegrid_bounces]++;
 				if (settings.bounceanglediffuse)
@@ -3409,7 +3455,6 @@ void R_Shadow_UpdateBounceGridTexture(void)
 	unsigned int range; // number of world lights
 	unsigned int range1; // number of dynamic lights (or zero if disabled)
 	unsigned int range2; // range+range1
-	float photonscaling;
 
 	enable = R_Shadow_BounceGrid_CheckEnable(flag);
 	
@@ -3461,10 +3506,10 @@ void R_Shadow_UpdateBounceGridTexture(void)
 	range2 = range + range1;
 
 	// calculate weighting factors for distributing photons among the lights
-	R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag, &photonscaling);
+	R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag);
 
 	// trace the photons from lights and accumulate illumination
-	R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, photonscaling, flag);
+	R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, flag);
 
 	// clear the texture
 	R_Shadow_BounceGrid_ClearPixels();
@@ -7144,9 +7189,9 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void)
 	// draw properties on screen
 	if (!r_editlights_drawproperties.integer)
 		return;
-	x = vid_conwidth.value - 240;
+	x = vid_conwidth.value - 320;
 	y = 5;
-	DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
+	DrawQ_Pic(x-5, y-5, NULL, 250, 243, 0, 0, 0, 0.75, 0);
 	lightnumber = -1;
 	lightcount = 0;
 	range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
@@ -7179,6 +7224,17 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void)
 	dpsnprintf(temp, sizeof(temp), "Specular     : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
 	dpsnprintf(temp, sizeof(temp), "NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
 	dpsnprintf(temp, sizeof(temp), "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
+	y += 8;
+	dpsnprintf(temp, sizeof(temp), "Render stats\n"); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "Current color: %.3f %.3f %.3f\n", r_shadow_selectedlight->rtlight.currentcolor[0], r_shadow_selectedlight->rtlight.currentcolor[1], r_shadow_selectedlight->rtlight.currentcolor[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "Shadow size  : %ix%ix6\n", r_shadow_selectedlight->rtlight.shadowmapatlassidesize, r_shadow_selectedlight->rtlight.shadowmapatlassidesize); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "World surfs  : %i\n", r_shadow_selectedlight->rtlight.cached_numsurfaces); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "Shadow ents  : %i + %i noself\n", r_shadow_selectedlight->rtlight.cached_numshadowentities, r_shadow_selectedlight->rtlight.cached_numshadowentities_noselfshadow); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "Lit ents     : %i + %i noself\n", r_shadow_selectedlight->rtlight.cached_numlightentities, r_shadow_selectedlight->rtlight.cached_numlightentities_noselfshadow); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "BG photons   : %.3f\n", r_shadow_selectedlight->rtlight.bouncegrid_photons); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "BG radius    : %.0f\n", r_shadow_selectedlight->rtlight.bouncegrid_effectiveradius); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "BG color     : %.3f %.3f %.3f\n", r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[0], r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[1], r_shadow_selectedlight->rtlight.bouncegrid_photoncolor[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
+	dpsnprintf(temp, sizeof(temp), "BG stats     : %i traces %i hits\n", r_shadow_selectedlight->rtlight.bouncegrid_traces, r_shadow_selectedlight->rtlight.bouncegrid_hits); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT); y += 8;
 }
 
 static void R_Shadow_EditLights_ToggleShadow_f(void)
diff --git a/r_shadow.h b/r_shadow.h
index f59edaa7..10f8e1e0 100644
--- a/r_shadow.h
+++ b/r_shadow.h
@@ -57,6 +57,7 @@ typedef struct r_shadow_bouncegrid_settings_s
 	float energyperphoton;
 	float spacing[3];
 	int stablerandom;
+	float bounceminimumintensity2;
 }
 r_shadow_bouncegrid_settings_t;
 
-- 
2.39.5