]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
Added r_shadow_bouncegrid_lightpathsize which is an alternative to blur
[xonotic/darkplaces.git] / r_shadow.c
index d4cf86b71b7152fc24c51d10ff6cf030fda0f669..65c822a9ec5a6d084f55dafca2763becfa95110a 100644 (file)
@@ -170,8 +170,8 @@ r_shadow_rendermode_t;
 
 typedef enum r_shadow_shadowmode_e
 {
-    R_SHADOW_SHADOWMODE_STENCIL,
-    R_SHADOW_SHADOWMODE_SHADOWMAP2D
+       R_SHADOW_SHADOWMODE_STENCIL,
+       R_SHADOW_SHADOWMODE_SHADOWMAP2D
 }
 r_shadow_shadowmode_t;
 
@@ -202,7 +202,7 @@ int r_shadow_shadowmapborder;
 matrix4x4_t r_shadow_shadowmapmatrix;
 int r_shadow_lightscissor[4];
 qboolean r_shadow_usingdeferredprepass;
-
+qboolean r_shadow_shadowmapdepthtexture;
 int maxshadowtriangles;
 int *shadowelements;
 
@@ -269,6 +269,9 @@ static rtexture_t *r_shadow_fb_colortexture;
 // lights are reloaded when this changes
 char r_shadow_mapname[MAX_QPATH];
 
+// buffer for doing corona fading
+unsigned int r_shadow_occlusion_buf = 0;
+
 // used only for light filters (cubemaps)
 rtexturepool_t *r_shadow_filters_texturepool;
 
@@ -295,6 +298,7 @@ cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_
 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"};
+cvar_t r_shadow_realtime_world_importlightentitiesfrommap = {0, "r_shadow_realtime_world_importlightentitiesfrommap", "1", "load lights from .ent file or map entities at startup if no .rtlights or .lights file is present (if set to 2, always use the .ent or map entities)"};
 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"};
 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
@@ -322,31 +326,39 @@ cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to
 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect OpenGL 2.0 render path)"};
 cvar_t r_shadow_bouncegrid = {CVAR_SAVE, "r_shadow_bouncegrid", "0", "perform particle tracing for indirect lighting (Global Illumination / radiosity) using a 3D texture covering the scene, only active on levels with realtime lights active (r_shadow_realtime_world is usually required for these)"};
+cvar_t r_shadow_bouncegrid_blur = {CVAR_SAVE, "r_shadow_bouncegrid_blur", "1", "apply a 1-radius blur on bouncegrid to denoise it and deal with boundary issues with surfaces"};
 cvar_t r_shadow_bouncegrid_bounceanglediffuse = {CVAR_SAVE, "r_shadow_bouncegrid_bounceanglediffuse", "0", "use random bounce direction rather than true reflection, makes some corner areas dark"};
-cvar_t r_shadow_bouncegrid_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"};
-cvar_t r_shadow_bouncegrid_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dlightparticlemultiplier", "0", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
-cvar_t r_shadow_bouncegrid_hitmodels = {CVAR_SAVE, "r_shadow_bouncegrid_hitmodels", "0", "enables hitting character model geometry (SLOW)"};
+cvar_t r_shadow_bouncegrid_dynamic_culllightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_culllightpaths", "1", "skip accumulating light in the bouncegrid texture where the light paths are out of view (dynamic mode only)"};
+cvar_t r_shadow_bouncegrid_dynamic_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_dlightparticlemultiplier", "1", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
+cvar_t r_shadow_bouncegrid_dynamic_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"};
+cvar_t r_shadow_bouncegrid_dynamic_hitmodels = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_hitmodels", "0", "enables hitting character model geometry (SLOW)"};
+cvar_t r_shadow_bouncegrid_dynamic_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_energyperphoton", "10000", "amount of light that one photon should represent"};
+cvar_t r_shadow_bouncegrid_dynamic_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1)"};
+cvar_t r_shadow_bouncegrid_dynamic_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_maxbounce", "5", "maximum number of bounces for a particle (minimum is 0)"};
+cvar_t r_shadow_bouncegrid_dynamic_maxphotons = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_maxphotons", "25000", "upper bound on photons to shoot per update, divided proportionately between lights - normally the number of photons is calculated by energyperphoton"};
+cvar_t r_shadow_bouncegrid_dynamic_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_spacing", "64", "unit size of bouncegrid pixel"};
+cvar_t r_shadow_bouncegrid_dynamic_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_stablerandom", "1", "make particle distribution consistent from frame to frame"};
+cvar_t r_shadow_bouncegrid_dynamic_updateinterval = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_updateinterval", "0", "update bouncegrid texture once per this many seconds, useful values are 0, 0.05, or 1000000"};
+cvar_t r_shadow_bouncegrid_dynamic_x = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_x", "64", "maximum texture size of bouncegrid on X axis"};
+cvar_t r_shadow_bouncegrid_dynamic_y = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_y", "64", "maximum texture size of bouncegrid on Y axis"};
+cvar_t r_shadow_bouncegrid_dynamic_z = {CVAR_SAVE, "r_shadow_bouncegrid_dynamic_z", "32", "maximum texture size of bouncegrid on Z axis"};
+cvar_t r_shadow_bouncegrid_floatcolors = {CVAR_SAVE, "r_shadow_bouncegrid_floatcolors", "1", "upload texture as RGBA16F (or RGBA32F when set to 2) rather than RGBA8 format - this gives more dynamic range and accuracy"};
 cvar_t r_shadow_bouncegrid_includedirectlighting = {CVAR_SAVE, "r_shadow_bouncegrid_includedirectlighting", "0", "allows direct lighting to be recorded, not just indirect (gives an effect somewhat like r_shadow_realtime_world_lightmaps)"};
 cvar_t r_shadow_bouncegrid_intensity = {CVAR_SAVE, "r_shadow_bouncegrid_intensity", "4", "overall brightness of bouncegrid texture"};
-cvar_t r_shadow_bouncegrid_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_lightradiusscale", "4", "particles stop at this fraction of light radius (can be more than 1)"};
-cvar_t r_shadow_bouncegrid_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_maxbounce", "2", "maximum number of bounces for a particle (minimum is 0)"};
-cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "1", "amount of energy carried over after each bounce, this is a multiplier of texture color and the result is clamped to 1 or less, to prevent adding energy on each bounce"};
-cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particleintensity", "1", "brightness of particles contributing to bouncegrid texture"};
-cvar_t r_shadow_bouncegrid_photons = {CVAR_SAVE, "r_shadow_bouncegrid_photons", "2000", "total photons to shoot per update, divided proportionately between lights"};
-cvar_t r_shadow_bouncegrid_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_spacing", "64", "unit size of bouncegrid pixel"};
-cvar_t r_shadow_bouncegrid_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_stablerandom", "1", "make particle distribution consistent from frame to frame"};
+cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "2", "amount of energy carried over after each bounce, this is a multiplier of texture color and the result is clamped to 1 or less, to prevent adding energy on each bounce"};
+cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particleintensity", "0.25", "brightness of particles contributing to bouncegrid texture"};
+cvar_t r_shadow_bouncegrid_sortlightpaths = {CVAR_SAVE, "r_shadow_bouncegrid_sortlightpaths", "1", "sort light paths before accumulating them into the bouncegrid texture, this reduces cpu cache misses"};
+cvar_t r_shadow_bouncegrid_lightpathsize = {CVAR_SAVE, "r_shadow_bouncegrid_lightpathsize", "1", "width of the light path for accumulation of light in the bouncegrid texture"};
 cvar_t r_shadow_bouncegrid_static = {CVAR_SAVE, "r_shadow_bouncegrid_static", "1", "use static radiosity solution (high quality) rather than dynamic (splotchy)"};
 cvar_t r_shadow_bouncegrid_static_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_static_directionalshading", "1", "whether to use directionalshading when in static mode"};
+cvar_t r_shadow_bouncegrid_static_energyperphoton = {CVAR_SAVE, "r_shadow_bouncegrid_static_energyperphoton", "10000", "amount of light that one photon should represent in static mode"};
 cvar_t r_shadow_bouncegrid_static_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_static_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1) when in static mode"};
 cvar_t r_shadow_bouncegrid_static_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_static_maxbounce", "5", "maximum number of bounces for a particle (minimum is 0) in static mode"};
-cvar_t r_shadow_bouncegrid_static_photons = {CVAR_SAVE, "r_shadow_bouncegrid_static_photons", "25000", "photons value to use when in static mode"};
-cvar_t r_shadow_bouncegrid_updateinterval = {CVAR_SAVE, "r_shadow_bouncegrid_updateinterval", "0", "update bouncegrid texture once per this many seconds, useful values are 0, 0.05, or 1000000"};
-cvar_t r_shadow_bouncegrid_x = {CVAR_SAVE, "r_shadow_bouncegrid_x", "64", "maximum texture size of bouncegrid on X axis"};
-cvar_t r_shadow_bouncegrid_y = {CVAR_SAVE, "r_shadow_bouncegrid_y", "64", "maximum texture size of bouncegrid on Y axis"};
-cvar_t r_shadow_bouncegrid_z = {CVAR_SAVE, "r_shadow_bouncegrid_z", "32", "maximum texture size of bouncegrid on Z axis"};
-cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
+cvar_t r_shadow_bouncegrid_static_maxphotons = {CVAR_SAVE, "r_shadow_bouncegrid_static_maxphotons", "250000", "upper bound on photons in static mode"};
+cvar_t r_shadow_bouncegrid_static_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_static_spacing", "64", "unit size of bouncegrid pixel when in static mode"};
+cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "0", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
 cvar_t r_coronas_occlusionsizescale = {CVAR_SAVE, "r_coronas_occlusionsizescale", "0.1", "size of light source for corona occlusion checksum the proportion of hidden pixels controls corona intensity"};
-cvar_t r_coronas_occlusionquery = {CVAR_SAVE, "r_coronas_occlusionquery", "1", "use GL_ARB_occlusion_query extension if supported (fades coronas according to visibility)"};
+cvar_t r_coronas_occlusionquery = {CVAR_SAVE, "r_coronas_occlusionquery", "0", "use GL_ARB_occlusion_query extension if supported (fades coronas according to visibility) - bad performance (synchronous rendering) - worse on multi-gpu!"};
 cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
