+ // 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;