+ // 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;
+ }
+ // 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);
+}
+
+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
+ r_shadow_bouncegrid_state.highpixels = (float *)R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ memset(r_shadow_bouncegrid_state.highpixels, 0, r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+}
+
+static void R_Shadow_BounceGrid_PerformSplats(void)
+{
+ int splatsize = r_shadow_bouncegrid_state.settings.lightpathsize;
+ int splatsize1 = splatsize + 1;
+ 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;
+ float texcorner[3];
+ float texlerp[MAXBOUNCEGRIDSPLATSIZE1][3];
+ float splatcolor[32];
+ float boxweight = 1.0f / (splatsize * splatsize * splatsize);
+ int resolution[3];
+ int tex[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);
+
+ // the middle row/column/layer of each splat are full intensity
+ for (step = 1;step < splatsize;step++)
+ VectorSet(texlerp[step], 1.0f, 1.0f, 1.0f);
+
+ 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;
+ for (step = 0;step < numsteps;step++)
+ {
+ r_refdef.stats[r_stat_bouncegrid_splats]++;
+ // figure out the min corner of the pixels we'll need to update
+ texcorner[0] = steppos[0] - (splatsize1 * 0.5f);
+ texcorner[1] = steppos[1] - (splatsize1 * 0.5f);
+ texcorner[2] = steppos[2] - (splatsize1 * 0.5f);
+ tex[0] = (int)floor(texcorner[0]);
+ tex[1] = (int)floor(texcorner[1]);
+ tex[2] = (int)floor(texcorner[2]);
+ // only update if it is within reasonable bounds
+ if (tex[0] >= 1
+ && tex[1] >= 1
+ && tex[2] >= 1
+ && tex[0] < resolution[0] - splatsize1
+ && tex[1] < resolution[1] - splatsize1
+ && tex[2] < resolution[2] - splatsize1)
+ {
+ // it is within bounds... do the real work now
+ int xi, yi, zi;
+
+ // calculate the antialiased box edges
+ texlerp[splatsize][0] = texcorner[0] - tex[0];
+ texlerp[splatsize][1] = texcorner[1] - tex[1];
+ texlerp[splatsize][2] = texcorner[2] - tex[2];
+ texlerp[0][0] = 1.0f - texlerp[splatsize][0];
+ texlerp[0][1] = 1.0f - texlerp[splatsize][1];
+ texlerp[0][2] = 1.0f - texlerp[splatsize][2];
+
+ // accumulate light onto the pixels
+ for (zi = 0;zi < splatsize1;zi++)
+ {
+ for (yi = 0;yi < splatsize1;yi++)
+ {
+ int index = ((tex[2]+zi)*resolution[1]+tex[1]+yi)*resolution[0]+tex[0];
+ for (xi = 0;xi < splatsize1;xi++, index++)
+ {
+ float w = texlerp[xi][0]*texlerp[yi][1]*texlerp[zi][2] * boxweight;
+ int band = 0;
+ float *p = highpixels + 4 * index + band * pixelsperband * 4;
+ for (;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);
+ }
+ }
+}
+
+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 *highpixels = r_shadow_bouncegrid_state.highpixels;
+ float *temppixels1 = (float *)R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ float *temppixels2 = (float *)R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ unsigned int resolution[3];
+
+ if (!r_shadow_bouncegrid_blur.integer)
+ return;
+
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+
+ // blur on X
+ R_Shadow_BounceGrid_BlurPixelsInDirection(highpixels, temppixels1, 4);
+ // blur on Y
+ R_Shadow_BounceGrid_BlurPixelsInDirection(temppixels1, temppixels2, resolution[0] * 4);
+ // blur on Z
+ R_Shadow_BounceGrid_BlurPixelsInDirection(temppixels2, highpixels, resolution[0] * resolution[1] * 4);
+}
+
+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;
+ }
+ }
+ }