@@ -372,35 +384,7 @@ cvar_t r_editlights_current_specular = {0, "r_editlights_current_specular", "1",
 cvar_t r_editlights_current_normalmode = {0, "r_editlights_current_normalmode", "0", "normalmode flag of selected light"};
 cvar_t r_editlights_current_realtimemode = {0, "r_editlights_current_realtimemode", "0", "realtimemode flag of selected light"};
 
-
-typedef struct r_shadow_bouncegrid_settings_s
-{
-       qboolean staticmode;
-       qboolean bounceanglediffuse;
-       qboolean directionalshading;
-       qboolean includedirectlighting;
-       float dlightparticlemultiplier;
-       qboolean hitmodels;
-       float lightradiusscale;
-       int maxbounce;
-       float particlebounceintensity;
-       float particleintensity;
-       int photons;
-       float spacing[3];
-       int stablerandom;
-}
-r_shadow_bouncegrid_settings_t;
-
-r_shadow_bouncegrid_settings_t r_shadow_bouncegridsettings;
-rtexture_t *r_shadow_bouncegridtexture;
-matrix4x4_t r_shadow_bouncegridmatrix;
-vec_t r_shadow_bouncegridintensity;
-qboolean r_shadow_bouncegriddirectional;
-static double r_shadow_bouncegridtime;
-static int r_shadow_bouncegridresolution[3];
-static int r_shadow_bouncegridnumpixels;
-static unsigned char *r_shadow_bouncegridpixels;
-static float *r_shadow_bouncegridhighpixels;
+r_shadow_bouncegrid_state_t r_shadow_bouncegrid_state;
 
 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
 #define ATTENTABLESIZE 256
@@ -452,6 +436,7 @@ static void R_Shadow_SetShadowMode(void)
        r_shadow_shadowmapsize = 0;
        r_shadow_shadowmapsampler = false;
        r_shadow_shadowmappcf = 0;
+       r_shadow_shadowmapdepthtexture = r_fb.usedepthtextures;
        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_STENCIL;
        if ((r_shadow_shadowmapping.integer || r_shadow_deferred.integer) && vid.support.ext_framebuffer_object)
        {
@@ -462,13 +447,13 @@ static void R_Shadow_SetShadowMode(void)
                        {
                                if (!r_fb.usedepthtextures)
                                        r_shadow_shadowmappcf = 1;
-                               else if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather)
-                                       r_shadow_shadowmappcf = 1;
-                               else if(strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) 
+                               else if((strstr(gl_vendor, "NVIDIA") || strstr(gl_renderer, "Radeon HD")) && vid.support.arb_shadow && r_shadow_shadowmapshadowsampler) 
                                {
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
+                                       r_shadow_shadowmapsampler = true;
                                        r_shadow_shadowmappcf = 1;
                                }
+                               else if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather)
+                                       r_shadow_shadowmappcf = 1;
                                else if((strstr(gl_vendor, "ATI") || strstr(gl_vendor, "Advanced Micro Devices")) && !strstr(gl_renderer, "Mesa") && !strstr(gl_version, "Mesa")) 
                                        r_shadow_shadowmappcf = 1;
                                else 
@@ -476,13 +461,12 @@ static void R_Shadow_SetShadowMode(void)
                        }
                        else 
                        {
+                r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                                switch (r_shadow_shadowmapfilterquality)
                                {
                                case 1:
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                                        break;
                                case 2:
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                                        r_shadow_shadowmappcf = 1;
                                        break;
                                case 3:
@@ -552,11 +536,8 @@ static void R_Shadow_FreeShadowMaps(void)
 static void r_shadow_start(void)
 {
        // allocate vertex processing arrays
-       r_shadow_bouncegridpixels = NULL;
-       r_shadow_bouncegridhighpixels = NULL;
-       r_shadow_bouncegridnumpixels = 0;
-       r_shadow_bouncegridtexture = NULL;
-       r_shadow_bouncegriddirectional = false;
+       memset(&r_shadow_bouncegrid_state, 0, sizeof(r_shadow_bouncegrid_state));
+       r_shadow_bouncegrid_state.maxsplatpaths = 16384;
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -612,6 +593,29 @@ static void r_shadow_start(void)
 
        r_shadow_usingdeferredprepass = false;
        r_shadow_prepass_width = r_shadow_prepass_height = 0;
+
+       // determine renderpath specific capabilities, we don't need to figure
+       // these out per frame...
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               r_shadow_bouncegrid_state.allowdirectionalshading = true;
+               r_shadow_bouncegrid_state.capable = vid.support.ext_texture_3d;
+               break;
+       case RENDERPATH_GLES2:
+               // for performance reasons, do not use directional shading on GLES devices
+               r_shadow_bouncegrid_state.capable = vid.support.ext_texture_3d;
+               break;
+               // these renderpaths do not currently have the code to display the bouncegrid, so disable it on them...
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GLES1:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+               break;
+       }
 }
 
 static void R_Shadow_FreeDeferred(void);
@@ -628,11 +632,7 @@ static void r_shadow_shutdown(void)
        r_shadow_prepass_width = r_shadow_prepass_height = 0;
 
        CHECKGLERROR
-       r_shadow_bouncegridtexture = NULL;
-       r_shadow_bouncegridpixels = NULL;
-       r_shadow_bouncegridhighpixels = NULL;
-       r_shadow_bouncegridnumpixels = 0;
-       r_shadow_bouncegriddirectional = false;
+       memset(&r_shadow_bouncegrid_state, 0, sizeof(r_shadow_bouncegrid_state));
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -700,7 +700,7 @@ static void r_shadow_shutdown(void)
 
 static void r_shadow_newmap(void)
 {
-       if (r_shadow_bouncegridtexture) R_FreeTexture(r_shadow_bouncegridtexture);r_shadow_bouncegridtexture = NULL;
+       if (r_shadow_bouncegrid_state.texture)    R_FreeTexture(r_shadow_bouncegrid_state.texture);r_shadow_bouncegrid_state.texture = NULL;
        if (r_shadow_lightcorona)                 R_SkinFrame_MarkUsed(r_shadow_lightcorona);
        if (r_editlights_sprcursor)               R_SkinFrame_MarkUsed(r_editlights_sprcursor);
        if (r_editlights_sprlight)                R_SkinFrame_MarkUsed(r_editlights_sprlight);
@@ -732,6 +732,7 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_lightradiusscale);
        Cvar_RegisterVariable(&r_shadow_projectdistance);
        Cvar_RegisterVariable(&r_shadow_frontsidecasting);
+       Cvar_RegisterVariable(&r_shadow_realtime_world_importlightentitiesfrommap);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
@@ -764,28 +765,36 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
        Cvar_RegisterVariable(&r_shadow_bouncegrid);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_blur);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_bounceanglediffuse);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_directionalshading);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_dlightparticlemultiplier);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_hitmodels);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_culllightpaths);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_directionalshading);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_dlightparticlemultiplier);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_hitmodels);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_energyperphoton);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_lightradiusscale);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_maxbounce);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_maxphotons);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_spacing);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_stablerandom);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_updateinterval);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_x);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_y);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_dynamic_z);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_floatcolors);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_includedirectlighting);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_intensity);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_lightradiusscale);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_maxbounce);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_lightpathsize);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_particlebounceintensity);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_particleintensity);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_photons);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacing);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_sortlightpaths);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static_lightradiusscale);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_photons);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_updateinterval);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_x);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_y);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_z);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxphotons);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_energyperphoton);
        Cvar_RegisterVariable(&r_coronas);
        Cvar_RegisterVariable(&r_coronas_occlusionsizescale);
        Cvar_RegisterVariable(&r_coronas_occlusionquery);
@@ -940,10 +949,10 @@ void R_Shadow_PrepareShadowMark(int numtris)
 
 void R_Shadow_PrepareShadowSides(int numtris)
 {
-    if (maxshadowsides < numtris)
-    {
-        maxshadowsides = numtris;
-        if (shadowsides)
+       if (maxshadowsides < numtris)
+       {
+               maxshadowsides = numtris;
+               if (shadowsides)
                        Mem_Free(shadowsides);
                if (shadowsideslist)
                        Mem_Free(shadowsideslist);
@@ -1300,7 +1309,7 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv
                                v[2] = invertex3f + e[2] * 3;
                                TriangleNormal(v[0], v[1], v[2], normal);
                                if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
-                                && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+                                && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
                                        shadowmarklist[numshadowmark++] = t;
                        }
                }
@@ -1312,7 +1321,7 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv
                                v[1] = invertex3f + e[1] * 3;
                                v[2] = invertex3f + e[2] * 3;
                                if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
-                                && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+                                && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
                                        shadowmarklist[numshadowmark++] = t;
                        }
                }
@@ -1384,7 +1393,7 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_VISIBLEVOLUMES)
        {
                tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
-               R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL);
+               R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL, 0);
                R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, NULL, NULL, 0);
        }
        else
@@ -1396,8 +1405,8 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
                        tris = R_Shadow_ConstructShadowVolume_ZPass(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
                else
                        tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
-               r_refdef.stats.lights_dynamicshadowtriangles += tris;
-               r_refdef.stats.lights_shadowtriangles += tris;
+               r_refdef.stats[r_stat_lights_dynamicshadowtriangles] += tris;
+               r_refdef.stats[r_stat_lights_shadowtriangles] += tris;
                if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL)
                {
                        // increment stencil if frontface is infront of depthbuffer
@@ -1418,58 +1427,58 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
                        GL_CullFace(r_refdef.view.cullface_back);
                        R_SetStencil(true, 255, GL_KEEP, GL_INCR, GL_KEEP, GL_ALWAYS, 128, 255);
                }
-               R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL);
+               R_Mesh_PrepareVertices_Vertex3f(outverts, shadowvertex3f, NULL, 0);
                R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, NULL, NULL, 0);
        }
 }
 
 int R_Shadow_CalcTriangleSideMask(const vec3_t p1, const vec3_t p2, const vec3_t p3, float bias)
 {
-    // p1, p2, p3 are in the cubemap's local coordinate system
-    // bias = border/(size - border)
+       // p1, p2, p3 are in the cubemap's local coordinate system
+       // bias = border/(size - border)
        int mask = 0x3F;
 
-    float dp1 = p1[0] + p1[1], dn1 = p1[0] - p1[1], ap1 = fabs(dp1), an1 = fabs(dn1),
-         dp2 = p2[0] + p2[1], dn2 = p2[0] - p2[1], ap2 = fabs(dp2), an2 = fabs(dn2),
-         dp3 = p3[0] + p3[1], dn3 = p3[0] - p3[1], ap3 = fabs(dp3), an3 = fabs(dn3);
+       float dp1 = p1[0] + p1[1], dn1 = p1[0] - p1[1], ap1 = fabs(dp1), an1 = fabs(dn1),
+                 dp2 = p2[0] + p2[1], dn2 = p2[0] - p2[1], ap2 = fabs(dp2), an2 = fabs(dn2),
+                 dp3 = p3[0] + p3[1], dn3 = p3[0] - p3[1], ap3 = fabs(dp3), an3 = fabs(dn3);
        if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
-       mask &= (3<<4)
+               mask &= (3<<4)
                        | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
                        | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
                        | (dp3 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
-    if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
-        mask &= (3<<4)
-            | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
-            | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))            
-            | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
-
-    dp1 = p1[1] + p1[2], dn1 = p1[1] - p1[2], ap1 = fabs(dp1), an1 = fabs(dn1),
-    dp2 = p2[1] + p2[2], dn2 = p2[1] - p2[2], ap2 = fabs(dp2), an2 = fabs(dn2),
-    dp3 = p3[1] + p3[2], dn3 = p3[1] - p3[2], ap3 = fabs(dp3), an3 = fabs(dn3);
-    if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
-        mask &= (3<<0)
-            | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
-            | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))            
-            | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
-    if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
-        mask &= (3<<0)
-            | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
-            | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
-            | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
-
-    dp1 = p1[2] + p1[0], dn1 = p1[2] - p1[0], ap1 = fabs(dp1), an1 = fabs(dn1),
-    dp2 = p2[2] + p2[0], dn2 = p2[2] - p2[0], ap2 = fabs(dp2), an2 = fabs(dn2),
-    dp3 = p3[2] + p3[0], dn3 = p3[2] - p3[0], ap3 = fabs(dp3), an3 = fabs(dn3);
-    if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
-        mask &= (3<<2)
-            | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
-            | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
-            | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
-    if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
-        mask &= (3<<2)
-            | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
-            | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
-            | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
+       if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
+               mask &= (3<<4)
+                       | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
+                       | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))                    
+                       | (dn3 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
+
+       dp1 = p1[1] + p1[2], dn1 = p1[1] - p1[2], ap1 = fabs(dp1), an1 = fabs(dn1),
+       dp2 = p2[1] + p2[2], dn2 = p2[1] - p2[2], ap2 = fabs(dp2), an2 = fabs(dn2),
+       dp3 = p3[1] + p3[2], dn3 = p3[1] - p3[2], ap3 = fabs(dp3), an3 = fabs(dn3);
+       if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
+               mask &= (3<<0)
+                       | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
+                       | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))                    
+                       | (dp3 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
+       if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
+               mask &= (3<<0)
+                       | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
+                       | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
+                       | (dn3 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
+
+       dp1 = p1[2] + p1[0], dn1 = p1[2] - p1[0], ap1 = fabs(dp1), an1 = fabs(dn1),
+       dp2 = p2[2] + p2[0], dn2 = p2[2] - p2[0], ap2 = fabs(dp2), an2 = fabs(dn2),
+       dp3 = p3[2] + p3[0], dn3 = p3[2] - p3[0], ap3 = fabs(dp3), an3 = fabs(dn3);
+       if(ap1 > bias*an1 && ap2 > bias*an2 && ap3 > bias*an3)
+               mask &= (3<<2)
+                       | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
+                       | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
+                       | (dp3 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
+       if(an1 > bias*ap1 && an2 > bias*ap2 && an3 > bias*ap3)
+               mask &= (3<<2)
+                       | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
+                       | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
+                       | (dn3 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
 
        return mask;
 }
@@ -1481,66 +1490,66 @@ static int R_Shadow_CalcBBoxSideMask(const vec3_t mins, const vec3_t maxs, const
        int mask = 0x3F;
 
        VectorSubtract(maxs, mins, radius);
-    VectorScale(radius, 0.5f, radius);
-    VectorAdd(mins, radius, center);
-    Matrix4x4_Transform(worldtolight, center, lightcenter);
+       VectorScale(radius, 0.5f, radius);
+       VectorAdd(mins, radius, center);
+       Matrix4x4_Transform(worldtolight, center, lightcenter);
        Matrix4x4_Transform3x3(radiustolight, radius, lightradius);
        VectorSubtract(lightcenter, lightradius, pmin);
        VectorAdd(lightcenter, lightradius, pmax);
 
-    dp1 = pmax[0] + pmax[1], dn1 = pmax[0] - pmin[1], ap1 = fabs(dp1), an1 = fabs(dn1),
-    dp2 = pmin[0] + pmin[1], dn2 = pmin[0] - pmax[1], ap2 = fabs(dp2), an2 = fabs(dn2);
-    if(ap1 > bias*an1 && ap2 > bias*an2)
-        mask &= (3<<4)
-            | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
-            | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
-    if(an1 > bias*ap1 && an2 > bias*ap2)
-        mask &= (3<<4)
-            | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
-            | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
-
-    dp1 = pmax[1] + pmax[2], dn1 = pmax[1] - pmin[2], ap1 = fabs(dp1), an1 = fabs(dn1),
-    dp2 = pmin[1] + pmin[2], dn2 = pmin[1] - pmax[2], ap2 = fabs(dp2), an2 = fabs(dn2);
-    if(ap1 > bias*an1 && ap2 > bias*an2)
-        mask &= (3<<0)
-            | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
-            | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
-    if(an1 > bias*ap1 && an2 > bias*ap2)
-        mask &= (3<<0)
-            | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
-            | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
-
-    dp1 = pmax[2] + pmax[0], dn1 = pmax[2] - pmin[0], ap1 = fabs(dp1), an1 = fabs(dn1),
-    dp2 = pmin[2] + pmin[0], dn2 = pmin[2] - pmax[0], ap2 = fabs(dp2), an2 = fabs(dn2);
-    if(ap1 > bias*an1 && ap2 > bias*an2)
-        mask &= (3<<2)
-            | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
-            | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
-    if(an1 > bias*ap1 && an2 > bias*ap2)
-        mask &= (3<<2)
-            | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
-            | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
-
-    return mask;
+       dp1 = pmax[0] + pmax[1], dn1 = pmax[0] - pmin[1], ap1 = fabs(dp1), an1 = fabs(dn1),
+       dp2 = pmin[0] + pmin[1], dn2 = pmin[0] - pmax[1], ap2 = fabs(dp2), an2 = fabs(dn2);
+       if(ap1 > bias*an1 && ap2 > bias*an2)
+               mask &= (3<<4)
+                       | (dp1 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2))
+                       | (dp2 >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
+       if(an1 > bias*ap1 && an2 > bias*ap2)
+               mask &= (3<<4)
+                       | (dn1 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2))
+                       | (dn2 >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
+
+       dp1 = pmax[1] + pmax[2], dn1 = pmax[1] - pmin[2], ap1 = fabs(dp1), an1 = fabs(dn1),
+       dp2 = pmin[1] + pmin[2], dn2 = pmin[1] - pmax[2], ap2 = fabs(dp2), an2 = fabs(dn2);
+       if(ap1 > bias*an1 && ap2 > bias*an2)
+               mask &= (3<<0)
+                       | (dp1 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4))
+                       | (dp2 >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
+       if(an1 > bias*ap1 && an2 > bias*ap2)
+               mask &= (3<<0)
+                       | (dn1 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4))
+                       | (dn2 >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
+
+       dp1 = pmax[2] + pmax[0], dn1 = pmax[2] - pmin[0], ap1 = fabs(dp1), an1 = fabs(dn1),
+       dp2 = pmin[2] + pmin[0], dn2 = pmin[2] - pmax[0], ap2 = fabs(dp2), an2 = fabs(dn2);
+       if(ap1 > bias*an1 && ap2 > bias*an2)
+               mask &= (3<<2)
+                       | (dp1 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0))
+                       | (dp2 >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
+       if(an1 > bias*ap1 && an2 > bias*ap2)
+               mask &= (3<<2)
+                       | (dn1 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0))
+                       | (dn2 >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
+
+       return mask;
 }
 
 #define R_Shadow_CalcEntitySideMask(ent, worldtolight, radiustolight, bias) R_Shadow_CalcBBoxSideMask((ent)->mins, (ent)->maxs, worldtolight, radiustolight, bias)
 
 int R_Shadow_CalcSphereSideMask(const vec3_t p, float radius, float bias)
 {
-    // p is in the cubemap's local coordinate system
-    // bias = border/(size - border)
-    float dxyp = p[0] + p[1], dxyn = p[0] - p[1], axyp = fabs(dxyp), axyn = fabs(dxyn);
-    float dyzp = p[1] + p[2], dyzn = p[1] - p[2], ayzp = fabs(dyzp), ayzn = fabs(dyzn);
-    float dzxp = p[2] + p[0], dzxn = p[2] - p[0], azxp = fabs(dzxp), azxn = fabs(dzxn);
-    int mask = 0x3F;
-    if(axyp > bias*axyn + radius) mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2));
-    if(axyn > bias*axyp + radius) mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2));
-    if(ayzp > bias*ayzn + radius) mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4));
-    if(ayzn > bias*ayzp + radius) mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4));
-    if(azxp > bias*azxn + radius) mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0));
-    if(azxn > bias*azxp + radius) mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0));
-    return mask;
+       // p is in the cubemap's local coordinate system
+       // bias = border/(size - border)
+       float dxyp = p[0] + p[1], dxyn = p[0] - p[1], axyp = fabs(dxyp), axyn = fabs(dxyn);
+       float dyzp = p[1] + p[2], dyzn = p[1] - p[2], ayzp = fabs(dyzp), ayzn = fabs(dyzn);
+       float dzxp = p[2] + p[0], dzxn = p[2] - p[0], azxp = fabs(dzxp), azxn = fabs(dzxn);
+       int mask = 0x3F;
+       if(axyp > bias*axyn + radius) mask &= dxyp < 0 ? ~((1<<0)|(1<<2)) : ~((2<<0)|(2<<2));
+       if(axyn > bias*axyp + radius) mask &= dxyn < 0 ? ~((1<<0)|(2<<2)) : ~((2<<0)|(1<<2));
+       if(ayzp > bias*ayzn + radius) mask &= dyzp < 0 ? ~((1<<2)|(1<<4)) : ~((2<<2)|(2<<4));
+       if(ayzn > bias*ayzp + radius) mask &= dyzn < 0 ? ~((1<<2)|(2<<4)) : ~((2<<2)|(1<<4));
+       if(azxp > bias*azxn + radius) mask &= dzxp < 0 ? ~((1<<4)|(1<<0)) : ~((2<<4)|(2<<0));
+       if(azxn > bias*azxp + radius) mask &= dzxn < 0 ? ~((1<<4)|(2<<0)) : ~((2<<4)|(1<<0));
+       return mask;
 }
 
 static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border)
