+}
+
+static void R_Shadow_BounceGrid_BlurPixels(void)
+{
+ float *pixels[4];
+ unsigned int resolution[3];
+
+ if (!r_shadow_bouncegrid_state.settings.blur)
+ return;
+
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+
+ pixels[0] = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index];
+ pixels[1] = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index ^ 1];
+ pixels[2] = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index];
+ pixels[3] = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index ^ 1];
+
+ // blur on X
+ R_Shadow_BounceGrid_BlurPixelsInDirection(pixels[0], pixels[1], 4);
+ // blur on Y
+ R_Shadow_BounceGrid_BlurPixelsInDirection(pixels[1], pixels[2], resolution[0] * 4);
+ // blur on Z
+ R_Shadow_BounceGrid_BlurPixelsInDirection(pixels[2], pixels[3], resolution[0] * resolution[1] * 4);
+
+ // toggle the state, highpixels now points to pixels[3] result
+ r_shadow_bouncegrid_state.highpixels_index ^= 1;
+ r_shadow_bouncegrid_state.highpixels = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index];
+}
+
+static void R_Shadow_BounceGrid_ConvertPixelsAndUpload(void)
+{
+ int floatcolors = r_shadow_bouncegrid_state.settings.floatcolors;
+ unsigned char *pixelsbgra8 = NULL;
+ unsigned char *pixelbgra8;
+ unsigned short *pixelsrgba16f = NULL;
+ unsigned short *pixelrgba16f;
+ float *pixelsrgba32f = NULL;
+ float *highpixels = r_shadow_bouncegrid_state.highpixels;
+ float *highpixel;
+ float *bandpixel;
+ unsigned int pixelsperband = r_shadow_bouncegrid_state.pixelsperband;
+ unsigned int pixelbands = r_shadow_bouncegrid_state.pixelbands;
+ unsigned int pixelband;
+ unsigned int x, y, z;
+ unsigned int index, bandindex;
+ unsigned int resolution[3];
+ int c[4];
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+
+ if (r_shadow_bouncegrid_state.createtexture && r_shadow_bouncegrid_state.texture)
+ {
+ R_FreeTexture(r_shadow_bouncegrid_state.texture);
+ r_shadow_bouncegrid_state.texture = NULL;
+ }
+
+ // if bentnormals exist, we need to normalize and bias them for the shader
+ if (pixelbands > 1)
+ {
+ pixelband = 1;
+ for (z = 0;z < resolution[2]-1;z++)
+ {
+ for (y = 0;y < resolution[1]-1;y++)
+ {
+ x = 1;
+ index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x;
+ highpixel = highpixels + 4*index;
+ for (;x < resolution[0]-1;x++, index++, highpixel += 4)
+ {
+ // only convert pixels that were hit by photons
+ if (highpixel[3] != 0.0f)
+ VectorNormalize(highpixel);
+ VectorSet(highpixel, highpixel[0] * 0.5f + 0.5f, highpixel[1] * 0.5f + 0.5f, highpixel[2] * 0.5f + 0.5f);
+ highpixel[pixelsperband * 4 + 3] = 1.0f;
+ }
+ }
+ }
+ }
+
+ // start by clearing the pixels array - we won't be writing to all of it
+ //
+ // then process only the pixels that have at least some color, skipping
+ // the higher bands for speed on pixels that are black
+ switch (floatcolors)
+ {
+ case 0:
+ if (r_shadow_bouncegrid_state.u8pixels == NULL)
+ r_shadow_bouncegrid_state.u8pixels = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_bouncegrid_state.numpixels * sizeof(unsigned char[4]));
+ pixelsbgra8 = r_shadow_bouncegrid_state.u8pixels;
+ for (pixelband = 0;pixelband < pixelbands;pixelband++)
+ {
+ if (pixelband == 1)
+ memset(pixelsbgra8 + pixelband * r_shadow_bouncegrid_state.bytesperband, 128, r_shadow_bouncegrid_state.bytesperband);
+ else
+ memset(pixelsbgra8 + pixelband * r_shadow_bouncegrid_state.bytesperband, 0, r_shadow_bouncegrid_state.bytesperband);
+ }
+ for (z = 1;z < resolution[2]-1;z++)
+ {
+ for (y = 1;y < resolution[1]-1;y++)
+ {
+ x = 1;
+ pixelband = 0;
+ index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x;
+ highpixel = highpixels + 4*index;
+ for (;x < resolution[0]-1;x++, index++, highpixel += 4)
+ {
+ // only convert pixels that were hit by photons
+ if (VectorLength2(highpixel))
+ {
+ // normalize the bentnormal now
+ if (pixelbands > 1)
+ {
+ VectorNormalize(highpixel + pixelsperband * 4);
+ highpixel[pixelsperband * 4 + 3] = 1.0f;
+ }
+ // process all of the pixelbands for this pixel
+ for (pixelband = 0, bandindex = index;pixelband < pixelbands;pixelband++, bandindex += pixelsperband)
+ {
+ pixelbgra8 = pixelsbgra8 + 4*bandindex;
+ bandpixel = highpixels + 4*bandindex;
+ c[0] = (int)(bandpixel[0]*256.0f);
+ c[1] = (int)(bandpixel[1]*256.0f);
+ c[2] = (int)(bandpixel[2]*256.0f);
+ c[3] = (int)(bandpixel[3]*256.0f);
+ pixelbgra8[2] = (unsigned char)bound(0, c[0], 255);
+ pixelbgra8[1] = (unsigned char)bound(0, c[1], 255);
+ pixelbgra8[0] = (unsigned char)bound(0, c[2], 255);
+ pixelbgra8[3] = (unsigned char)bound(0, c[3], 255);
+ }
+ }
+ }
+ }
+ }
+
+ if (!r_shadow_bouncegrid_state.createtexture)
+ R_UpdateTexture(r_shadow_bouncegrid_state.texture, pixelsbgra8, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands);
+ else
+ r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixelsbgra8, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+ break;
+ case 1:
+ if (r_shadow_bouncegrid_state.fp16pixels == NULL)
+ r_shadow_bouncegrid_state.fp16pixels = (unsigned short *)Mem_Alloc(r_main_mempool, r_shadow_bouncegrid_state.numpixels * sizeof(unsigned short[4]));
+ pixelsrgba16f = r_shadow_bouncegrid_state.fp16pixels;
+ memset(pixelsrgba16f, 0, r_shadow_bouncegrid_state.numpixels * sizeof(unsigned short[4]));
+ for (z = 1;z < resolution[2]-1;z++)
+ {
+ for (y = 1;y < resolution[1]-1;y++)
+ {
+ x = 1;
+ pixelband = 0;
+ index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x;
+ highpixel = highpixels + 4*index;
+ for (;x < resolution[0]-1;x++, index++, highpixel += 4)
+ {
+ // only convert pixels that were hit by photons
+ if (VectorLength2(highpixel))
+ {
+ // process all of the pixelbands for this pixel
+ for (pixelband = 0, bandindex = index;pixelband < pixelbands;pixelband++, bandindex += pixelsperband)
+ {
+ // time to have fun with IEEE 754 bit hacking...
+ union {
+ float f[4];
+ unsigned int raw[4];
+ } u;
+ pixelrgba16f = pixelsrgba16f + 4*bandindex;
+ bandpixel = highpixels + 4*bandindex;
+ VectorCopy4(bandpixel, u.f);
+ VectorCopy4(u.raw, c);
+ // this math supports negative numbers, snaps denormals to zero
+ //pixelrgba16f[0] = (unsigned short)(((c[0] & 0x7FFFFFFF) < 0x38000000) ? 0 : (((c[0] - 0x38000000) >> 13) & 0x7FFF) | ((c[0] >> 16) & 0x8000));
+ //pixelrgba16f[1] = (unsigned short)(((c[1] & 0x7FFFFFFF) < 0x38000000) ? 0 : (((c[1] - 0x38000000) >> 13) & 0x7FFF) | ((c[1] >> 16) & 0x8000));
+ //pixelrgba16f[2] = (unsigned short)(((c[2] & 0x7FFFFFFF) < 0x38000000) ? 0 : (((c[2] - 0x38000000) >> 13) & 0x7FFF) | ((c[2] >> 16) & 0x8000));
+ //pixelrgba16f[3] = (unsigned short)(((c[3] & 0x7FFFFFFF) < 0x38000000) ? 0 : (((c[3] - 0x38000000) >> 13) & 0x7FFF) | ((c[3] >> 16) & 0x8000));
+ // this math does not support negative
+ pixelrgba16f[0] = (unsigned short)((c[0] < 0x38000000) ? 0 : ((c[0] - 0x38000000) >> 13));
+ pixelrgba16f[1] = (unsigned short)((c[1] < 0x38000000) ? 0 : ((c[1] - 0x38000000) >> 13));
+ pixelrgba16f[2] = (unsigned short)((c[2] < 0x38000000) ? 0 : ((c[2] - 0x38000000) >> 13));
+ pixelrgba16f[3] = (unsigned short)((c[3] < 0x38000000) ? 0 : ((c[3] - 0x38000000) >> 13));
+ }
+ }
+ }
+ }
+ }
+
+ if (!r_shadow_bouncegrid_state.createtexture)
+ R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba16f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands);
+ else
+ r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, (const unsigned char *)pixelsrgba16f, TEXTYPE_COLORBUFFER16F, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+ break;
+ case 2:
+ // our native format happens to match, so this is easy.
+ pixelsrgba32f = highpixels;
+
+ if (!r_shadow_bouncegrid_state.createtexture)
+ R_UpdateTexture(r_shadow_bouncegrid_state.texture, (const unsigned char *)pixelsrgba32f, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands);
+ else
+ r_shadow_bouncegrid_state.texture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, (const unsigned char *)pixelsrgba32f, TEXTYPE_COLORBUFFER32F, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+ break;
+ }
+
+ 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, int flag)
+{
+ vec3_t bouncerandom[10];
+ dlight_t *light;
+ int bouncecount;
+ int hitsupercontentsmask;
+ int skipsupercontentsmask;
+ int skipmaterialflagsmask;
+ int maxbounce;
+ int shootparticles;
+ int shotparticles;
+ float bounceminimumintensity2;
+ trace_t cliptrace;
+ //trace_t cliptrace2;
+ //trace_t cliptrace3;
+ unsigned int lightindex;
+ unsigned int seed;
+ randomseed_t randomseed;
+ vec3_t shotcolor;
+ vec3_t baseshotcolor;
+ vec3_t surfcolor;
+ vec3_t clipend;
+ vec3_t clipstart;
+ vec3_t clipdiff;
+ vec_t radius;
+ vec_t distancetraveled;
+ vec_t s;
+ rtlight_t *rtlight;
+
+ // compute a seed for the unstable random modes
+ Math_RandomSeed_FromInts(&randomseed, 0, 0, 0, realtime * 1000.0);
+ seed = realtime * 1000.0;
+
+ r_shadow_bouncegrid_state.numsplatpaths = 0;
+
+ // figure out what we want to interact with
+ if (settings.hitmodels)
+ hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
+ else
+ hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
+ skipsupercontentsmask = 0;
+ skipmaterialflagsmask = MATERIALFLAGMASK_TRANSLUCENT;
+ maxbounce = settings.maxbounce;
+