+}
+
+static int R_Shadow_BounceGrid_SplatPathCompare(const void *pa, const void *pb)
+{
+ r_shadow_bouncegrid_splatpath_t *a = (r_shadow_bouncegrid_splatpath_t *)pa;
+ r_shadow_bouncegrid_splatpath_t *b = (r_shadow_bouncegrid_splatpath_t *)pb;
+ // we only really care about sorting by Z
+ if (a->point[2] < b->point[2])
+ return -1;
+ if (a->point[2] > b->point[2])
+ return 1;
+ return 0;
+}
+
+static void R_Shadow_BounceGrid_ClearPixels(void)
+{
+ // clear the highpixels array we'll be accumulating into
+ if (r_shadow_bouncegrid_state.blurpixels[0] == NULL)
+ r_shadow_bouncegrid_state.blurpixels[0] = (float *)Mem_Alloc(r_main_mempool, r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ if (r_shadow_bouncegrid_state.settings.blur && r_shadow_bouncegrid_state.blurpixels[1] == NULL)
+ r_shadow_bouncegrid_state.blurpixels[1] = (float *)Mem_Alloc(r_main_mempool, r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ r_shadow_bouncegrid_state.highpixels_index = 0;
+ r_shadow_bouncegrid_state.highpixels = r_shadow_bouncegrid_state.blurpixels[r_shadow_bouncegrid_state.highpixels_index];
+ memset(r_shadow_bouncegrid_state.highpixels, 0, r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+}
+
+static void R_Shadow_BounceGrid_PerformSplats(void)
+{
+ r_shadow_bouncegrid_splatpath_t *splatpaths = r_shadow_bouncegrid_state.splatpaths;
+ r_shadow_bouncegrid_splatpath_t *splatpath;
+ float *highpixels = r_shadow_bouncegrid_state.highpixels;
+ int numsplatpaths = r_shadow_bouncegrid_state.numsplatpaths;
+ int splatindex;
+ vec3_t steppos;
+ vec3_t stepdelta;
+ vec3_t dir;
+ vec_t lightpathsize_current;
+ vec_t lightpathsize_perstep;
+ float splatcolor[32];
+ int resolution[3];
+ int pixelsperband = r_shadow_bouncegrid_state.pixelsperband;
+ int pixelbands = r_shadow_bouncegrid_state.pixelbands;
+ int numsteps;
+ int step;
+
+ // hush warnings about uninitialized data - pixelbands doesn't change but...
+ memset(splatcolor, 0, sizeof(splatcolor));
+
+ // we use this a lot, so get a local copy
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+
+ // sort the splats before we execute them, to reduce cache misses
+ if (r_shadow_bouncegrid_sortlightpaths.integer)
+ qsort(splatpaths, numsplatpaths, sizeof(*splatpaths), R_Shadow_BounceGrid_SplatPathCompare);
+
+ splatpath = splatpaths;
+ for (splatindex = 0;splatindex < numsplatpaths;splatindex++, splatpath++)
+ {
+ // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ)
+ // accumulate average shotcolor
+ VectorCopy(splatpath->splatdir, dir);
+ splatcolor[ 0] = splatpath->splatcolor[0];
+ splatcolor[ 1] = splatpath->splatcolor[1];
+ splatcolor[ 2] = splatpath->splatcolor[2];
+ splatcolor[ 3] = 0.0f;
+ if (pixelbands > 1)
+ {
+ // store bentnormal in case the shader has a use for it,
+ // bentnormal is an intensity-weighted average of the directions,
+ // and will be normalized on conversion to texture pixels.
+ splatcolor[ 4] = dir[0] * splatpath->splatintensity;
+ splatcolor[ 5] = dir[1] * splatpath->splatintensity;
+ splatcolor[ 6] = dir[2] * splatpath->splatintensity;
+ splatcolor[ 7] = splatpath->splatintensity;
+ // for each color component (R, G, B) calculate the amount that a
+ // direction contributes
+ splatcolor[ 8] = splatcolor[0] * max(0.0f, dir[0]);
+ splatcolor[ 9] = splatcolor[0] * max(0.0f, dir[1]);
+ splatcolor[10] = splatcolor[0] * max(0.0f, dir[2]);
+ splatcolor[11] = 0.0f;
+ splatcolor[12] = splatcolor[1] * max(0.0f, dir[0]);
+ splatcolor[13] = splatcolor[1] * max(0.0f, dir[1]);
+ splatcolor[14] = splatcolor[1] * max(0.0f, dir[2]);
+ splatcolor[15] = 0.0f;
+ splatcolor[16] = splatcolor[2] * max(0.0f, dir[0]);
+ splatcolor[17] = splatcolor[2] * max(0.0f, dir[1]);
+ splatcolor[18] = splatcolor[2] * max(0.0f, dir[2]);
+ splatcolor[19] = 0.0f;
+ // and do the same for negative directions
+ splatcolor[20] = splatcolor[0] * max(0.0f, -dir[0]);
+ splatcolor[21] = splatcolor[0] * max(0.0f, -dir[1]);
+ splatcolor[22] = splatcolor[0] * max(0.0f, -dir[2]);
+ splatcolor[23] = 0.0f;
+ splatcolor[24] = splatcolor[1] * max(0.0f, -dir[0]);
+ splatcolor[25] = splatcolor[1] * max(0.0f, -dir[1]);
+ splatcolor[26] = splatcolor[1] * max(0.0f, -dir[2]);
+ splatcolor[27] = 0.0f;
+ splatcolor[28] = splatcolor[2] * max(0.0f, -dir[0]);
+ splatcolor[29] = splatcolor[2] * max(0.0f, -dir[1]);
+ splatcolor[30] = splatcolor[2] * max(0.0f, -dir[2]);
+ splatcolor[31] = 0.0f;
+ }
+ // calculate the number of steps we need to traverse this distance
+ VectorCopy(splatpath->point, steppos);
+ VectorCopy(splatpath->step, stepdelta);
+ numsteps = splatpath->remainingsplats;
+ lightpathsize_current = splatpath->splatsize_current + 1.0f; // add 1.0 for the gradient fade around the sphere
+ lightpathsize_perstep = splatpath->splatsize_perstep;
+ for (step = 0;step < numsteps;step++)
+ {
+ // the middle row/column/layer of each splat are full intensity
+ float splatmins[3];
+ float splatmaxs[3];
+ if (lightpathsize_current > MAXBOUNCEGRIDSPLATSIZE)
+ lightpathsize_current = MAXBOUNCEGRIDSPLATSIZE;
+ splatmins[0] = max(1.0f, steppos[0] - lightpathsize_current * 0.5f);
+ splatmins[1] = max(1.0f, steppos[1] - lightpathsize_current * 0.5f);
+ splatmins[2] = max(1.0f, steppos[2] - lightpathsize_current * 0.5f);
+ splatmaxs[0] = min(steppos[0] + lightpathsize_current * 0.5f, resolution[0] - 1.0f);
+ splatmaxs[1] = min(steppos[1] + lightpathsize_current * 0.5f, resolution[1] - 1.0f);
+ splatmaxs[2] = min(steppos[2] + lightpathsize_current * 0.5f, resolution[2] - 1.0f);
+ if (splatmaxs[0] > splatmins[0] && splatmaxs[1] > splatmins[1] && splatmaxs[2] > splatmins[2])
+ {
+ // it is within bounds... do the real work now
+ int xi, yi, zi, band, row;
+ float pixelpos[3];
+ float w;
+ float *p;
+ float colorscale = 1.0f / lightpathsize_current;
+ r_refdef.stats[r_stat_bouncegrid_splats]++;
+ // accumulate light onto the pixels
+ for (zi = (int)floor(splatmins[2]);zi < splatmaxs[2];zi++)
+ {
+ pixelpos[2] = zi + 0.5f;
+ for (yi = (int)floor(splatmins[1]); yi < splatmaxs[1]; yi++)
+ {
+ pixelpos[1] = yi + 0.5f;
+ row = (zi*resolution[1] + yi)*resolution[0];
+ for (xi = (int)floor(splatmins[0]); xi < splatmaxs[0]; xi++)
+ {
+ pixelpos[0] = xi + 0.5f;
+ // simple radial antialiased sphere - linear gradient fade over 1 pixel from the edge
+ w = lightpathsize_current - VectorDistance(pixelpos, steppos);
+ if (w > 0.0f)
+ {
+ if (w > 1.0f)
+ w = 1.0f;
+ w *= colorscale;
+ p = highpixels + 4 * (row + xi);
+ for (band = 0; band < pixelbands; band++, p += pixelsperband * 4)
+ {
+ // add to the pixel color
+ p[0] += splatcolor[band * 4 + 0] * w;
+ p[1] += splatcolor[band * 4 + 1] * w;
+ p[2] += splatcolor[band * 4 + 2] * w;
+ p[3] += splatcolor[band * 4 + 3] * w;
+ }
+ }
+ }
+ }
+ }
+ }
+ VectorAdd(steppos, stepdelta, steppos);
+ lightpathsize_current += lightpathsize_perstep;
+ }
+ }
+}
+
+static void R_Shadow_BounceGrid_BlurPixelsInDirection(const float *inpixels, float *outpixels, int off)
+{
+ const float *inpixel;
+ float *outpixel;
+ int pixelbands = r_shadow_bouncegrid_state.pixelbands;
+ int pixelband;
+ unsigned int index;
+ unsigned int x, y, z;
+ unsigned int resolution[3];
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+ for (pixelband = 0;pixelband < pixelbands;pixelband++)
+ {
+ for (z = 1;z < resolution[2]-1;z++)
+ {
+ for (y = 1;y < resolution[1]-1;y++)
+ {
+ x = 1;
+ index = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x;
+ inpixel = inpixels + 4*index;
+ outpixel = outpixels + 4*index;
+ for (;x < resolution[0]-1;x++, inpixel += 4, outpixel += 4)
+ {
+ outpixel[0] = (inpixel[0] + inpixel[ off] + inpixel[0-off]) * (1.0f / 3.0);
+ outpixel[1] = (inpixel[1] + inpixel[1+off] + inpixel[1-off]) * (1.0f / 3.0);
+ outpixel[2] = (inpixel[2] + inpixel[2+off] + inpixel[2-off]) * (1.0f / 3.0);
+ outpixel[3] = (inpixel[3] + inpixel[3+off] + inpixel[3-off]) * (1.0f / 3.0);
+ }
+ }
+ }
+ }
+}
+
+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 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_BODY;// | SUPERCONTENTS_LIQUIDSMASK;
+ else
+ hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
+ skipsupercontentsmask = SUPERCONTENTS_SKY; // this allows the e1m5 sky shadow to work by ignoring the sky surfaces
+ maxbounce = settings.maxbounce;
+