@@ -1565,8 +1574,8 @@ static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float borde
        }
        if (PlaneDiff(o, &r_refdef.view.frustum[4]) >= r_refdef.farclip - r_refdef.nearclip + 0.03125)
        {
-        Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[4].normal, n);
-        len = scale*VectorLength2(n);
+               Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[4].normal, n);
+               len = scale*VectorLength2(n);
                if(n[0]*n[0] > len) sides &= n[0] >= 0 ? ~(1<<0) : ~(2 << 0);
                if(n[1]*n[1] > len) sides &= n[1] >= 0 ? ~(1<<2) : ~(2 << 2);
                if(n[2]*n[2] > len) sides &= n[2] >= 0 ? ~(1<<4) : ~(2 << 4);
@@ -1574,33 +1583,33 @@ static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float borde
        // this next test usually clips off more sides than the former, but occasionally clips fewer/different ones, so do both and combine results
        // check if frustum corners/origin cross plane sides
 #if 1
-    // infinite version, assumes frustum corners merely give direction and extend to infinite distance
-    Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.origin, p);
-    dp = p[0] + p[1], dn = p[0] - p[1], ap = fabs(dp), an = fabs(dn);
-    masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
-    masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
-    dp = p[1] + p[2], dn = p[1] - p[2], ap = fabs(dp), an = fabs(dn);
-    masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
-    masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
-    dp = p[2] + p[0], dn = p[2] - p[0], ap = fabs(dp), an = fabs(dn);
-    masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
-    masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
-    for (i = 0;i < 4;i++)
-    {
-        Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.frustumcorner[i], n);
-        VectorSubtract(n, p, n);
-        dp = n[0] + n[1], dn = n[0] - n[1], ap = fabs(dp), an = fabs(dn);
-        if(ap > 0) masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2);
-        if(an > 0) masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2);
-        dp = n[1] + n[2], dn = n[1] - n[2], ap = fabs(dp), an = fabs(dn);
-        if(ap > 0) masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4);
-        if(an > 0) masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4);
-        dp = n[2] + n[0], dn = n[2] - n[0], ap = fabs(dp), an = fabs(dn);
-        if(ap > 0) masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0);
-        if(an > 0) masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0);
-    }
+       // infinite version, assumes frustum corners merely give direction and extend to infinite distance
+       Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.origin, p);
+       dp = p[0] + p[1], dn = p[0] - p[1], ap = fabs(dp), an = fabs(dn);
+       masks[0] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2));
+       masks[1] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2));
+       dp = p[1] + p[2], dn = p[1] - p[2], ap = fabs(dp), an = fabs(dn);
+       masks[2] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4));
+       masks[3] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4));
+       dp = p[2] + p[0], dn = p[2] - p[0], ap = fabs(dp), an = fabs(dn);
+       masks[4] |= ap <= bias*an ? 0x3F : (dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0));
+       masks[5] |= an <= bias*ap ? 0x3F : (dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0));
+       for (i = 0;i < 4;i++)
+       {
+               Matrix4x4_Transform(&rtlight->matrix_worldtolight, r_refdef.view.frustumcorner[i], n);
+               VectorSubtract(n, p, n);
+               dp = n[0] + n[1], dn = n[0] - n[1], ap = fabs(dp), an = fabs(dn);
+               if(ap > 0) masks[0] |= dp >= 0 ? (1<<0)|(1<<2) : (2<<0)|(2<<2);
+               if(an > 0) masks[1] |= dn >= 0 ? (1<<0)|(2<<2) : (2<<0)|(1<<2);
+               dp = n[1] + n[2], dn = n[1] - n[2], ap = fabs(dp), an = fabs(dn);
+               if(ap > 0) masks[2] |= dp >= 0 ? (1<<2)|(1<<4) : (2<<2)|(2<<4);
+               if(an > 0) masks[3] |= dn >= 0 ? (1<<2)|(2<<4) : (2<<2)|(1<<4);
+               dp = n[2] + n[0], dn = n[2] - n[0], ap = fabs(dp), an = fabs(dn);
+               if(ap > 0) masks[4] |= dp >= 0 ? (1<<4)|(1<<0) : (2<<4)|(2<<0);
+               if(an > 0) masks[5] |= dn >= 0 ? (1<<4)|(2<<0) : (2<<4)|(1<<0);
+       }
 #else
-    // finite version, assumes corners are a finite distance from origin dependent on far plane
+       // finite version, assumes corners are a finite distance from origin dependent on far plane
        for (i = 0;i < 5;i++)
        {
                Matrix4x4_Transform(&rtlight->matrix_worldtolight, !i ? r_refdef.view.origin : r_refdef.view.frustumcorner[i-1], p);
@@ -1684,7 +1693,7 @@ int R_Shadow_ChooseSidesFromBox(int firsttriangle, int numtris, const float *inv
                                v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3,     v[2] = invertex3f + e[2] * 3;
                                TriangleNormal(v[0], v[1], v[2], normal);
                                if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
-                                && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+                                && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
                                {
                                        Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
                                        mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
@@ -1704,7 +1713,7 @@ int R_Shadow_ChooseSidesFromBox(int firsttriangle, int numtris, const float *inv
                        {
                                v[0] = invertex3f + e[0] * 3, v[1] = invertex3f + e[1] * 3, v[2] = invertex3f + e[2] * 3;
                                if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
-                                && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
+                                && TriangleBBoxOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
                                {
                                        Matrix4x4_Transform(worldtolight, v[0], p[0]), Matrix4x4_Transform(worldtolight, v[1], p[1]), Matrix4x4_Transform(worldtolight, v[2], p[2]);
                                        mask = R_Shadow_CalcTriangleSideMask(p[0], p[1], p[2], bias);
@@ -2063,7 +2072,7 @@ void R_Shadow_RenderMode_Reset(void)
 void R_Shadow_ClearStencil(void)
 {
        GL_Clear(GL_STENCIL_BUFFER_BIT, NULL, 1.0f, 128);
-       r_refdef.stats.lights_clears++;
+       r_refdef.stats[r_stat_lights_clears]++;
 }
 
 void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass)
@@ -2076,7 +2085,7 @@ void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass)
        GL_ColorMask(0, 0, 0, 0);
        GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
        GL_CullFace(GL_NONE);
-       R_SetupShader_DepthOrShadow(false, false);
+       R_SetupShader_DepthOrShadow(false, false, false); // FIXME test if we have a skeletal model?
        r_shadow_rendermode = mode;
        switch(mode)
        {
@@ -2173,7 +2182,7 @@ static void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
                R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL);
        else
                R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL);
-       R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL);
+       R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL, false); // FIXME test if we have a skeletal model?
        GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value);
        GL_DepthMask(true);
        GL_DepthTest(true);
@@ -2321,104 +2330,126 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow
        GL_DepthTest(true);
        GL_DepthFunc(GL_GREATER);
        GL_CullFace(r_refdef.view.cullface_back);
-       R_Mesh_PrepareVertices_Vertex3f(8, vertex3f, NULL);
+       R_Mesh_PrepareVertices_Vertex3f(8, vertex3f, NULL, 0);
        R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
