+ lightintensity *= settings->dlightparticlemultiplier;
+ rtlight->photons = bound(0.0f, lightintensity * s * s, MAXBOUNCEGRIDPARTICLESPERLIGHT);
+ photoncount += rtlight->photons;
+ // 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, r_shadow_bouncegrid_dynamic_energyperphoton.value);
+ 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
+ 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 *highpixel;
+ 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 texlerp[2][3];
+ float splatcolor[32];
+ float pixelweight[8];
+ float w;
+ int resolution[3];
+ int tex[3];
+ int pixelindex[8];
+ int corner;
+ int pixelsperband = r_shadow_bouncegrid_state.pixelsperband;
+ int pixelband;
+ 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;
+ for (step = 0;step < numsteps;step++)
+ {
+ r_refdef.stats[r_stat_bouncegrid_splats]++;
+ // figure out which texture pixels this is in
+ texlerp[1][0] = steppos[0] - 0.5f;
+ texlerp[1][1] = steppos[1] - 0.5f;
+ texlerp[1][2] = steppos[2] - 0.5f;
+ tex[0] = (int)floor(texlerp[1][0]);
+ tex[1] = (int)floor(texlerp[1][1]);
+ tex[2] = (int)floor(texlerp[1][2]);
+ if (tex[0] >= 1
+ && tex[1] >= 1
+ && tex[2] >= 1
+ && tex[0] < resolution[0] - 2
+ && tex[1] < resolution[1] - 2
+ && tex[2] < resolution[2] - 2)
+ {
+ // it is within bounds... do the real work now
+ // calculate the lerp factors
+ texlerp[1][0] -= tex[0];
+ texlerp[1][1] -= tex[1];
+ texlerp[1][2] -= tex[2];
+ texlerp[0][0] = 1.0f - texlerp[1][0];
+ texlerp[0][1] = 1.0f - texlerp[1][1];
+ texlerp[0][2] = 1.0f - texlerp[1][2];
+ // calculate individual pixel indexes and weights
+ pixelindex[0] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[0] = (texlerp[0][0]*texlerp[0][1]*texlerp[0][2]);
+ pixelindex[1] = (((tex[2] )*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[1] = (texlerp[1][0]*texlerp[0][1]*texlerp[0][2]);
+ pixelindex[2] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[2] = (texlerp[0][0]*texlerp[1][1]*texlerp[0][2]);
+ pixelindex[3] = (((tex[2] )*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[3] = (texlerp[1][0]*texlerp[1][1]*texlerp[0][2]);
+ pixelindex[4] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0] );pixelweight[4] = (texlerp[0][0]*texlerp[0][1]*texlerp[1][2]);
+ pixelindex[5] = (((tex[2]+1)*resolution[1]+tex[1] )*resolution[0]+tex[0]+1);pixelweight[5] = (texlerp[1][0]*texlerp[0][1]*texlerp[1][2]);
+ pixelindex[6] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0] );pixelweight[6] = (texlerp[0][0]*texlerp[1][1]*texlerp[1][2]);
+ pixelindex[7] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[7] = (texlerp[1][0]*texlerp[1][1]*texlerp[1][2]);
+ // update the 8 pixels...
+ for (pixelband = 0;pixelband < pixelbands;pixelband++)
+ {
+ for (corner = 0;corner < 8;corner++)
+ {
+ // calculate address for pixel
+ w = pixelweight[corner];
+ highpixel = highpixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4;
+ // add to the high precision pixel color
+ highpixel[0] += (splatcolor[pixelband*4+0]*w);
+ highpixel[1] += (splatcolor[pixelband*4+1]*w);
+ highpixel[2] += (splatcolor[pixelband*4+2]*w);
+ highpixel[3] += (splatcolor[pixelband*4+3]*w);
+ }
+ }
+ }
+ VectorAdd(steppos, stepdelta, steppos);
+ }