+ radius = rtlight->radius * r_shadow_bouncegrid_state.settings.lightradiusscale;
+ //s = settings.particleintensity / shootparticles;
+ //VectorScale(rtlight->bouncegrid_photoncolor, s, baseshotcolor);
+ VectorCopy(rtlight->bouncegrid_photoncolor, baseshotcolor);
+ if (VectorLength2(baseshotcolor) <= 0.0f)
+ continue;
+ r_refdef.stats[r_stat_bouncegrid_lights]++;
+ r_refdef.stats[r_stat_bouncegrid_particles] += shootparticles;
+ // we stop caring about bounces once the brightness goes below this fraction of the original intensity
+ bounceminimumintensity2 = VectorLength(baseshotcolor) * r_shadow_bouncegrid_state.settings.bounceminimumintensity2;
+
+ // check material at shadoworigin to see what the initial refractive index should be
+ startrefractiveindex = R_Shadow_BounceGrid_RefractiveIndexAtPoint(rtlight->shadoworigin);
+
+ // for seeded random we start the RNG with the position of the light
+ if (r_shadow_bouncegrid_state.settings.rng_seed >= 0)
+ {
+ union
+ {
+ unsigned int i[4];
+ float f[4];
+ }
+ u;
+ u.f[0] = rtlight->shadoworigin[0];
+ u.f[1] = rtlight->shadoworigin[1];
+ u.f[2] = rtlight->shadoworigin[2];
+ u.f[3] = 1;
+ switch (r_shadow_bouncegrid_state.settings.rng_type)
+ {
+ default:
+ case 0:
+ // we have to shift the seed provided by the user because the result must be odd
+ Math_RandomSeed_FromInts(&randomseed, u.i[0], u.i[1], u.i[2], u.i[3] ^ (r_shadow_bouncegrid_state.settings.rng_seed << 1));
+ break;
+ case 1:
+ seed = u.i[0] ^ u.i[1] ^ u.i[2] ^ u.i[3] ^ r_shadow_bouncegrid_state.settings.rng_seed;
+ break;
+ }
+ }
+
+ for (shotparticles = 0; shotparticles < shootparticles && r_shadow_bouncegrid_state.numphotons < r_shadow_bouncegrid_state.settings.maxphotons; shotparticles++)
+ {
+ r_shadow_bouncegrid_photon_t *p = r_shadow_bouncegrid_state.photons + r_shadow_bouncegrid_state.numphotons++;
+ VectorCopy(baseshotcolor, p->color);
+ VectorCopy(rtlight->shadoworigin, p->start);
+ switch (r_shadow_bouncegrid_state.settings.rng_type)
+ {
+ default:
+ case 0:
+ // figure out a random direction for the initial photon to go
+ VectorLehmerRandom(&randomseed, p->end);
+ break;
+ case 1:
+ // figure out a random direction for the initial photon to go
+ VectorCheeseRandom(seed, p->end);
+ break;
+ }
+
+ // we want a uniform distribution spherically, not merely within the sphere
+ if (r_shadow_bouncegrid_state.settings.normalizevectors)
+ VectorNormalize(p->end);
+
+ VectorMA(p->start, radius, p->end, p->end);
+ p->bounceminimumintensity2 = bounceminimumintensity2;
+ p->startrefractiveindex = startrefractiveindex;
+ p->numpaths = 0;
+ }
+ }
+
+ t->done = 1;
+}
+
+static void R_Shadow_BounceGrid_Slice(int zi)
+{
+ float *highpixels = r_shadow_bouncegrid_state.highpixels;
+ int xi, yi; // pixel increments
+ float color[32] = { 0 };
+ float radius = r_shadow_bouncegrid_state.settings.lightpathsize;
+ float iradius = 1.0f / radius;
+ int slicemins[3], slicemaxs[3];
+ int resolution[3];
+ int pixelsperband = r_shadow_bouncegrid_state.pixelsperband;
+ int pixelbands = r_shadow_bouncegrid_state.pixelbands;
+ int photonindex;
+ int samples = r_shadow_bouncegrid_state.settings.subsamples;
+ float isamples = 1.0f / samples;
+ float samplescolorscale = isamples * isamples * isamples;
+
+ // we use these a lot, so get a local copy
+ VectorCopy(r_shadow_bouncegrid_state.resolution, resolution);
+
+ for (photonindex = 0; photonindex < r_shadow_bouncegrid_state.numphotons; photonindex++)
+ {
+ r_shadow_bouncegrid_photon_t *photon = r_shadow_bouncegrid_state.photons + photonindex;
+ int pathindex;
+ for (pathindex = 0; pathindex < photon->numpaths; pathindex++)
+ {
+ r_shadow_bouncegrid_photon_path_t *path = photon->paths + pathindex;
+ float pathstart[3], pathend[3], pathmins[3], pathmaxs[3], pathdelta[3], pathdir[3], pathlength2, pathilength;
+
+ VectorSubtract(path->start, r_shadow_bouncegrid_state.mins, pathstart);
+ VectorSubtract(path->end, r_shadow_bouncegrid_state.mins, pathend);
+
+ pathmins[2] = min(pathstart[2], pathend[2]);
+ slicemins[2] = (int)floor((pathmins[2] - radius) * r_shadow_bouncegrid_state.ispacing[2]);
+ pathmaxs[2] = max(pathstart[2], pathend[2]);
+ slicemaxs[2] = (int)floor((pathmaxs[2] + radius) * r_shadow_bouncegrid_state.ispacing[2] + 1);
+
+ // skip if the path doesn't touch this slice
+ if (zi < slicemins[2] || zi >= slicemaxs[2])
+ continue;
+
+ pathmins[0] = min(pathstart[0], pathend[0]);
+ slicemins[0] = (int)floor((pathmins[0] - radius) * r_shadow_bouncegrid_state.ispacing[0]);
+ slicemins[0] = max(slicemins[0], 1);
+ pathmaxs[0] = max(pathstart[0], pathend[0]);
+ slicemaxs[0] = (int)floor((pathmaxs[0] + radius) * r_shadow_bouncegrid_state.ispacing[0]);
+ slicemaxs[0] = min(slicemaxs[0], resolution[0] - 1);
+
+ pathmins[1] = min(pathstart[1], pathend[1]);
+ slicemins[1] = (int)floor((pathmins[1] - radius) * r_shadow_bouncegrid_state.ispacing[1] + 1);
+ slicemins[1] = max(slicemins[1], 1);
+ pathmaxs[1] = max(pathstart[1], pathend[1]);
+ slicemaxs[1] = (int)floor((pathmaxs[1] + radius) * r_shadow_bouncegrid_state.ispacing[1] + 1);
+ slicemaxs[1] = min(slicemaxs[1], resolution[1] - 1);
+
+ // skip if the path is out of bounds on X or Y
+ if (slicemins[0] >= slicemaxs[0] || slicemins[1] >= slicemaxs[1])
+ continue;
+
+ // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ)
+ // accumulate average shotcolor
+ VectorSubtract(pathend, pathstart, pathdelta);
+ pathlength2 = VectorLength2(pathdelta);
+ pathilength = pathlength2 > 0.0f ? 1.0f / sqrt(pathlength2) : 0.0f;
+ VectorScale(pathdelta, pathilength, pathdir);
+ // the color is scaled by the number of subsamples
+ color[0] = path->color[0] * samplescolorscale;
+ color[1] = path->color[1] * samplescolorscale;
+ color[2] = path->color[2] * samplescolorscale;
+ color[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.
+ float intensity = VectorLength(color);
+ color[4] = pathdir[0] * intensity;
+ color[5] = pathdir[1] * intensity;
+ color[6] = pathdir[2] * intensity;
+ color[7] = intensity;
+ // for each color component (R, G, B) calculate the amount that a
+ // direction contributes
+ color[8] = color[0] * max(0.0f, pathdir[0]);
+ color[9] = color[0] * max(0.0f, pathdir[1]);
+ color[10] = color[0] * max(0.0f, pathdir[2]);
+ color[11] = 0.0f;
+ color[12] = color[1] * max(0.0f, pathdir[0]);
+ color[13] = color[1] * max(0.0f, pathdir[1]);
+ color[14] = color[1] * max(0.0f, pathdir[2]);
+ color[15] = 0.0f;
+ color[16] = color[2] * max(0.0f, pathdir[0]);
+ color[17] = color[2] * max(0.0f, pathdir[1]);
+ color[18] = color[2] * max(0.0f, pathdir[2]);
+ color[19] = 0.0f;
+ // and do the same for negative directions
+ color[20] = color[0] * max(0.0f, -pathdir[0]);
+ color[21] = color[0] * max(0.0f, -pathdir[1]);
+ color[22] = color[0] * max(0.0f, -pathdir[2]);
+ color[23] = 0.0f;
+ color[24] = color[1] * max(0.0f, -pathdir[0]);
+ color[25] = color[1] * max(0.0f, -pathdir[1]);
+ color[26] = color[1] * max(0.0f, -pathdir[2]);
+ color[27] = 0.0f;
+ color[28] = color[2] * max(0.0f, -pathdir[0]);
+ color[29] = color[2] * max(0.0f, -pathdir[1]);
+ color[30] = color[2] * max(0.0f, -pathdir[2]);
+ color[31] = 0.0f;
+ }
+
+ for (yi = slicemins[1]; yi < slicemaxs[1]; yi++)
+ {
+ for (xi = slicemins[0]; xi < slicemaxs[0]; xi++)
+ {
+ float sample[3], diff[3], nearest[3], along, distance2;
+ float *p = highpixels + 4 * ((zi * resolution[1] + yi) * resolution[0] + xi);
+ int xs, ys, zs;
+ // loop over the subsamples
+ for (zs = 0; zs < samples; zs++)
+ {
+ sample[2] = (zi + (zs + 0.5f) * isamples) * r_shadow_bouncegrid_state.spacing[2];
+ for (ys = 0; ys < samples; ys++)
+ {
+ sample[1] = (yi + (ys + 0.5f) * isamples) * r_shadow_bouncegrid_state.spacing[1];
+ for (xs = 0; xs < samples; xs++)
+ {
+ sample[0] = (xi + (xs + 0.5f) * isamples) * r_shadow_bouncegrid_state.spacing[0];
+
+ // measure distance from subsample to line segment and see if it is within radius
+ along = DotProduct(sample, pathdir) * pathilength;
+ if (along <= 0)
+ VectorCopy(pathstart, nearest);
+ else if (along >= 1)
+ VectorCopy(pathend, nearest);
+ else
+ VectorLerp(pathstart, along, pathend, nearest);
+ VectorSubtract(sample, nearest, diff);
+ VectorScale(diff, iradius, diff);
+ distance2 = VectorLength2(diff);
+ if (distance2 < 1.0f)
+ {
+ // contribute some color to this pixel, across all bands
+ float w = 1.0f - sqrt(distance2);
+ int band;
+ w *= w;
+ if (pixelbands > 1)
+ {
+ // small optimization for alpha - only color[7] is non-zero, so skip the rest of the alpha elements.
+ p[pixelsperband * 4 + 3] += color[7] * w;
+ }
+ for (band = 0; band < pixelbands; band++)
+ {
+ // add to the pixel color (RGB only - see above)
+ p[band * pixelsperband * 4 + 0] += color[band * 4 + 0] * w;
+ p[band * pixelsperband * 4 + 1] += color[band * 4 + 1] * w;
+ p[band * pixelsperband * 4 + 2] += color[band * 4 + 2] * w;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void R_Shadow_BounceGrid_Slice_Task(taskqueue_task_t *t)
+{
+ R_Shadow_BounceGrid_Slice((int)t->i[0]);
+ t->done = 1;
+}
+
+static void R_Shadow_BounceGrid_EnqueueSlices_Task(taskqueue_task_t *t)
+{
+ int i, slices;
+ // we need to wait for the texture clear to finish before we start adding light to it
+ if (r_shadow_bouncegrid_state.cleartex_task.done == 0)
+ {
+ TaskQueue_Yield(t);
+ return;
+ }
+ slices = r_shadow_bouncegrid_state.resolution[2] - 2;
+ for (i = 0; i < slices; i++)
+ TaskQueue_Setup(r_shadow_bouncegrid_state.slices_tasks + i, NULL, R_Shadow_BounceGrid_Slice_Task, i + 1, 0, NULL, NULL);
+ TaskQueue_Enqueue(slices, r_shadow_bouncegrid_state.slices_tasks);
+ TaskQueue_Setup(&r_shadow_bouncegrid_state.slices_done_task, NULL, TaskQueue_Task_CheckTasksDone, slices, 0, r_shadow_bouncegrid_state.slices_tasks, 0);
+ TaskQueue_Enqueue(1, &r_shadow_bouncegrid_state.slices_done_task);
+ t->done = 1;
+}
+
+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_Task(taskqueue_task_t *t)
+{
+ float *pixels[4];
+ unsigned int resolution[3];
+ if (r_shadow_bouncegrid_state.settings.blur)
+ {
+ 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];
+ }
+ t->done = 1;
+}
+
+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, 0);
+ 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, 0);
+ 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, 0);
+ 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 = host.realtime;
+}
+
+static void R_Shadow_BounceGrid_ClearTex_Task(taskqueue_task_t *t)
+{
+ memset(r_shadow_bouncegrid_state.highpixels, 0, r_shadow_bouncegrid_state.numpixels * sizeof(float[4]));
+ t->done = 1;
+}
+
+static void R_Shadow_BounceGrid_TracePhotons_Shot(r_shadow_bouncegrid_photon_t *p, int remainingbounces, vec3_t shotstart, vec3_t shotend, vec3_t shotcolor, float bounceminimumintensity2, float previousrefractiveindex)
+{
+ int hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask;
+ vec3_t shothit;
+ vec3_t surfacenormal;
+ vec3_t reflectstart, reflectend, reflectcolor;
+ vec3_t refractstart, refractend, refractcolor;
+ vec_t s;
+ float reflectamount = 1.0f;
+ trace_t cliptrace;
+ // figure out what we want to interact with
+ hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK;
+ skipsupercontentsmask = 0;
+ skipmaterialflagsmask = MATERIALFLAG_CUSTOMBLEND;
+ //r_refdef.scene.worldmodel->TraceLineAgainstSurfaces(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace, clipstart, clipend, hitsupercontentsmask);
+ //r_refdef.scene.worldmodel->TraceLine(r_refdef.scene.worldmodel, NULL, NULL, &cliptrace2, clipstart, clipend, hitsupercontentsmask);
+ if (r_shadow_bouncegrid_state.settings.staticmode || r_shadow_bouncegrid_state.settings.rng_seed < 0 || r_shadow_bouncegrid_threaded.integer)
+ {
+ // static mode fires a LOT of rays but none of them are identical, so they are not cached
+ // non-stable random in dynamic mode also never reuses a direction, so there's no reason to cache it
+ cliptrace = CL_TraceLine(shotstart, shotend, r_shadow_bouncegrid_state.settings.staticmode ? MOVE_WORLDONLY : (r_shadow_bouncegrid_state.settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value, true, false, NULL, true, true);
+ }
+ else
+ {
+ // dynamic mode fires many rays and most will match the cache from the previous frame
+ cliptrace = CL_Cache_TraceLineSurfaces(shotstart, shotend, r_shadow_bouncegrid_state.settings.staticmode ? MOVE_WORLDONLY : (r_shadow_bouncegrid_state.settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
+ }
+ VectorCopy(cliptrace.endpos, shothit);
+ if ((remainingbounces == r_shadow_bouncegrid_state.settings.maxbounce || r_shadow_bouncegrid_state.settings.includedirectlighting) && p->numpaths < PHOTON_MAX_PATHS)
+ {
+ qbool notculled = true;
+ // cull paths that fail R_CullFrustum in dynamic mode
+ if (!r_shadow_bouncegrid_state.settings.staticmode
+ && r_shadow_bouncegrid_dynamic_culllightpaths.integer)
+ {
+ vec3_t cullmins, cullmaxs;
+ cullmins[0] = min(shotstart[0], shothit[0]) - r_shadow_bouncegrid_state.settings.spacing[0] - r_shadow_bouncegrid_state.settings.lightpathsize;
+ cullmins[1] = min(shotstart[1], shothit[1]) - r_shadow_bouncegrid_state.settings.spacing[1] - r_shadow_bouncegrid_state.settings.lightpathsize;
+ cullmins[2] = min(shotstart[2], shothit[2]) - r_shadow_bouncegrid_state.settings.spacing[2] - r_shadow_bouncegrid_state.settings.lightpathsize;
+ cullmaxs[0] = max(shotstart[0], shothit[0]) + r_shadow_bouncegrid_state.settings.spacing[0] + r_shadow_bouncegrid_state.settings.lightpathsize;
+ cullmaxs[1] = max(shotstart[1], shothit[1]) + r_shadow_bouncegrid_state.settings.spacing[1] + r_shadow_bouncegrid_state.settings.lightpathsize;
+ cullmaxs[2] = max(shotstart[2], shothit[2]) + r_shadow_bouncegrid_state.settings.spacing[2] + r_shadow_bouncegrid_state.settings.lightpathsize;
+ if (R_CullFrustum(cullmins, cullmaxs))
+ notculled = false;
+ }
+ if (notculled)
+ {
+ r_shadow_bouncegrid_photon_path_t *path = p->paths + p->numpaths++;
+ VectorCopy(shotstart, path->start);
+ VectorCopy(shothit, path->end);
+ VectorCopy(shotcolor, path->color);
+ }
+ }
+ if (cliptrace.fraction < 1.0f && remainingbounces > 0)
+ {
+ // scale down shot color by bounce intensity and texture color (or 50% if no texture reported)
+ // also clamp the resulting color to never add energy, even if the user requests extreme values
+ VectorCopy(cliptrace.plane.normal, surfacenormal);
+ VectorSet(reflectcolor, 0.5f, 0.5f, 0.5f);
+ VectorClear(refractcolor);
+ // FIXME: we need to determine the exact triangle, vertex color and texcoords and texture color and texture normal for the impacted point
+ if (cliptrace.hittexture)