-void R_Shadow_UpdateBounceGridTexture(void)
+#define MAXBOUNCEGRIDSPLATSIZE 7
+#define MAXBOUNCEGRIDSPLATSIZE1 (MAXBOUNCEGRIDSPLATSIZE+1)
+
+// these are temporary data per-frame, sorted and performed in a more
+// cache-friendly order than the original photons
+typedef struct r_shadow_bouncegrid_splatpath_s
 {
-#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
-       dlight_t *light;
-       int flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
-       int bouncecount;
-       int hitsupercontentsmask;
-       int maxbounce;
-       int numpixels;
-       int resolution[3];
-       int shootparticles;
-       int shotparticles;
-       int photoncount;
-       int tex[3];
-       trace_t cliptrace;
-       //trace_t cliptrace2;
-       //trace_t cliptrace3;
-       unsigned char *pixel;
-       unsigned char *pixels;
-       float *highpixel;
-       float *highpixels;
-       unsigned int lightindex;
-       unsigned int range;
-       unsigned int range1;
-       unsigned int range2;
-       unsigned int seed = (unsigned int)(realtime * 1000.0f);
-       vec3_t shotcolor;
-       vec3_t baseshotcolor;
-       vec3_t surfcolor;
-       vec3_t clipend;
-       vec3_t clipstart;
-       vec3_t clipdiff;
-       vec3_t ispacing;
-       vec3_t maxs;
-       vec3_t mins;
-       vec3_t size;
-       vec3_t spacing;
-       vec3_t lightcolor;
-       vec3_t steppos;
-       vec3_t stepdelta;
-       vec3_t cullmins, cullmaxs;
-       vec_t radius;
-       vec_t s;
-       vec_t lightintensity;
-       vec_t photonscaling;
-       vec_t photonresidual;
-       float m[16];
-       float texlerp[2][3];
-       float splatcolor[32];
-       float pixelweight[8];
-       float w;
-       int c[4];
-       int pixelindex[8];
-       int corner;
-       int pixelsperband;
-       int pixelband;
-       int pixelbands;
-       int numsteps;
-       int step;
-       int x, y, z;
-       rtlight_t *rtlight;
-       r_shadow_bouncegrid_settings_t settings;
-       qboolean enable = r_shadow_bouncegrid.integer != 0 && r_refdef.scene.worldmodel;
-       qboolean allowdirectionalshading = false;
-       switch(vid.renderpath)
-       {
-       case RENDERPATH_GL20:
-               allowdirectionalshading = true;
-               if (!vid.support.ext_texture_3d)
-                       return;
-               break;
-       case RENDERPATH_GLES2:
-               // for performance reasons, do not use directional shading on GLES devices
-               if (!vid.support.ext_texture_3d)
+       vec3_t point;
+       vec3_t step;
+       vec3_t splatcolor;
+       vec3_t splatdir;
+       vec_t splatintensity;
+       int remainingsplats;
+}
+r_shadow_bouncegrid_splatpath_t;
+
+static void R_Shadow_BounceGrid_AddSplatPath(vec3_t originalstart, vec3_t originalend, vec3_t color)
+{
+       int bestaxis;
+       int numsplats;
+       float len;
+       float ilen;
+       vec3_t start;
+       vec3_t end;
+       vec3_t diff;
+       vec3_t originaldir;
+       r_shadow_bouncegrid_splatpath_t *path;
+
+       // cull paths that fail R_CullBox in dynamic mode
+       if (!r_shadow_bouncegrid_state.settings.staticmode
+        && r_shadow_bouncegrid_dynamic_culllightpaths.integer)
+       {
+               vec3_t cullmins, cullmaxs;
+               cullmins[0] = min(originalstart[0], originalend[0]) - r_shadow_bouncegrid_state.settings.spacing[0];
+               cullmins[1] = min(originalstart[1], originalend[1]) - r_shadow_bouncegrid_state.settings.spacing[1];
+               cullmins[2] = min(originalstart[2], originalend[2]) - r_shadow_bouncegrid_state.settings.spacing[2];
+               cullmaxs[0] = max(originalstart[0], originalend[0]) + r_shadow_bouncegrid_state.settings.spacing[0];
+               cullmaxs[1] = max(originalstart[1], originalend[1]) + r_shadow_bouncegrid_state.settings.spacing[1];
+               cullmaxs[2] = max(originalstart[2], originalend[2]) + r_shadow_bouncegrid_state.settings.spacing[2];
+               if (R_CullBox(cullmins, cullmaxs))
                        return;
-               break;
-               // these renderpaths do not currently have the code to display the bouncegrid, so disable it on them...
-       case RENDERPATH_GL11:
-       case RENDERPATH_GL13:
-       case RENDERPATH_GLES1:
-       case RENDERPATH_SOFT:
-       case RENDERPATH_D3D9:
-       case RENDERPATH_D3D10:
-       case RENDERPATH_D3D11:
-               return;
        }
 
-       r_shadow_bouncegridintensity = r_shadow_bouncegrid_intensity.value;
+       // if the light path is going upward, reverse it - we always draw down.
+       if (originalend[2] < originalstart[2])
+       {
+               VectorCopy(originalend, start);
+               VectorCopy(originalstart, end);
+       }
+       else
+       {
+               VectorCopy(originalstart, start);
+               VectorCopy(originalend, end);
+       }
+
+       // transform to texture pixels
+       start[0] = (start[0] - r_shadow_bouncegrid_state.mins[0]) * r_shadow_bouncegrid_state.ispacing[0];
+       start[1] = (start[1] - r_shadow_bouncegrid_state.mins[1]) * r_shadow_bouncegrid_state.ispacing[1];
+       start[2] = (start[2] - r_shadow_bouncegrid_state.mins[2]) * r_shadow_bouncegrid_state.ispacing[2];
+       end[0] = (end[0] - r_shadow_bouncegrid_state.mins[0]) * r_shadow_bouncegrid_state.ispacing[0];
+       end[1] = (end[1] - r_shadow_bouncegrid_state.mins[1]) * r_shadow_bouncegrid_state.ispacing[1];
+       end[2] = (end[2] - r_shadow_bouncegrid_state.mins[2]) * r_shadow_bouncegrid_state.ispacing[2];
+
+       // check if we need to grow the splatpaths array
+       if (r_shadow_bouncegrid_state.maxsplatpaths <= r_shadow_bouncegrid_state.numsplatpaths)
+       {
+               // double the limit, this will persist from frame to frame so we don't
+               // make the same mistake each time
+               r_shadow_bouncegrid_splatpath_t *newpaths;
+               r_shadow_bouncegrid_state.maxsplatpaths *= 2;
+               newpaths = (r_shadow_bouncegrid_splatpath_t *)R_FrameData_Alloc(sizeof(r_shadow_bouncegrid_splatpath_t) * r_shadow_bouncegrid_state.maxsplatpaths);
+               if (r_shadow_bouncegrid_state.splatpaths)
+                       memcpy(newpaths, r_shadow_bouncegrid_state.splatpaths, r_shadow_bouncegrid_state.numsplatpaths * sizeof(r_shadow_bouncegrid_splatpath_t));
+               r_shadow_bouncegrid_state.splatpaths = newpaths;
+       }
+
+       // divide a series of splats along the length using the maximum axis
+       VectorSubtract(end, start, diff);
+       // pick the best axis to trace along
+       bestaxis = 0;
+       if (diff[1]*diff[1] > diff[bestaxis]*diff[bestaxis])
+               bestaxis = 1;
+       if (diff[2]*diff[2] > diff[bestaxis]*diff[bestaxis])
+               bestaxis = 2;
+       len = fabs(diff[bestaxis]);
+       ilen = 1.0f / len;
+       numsplats = (int)(floor(len + 0.5f));
+       // sanity limits
+       numsplats = bound(0, numsplats, 1024);
+
+       VectorSubtract(originalstart, originalend, originaldir);
+       VectorNormalize(originaldir);
+
+       path = r_shadow_bouncegrid_state.splatpaths + r_shadow_bouncegrid_state.numsplatpaths++;
+       VectorCopy(start, path->point);
+       VectorScale(diff, ilen, path->step);
+       VectorCopy(color, path->splatcolor);
+       VectorCopy(originaldir, path->splatdir);
+       path->splatintensity = VectorLength(color);
+       path->remainingsplats = numsplats;
+}
+
+static qboolean R_Shadow_BounceGrid_CheckEnable(int flag)
+{
+       qboolean enable = r_shadow_bouncegrid_state.capable && r_shadow_bouncegrid.integer != 0 && r_refdef.scene.worldmodel;
+       int lightindex;
+       int range;
+       dlight_t *light;
+       rtlight_t *rtlight;
+       vec3_t lightcolor;
 
        // see if there are really any lights to render...
        if (enable && r_shadow_bouncegrid_static.integer)
        {
                enable = false;
-               range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+               range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
                for (lightindex = 0;lightindex < range;lightindex++)
                {
                        light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
@@ -2436,61 +2467,79 @@ void R_Shadow_UpdateBounceGridTexture(void)
                }
        }
 
-       if (!enable)
-       {
-               if (r_shadow_bouncegridtexture)
-               {
-                       R_FreeTexture(r_shadow_bouncegridtexture);
-                       r_shadow_bouncegridtexture = NULL;
-               }
-               if (r_shadow_bouncegridpixels)
-                       Mem_Free(r_shadow_bouncegridpixels);
-               r_shadow_bouncegridpixels = NULL;
-               if (r_shadow_bouncegridhighpixels)
-                       Mem_Free(r_shadow_bouncegridhighpixels);
-               r_shadow_bouncegridhighpixels = NULL;
-               r_shadow_bouncegridnumpixels = 0;
-               r_shadow_bouncegriddirectional = false;
-               return;
-       }
+       return enable;
+}
+
+static void R_Shadow_BounceGrid_GenerateSettings(r_shadow_bouncegrid_settings_t *settings)
+{
+       qboolean s = r_shadow_bouncegrid_static.integer != 0;
+       float spacing = s ? r_shadow_bouncegrid_static_spacing.value : r_shadow_bouncegrid_dynamic_spacing.value;
+
+       // prevent any garbage in alignment padded areas as we'll be using memcmp
+       memset(settings, 0, sizeof(*settings)); 
 
        // build up a complete collection of the desired settings, so that memcmp can be used to compare parameters
-       memset(&settings, 0, sizeof(settings));
-       settings.staticmode                    = r_shadow_bouncegrid_static.integer != 0;
-       settings.bounceanglediffuse            = r_shadow_bouncegrid_bounceanglediffuse.integer != 0;
-       settings.directionalshading            = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_directionalshading.integer != 0 : r_shadow_bouncegrid_directionalshading.integer != 0) && allowdirectionalshading;
-       settings.dlightparticlemultiplier      = r_shadow_bouncegrid_dlightparticlemultiplier.value;
-       settings.hitmodels                     = r_shadow_bouncegrid_hitmodels.integer != 0;
-       settings.includedirectlighting         = r_shadow_bouncegrid_includedirectlighting.integer != 0 || r_shadow_bouncegrid.integer == 2;
-       settings.lightradiusscale              = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_lightradiusscale.value);
-       settings.maxbounce                     = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_maxbounce.integer);
-       settings.particlebounceintensity       = r_shadow_bouncegrid_particlebounceintensity.value;
-       settings.particleintensity             = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings.directionalshading ? 4.0f : 1.0f) / (r_shadow_bouncegrid_spacing.value * r_shadow_bouncegrid_spacing.value);
-       settings.photons                       = r_shadow_bouncegrid_static.integer ? r_shadow_bouncegrid_static_photons.integer : r_shadow_bouncegrid_photons.integer;
-       settings.spacing[0]                    = r_shadow_bouncegrid_spacing.value;
-       settings.spacing[1]                    = r_shadow_bouncegrid_spacing.value;
-       settings.spacing[2]                    = r_shadow_bouncegrid_spacing.value;
-       settings.stablerandom                  = r_shadow_bouncegrid_stablerandom.integer;
+       settings->staticmode                    = s;
+       settings->blur                          = r_shadow_bouncegrid_blur.integer != 0;
+       settings->floatcolors                   = bound(0, r_shadow_bouncegrid_floatcolors.integer, 2);
+       settings->lightpathsize                 = bound(1, r_shadow_bouncegrid_lightpathsize.integer, MAXBOUNCEGRIDSPLATSIZE);
+       settings->bounceanglediffuse            = r_shadow_bouncegrid_bounceanglediffuse.integer != 0;
+       settings->directionalshading            = (s ? r_shadow_bouncegrid_static_directionalshading.integer != 0 : r_shadow_bouncegrid_dynamic_directionalshading.integer != 0) && r_shadow_bouncegrid_state.allowdirectionalshading;
+       settings->dlightparticlemultiplier      = s ? 0 : r_shadow_bouncegrid_dynamic_dlightparticlemultiplier.value;
+       settings->hitmodels                     = s ? false : r_shadow_bouncegrid_dynamic_hitmodels.integer != 0;
+       settings->includedirectlighting         = r_shadow_bouncegrid_includedirectlighting.integer != 0 || r_shadow_bouncegrid.integer == 2;
+       settings->lightradiusscale              = (s ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_dynamic_lightradiusscale.value);
+       settings->maxbounce                     = (s ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_dynamic_maxbounce.integer);
+       settings->particlebounceintensity       = r_shadow_bouncegrid_particlebounceintensity.value;
+       settings->particleintensity             = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings->directionalshading ? 4.0f : 1.0f) / (spacing * spacing);
+       settings->maxphotons                    = s ? r_shadow_bouncegrid_static_maxphotons.integer : r_shadow_bouncegrid_dynamic_maxphotons.integer;
+       settings->energyperphoton            = s ? r_shadow_bouncegrid_static_energyperphoton.integer : r_shadow_bouncegrid_dynamic_energyperphoton.integer;
+       settings->spacing[0]                    = spacing;
+       settings->spacing[1]                    = spacing;
+       settings->spacing[2]                    = spacing;
+       settings->stablerandom                  = s ? 1 : r_shadow_bouncegrid_dynamic_stablerandom.integer;
 
        // bound the values for sanity
