+ light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+ rtlight = &light->rtlight;
+ }
+ else
+ rtlight = r_refdef.scene.lights[lightindex - range];
+ // draw only visible lights (major speedup)
+ if (!rtlight->draw)
+ continue;
+ VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
+ }
+ if (!VectorLength2(lightcolor))
+ continue;
+ // shoot particles from this light
+ // use a calculation for the number of particles that will not
+ // vary with lightstyle, otherwise we get randomized particle
+ // distribution, the seeded random is only consistent for a
+ // consistent number of particles on this light...
+ radius = rtlight->radius * bound(0.0001f, r_shadow_bouncegrid_lightradiusscale.value, 1024.0f);
+ s = rtlight->radius / bound(1.0f, r_shadow_bouncegrid_particlespacing.value, 1048576.0f);
+ lightintensity = VectorLength(rtlight->color) * rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale;
+ if (lightindex >= range)
+ lightintensity *= r_shadow_bouncegrid_dlightparticlemultiplier.value;
+ shootparticles = (int)bound(0, lightintensity * s *s, MAXBOUNCEGRIDPARTICLESPERLIGHT);
+ if (!shootparticles)
+ continue;
+ s = 255.0f * r_shadow_bouncegrid_particleintensity.value / shootparticles;
+ VectorScale(lightcolor, s, baseshotcolor);
+ maxbounce = bound(1, r_shadow_bouncegrid_maxbounce.integer, 16);
+ if (VectorLength2(baseshotcolor) < 3.0f)
+ break;
+ r_refdef.stats.bouncegrid_lights++;
+ r_refdef.stats.bouncegrid_particles += shootparticles;
+ for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
+ {
+ if (r_shadow_bouncegrid_stablerandom.integer > 0)
+ seed = lightindex * 11937 + shotparticles;
+ VectorCopy(baseshotcolor, shotcolor);
+ VectorCopy(rtlight->shadoworigin, clipstart);
+ if (r_shadow_bouncegrid_stablerandom.integer < 0)
+ VectorRandom(clipend);
+ else
+ VectorCheeseRandom(clipend);
+ VectorMA(clipstart, radius, clipend, clipend);
+ bouncelimit = 1 + (rtlight->particlecache_updateparticle % maxbounce);
+ for (bouncecount = 0;;bouncecount++)
+ {
+ r_refdef.stats.bouncegrid_traces++;
+ cliptrace = CL_TraceLine(clipstart, clipend, r_shadow_bouncegrid_hitmodels.integer ? MOVE_HITMODEL : MOVE_NOMONSTERS, NULL, hitsupercontentsmask, true, false, NULL, true);
+ //Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
+ if (cliptrace.fraction >= 1.0f)
+ break;
+ r_refdef.stats.bouncegrid_hits++;
+ if (bouncecount > 0)
+ {
+ r_refdef.stats.bouncegrid_splats++;
+ // figure out which texture pixel this is in
+ tex[0] = (int)((cliptrace.endpos[0] - mins[0]) * ispacing[0]);
+ tex[1] = (int)((cliptrace.endpos[1] - mins[1]) * ispacing[1]);
+ tex[2] = (int)((cliptrace.endpos[2] - mins[2]) * ispacing[2]);
+ if (tex[0] >= 1 && tex[1] >= 1 && tex[2] >= 1 && tex[0] < resolution[0] - 1 && tex[1] < resolution[1] - 1 && tex[2] < resolution[2] - 1)
+ {
+ // it is within bounds...
+ pixel = pixels + 4 * ((tex[2]*resolution[1]+tex[1])*resolution[0]+tex[0]);
+ // add to the pixel color
+ c[0] = pixel[0] + (int)shotcolor[2];
+ c[1] = pixel[1] + (int)shotcolor[1];
+ c[2] = pixel[2] + (int)shotcolor[0];
+ pixel[0] = (unsigned char)min(c[0], 255);
+ pixel[1] = (unsigned char)min(c[1], 255);
+ pixel[2] = (unsigned char)min(c[2], 255);
+ pixel[3] = 255;
+ }
+ }
+ if (bouncecount >= bouncelimit)
+ break;
+ // scale down shot color by bounce intensity and texture color
+ VectorScale(shotcolor, r_shadow_bouncegrid_particlebounceintensity.value, shotcolor);
+ if (cliptrace.hittexture && cliptrace.hittexture->currentskinframe)
+ VectorMultiply(shotcolor, rsurface.texture->currentskinframe->avgcolor, shotcolor);
+ if (VectorLength2(shotcolor) < 3.0f)
+ break;
+ r_refdef.stats.bouncegrid_bounces++;
+ if (r_shadow_bouncegrid_bounceanglediffuse.integer)
+ {
+ // random direction, primarily along plane normal
+ s = VectorDistance(cliptrace.endpos, clipend);
+ if (r_shadow_bouncegrid_stablerandom.integer < 0)
+ VectorRandom(clipend);
+ else
+ VectorCheeseRandom(clipend);
+ VectorMA(cliptrace.plane.normal, 0.95f, clipend, clipend);
+ VectorNormalize(clipend);
+ VectorScale(clipend, s, clipend);
+ }
+ else
+ {
+ // reflect the remaining portion of the line across plane normal
+ VectorSubtract(clipend, cliptrace.endpos, clipdiff);
+ VectorReflect(clipdiff, 1.0, cliptrace.plane.normal, clipend);
+ }
+ // calculate the new line start and end
+ VectorCopy(cliptrace.endpos, clipstart);
+ VectorAdd(clipstart, clipend, clipend);
+ }
+ }
+ }
+ if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2])
+ R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]);
+ else
+ {
+ VectorCopy(resolution, r_shadow_bouncegridresolution);
+ if (r_shadow_bouncegridtexture)
+ R_FreeTexture(r_shadow_bouncegridtexture);
+ r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2], pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+ }
+ Mem_Free(pixels);
+ r_shadow_bouncegridtime = realtime;
+}
+
+#define MAXPARTICLESPERLIGHT 262144
+#define MAXLIGHTSPERDRAW 1024
+
+static void R_Shadow_RenderParticlesForLight(rtlight_t *rtlight)
+{
+ int batchcount;
+ int i;
+ int j;
+ int bouncecount;
+ int hitsupercontentsmask;
+ int n;
+ int shotparticles;
+ int shootparticles = 0;
+ int bouncelimit;
+ int maxbounce;
+ unsigned int seed = 0;
+ static unsigned short bouncelight_elements[MAXLIGHTSPERDRAW*36];
+ static float vertex3f[MAXLIGHTSPERDRAW*24];
+ static float lightorigin4f[MAXLIGHTSPERDRAW*32];
+ static float color4f[MAXLIGHTSPERDRAW*32];
+ float scaledpoints[8][3];
+ float *v3f;
+ float *lo4f;
+ float *c4f;
+ rtlight_particle_t *p;
+ vec_t wantparticles = 0;
+ vec_t s;
+ vec_t radius;
+ vec_t particlesize;
+ vec_t iparticlesize;
+// vec3_t offset;
+// vec3_t right;
+// vec3_t up;
+ vec4_t org;
+ vec4_t color;
+ vec3_t currentcolor;
+ vec3_t clipstart;
+ vec3_t clipend;
+ vec3_t shotcolor;
+ trace_t cliptrace;
+ if (!rtlight->draw || !rtlight->isstatic || !r_shadow_usingdeferredprepass)
+ return;
+ if (r_shadow_particletrace.integer)
+ {
+ radius = rtlight->radius * bound(0.0001f, r_shadow_particletrace_radiusscale.value, 1.0f) - r_shadow_particletrace_size.value;
+ s = rtlight->radius / bound(1.0f, r_shadow_particletrace_particlespacing.value * r_shadow_particletrace_size.value, 1048576.0f);
+ wantparticles = s*s;
+ n = (int)bound(0, wantparticles, MAXPARTICLESPERLIGHT);
+ }
+ else
+ n = 0;
+ shootparticles = (int)(n * r_shadow_particletrace_updatepercentage.value);
+ if ((n && !rtlight->particlecache_particles) || rtlight->particlecache_maxparticles != n)
+ {
+ if (rtlight->particlecache_particles)
+ Mem_Free(rtlight->particlecache_particles);
+ rtlight->particlecache_particles = NULL;
+ rtlight->particlecache_numparticles = 0;
+ rtlight->particlecache_maxparticles = n;
+ rtlight->particlecache_updateparticle = 0;
+ if (rtlight->particlecache_maxparticles)
+ rtlight->particlecache_particles = Mem_Alloc(r_main_mempool, rtlight->particlecache_maxparticles * sizeof(*rtlight->particlecache_particles));
+ shootparticles = n * 16;
+ }
+
+ if (!rtlight->particlecache_maxparticles)
+ return;
+
+// if (rtlight->particlecache_numparticles < rtlight->particlecache_maxparticles)
+// shootparticles = rtlight->particlecache_maxparticles;
+
+// if (rtlight->particlecache_numparticles >= rtlight->particlecache_maxparticles)
+// shootparticles = 0;
+
+ maxbounce = bound(1, r_shadow_particletrace_maxbounce.integer, 16);
+ //r_refdef.stats.lights_bouncelightsupdated += shootparticles;
+ for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
+ {
+ seed = rtlight->particlecache_updateparticle;
+ VectorSet(shotcolor, 1.0f, 1.0f, 1.0f);
+ VectorCopy(rtlight->shadoworigin, clipstart);
+ VectorRandom(clipend);
+ VectorMA(clipstart, radius, clipend, clipend);
+ hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK;
+ bouncelimit = 1 + (rtlight->particlecache_updateparticle % maxbounce);
+ for (bouncecount = 0;;bouncecount++)
+ {
+ cliptrace = CL_TraceLine(clipstart, clipend, MOVE_NOMONSTERS, NULL, hitsupercontentsmask, true, false, NULL, true);
+ //Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask);
+ if (cliptrace.fraction >= 1.0f)