-       settings.photons = bound(1, settings.photons, 1048576);
-       settings.lightradiusscale = bound(0.0001f, settings.lightradiusscale, 1024.0f);
-       settings.maxbounce = bound(0, settings.maxbounce, 16);
-       settings.spacing[0] = bound(1, settings.spacing[0], 512);
-       settings.spacing[1] = bound(1, settings.spacing[1], 512);
-       settings.spacing[2] = bound(1, settings.spacing[2], 512);
+       settings->maxphotons = bound(1, settings->maxphotons, 25000000);
+       settings->lightradiusscale = bound(0.0001f, settings->lightradiusscale, 1024.0f);
+       settings->maxbounce = bound(0, settings->maxbounce, 16);
+       settings->spacing[0] = bound(1, settings->spacing[0], 512);
+       settings->spacing[1] = bound(1, settings->spacing[1], 512);
+       settings->spacing[2] = bound(1, settings->spacing[2], 512);
+}
+
+static void R_Shadow_BounceGrid_UpdateSpacing(void)
+{
+       float m[16];
+       int c[4];
+       int resolution[3];
+       int numpixels;
+       vec3_t ispacing;
+       vec3_t maxs;
+       vec3_t mins;
+       vec3_t size;
+       vec3_t spacing;
+       r_shadow_bouncegrid_settings_t *settings = &r_shadow_bouncegrid_state.settings;
 
        // get the spacing values
-       spacing[0] = settings.spacing[0];
-       spacing[1] = settings.spacing[1];
-       spacing[2] = settings.spacing[2];
+       spacing[0] = settings->spacing[0];
+       spacing[1] = settings->spacing[1];
+       spacing[2] = settings->spacing[2];
        ispacing[0] = 1.0f / spacing[0];
        ispacing[1] = 1.0f / spacing[1];
        ispacing[2] = 1.0f / spacing[2];
 
        // calculate texture size enclosing entire world bounds at the spacing
-       VectorMA(r_refdef.scene.worldmodel->normalmins, -2.0f, spacing, mins);
-       VectorMA(r_refdef.scene.worldmodel->normalmaxs, 2.0f, spacing, maxs);
+       if (r_refdef.scene.worldmodel)
+       {
+               VectorMA(r_refdef.scene.worldmodel->normalmins, -2.0f, spacing, mins);
+               VectorMA(r_refdef.scene.worldmodel->normalmaxs, 2.0f, spacing, maxs);
+       }
+       else
+       {
+               VectorSet(mins, -1048576.0f, -1048576.0f, -1048576.0f);
+               VectorSet(maxs,  1048576.0f,  1048576.0f,  1048576.0f);
+       }
        VectorSubtract(maxs, mins, size);
        // now we can calculate the resolution we want
        c[0] = (int)floor(size[0] / spacing[0] + 0.5f);
@@ -2518,12 +2567,12 @@ void R_Shadow_UpdateBounceGridTexture(void)
 
        // if dynamic we may or may not want to use the world bounds
        // if the dynamic size is smaller than the world bounds, use it instead
-       if (!settings.staticmode && (r_shadow_bouncegrid_x.integer * r_shadow_bouncegrid_y.integer * r_shadow_bouncegrid_z.integer < resolution[0] * resolution[1] * resolution[2]))
+       if (!settings->staticmode && (r_shadow_bouncegrid_dynamic_x.integer * r_shadow_bouncegrid_dynamic_y.integer * r_shadow_bouncegrid_dynamic_z.integer < resolution[0] * resolution[1] * resolution[2]))
        {
                // we know the resolution we want
-               c[0] = r_shadow_bouncegrid_x.integer;
-               c[1] = r_shadow_bouncegrid_y.integer;
-               c[2] = r_shadow_bouncegrid_z.integer;
+               c[0] = r_shadow_bouncegrid_dynamic_x.integer;
+               c[1] = r_shadow_bouncegrid_dynamic_y.integer;
+               c[2] = r_shadow_bouncegrid_dynamic_z.integer;
                // now we can calculate the texture size (power of 2 if required)
                c[0] = bound(4, c[0], (int)vid.maxtexturesize_3d);
                c[1] = bound(4, c[1], (int)vid.maxtexturesize_3d);
@@ -2552,63 +2601,64 @@ void R_Shadow_UpdateBounceGridTexture(void)
        // recalculate the maxs in case the resolution was not satisfactory
        VectorAdd(mins, size, maxs);
 
-       // if all the settings seem identical to the previous update, return
-       if (r_shadow_bouncegridtexture && (settings.staticmode || realtime < r_shadow_bouncegridtime + r_shadow_bouncegrid_updateinterval.value) && !memcmp(&r_shadow_bouncegridsettings, &settings, sizeof(settings)))
-               return;
-
-       // store the new settings
-       r_shadow_bouncegridsettings = settings;
-
-       pixelbands = settings.directionalshading ? 8 : 1;
-       pixelsperband = resolution[0]*resolution[1]*resolution[2];
-       numpixels = pixelsperband*pixelbands;
+       // check if this changed the texture size
+       r_shadow_bouncegrid_state.createtexture = !(r_shadow_bouncegrid_state.texture && r_shadow_bouncegrid_state.resolution[0] == resolution[0] && r_shadow_bouncegrid_state.resolution[1] == resolution[1] && r_shadow_bouncegrid_state.resolution[2] == resolution[2] && r_shadow_bouncegrid_state.directional == r_shadow_bouncegrid_state.settings.directionalshading);
+       r_shadow_bouncegrid_state.directional = r_shadow_bouncegrid_state.settings.directionalshading;
+       VectorCopy(mins, r_shadow_bouncegrid_state.mins);
+       VectorCopy(maxs, r_shadow_bouncegrid_state.maxs);
+       VectorCopy(size, r_shadow_bouncegrid_state.size);
+       VectorCopy(spacing, r_shadow_bouncegrid_state.spacing);
+       VectorCopy(ispacing, r_shadow_bouncegrid_state.ispacing);
+       VectorCopy(resolution, r_shadow_bouncegrid_state.resolution);
 
-       // we're going to update the bouncegrid, update the matrix...
-       memset(m, 0, sizeof(m));
-       m[0] = 1.0f / size[0];
-       m[3] = -mins[0] * m[0];
-       m[5] = 1.0f / size[1];
-       m[7] = -mins[1] * m[5];
-       m[10] = 1.0f / size[2];
-       m[11] = -mins[2] * m[10];
-       m[15] = 1.0f;
-       Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegridmatrix, m);
        // reallocate pixels for this update if needed...
-       if (r_shadow_bouncegridnumpixels != numpixels || !r_shadow_bouncegridpixels || !r_shadow_bouncegridhighpixels)
+       r_shadow_bouncegrid_state.pixelbands = settings->directionalshading ? 8 : 1;
+       r_shadow_bouncegrid_state.pixelsperband = resolution[0]*resolution[1]*resolution[2];
+       r_shadow_bouncegrid_state.bytesperband = r_shadow_bouncegrid_state.pixelsperband*4;
+       numpixels = r_shadow_bouncegrid_state.pixelsperband*r_shadow_bouncegrid_state.pixelbands;
+       if (r_shadow_bouncegrid_state.numpixels != numpixels)
        {
-               if (r_shadow_bouncegridtexture)
+               if (r_shadow_bouncegrid_state.texture)
                {
-                       R_FreeTexture(r_shadow_bouncegridtexture);
-                       r_shadow_bouncegridtexture = NULL;
+                       R_FreeTexture(r_shadow_bouncegrid_state.texture);
+                       r_shadow_bouncegrid_state.texture = NULL;
                }
-               r_shadow_bouncegridpixels = (unsigned char *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridpixels, numpixels * sizeof(unsigned char[4]));
-               r_shadow_bouncegridhighpixels = (float *)Mem_Realloc(r_main_mempool, r_shadow_bouncegridhighpixels, numpixels * sizeof(float[4]));
-       }
-       r_shadow_bouncegridnumpixels = numpixels;
-       pixels = r_shadow_bouncegridpixels;
-       highpixels = r_shadow_bouncegridhighpixels;
-       x = pixelsperband*4;
-       for (pixelband = 0;pixelband < pixelbands;pixelband++)
-       {
-               if (pixelband == 1)
-                       memset(pixels + pixelband * x, 128, x);
-               else
-                       memset(pixels + pixelband * x, 0, x);
+               r_shadow_bouncegrid_state.numpixels = numpixels;
        }
-       memset(highpixels, 0, numpixels * sizeof(float[4]));
-       // figure out what we want to interact with
-       if (settings.hitmodels)
-               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;// | SUPERCONTENTS_LIQUIDSMASK;
-       else
-               hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
-       maxbounce = settings.maxbounce;
-       // clear variables that produce warnings otherwise
-       memset(splatcolor, 0, sizeof(splatcolor));
-       // iterate world rtlights
-       range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
-       range1 = settings.staticmode ? 0 : r_refdef.scene.numlights;
-       range2 = range + range1;
-       photoncount = 0;
+
+       // update the bouncegrid matrix to put it in the world properly
+       memset(m, 0, sizeof(m));
+       m[0] = 1.0f / r_shadow_bouncegrid_state.size[0];
+       m[3] = -r_shadow_bouncegrid_state.mins[0] * m[0];
+       m[5] = 1.0f / r_shadow_bouncegrid_state.size[1];
+       m[7] = -r_shadow_bouncegrid_state.mins[1] * m[5];
+       m[10] = 1.0f / r_shadow_bouncegrid_state.size[2];
+       m[11] = -r_shadow_bouncegrid_state.mins[2] * m[10];
+       m[15] = 1.0f;
+       Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegrid_state.matrix, m);
+}
+
+#define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
+
+// enumerate world rtlights and sum the overall amount of light in the world,
+// from that we can calculate a scaling factor to fairly distribute photons
+// to all the lights
+//
+// this modifies rtlight->photoncolor and rtlight->photons
+static void R_Shadow_BounceGrid_AssignPhotons(r_shadow_bouncegrid_settings_t *settings, unsigned int range, unsigned int range1, unsigned int range2, int flag, float *photonscaling)
+{
+       float normalphotonscaling;
+       float maxphotonscaling;
+       float photoncount = 0.0f;
+       float lightintensity;
+       float radius;
+       float s;
+       float w;
+       vec3_t cullmins;
+       vec3_t cullmaxs;
+       unsigned int lightindex;
+       dlight_t *light;
+       rtlight_t *rtlight;
        for (lightindex = 0;lightindex < range2;lightindex++)
        {
                if (lightindex < range)
@@ -2621,7 +2671,7 @@ void R_Shadow_UpdateBounceGridTexture(void)
                        rtlight->photons = 0;
                        if (!(light->flags & flag))
                                continue;
-                       if (settings.staticmode)
+                       if (settings->staticmode)
                        {
                                // when static, we skip styled lights because they tend to change...
                                if (rtlight->style > 0 && r_shadow_bouncegrid.integer != 2)
@@ -2635,26 +2685,34 @@ void R_Shadow_UpdateBounceGridTexture(void)
                        rtlight->photons = 0;
                }
                // draw only visible lights (major speedup)
-               radius = rtlight->radius * settings.lightradiusscale;
+               radius = rtlight->radius * settings->lightradiusscale;
                cullmins[0] = rtlight->shadoworigin[0] - radius;
                cullmins[1] = rtlight->shadoworigin[1] - radius;
                cullmins[2] = rtlight->shadoworigin[2] - radius;
                cullmaxs[0] = rtlight->shadoworigin[0] + radius;
                cullmaxs[1] = rtlight->shadoworigin[1] + radius;
                cullmaxs[2] = rtlight->shadoworigin[2] + radius;
-               if (R_CullBox(cullmins, cullmaxs))
-                       continue;
-               if (r_refdef.scene.worldmodel
-                && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
-                && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs))
-                       continue;
                w = r_shadow_lightintensityscale.value * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
-               if (w * VectorLength2(rtlight->color) == 0.0f)
+               if (!settings->staticmode)
+               {
+                       if (R_CullBox(cullmins, cullmaxs))
+                               continue;
+                       if (r_refdef.scene.worldmodel
+                        && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
+                        && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs))
+                               continue;
+                       if (w * VectorLength2(rtlight->color) == 0.0f)
+                               continue;
+               }
+               // a light that does not emit any light before style is applied, can be
+               // skipped entirely (it may just be a corona)
+               if (rtlight->radius == 0.0f || VectorLength2(rtlight->color) == 0.0f)
                        continue;
-               w *= (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
+               w *= ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
                VectorScale(rtlight->color, w, rtlight->photoncolor);
-               //if (!VectorLength2(rtlight->photoncolor))
-               //      continue;
+               // skip lights that will emit no photons
+               if (!VectorLength2(rtlight->photoncolor))
+                       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
@@ -2663,12 +2721,443 @@ void R_Shadow_UpdateBounceGridTexture(void)
                s = rtlight->radius;
                lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
                if (lightindex >= range)
-                       lightintensity *= settings.dlightparticlemultiplier;
-               rtlight->photons = max(0.0f, lightintensity * s * s);
+                       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, 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);
+                               }
+                       }
+               }
        }
-       photonscaling = (float)settings.photons / max(1, photoncount);
-       photonresidual = 0.0f;
+}
+
+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;
+                               }
+                       }
+               }
+       }
+
+       // 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:
+               pixelsbgra8 = R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(unsigned char[4]));
+               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);
+               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:
+               pixelsrgba16f = R_FrameData_Alloc(r_shadow_bouncegrid_state.numpixels * sizeof(unsigned short[4]));
+               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);
+               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);
+               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 = realtime;
+}
+
+static void R_Shadow_BounceGrid_TracePhotons(r_shadow_bouncegrid_settings_t settings, unsigned int range, unsigned int range1, unsigned int range2, float photonscaling, int flag)
+{
+       dlight_t *light;
+       int bouncecount;
+       int hitsupercontentsmask;
+       int maxbounce;
+       int shootparticles;
+       int shotparticles;
+       trace_t cliptrace;
+       //trace_t cliptrace2;
+       //trace_t cliptrace3;
+       unsigned int lightindex;
+       unsigned int seed = (unsigned int)(realtime * 1000.0f);
+       vec3_t shotcolor;
+       vec3_t baseshotcolor;
+       vec3_t surfcolor;
+       vec3_t clipend;
+       vec3_t clipstart;
+       vec3_t clipdiff;
+       vec_t radius;
+       vec_t s;
+       rtlight_t *rtlight;
+
+       // we'll need somewhere to store these
+       r_shadow_bouncegrid_state.numsplatpaths = 0;
+       r_shadow_bouncegrid_state.splatpaths = (r_shadow_bouncegrid_splatpath_t *)R_FrameData_Alloc(sizeof(r_shadow_bouncegrid_splatpath_t) * r_shadow_bouncegrid_state.maxsplatpaths);
+
+       // figure out what we want to interact with
+       if (settings.hitmodels)
+               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;// | SUPERCONTENTS_LIQUIDSMASK;
+       else
+               hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
+       maxbounce = settings.maxbounce;
+
        for (lightindex = 0;lightindex < range2;lightindex++)
        {
                if (lightindex < range)
@@ -2680,22 +3169,18 @@ void R_Shadow_UpdateBounceGridTexture(void)
                }
                else
                        rtlight = r_refdef.scene.lights[lightindex - range];
-               // skip a light with no photons
-               if (rtlight->photons == 0.0f)
-                       continue;
-               // skip a light with no photon color)
-               if (VectorLength2(rtlight->photoncolor) == 0.0f)
-                       continue;
-               photonresidual += rtlight->photons * photonscaling;
-               shootparticles = (int)bound(0, photonresidual, MAXBOUNCEGRIDPARTICLESPERLIGHT);
+               // note that this code used to keep track of residual photons and
+               // distribute them evenly to achieve exactly a desired photon count,
+               // but that caused unwanted flickering in dynamic mode
+               shootparticles = (int)floor(rtlight->photons * photonscaling);
+               // skip if we won't be shooting any photons
                if (!shootparticles)
                        continue;
-               photonresidual -= shootparticles;
                radius = rtlight->radius * settings.lightradiusscale;
                s = settings.particleintensity / shootparticles;
                VectorScale(rtlight->photoncolor, s, baseshotcolor);
-               r_refdef.stats.bouncegrid_lights++;
-               r_refdef.stats.bouncegrid_particles += shootparticles;
+               r_refdef.stats[r_stat_bouncegrid_lights]++;
+               r_refdef.stats[r_stat_bouncegrid_particles] += shootparticles;
                for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
                {
                        if (settings.stablerandom > 0)
@@ -2709,13 +3194,13 @@ void R_Shadow_UpdateBounceGridTexture(void)
                        VectorMA(clipstart, radius, clipend, clipend);
                        for (bouncecount = 0;;bouncecount++)
                        {
-                               r_refdef.stats.bouncegrid_traces++;
+                               r_refdef.stats[r_stat_bouncegrid_traces]++;
                                //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 (settings.staticmode)
                                {
                                        // static mode fires a LOT of rays but none of them are identical, so they are not cached
-                                       cliptrace = CL_TraceLine(clipstart, clipend, settings.staticmode ? MOVE_WORLDONLY : (settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), NULL, hitsupercontentsmask, true, false, NULL, true, true);
+                                       cliptrace = CL_TraceLine(clipstart, clipend, settings.staticmode ? MOVE_WORLDONLY : (settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), NULL, hitsupercontentsmask, collision_extendmovelength.value, true, false, NULL, true, true);
                                }
                                else
                                {
@@ -2724,112 +3209,13 @@ void R_Shadow_UpdateBounceGridTexture(void)
                                }
                                if (bouncecount > 0 || settings.includedirectlighting)
                                {
-                                       // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ)
-                                       // accumulate average shotcolor
-                                       w = VectorLength(shotcolor);
-                                       splatcolor[ 0] = shotcolor[0];
-                                       splatcolor[ 1] = shotcolor[1];
-                                       splatcolor[ 2] = shotcolor[2];
-                                       splatcolor[ 3] = 0.0f;
-                                       if (pixelbands > 1)
-                                       {
-                                               VectorSubtract(clipstart, cliptrace.endpos, clipdiff);
-                                               VectorNormalize(clipdiff);
-                                               // store bentnormal in case the shader has a use for it
-                                               splatcolor[ 4] = clipdiff[0] * w;
-                                               splatcolor[ 5] = clipdiff[1] * w;
-                                               splatcolor[ 6] = clipdiff[2] * w;
-                                               splatcolor[ 7] = w;
-                                               // accumulate directional contributions (+X, +Y, +Z, -X, -Y, -Z)
-                                               splatcolor[ 8] = shotcolor[0] * max(0.0f, clipdiff[0]);
-                                               splatcolor[ 9] = shotcolor[0] * max(0.0f, clipdiff[1]);
-                                               splatcolor[10] = shotcolor[0] * max(0.0f, clipdiff[2]);
-                                               splatcolor[11] = 0.0f;
-                                               splatcolor[12] = shotcolor[1] * max(0.0f, clipdiff[0]);
-                                               splatcolor[13] = shotcolor[1] * max(0.0f, clipdiff[1]);
-                                               splatcolor[14] = shotcolor[1] * max(0.0f, clipdiff[2]);
-                                               splatcolor[15] = 0.0f;
-                                               splatcolor[16] = shotcolor[2] * max(0.0f, clipdiff[0]);
-                                               splatcolor[17] = shotcolor[2] * max(0.0f, clipdiff[1]);
-                                               splatcolor[18] = shotcolor[2] * max(0.0f, clipdiff[2]);
-                                               splatcolor[19] = 0.0f;
-                                               splatcolor[20] = shotcolor[0] * max(0.0f, -clipdiff[0]);
-                                               splatcolor[21] = shotcolor[0] * max(0.0f, -clipdiff[1]);
-                                               splatcolor[22] = shotcolor[0] * max(0.0f, -clipdiff[2]);
-                                               splatcolor[23] = 0.0f;
-                                               splatcolor[24] = shotcolor[1] * max(0.0f, -clipdiff[0]);
-                                               splatcolor[25] = shotcolor[1] * max(0.0f, -clipdiff[1]);
-                                               splatcolor[26] = shotcolor[1] * max(0.0f, -clipdiff[2]);
-                                               splatcolor[27] = 0.0f;
-                                               splatcolor[28] = shotcolor[2] * max(0.0f, -clipdiff[0]);
-                                               splatcolor[29] = shotcolor[2] * max(0.0f, -clipdiff[1]);
-                                               splatcolor[30] = shotcolor[2] * max(0.0f, -clipdiff[2]);
-                                               splatcolor[31] = 0.0f;
-                                       }
-                                       // calculate the number of steps we need to traverse this distance
-                                       VectorSubtract(cliptrace.endpos, clipstart, stepdelta);
-                                       numsteps = (int)(VectorLength(stepdelta) * ispacing[0]);
-                                       numsteps = bound(1, numsteps, 1024);
-                                       w = 1.0f / numsteps;
-                                       VectorScale(stepdelta, w, stepdelta);
-                                       VectorMA(clipstart, 0.5f, stepdelta, steppos);
-                                       for (step = 0;step < numsteps;step++)
-                                       {
-                                               r_refdef.stats.bouncegrid_splats++;
-                                               // figure out which texture pixel this is in
-                                               texlerp[1][0] = ((steppos[0] - mins[0]) * ispacing[0]) - 0.5f;
-                                               texlerp[1][1] = ((steppos[1] - mins[1]) * ispacing[1]) - 0.5f;
-                                               texlerp[1][2] = ((steppos[2] - mins[2]) * ispacing[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];
-                                                                       pixel = pixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4;
-                                                                       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);
-                                                                       // flag the low precision pixel as needing to be updated
-                                                                       pixel[3] = 255;
-                                                                       // advance to next band of coefficients
-                                                                       //pixel += pixelsperband*4;
-                                                                       //highpixel += pixelsperband*4;
-                                                               }
-                                                       }
-                                               }
-                                               VectorAdd(steppos, stepdelta, steppos);
-                                       }
+                                       vec3_t hitpos;
+                                       VectorCopy(cliptrace.endpos, hitpos);
+                                       R_Shadow_BounceGrid_AddSplatPath(clipstart, hitpos, shotcolor);
                                }
                                if (cliptrace.fraction >= 1.0f)
                                        break;
-                               r_refdef.stats.bouncegrid_hits++;
+                               r_refdef.stats[r_stat_bouncegrid_hits]++;
                                if (bouncecount >= maxbounce)
                                        break;
                                // scale down shot color by bounce intensity and texture color (or 50% if no texture reported)
@@ -2845,7 +3231,7 @@ void R_Shadow_UpdateBounceGridTexture(void)
                                VectorMultiply(shotcolor, surfcolor, shotcolor);
                                if (VectorLength2(baseshotcolor) == 0.0f)
                                        break;
-                               r_refdef.stats.bouncegrid_bounces++;
+                               r_refdef.stats[r_stat_bouncegrid_bounces]++;
                                if (settings.bounceanglediffuse)
                                {
                                        // random direction, primarily along plane normal
@@ -2870,56 +3256,78 @@ void R_Shadow_UpdateBounceGridTexture(void)
                        }
                }
        }
-       // generate pixels array from highpixels array
-       // skip first and last columns, rows, and layers as these are blank
-       // the pixel[3] value was written above, so we can use it to detect only pixels that need to be calculated
-       for (pixelband = 0;pixelband < pixelbands;pixelband++)
+}
+
+void R_Shadow_UpdateBounceGridTexture(void)
+{
+       int flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
+       r_shadow_bouncegrid_settings_t settings;
+       qboolean enable = false;
+       qboolean settingschanged;
+       unsigned int range; // number of world lights
+       unsigned int range1; // number of dynamic lights (or zero if disabled)
+       unsigned int range2; // range+range1
+       float photonscaling;
+
+       enable = R_Shadow_BounceGrid_CheckEnable(flag);
+       
+       R_Shadow_BounceGrid_GenerateSettings(&settings);
+       
+       // changing intensity does not require an update
+       r_shadow_bouncegrid_state.intensity = r_shadow_bouncegrid_intensity.value;
+
+       settingschanged = memcmp(&r_shadow_bouncegrid_state.settings, &settings, sizeof(settings)) != 0;
+
+       // when settings change, we free everything as it is just simpler that way.
+       if (settingschanged || !enable)
        {
-               for (z = 1;z < resolution[2]-1;z++)
+               // not enabled, make sure we free anything we don't need anymore.
+               if (r_shadow_bouncegrid_state.texture)
                {
-                       for (y = 1;y < resolution[1]-1;y++)
-                       {
-                               for (x = 1, pixelindex[0] = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x, pixel = pixels + 4*pixelindex[0], highpixel = highpixels + 4*pixelindex[0];x < resolution[0]-1;x++, pixel += 4, highpixel += 4)
-                               {
-                                       // only convert pixels that were hit by photons
-                                       if (pixel[3] == 255)
-                                       {
-                                               // normalize the bentnormal...
-                                               if (pixelband == 1)
-                                               {
-                                                       VectorNormalize(highpixel);
-                                                       c[0] = (int)(highpixel[0]*128.0f+128.0f);
-                                                       c[1] = (int)(highpixel[1]*128.0f+128.0f);
-                                                       c[2] = (int)(highpixel[2]*128.0f+128.0f);
-                                                       c[3] = (int)(highpixel[3]*128.0f+128.0f);
-                                               }
-                                               else
-                                               {
-                                                       c[0] = (int)(highpixel[0]*256.0f);
-                                                       c[1] = (int)(highpixel[1]*256.0f);
-                                                       c[2] = (int)(highpixel[2]*256.0f);
-                                                       c[3] = (int)(highpixel[3]*256.0f);
-                                               }
-                                               pixel[2] = (unsigned char)bound(0, c[0], 255);
-                                               pixel[1] = (unsigned char)bound(0, c[1], 255);
-                                               pixel[0] = (unsigned char)bound(0, c[2], 255);
-                                               pixel[3] = (unsigned char)bound(0, c[3], 255);
-                                       }
-                               }
-                       }
+                       R_FreeTexture(r_shadow_bouncegrid_state.texture);
+                       r_shadow_bouncegrid_state.texture = NULL;
                }
+               r_shadow_bouncegrid_state.numpixels = 0;
+               r_shadow_bouncegrid_state.directional = false;
+
+               if (!enable)
+                       return;
        }
-       if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2] && r_shadow_bouncegriddirectional == settings.directionalshading)
-               R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands);
-       else
-       {
-               VectorCopy(resolution, r_shadow_bouncegridresolution);
-               r_shadow_bouncegriddirectional = settings.directionalshading;
-               if (r_shadow_bouncegridtexture)
-                       R_FreeTexture(r_shadow_bouncegridtexture);
-               r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
-       }
-       r_shadow_bouncegridtime = realtime;
+
+       // if all the settings seem identical to the previous update, return
+       if (r_shadow_bouncegrid_state.texture && (settings.staticmode || realtime < r_shadow_bouncegrid_state.lastupdatetime + r_shadow_bouncegrid_dynamic_updateinterval.value) && !settingschanged)
+               return;
+
+       // store the new settings
+       r_shadow_bouncegrid_state.settings = settings;
+
+       R_Shadow_BounceGrid_UpdateSpacing();
+
+       // get the range of light numbers we'll be looping over:
+       // range = static lights
+       // range1 = dynamic lights (optional)
+       // range2 = range + range1
+       range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+       range1 = settings.staticmode ? 0 : r_refdef.scene.numlights;
+       range2 = range + range1;
+
+       // calculate weighting factors for distributing photons among the lights
+       R_Shadow_BounceGrid_AssignPhotons(&settings, range, range1, range2, flag, &photonscaling);
+
+       // trace the photons from lights and accumulate illumination
+       R_Shadow_BounceGrid_TracePhotons(settings, range, range1, range2, photonscaling, flag);
+
+       // clear the texture
+       R_Shadow_BounceGrid_ClearPixels();
+       
+       // accumulate the light splatting into texture
+       R_Shadow_BounceGrid_PerformSplats();
+
+       // apply a mild blur filter to the texture
+       R_Shadow_BounceGrid_BlurPixels();
+
+       // convert the pixels to lower precision and upload the texture
+       R_Shadow_BounceGrid_ConvertPixelsAndUpload();
 }
 
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
@@ -2991,7 +3399,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        || r_shadow_lightscissor[1] != r_refdef.view.viewport.y
        || r_shadow_lightscissor[2] != r_refdef.view.viewport.width
        || r_shadow_lightscissor[3] != r_refdef.view.viewport.height)
-               r_refdef.stats.lights_scissored++;
+               r_refdef.stats[r_stat_lights_scissored]++;
        return false;
 }
 
@@ -3527,7 +3935,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                                lighttris++;
 
        shadowtris = 0;
-       if (rtlight->static_numlighttrispvsbytes)
+       if (rtlight->static_numshadowtrispvsbytes)
                for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
                        if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
                                shadowtris++;
@@ -3764,11 +4172,8 @@ static void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist
                {
                        if (!mesh->sidetotals[r_shadow_shadowmapside])
                                continue;
-                       r_refdef.stats.lights_shadowtriangles += mesh->sidetotals[r_shadow_shadowmapside];
-                       if (mesh->vertex3fbuffer)
-                               R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vertex3fbuffer);
-                       else
-                               R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer);
+                       r_refdef.stats[r_stat_lights_shadowtriangles] += mesh->sidetotals[r_shadow_shadowmapside];
+                       R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer, mesh->vbooffset_vertex3f);
                        R_Mesh_Draw(0, mesh->numverts, mesh->sideoffsets[r_shadow_shadowmapside], mesh->sidetotals[r_shadow_shadowmapside], mesh->element3i, mesh->element3i_indexbuffer, mesh->element3i_bufferoffset, mesh->element3s, mesh->element3s_indexbuffer, mesh->element3s_bufferoffset);
                }
                CHECKGLERROR
@@ -3804,11 +4209,8 @@ static void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacel
                mesh = zpass ? rsurface.rtlight->static_meshchain_shadow_zpass : rsurface.rtlight->static_meshchain_shadow_zfail;
                for (;mesh;mesh = mesh->next)
                {
-                       r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
-                       if (mesh->vertex3fbuffer)
-                               R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vertex3fbuffer);
-                       else
-                               R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer);
+                       r_refdef.stats[r_stat_lights_shadowtriangles] += mesh->numtriangles;
+                       R_Mesh_PrepareVertices_Vertex3f(mesh->numverts, mesh->vertex3f, mesh->vbo_vertexbuffer, mesh->vbooffset_vertex3f);
                        if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL)
                        {
                                // increment stencil if frontface is infront of depthbuffer
@@ -3939,6 +4341,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        static entity_render_t *shadowentities[MAX_EDICTS];
        static entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
        qboolean nolight;
+       qboolean castshadows;
 
        rtlight->draw = false;
        rtlight->cached_numlightentities               = 0;
@@ -3973,7 +4376,7 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
        rtlight->currentcubemap = rtlight->cubemapname[0] ? R_GetCubemap(rtlight->cubemapname) : r_texture_whitecube;
 
        // look up the light style value at this time
-       f = (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
+       f = ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
        VectorScale(rtlight->color, f, rtlight->currentcolor);
        /*
        if (rtlight->selected)
@@ -4129,18 +4532,21 @@ static void R_Shadow_PrepareLight(rtlight_t *rtlight)
                return;
 
        // count this light in the r_speeds
-       r_refdef.stats.lights++;
+       r_refdef.stats[r_stat_lights]++;
 
        // flag it as worth drawing later
        rtlight->draw = true;
 
+       // if we have shadows disabled, don't count the shadow entities, this way we don't do the R_AnimCache_GetEntity on each one
+       castshadows = numsurfaces + numshadowentities + numshadowentities_noselfshadow > 0 && rtlight->shadow && (rtlight->isstatic ? r_refdef.scene.rtworldshadows : r_refdef.scene.rtdlightshadows);
+       if (!castshadows)
+               numshadowentities = numshadowentities_noselfshadow = 0;
+
        // cache all the animated entities that cast a shadow but are not visible
        for (i = 0;i < numshadowentities;i++)
-               if (!shadowentities[i]->animcache_vertex3f)
-                       R_AnimCache_GetEntity(shadowentities[i], false, false);
+               R_AnimCache_GetEntity(shadowentities[i], false, false);
        for (i = 0;i < numshadowentities_noselfshadow;i++)
-               if (!shadowentities_noselfshadow[i]->animcache_vertex3f)
-                       R_AnimCache_GetEntity(shadowentities_noselfshadow[i], false, false);
+               R_AnimCache_GetEntity(shadowentities_noselfshadow[i], false, false);
 
        // allocate some temporary memory for rendering this light later in the frame
        // reusable buffers need to be copied, static data can be used as-is
@@ -4533,7 +4939,8 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color
                r_shadow_shadowmapfilterquality != r_shadow_shadowmapping_filterquality.integer || 
                r_shadow_shadowmapshadowsampler != (vid.support.arb_shadow && r_shadow_shadowmapping_useshadowsampler.integer) || 
                r_shadow_shadowmapdepthbits != r_shadow_shadowmapping_depthbits.integer || 
-               r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
+               r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16) ||
+               r_shadow_shadowmapdepthtexture != r_fb.usedepthtextures)
                R_Shadow_FreeShadowMaps();
 
        r_shadow_fb_fbo = fbo;
@@ -4630,7 +5037,7 @@ void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *color
                for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++)
                {
                        rtlight_t *rtlight = r_refdef.scene.lights[lnum];
-                       f = (rtlight->style >= 0 ? r_refdef.scene.lightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
+                       f = ((rtlight->style >= 0 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.lightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
                        VectorScale(rtlight->color, f, rtlight->currentcolor);
                }
        }
@@ -4674,6 +5081,10 @@ void R_Shadow_DrawLights(void)
        R_Shadow_RenderMode_End();
 }
 
+#define MAX_MODELSHADOWS 1024
+static int r_shadow_nummodelshadows;
+static entity_render_t *r_shadow_modelshadows[MAX_MODELSHADOWS];
+
 void R_Shadow_PrepareModelShadows(void)
 {
        int i;
@@ -4682,6 +5093,7 @@ void R_Shadow_PrepareModelShadows(void)
        vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus, shadowmins, shadowmaxs;
        entity_render_t *ent;
 
+       r_shadow_nummodelshadows = 0;
        if (!r_refdef.scene.numentities)
                return;
 
@@ -4692,11 +5104,18 @@ void R_Shadow_PrepareModelShadows(void)
                        break;
                // fall through
        case R_SHADOW_SHADOWMODE_STENCIL:
+               if (!vid.stencil)
+                       return;
                for (i = 0;i < r_refdef.scene.numentities;i++)
                {
                        ent = r_refdef.scene.entities[i];
-                       if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+                       if (ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+                       {
+                               if (r_shadow_nummodelshadows >= MAX_MODELSHADOWS)
+                                       break;
+                               r_shadow_modelshadows[r_shadow_nummodelshadows++] = ent;
                                R_AnimCache_GetEntity(ent, false, false);
+                       }
                }
                return;
        default:
@@ -4741,8 +5160,13 @@ void R_Shadow_PrepareModelShadows(void)
                if (!BoxesOverlap(ent->mins, ent->maxs, shadowmins, shadowmaxs))
                        continue;
                // cast shadows from anything of the map (submodels are optional)
-               if (!ent->animcache_vertex3f && ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+               if (ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+               {
+                       if (r_shadow_nummodelshadows >= MAX_MODELSHADOWS)
+                               break;
+                       r_shadow_modelshadows[r_shadow_nummodelshadows++] = ent;
                        R_AnimCache_GetEntity(ent, false, false);
+               }
        }
 }
 
@@ -4762,7 +5186,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort
        GLuint shadowfbo = 0;
        float clearcolor[4];
 
-       if (!r_refdef.scene.numentities)
+       if (!r_shadow_nummodelshadows)
                return;
 
        switch (r_shadow_shadowmode)
@@ -4800,7 +5224,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort
        radius = 0.5f / scale;
        nearclip = -r_shadows_throwdistance.value;
        farclip = r_shadows_throwdistance.value;
-       bias = r_shadow_shadowmapping_bias.value * r_shadow_shadowmapping_nearclip.value / (2 * r_shadows_throwdistance.value) * (1024.0f / size);
+       bias = (r_shadows_shadowmapbias.value < 0) ? r_shadow_shadowmapping_bias.value : r_shadows_shadowmapbias.value * r_shadow_shadowmapping_nearclip.value / (2 * r_shadows_throwdistance.value) * (1024.0f / size);
 
        r_shadow_shadowmap_parameters[0] = size;
        r_shadow_shadowmap_parameters[1] = size;
@@ -4842,7 +5266,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort
                R_Mesh_SetRenderTargets(shadowfbo, r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL);
        else
                R_Mesh_SetRenderTargets(shadowfbo, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL);
-       R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL);
+       R_SetupShader_DepthOrShadow(true, r_shadow_shadowmap2ddepthbuffer != NULL, false); // FIXME test if we have a skeletal model?
        GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value);
        GL_DepthMask(true);
        GL_DepthTest(true);
@@ -4860,36 +5284,23 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort
        // outside the usable area
        GL_Scissor(viewport.x + r_shadow_shadowmapborder, viewport.y + r_shadow_shadowmapborder, viewport.width - 2*r_shadow_shadowmapborder, viewport.height - 2*r_shadow_shadowmapborder);
 
-#if 0
-       // debugging
-       R_Mesh_SetRenderTargets(r_shadow_fb_fbo, r_shadow_fb_depthtexture, r_shadow_fb_colortexture, NULL, NULL, NULL);
-       R_SetupShader_ShowDepth(true);
-       GL_ColorMask(1,1,1,1);
-       GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
-#endif
-
-       for (i = 0;i < r_refdef.scene.numentities;i++)
-       {
-               ent = r_refdef.scene.entities[i];
-
-               // cast shadows from anything of the map (submodels are optional)
-               if (ent->model && ent->model->DrawShadowMap != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
-               {
-                       relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
-                       Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativelightorigin);
-                       Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
-                       Matrix4x4_Transform3x3(&ent->inversematrix, shadowforward, relativeforward);
-                       Matrix4x4_Transform3x3(&ent->inversematrix, shadowright, relativeright);
-                       relativeshadowmins[0] = relativelightorigin[0] - r_shadows_throwdistance.value * fabs(relativelightdirection[0]) - radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
-                       relativeshadowmins[1] = relativelightorigin[1] - r_shadows_throwdistance.value * fabs(relativelightdirection[1]) - radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
-                       relativeshadowmins[2] = relativelightorigin[2] - r_shadows_throwdistance.value * fabs(relativelightdirection[2]) - radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
-                       relativeshadowmaxs[0] = relativelightorigin[0] + r_shadows_throwdistance.value * fabs(relativelightdirection[0]) + radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
-                       relativeshadowmaxs[1] = relativelightorigin[1] + r_shadows_throwdistance.value * fabs(relativelightdirection[1]) + radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
-                       relativeshadowmaxs[2] = relativelightorigin[2] + r_shadows_throwdistance.value * fabs(relativelightdirection[2]) + radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
-                       RSurf_ActiveModelEntity(ent, false, false, false);
-                       ent->model->DrawShadowMap(0, ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, NULL, relativeshadowmins, relativeshadowmaxs);
-                       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
-               }
+       for (i = 0;i < r_shadow_nummodelshadows;i++)
+       {
+               ent = r_shadow_modelshadows[i];
+               relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
+               Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativelightorigin);
+               Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
+               Matrix4x4_Transform3x3(&ent->inversematrix, shadowforward, relativeforward);
+               Matrix4x4_Transform3x3(&ent->inversematrix, shadowright, relativeright);
+               relativeshadowmins[0] = relativelightorigin[0] - r_shadows_throwdistance.value * fabs(relativelightdirection[0]) - radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
+               relativeshadowmins[1] = relativelightorigin[1] - r_shadows_throwdistance.value * fabs(relativelightdirection[1]) - radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
+               relativeshadowmins[2] = relativelightorigin[2] - r_shadows_throwdistance.value * fabs(relativelightdirection[2]) - radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
+               relativeshadowmaxs[0] = relativelightorigin[0] + r_shadows_throwdistance.value * fabs(relativelightdirection[0]) + radius * (fabs(relativeforward[0]) + fabs(relativeright[0]));
+               relativeshadowmaxs[1] = relativelightorigin[1] + r_shadows_throwdistance.value * fabs(relativelightdirection[1]) + radius * (fabs(relativeforward[1]) + fabs(relativeright[1]));
+               relativeshadowmaxs[2] = relativelightorigin[2] + r_shadows_throwdistance.value * fabs(relativelightdirection[2]) + radius * (fabs(relativeforward[2]) + fabs(relativeright[2]));
+               RSurf_ActiveModelEntity(ent, false, false, false);
+               ent->model->DrawShadowMap(0, ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, NULL, relativeshadowmins, relativeshadowmaxs);
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
        }
 
 #if 0
@@ -4926,7 +5337,7 @@ void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colort
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
-#ifdef OPENGL_ORIENTATION
+#ifdef MATRIX4x4_OPENGLORIENTATION
                r_shadow_shadowmapmatrix.m[0][0]        *= -1.0f;
                r_shadow_shadowmapmatrix.m[0][1]        *= -1.0f;
                r_shadow_shadowmapmatrix.m[0][2]        *= -1.0f;
@@ -4962,7 +5373,7 @@ void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
        vec3_t tmp, shadowdir;
        prvm_vec3_t prvmshadowdir;
 
-       if (!r_refdef.scene.numentities || !vid.stencil || (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL && r_shadows.integer != 1))
+       if (!r_shadow_nummodelshadows || (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL && r_shadows.integer != 1))
                return;
 
        r_shadow_fb_fbo = fbo;
@@ -4990,60 +5401,57 @@ void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortext
 
        R_Shadow_ClearStencil();
 
-       for (i = 0;i < r_refdef.scene.numentities;i++)
+       for (i = 0;i < r_shadow_nummodelshadows;i++)
        {
-               ent = r_refdef.scene.entities[i];
+               ent = r_shadow_modelshadows[i];
 
                // cast shadows from anything of the map (submodels are optional)
-               if (ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
+               relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
+               VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
+               VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
+               if (r_shadows.integer == 2) // 2: simpler mode, throw shadows always in same direction
+                       Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
+               else
                {
-                       relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
-                       VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
-                       VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
-                       if (r_shadows.integer == 2) // 2: simpler mode, throw shadows always in same direction
-                               Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
-                       else
+                       if(ent->entitynumber != 0)
                        {
-                               if(ent->entitynumber != 0)
+                               if(ent->entitynumber >= MAX_EDICTS) // csqc entity
+                               {
+                                       // FIXME handle this
+                                       VectorNegate(ent->modellight_lightdir, relativelightdirection);
+                               }
+                               else
                                {
-                                       if(ent->entitynumber >= MAX_EDICTS) // csqc entity
+                                       // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities)
+                                       int entnum, entnum2, recursion;
+                                       entnum = entnum2 = ent->entitynumber;
+                                       for(recursion = 32; recursion > 0; --recursion)
                                        {
-                                               // FIXME handle this
-                                               VectorNegate(ent->modellight_lightdir, relativelightdirection);
+                                               entnum2 = cl.entities[entnum].state_current.tagentity;
+                                               if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2])
+                                                       entnum = entnum2;
+                                               else
+                                                       break;
                                        }
-                                       else
+                                       if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain
                                        {
-                                               // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities)
-                                               int entnum, entnum2, recursion;
-                                               entnum = entnum2 = ent->entitynumber;
-                                               for(recursion = 32; recursion > 0; --recursion)
-                                               {
-                                                       entnum2 = cl.entities[entnum].state_current.tagentity;
-                                                       if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2])
-                                                               entnum = entnum2;
-                                                       else
-                                                               break;
-                                               }
-                                               if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain
-                                               {
-                                                       VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection);
-                                                       // transform into modelspace of OUR entity
-                                                       Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp);
-                                                       Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection);
-                                               }
-                                               else
-                                                       VectorNegate(ent->modellight_lightdir, relativelightdirection);
+                                               VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection);
+                                               // transform into modelspace of OUR entity
+                                               Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp);
+                                               Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection);
                                        }
+                                       else
+                                               VectorNegate(ent->modellight_lightdir, relativelightdirection);
                                }
-                               else
-                                       VectorNegate(ent->modellight_lightdir, relativelightdirection);
                        }
-
-                       VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
-                       RSurf_ActiveModelEntity(ent, false, false, false);
-                       ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs);
-                       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+                       else
+                               VectorNegate(ent->modellight_lightdir, relativelightdirection);
                }
+
+               VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
+               RSurf_ActiveModelEntity(ent, false, false, false);
+               ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs);
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
        }
 
        // not really the right mode, but this will disable any silly stencil features
@@ -5084,7 +5492,9 @@ static void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequer
 {
        float zdist;
        vec3_t centerorigin;
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
        float vertex3f[12];
+#endif
        // if it's too close, skip it
        if (VectorLength(rtlight->currentcolor) < (1.0f / 256.0f))
                return;
@@ -5105,19 +5515,19 @@ static void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequer
                case RENDERPATH_GL20:
                case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
-#ifdef GL_SAMPLES_PASSED_ARB
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
                        CHECKGLERROR
                        // NOTE: GL_DEPTH_TEST must be enabled or ATI won't count samples, so use GL_DepthFunc instead
                        qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_allpixels);
                        GL_DepthFunc(GL_ALWAYS);
                        R_CalcSprite_Vertex3f(vertex3f, centerorigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale);
-                       R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL);
+                       R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL, 0);
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        qglEndQueryARB(GL_SAMPLES_PASSED_ARB);
                        GL_DepthFunc(GL_LEQUAL);
                        qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_visiblepixels);
                        R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale);
-                       R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL);
+                       R_Mesh_PrepareVertices_Vertex3f(4, vertex3f, NULL, 0);
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        qglEndQueryARB(GL_SAMPLES_PASSED_ARB);
                        CHECKGLERROR
@@ -5145,7 +5555,9 @@ static float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
 static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
 {
        vec3_t color;
+       unsigned int occlude = 0;
        GLint allpixels = 0, visiblepixels = 0;
+
        // now we have to check the query result
        if (rtlight->corona_queryindex_visiblepixels)
        {
@@ -5156,36 +5568,55 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                case RENDERPATH_GL20:
                case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
-#ifdef GL_SAMPLES_PASSED_ARB
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
                        CHECKGLERROR
-                       qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
-                       qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels);
+                       // See if we can use the GPU-side method to prevent implicit sync
+                       if (vid.support.arb_query_buffer_object) {
+#define BUFFER_OFFSET(i)    ((GLint *)((unsigned char*)NULL + (i)))
+                               if (!r_shadow_occlusion_buf) {
+                                       qglGenBuffersARB(1, &r_shadow_occlusion_buf);
+                                       qglBindBufferARB(GL_QUERY_BUFFER_ARB, r_shadow_occlusion_buf);
+                                       qglBufferDataARB(GL_QUERY_BUFFER_ARB, 8, NULL, GL_DYNAMIC_COPY);
+                               } else {
+                                       qglBindBufferARB(GL_QUERY_BUFFER_ARB, r_shadow_occlusion_buf);
+                               }
+                               qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, BUFFER_OFFSET(0));
+                               qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, BUFFER_OFFSET(4));
+                               qglBindBufferBase(GL_UNIFORM_BUFFER, 0, r_shadow_occlusion_buf);
+                               occlude = MATERIALFLAG_OCCLUDE;
+                       } else {
+                               qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
+                               qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels); 
+                               if (visiblepixels < 1 || allpixels < 1)
+                                       return;
+                               rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1);
+                       }
+                       cscale *= rtlight->corona_visibility;
                        CHECKGLERROR
-#endif
                        break;
+#else
+                       return;
+#endif
                case RENDERPATH_D3D9:
                        Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
+                       return;
                case RENDERPATH_D3D10:
                        Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
+                       return;
                case RENDERPATH_D3D11:
                        Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
+                       return;
                case RENDERPATH_SOFT:
                        //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
-               }
-               //Con_Printf("%i of %i pixels\n", (int)visiblepixels, (int)allpixels);
-               if (visiblepixels < 1 || allpixels < 1)
                        return;
-               rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1);
-               cscale *= rtlight->corona_visibility;
+               default:
+                       return;
+               }
        }
        else
        {
                // FIXME: these traces should scan all render entities instead of cl.world
-               if (CL_TraceLine(r_refdef.view.origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1)
+               if (CL_TraceLine(r_refdef.view.origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1)
                        return;
        }
        VectorScale(rtlight->currentcolor, cscale, color);
@@ -5200,7 +5631,7 @@ static void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                }
                R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale);
                RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, RENDER_NODEPTHTEST, 0, color[0], color[1], color[2], 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false);
-               R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
+               R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE | MATERIALFLAG_NODEPTHTEST | occlude, 0, 4, 0, 2, false, false);
                if(negated)
                        GL_BlendEquationSubtract(false);
        }
@@ -5235,15 +5666,15 @@ void R_Shadow_DrawCoronas(void)
        case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
                usequery = vid.support.arb_occlusion_query && r_coronas_occlusionquery.integer;
-#ifdef GL_SAMPLES_PASSED_ARB
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
                if (usequery)
                {
                        GL_ColorMask(0,0,0,0);
-                       if (r_maxqueries < (range + r_refdef.scene.numlights) * 2)
+                       if (r_maxqueries < ((unsigned int)range + r_refdef.scene.numlights) * 2)
                        if (r_maxqueries < MAX_OCCLUSION_QUERIES)
                        {
                                i = r_maxqueries;
-                               r_maxqueries = (range + r_refdef.scene.numlights) * 4;
+                               r_maxqueries = ((unsigned int)range + r_refdef.scene.numlights) * 4;
                                r_maxqueries = min(r_maxqueries, MAX_OCCLUSION_QUERIES);
                                CHECKGLERROR
                                qglGenQueriesARB(r_maxqueries - i, r_queries + i);
@@ -5341,12 +5772,10 @@ static dlight_t *R_Shadow_NewWorldLight(void)
 static void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
 {
        matrix4x4_t matrix;
+
+       // note that style is no longer validated here, -1 is used for unstyled lights and >= MAX_LIGHTSTYLES is accepted for sake of editing rtlights files that might be out of bounds but perfectly formatted
+
        // validate parameters
-       if (style < 0 || style >= MAX_LIGHTSTYLES)
-       {
-               Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
-               style = 0;
-       }
        if (!cubemapname)
                cubemapname = "";
 
@@ -5472,10 +5901,10 @@ void R_Shadow_DrawLightSprites(void)
        {
                light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
                if (light)
-                       R_MeshQueue_AddTransparent(MESHQUEUE_SORT_DISTANCE, light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
+                       R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
        }
        if (!r_editlights_lockcursor)
-               R_MeshQueue_AddTransparent(MESHQUEUE_SORT_DISTANCE, r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
+               R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
 }
 
 int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radius, float *color)
@@ -5483,7 +5912,7 @@ int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radiu
        unsigned int range;
        dlight_t *light;
        rtlight_t *rtlight;
-       range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
+       range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
        if (lightindex >= range)
                return -1;
        light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
@@ -5520,7 +5949,7 @@ static void R_Shadow_SelectLightInView(void)
                if (rating >= 0.95)
                {
                        rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
-                       if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1.0f)
+                       if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1.0f)
                        {
                                bestrating = rating;
                                best = light;
@@ -5970,7 +6399,7 @@ static void R_Shadow_SetCursorLocationForView(void)
        vec3_t dest, endpos;
        trace_t trace;
        VectorMA(r_refdef.view.origin, r_editlights_cursordistance.value, r_refdef.view.forward, dest);
-       trace = CL_TraceLine(r_refdef.view.origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true);
+       trace = CL_TraceLine(r_refdef.view.origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true);
        if (trace.fraction < 1)
        {
                dist = trace.fraction * r_editlights_cursordistance.value;
@@ -6012,10 +6441,14 @@ void R_Shadow_EditLights_Reload_f(void)
                return;
        strlcpy(r_shadow_mapname, cl.worldname, sizeof(r_shadow_mapname));
        R_Shadow_ClearWorldLights();
-       R_Shadow_LoadWorldLights();
-       if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
+       if (r_shadow_realtime_world_importlightentitiesfrommap.integer <= 1)
+       {
+               R_Shadow_LoadWorldLights();
+               if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
+                       R_Shadow_LoadLightsFile();
+       }
+       if (r_shadow_realtime_world_importlightentitiesfrommap.integer >= 1)
        {
-               R_Shadow_LoadLightsFile();
                if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
                        R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
        }
@@ -6458,7 +6891,7 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void)
                if (!light)
                        continue;
                if (light == r_shadow_selectedlight)
-                       lightnumber = lightindex;
+                       lightnumber = (int)lightindex;
                lightcount++;
        }
        dpsnprintf(temp, sizeof(temp), "Cursor origin: %.0f %.0f %.0f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false, FONT_DEFAULT);y += 8;
@@ -6734,7 +7167,7 @@ void R_LightPoint(float *color, const vec3_t p, const int flags)
        if (flags & LP_RTWORLD)
        {
                flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
-               numlights = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
+               numlights = (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
                for (i = 0; i < numlights; i++)
                {
                        dlight = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, i);
@@ -6754,7 +7187,7 @@ void R_LightPoint(float *color, const vec3_t p, const int flags)
                        if (f <= 0)
                                continue;
                        // todo: add to both ambient and diffuse
-                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1)
+                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1)
                                VectorMA(color, f, light->currentcolor, color);
                }
        }
@@ -6775,7 +7208,7 @@ void R_LightPoint(float *color, const vec3_t p, const int flags)
                        if (f <= 0)
                                continue;
                        // todo: add to both ambient and diffuse
-                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction == 1)
+                       if (!light->shadow || CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction == 1)
                                VectorMA(color, f, light->color, color);
                }
        }
@@ -6840,7 +7273,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const
        if (flags & LP_RTWORLD)
        {
                flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
-               numlights = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
+               numlights = (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);
                for (i = 0; i < numlights; i++)
                {
                        dlight = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, i);
@@ -6859,7 +7292,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const
                        intensity = min(1.0f, (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) * r_shadow_lightintensityscale.value;
                        if (intensity <= 0.0f)
                                continue;
-                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1)
+                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1)
                                continue;
                        // scale down intensity to add to both ambient and diffuse
                        //intensity *= 0.5f;
@@ -6892,7 +7325,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const
                        intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist) * r_shadow_lightintensityscale.value;
                        if (intensity <= 0.0f)
                                continue;
-                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, true).fraction < 1)
+                       if (light->shadow && CL_TraceLine(p, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, collision_extendmovelength.value, true, false, NULL, false, true).fraction < 1)
                                continue;
                        // scale down intensity to add to both ambient and diffuse
                        //intensity *= 0.5f;