]> 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 062892e411f031ce02083e96d00bc4df0c28d094..65c822a9ec5a6d084f55dafca2763becfa95110a 100644 (file)
@@ -146,7 +146,7 @@ demonstrated by the game Doom3.
 extern LPDIRECT3DDEVICE9 vid_d3d9dev;
 #endif
 
-extern void R_Shadow_EditLights_Init(void);
+static void R_Shadow_EditLights_Init(void);
 
 typedef enum r_shadow_rendermode_e
 {
@@ -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;
 
@@ -196,12 +196,13 @@ int r_shadow_shadowmapdepthbits;
 int r_shadow_shadowmapmaxsize;
 qboolean r_shadow_shadowmapvsdct;
 qboolean r_shadow_shadowmapsampler;
+qboolean r_shadow_shadowmapshadowsampler;
 int r_shadow_shadowmappcf;
 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;
 
@@ -244,8 +245,8 @@ rtexture_t *r_shadow_attenuationgradienttexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
 skinframe_t *r_shadow_lightcorona;
-rtexture_t *r_shadow_shadowmap2dtexture;
-rtexture_t *r_shadow_shadowmap2dcolortexture;
+rtexture_t *r_shadow_shadowmap2ddepthbuffer;
+rtexture_t *r_shadow_shadowmap2ddepthtexture;
 rtexture_t *r_shadow_shadowmapvsdcttexture;
 int r_shadow_shadowmapsize; // changes for each light based on distance
 int r_shadow_shadowmaplod; // changes for each light based on distance
@@ -255,26 +256,29 @@ GLuint r_shadow_prepasslightingdiffusespecularfbo;
 GLuint r_shadow_prepasslightingdiffusefbo;
 int r_shadow_prepass_width;
 int r_shadow_prepass_height;
-rtexture_t *r_shadow_prepassgeometrydepthtexture;
-rtexture_t *r_shadow_prepassgeometrydepthcolortexture;
+rtexture_t *r_shadow_prepassgeometrydepthbuffer;
 rtexture_t *r_shadow_prepassgeometrynormalmaptexture;
 rtexture_t *r_shadow_prepasslightingdiffusetexture;
 rtexture_t *r_shadow_prepasslightingspeculartexture;
 
+// keep track of the provided framebuffer info
+static int r_shadow_fb_fbo;
+static rtexture_t *r_shadow_fb_depthtexture;
+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;
 
-static const GLenum r_shadow_prepasslightingdrawbuffers[2] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
-
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
 cvar_t r_shadow_deferred = {CVAR_SAVE, "r_shadow_deferred", "0", "uses image-based lighting instead of geometry-based lighting, the method used renders a depth image and a normalmap image, renders lights into separate diffuse and specular images, and then combines this into the normal rendering, requires r_shadow_shadowmapping"};
-cvar_t r_shadow_deferred_8bitrange = {CVAR_SAVE, "r_shadow_deferred_8bitrange", "4", "dynamic range of image-based lighting when using 32bit color (does not apply to fp)"};
-//cvar_t r_shadow_deferred_fp = {CVAR_SAVE, "r_shadow_deferred_fp", "0", "use 16bit (1) or 32bit (2) floating point for accumulation of image-based lighting"};
 cvar_t r_shadow_usebihculling = {0, "r_shadow_usebihculling", "1", "use BIH (Bounding Interval Hierarchy) for culling lit surfaces instead of BSP (Binary Space Partitioning)"};
 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
@@ -294,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"};
@@ -303,6 +308,7 @@ cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_wor
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
 cvar_t r_shadow_shadowmapping = {CVAR_SAVE, "r_shadow_shadowmapping", "1", "enables use of shadowmapping (depth texture sampling) instead of stencil shadow volumes, requires gl_fbo 1"};
 cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "-1", "shadowmap filter modes: -1 = auto-select, 0 = no filtering, 1 = bilinear, 2 = bilinear 2x2 blur (fast), 3 = 3x3 blur (moderate), 4 = 4x4 blur (slow)"};
+cvar_t r_shadow_shadowmapping_useshadowsampler = {CVAR_SAVE, "r_shadow_shadowmapping_useshadowsampler", "1", "whether to use sampler2DShadow if available"};
 cvar_t r_shadow_shadowmapping_depthbits = {CVAR_SAVE, "r_shadow_shadowmapping_depthbits", "24", "requested minimum shadowmap texture depth bits"};
 cvar_t r_shadow_shadowmapping_vsdct = {CVAR_SAVE, "r_shadow_shadowmapping_vsdct", "1", "enables use of virtual shadow depth cube texture"};
 cvar_t r_shadow_shadowmapping_minsize = {CVAR_SAVE, "r_shadow_shadowmapping_minsize", "32", "shadowmap size limit"};
@@ -320,30 +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 4x 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", "10", "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", "5", "maximum number of bounces for a particle (minimum is 1)"};
-cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "4", "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", "2", "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_spacingx = {CVAR_SAVE, "r_shadow_bouncegrid_spacingx", "64", "unit size of bouncegrid pixel on X axis"};
-cvar_t r_shadow_bouncegrid_spacingy = {CVAR_SAVE, "r_shadow_bouncegrid_spacingy", "64", "unit size of bouncegrid pixel on Y axis"};
-cvar_t r_shadow_bouncegrid_spacingz = {CVAR_SAVE, "r_shadow_bouncegrid_spacingz", "64", "unit size of bouncegrid pixel on Z axis"};
+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_photons = {CVAR_SAVE, "r_shadow_bouncegrid_static_photons", "25000", "photons value to use when in static mode"};
 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_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_stablerandom", "1", "make particle distribution consistent from frame to frame"};
-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_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_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)"};
@@ -353,34 +368,23 @@ cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "ho
 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
-
-typedef struct r_shadow_bouncegrid_settings_s
-{
-       qboolean staticmode;
-       qboolean bounceanglediffuse;
-       qboolean directionalshading;
-       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;
+cvar_t r_editlights_drawproperties = {0, "r_editlights_drawproperties", "1", "draw properties of currently selected light"};
+cvar_t r_editlights_current_origin = {0, "r_editlights_current_origin", "0 0 0", "origin of selected light"};
+cvar_t r_editlights_current_angles = {0, "r_editlights_current_angles", "0 0 0", "angles of selected light"};
+cvar_t r_editlights_current_color = {0, "r_editlights_current_color", "1 1 1", "color of selected light"};
+cvar_t r_editlights_current_radius = {0, "r_editlights_current_radius", "0", "radius of selected light"};
+cvar_t r_editlights_current_corona = {0, "r_editlights_current_corona", "0", "corona intensity of selected light"};
+cvar_t r_editlights_current_coronasize = {0, "r_editlights_current_coronasize", "0", "corona size of selected light"};
+cvar_t r_editlights_current_style = {0, "r_editlights_current_style", "0", "style of selected light"};
+cvar_t r_editlights_current_shadows = {0, "r_editlights_current_shadows", "0", "shadows flag of selected light"};
+cvar_t r_editlights_current_cubemap = {0, "r_editlights_current_cubemap", "0", "cubemap of selected light"};
+cvar_t r_editlights_current_ambient = {0, "r_editlights_current_ambient", "0", "ambient intensity of selected light"};
+cvar_t r_editlights_current_diffuse = {0, "r_editlights_current_diffuse", "1", "diffuse intensity of selected light"};
+cvar_t r_editlights_current_specular = {0, "r_editlights_current_specular", "1", "specular intensity of selected light"};
+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"};
+
+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
@@ -420,17 +424,19 @@ skinframe_t *r_editlights_sprcubemaplight;
 skinframe_t *r_editlights_sprcubemapnoshadowlight;
 skinframe_t *r_editlights_sprselection;
 
-void R_Shadow_SetShadowMode(void)
+static void R_Shadow_SetShadowMode(void)
 {
        r_shadow_shadowmapmaxsize = bound(1, r_shadow_shadowmapping_maxsize.integer, (int)vid.maxtexturesize_2d / 4);
        r_shadow_shadowmapvsdct = r_shadow_shadowmapping_vsdct.integer != 0 && vid.renderpath == RENDERPATH_GL20;
        r_shadow_shadowmapfilterquality = r_shadow_shadowmapping_filterquality.integer;
+       r_shadow_shadowmapshadowsampler = r_shadow_shadowmapping_useshadowsampler.integer != 0;
        r_shadow_shadowmapdepthbits = r_shadow_shadowmapping_depthbits.integer;
        r_shadow_shadowmapborder = bound(0, r_shadow_shadowmapping_bordersize.integer, 16);
        r_shadow_shadowmaplod = -1;
        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)
        {
@@ -439,27 +445,28 @@ void R_Shadow_SetShadowMode(void)
                case RENDERPATH_GL20:
                        if(r_shadow_shadowmapfilterquality < 0)
                        {
-                               if(vid.support.amd_texture_texture4 || vid.support.arb_texture_gather)
+                               if (!r_fb.usedepthtextures)
                                        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_shadowmapsampler = true;
                                        r_shadow_shadowmappcf = 1;
                                }
-                               else if(strstr(gl_vendor, "ATI")) 
+                               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 
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow;
+                                       r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                        }
                        else 
                        {
+                r_shadow_shadowmapsampler = vid.support.arb_shadow && r_shadow_shadowmapshadowsampler;
                                switch (r_shadow_shadowmapfilterquality)
                                {
                                case 1:
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow;
                                        break;
                                case 2:
-                                       r_shadow_shadowmapsampler = vid.support.arb_shadow;
                                        r_shadow_shadowmappcf = 1;
                                        break;
                                case 3:
@@ -470,6 +477,8 @@ void R_Shadow_SetShadowMode(void)
                                        break;
                                }
                        }
+                       if (!r_fb.usedepthtextures)
+                               r_shadow_shadowmapsampler = false;
                        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D;
                        break;
                case RENDERPATH_D3D9:
@@ -480,14 +489,16 @@ void R_Shadow_SetShadowMode(void)
                        r_shadow_shadowmappcf = 1;
                        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_SHADOWMAP2D;
                        break;
-               case RENDERPATH_GL13:
-                       break;
                case RENDERPATH_GL11:
-                       break;
+               case RENDERPATH_GL13:
+               case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
                        break;
                }
        }
+
+       if(R_CompileShader_CheckStaticParms())
+               R_GLSL_Restart_f();
 }
 
 qboolean R_Shadow_ShadowMappingEnabled(void)
@@ -501,7 +512,7 @@ qboolean R_Shadow_ShadowMappingEnabled(void)
        }
 }
 
-void R_Shadow_FreeShadowMaps(void)
+static void R_Shadow_FreeShadowMaps(void)
 {
        R_Shadow_SetShadowMode();
 
@@ -509,33 +520,30 @@ void R_Shadow_FreeShadowMaps(void)
 
        r_shadow_fbo2d = 0;
 
-       if (r_shadow_shadowmap2dtexture)
-               R_FreeTexture(r_shadow_shadowmap2dtexture);
-       r_shadow_shadowmap2dtexture = NULL;
+       if (r_shadow_shadowmap2ddepthtexture)
+               R_FreeTexture(r_shadow_shadowmap2ddepthtexture);
+       r_shadow_shadowmap2ddepthtexture = NULL;
 
-       if (r_shadow_shadowmap2dcolortexture)
-               R_FreeTexture(r_shadow_shadowmap2dcolortexture);
-       r_shadow_shadowmap2dcolortexture = NULL;
+       if (r_shadow_shadowmap2ddepthbuffer)
+               R_FreeTexture(r_shadow_shadowmap2ddepthbuffer);
+       r_shadow_shadowmap2ddepthbuffer = NULL;
 
        if (r_shadow_shadowmapvsdcttexture)
                R_FreeTexture(r_shadow_shadowmapvsdcttexture);
        r_shadow_shadowmapvsdcttexture = NULL;
 }
 
-void r_shadow_start(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;
        r_shadow_shadowmode = R_SHADOW_SHADOWMODE_STENCIL;
-       r_shadow_shadowmap2dtexture = NULL;
-       r_shadow_shadowmap2dcolortexture = NULL;
+       r_shadow_shadowmap2ddepthtexture = NULL;
+       r_shadow_shadowmap2ddepthbuffer = NULL;
        r_shadow_shadowmapvsdcttexture = NULL;
        r_shadow_shadowmapmaxsize = 0;
        r_shadow_shadowmapsize = 0;
@@ -585,10 +593,33 @@ 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);
-void r_shadow_shutdown(void)
+static void r_shadow_shutdown(void)
 {
        CHECKGLERROR
        R_Shadow_UncompileWorldLights();
@@ -601,11 +632,7 @@ 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;
@@ -671,9 +698,9 @@ void r_shadow_shutdown(void)
                Mem_Free(r_shadow_buffer_lighttrispvs);
 }
 
-void r_shadow_newmap(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);
@@ -693,8 +720,6 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_usenormalmap);
        Cvar_RegisterVariable(&r_shadow_debuglight);
        Cvar_RegisterVariable(&r_shadow_deferred);
-       Cvar_RegisterVariable(&r_shadow_deferred_8bitrange);
-//     Cvar_RegisterVariable(&r_shadow_deferred_fp);
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
        Cvar_RegisterVariable(&r_shadow_glossintensity);
@@ -707,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);
@@ -722,6 +748,7 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_shadowmapping);
        Cvar_RegisterVariable(&r_shadow_shadowmapping_vsdct);
        Cvar_RegisterVariable(&r_shadow_shadowmapping_filterquality);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_useshadowsampler);
        Cvar_RegisterVariable(&r_shadow_shadowmapping_depthbits);
        Cvar_RegisterVariable(&r_shadow_shadowmapping_precision);
        Cvar_RegisterVariable(&r_shadow_shadowmapping_maxsize);
@@ -738,27 +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_spacingx);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacingy);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_spacingz);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_sortlightpaths);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_photons);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_spacing);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom);
-       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_lightradiusscale);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce);
+       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);
@@ -817,7 +853,7 @@ matrix4x4_t matrix_attenuationz =
        }
 };
 
-void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles, int vertscale, int triscale)
+static void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles, int vertscale, int triscale)
 {
        numvertices = ((numvertices + 255) & ~255) * vertscale;
        numtriangles = ((numtriangles + 255) & ~255) * triscale;
@@ -913,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);
@@ -1273,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;
                        }
                }
@@ -1285,14 +1321,14 @@ 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;
                        }
                }
        }
 }
 
-qboolean R_Shadow_UseZPass(vec3_t mins, vec3_t maxs)
+static qboolean R_Shadow_UseZPass(vec3_t mins, vec3_t maxs)
 {
 #if 1
        return false;
@@ -1357,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
@@ -1369,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
@@ -1391,143 +1427,144 @@ 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;
 }
 
-int R_Shadow_CalcBBoxSideMask(const vec3_t mins, const vec3_t maxs, const matrix4x4_t *worldtolight, const matrix4x4_t *radiustolight, float bias)
+static int R_Shadow_CalcBBoxSideMask(const vec3_t mins, const vec3_t maxs, const matrix4x4_t *worldtolight, const matrix4x4_t *radiustolight, float bias)
 {
        vec3_t center, radius, lightcenter, lightradius, pmin, pmax;
        float dp1, dn1, ap1, an1, dp2, dn2, ap2, an2;
        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;
 }
 
-int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border)
+static int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border)
 {
        int i;
-       vec3_t p, n;
+       vec3_t o, p, n;
        int sides = 0x3F, masks[6] = { 3<<4, 3<<4, 3<<0, 3<<0, 3<<2, 3<<2 };
        float scale = (size - 2*border)/size, len;
        float bias = border / (float)(size - border), dp, dn, ap, an;
        // check if cone enclosing side would cross frustum plane 
        scale = 2 / (scale*scale + 2);
+       Matrix4x4_OriginFromMatrix(&rtlight->matrix_lighttoworld, o);
        for (i = 0;i < 5;i++)
        {
-               if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) > -0.03125)
+               if (PlaneDiff(o, &r_refdef.view.frustum[i]) > -0.03125)
                        continue;
                Matrix4x4_Transform3x3(&rtlight->matrix_worldtolight, r_refdef.view.frustum[i].normal, n);
                len = scale*VectorLength2(n);
@@ -1535,10 +1572,10 @@ int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border)
                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);
        }
-       if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[4]) >= r_refdef.farclip - r_refdef.nearclip + 0.03125)
+       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*VectorLength(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);
@@ -1546,33 +1583,33 @@ int R_Shadow_CullFrustumSides(rtlight_t *rtlight, float size, float border)
        // 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);
@@ -1656,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);
@@ -1676,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);
@@ -1753,7 +1790,7 @@ static void R_Shadow_MakeTextures_MakeCorona(void)
                        pixels[y][x][3] = 255;
                }
        }
-       r_shadow_lightcorona = R_SkinFrame_LoadInternalBGRA("lightcorona", TEXF_FORCELINEAR, &pixels[0][0][0], 32, 32);
+       r_shadow_lightcorona = R_SkinFrame_LoadInternalBGRA("lightcorona", TEXF_FORCELINEAR, &pixels[0][0][0], 32, 32, false);
 }
 
 static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
@@ -1950,7 +1987,7 @@ void R_Shadow_RenderMode_Begin(void)
        GL_DepthMask(false);
        GL_Color(0, 0, 0, 1);
        GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
-
+       
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 
        if (gl_ext_separatestencil.integer && vid.support.ati_separate_stencil)
@@ -1979,8 +2016,9 @@ void R_Shadow_RenderMode_Begin(void)
        case RENDERPATH_GLES2:
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
                break;
-       case RENDERPATH_GL13:
        case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GLES1:
                if (r_textureunits.integer >= 2 && vid.texunits >= 2 && r_shadow_texture3d.integer && r_shadow_attenuation3dtexture)
                        r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX3DATTEN;
                else if (r_textureunits.integer >= 3 && vid.texunits >= 3)
@@ -2010,10 +2048,10 @@ void R_Shadow_RenderMode_ActiveLight(const rtlight_t *rtlight)
 
 void R_Shadow_RenderMode_Reset(void)
 {
-       R_Mesh_ResetRenderTargets();
+       R_Mesh_ResetTextureState();
+       R_Mesh_SetRenderTargets(r_shadow_fb_fbo, r_shadow_fb_depthtexture, r_shadow_fb_colortexture, NULL, NULL, NULL);
        R_SetViewport(&r_refdef.view.viewport);
        GL_Scissor(r_shadow_lightscissor[0], r_shadow_lightscissor[1], r_shadow_lightscissor[2], r_shadow_lightscissor[3]);
-       R_Mesh_ResetTextureState();
        GL_DepthRange(0, 1);
        GL_DepthTest(true);
        GL_DepthMask(false);
@@ -2025,7 +2063,7 @@ void R_Shadow_RenderMode_Reset(void)
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic_NoTexture(false, false);
        r_shadow_usingshadowmap2d = false;
        r_shadow_usingshadowmaportho = false;
        R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_ALWAYS, 128, 255);
@@ -2034,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)
@@ -2047,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();
+       R_SetupShader_DepthOrShadow(false, false, false); // FIXME test if we have a skeletal model?
        r_shadow_rendermode = mode;
        switch(mode)
        {
@@ -2089,49 +2127,31 @@ static void R_Shadow_MakeShadowMap(int side, int size)
        switch (r_shadow_shadowmode)
        {
        case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
-               if (r_shadow_shadowmap2dtexture) return;
-               r_shadow_shadowmap2dtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), r_shadow_shadowmapdepthbits, r_shadow_shadowmapsampler);
-               r_shadow_shadowmap2dcolortexture = NULL;
-               switch(vid.renderpath)
+               if (r_shadow_shadowmap2ddepthtexture) return;
+               if (r_fb.usedepthtextures)
                {
-#ifdef SUPPORTD3D
-               case RENDERPATH_D3D9:
-                       r_shadow_shadowmap2dcolortexture = R_LoadTexture2D(r_shadow_texturepool, "shadowmaprendertarget", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), NULL, TEXTYPE_BGRA, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALPHA, -1, NULL);
-                       r_shadow_fbo2d = R_Mesh_CreateFramebufferObject(r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
-                       break;
-#endif
-               default:
-                       r_shadow_fbo2d = R_Mesh_CreateFramebufferObject(r_shadow_shadowmap2dtexture, NULL, NULL, NULL, NULL);
-                       break;
+                       r_shadow_shadowmap2ddepthtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), r_shadow_shadowmapdepthbits >= 24 ? (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP24_COMP : TEXTYPE_SHADOWMAP24_RAW) : (r_shadow_shadowmapsampler ? TEXTYPE_SHADOWMAP16_COMP : TEXTYPE_SHADOWMAP16_RAW), r_shadow_shadowmapsampler);
+                       r_shadow_shadowmap2ddepthbuffer = NULL;
+                       r_shadow_fbo2d = R_Mesh_CreateFramebufferObject(r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, NULL);
+               }
+               else
+               {
+                       r_shadow_shadowmap2ddepthtexture = R_LoadTexture2D(r_shadow_texturepool, "shadowmaprendertarget", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALPHA, -1, NULL);
+                       r_shadow_shadowmap2ddepthbuffer = R_LoadTextureRenderBuffer(r_shadow_texturepool, "shadowmap", size*2, size*(vid.support.arb_texture_non_power_of_two ? 3 : 4), r_shadow_shadowmapdepthbits >= 24 ? TEXTYPE_DEPTHBUFFER24 : TEXTYPE_DEPTHBUFFER16);
+                       r_shadow_fbo2d = R_Mesh_CreateFramebufferObject(r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL);
                }
                break;
        default:
                return;
        }
-
-       // render depth into the fbo, do not render color at all
-       // validate the fbo now
-       if (qglDrawBuffer)
-       {
-               int status;
-               qglDrawBuffer(GL_NONE);CHECKGLERROR
-               qglReadBuffer(GL_NONE);CHECKGLERROR
-               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-               if (status != GL_FRAMEBUFFER_COMPLETE_EXT && (r_shadow_shadowmapping.integer || r_shadow_deferred.integer))
-               {
-                       Con_Printf("R_Shadow_MakeShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
-                       Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
-                       Cvar_SetValueQuick(&r_shadow_deferred, 0);
-               }
-       }
 }
 
-void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
+static void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
 {
        float nearclip, farclip, bias;
        r_viewport_t viewport;
        int flipped;
-       GLuint fbo = 0;
+       GLuint fbo2d = 0;
        float clearcolor[4];
        nearclip = r_shadow_shadowmapping_nearclip.value / rsurface.rtlight->radius;
        farclip = 1.0f;
@@ -2149,23 +2169,20 @@ void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
        // complex unrolled cube approach (more flexible)
        if (r_shadow_shadowmapvsdct && !r_shadow_shadowmapvsdcttexture)
                R_Shadow_MakeVSDCT();
-       if (!r_shadow_shadowmap2dtexture)
+       if (!r_shadow_shadowmap2ddepthtexture)
                R_Shadow_MakeShadowMap(side, r_shadow_shadowmapmaxsize);
-       if (r_shadow_shadowmap2dtexture) fbo = r_shadow_fbo2d;
-       r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2dtexture);
-       r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2dtexture);
+       fbo2d = r_shadow_fbo2d;
+       r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2ddepthtexture);
+       r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2ddepthtexture);
        r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAP2D;
 
        R_Mesh_ResetTextureState();
-       R_Mesh_ResetRenderTargets();
        R_Shadow_RenderMode_Reset();
-       if (fbo)
-       {
-               R_Mesh_SetRenderTargets(fbo, r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
-               R_SetupShader_DepthOrShadow();
-       }
+       if (r_shadow_shadowmap2ddepthbuffer)
+               R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthbuffer, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL);
        else
-               R_SetupShader_ShowDepth();
+               R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2ddepthtexture, NULL, NULL, NULL, 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);
@@ -2175,12 +2192,24 @@ init_done:
        flipped = (side & 1) ^ (side >> 2);
        r_refdef.view.cullface_front = flipped ? r_shadow_cullface_back : r_shadow_cullface_front;
        r_refdef.view.cullface_back = flipped ? r_shadow_cullface_front : r_shadow_cullface_back;
+       if (r_shadow_shadowmap2ddepthbuffer)
+       {
+               // completely different meaning than in depthtexture approach
+               r_shadow_shadowmap_parameters[1] = 0;
+               r_shadow_shadowmap_parameters[3] = -bias;
+       }
+       Vector4Set(clearcolor, 1,1,1,1);
+       if (r_shadow_shadowmap2ddepthbuffer)
+               GL_ColorMask(1,1,1,1);
+       else
+               GL_ColorMask(0,0,0,0);
        switch(vid.renderpath)
        {
        case RENDERPATH_GL11:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
                GL_CullFace(r_refdef.view.cullface_back);
                // OpenGL lets us scissor larger than the viewport, so go ahead and clear all views at once
@@ -2192,33 +2221,30 @@ init_done:
                        int y1 = clear & 0x03 ? 0 : (clear & 0xC ? size : 2 * size);
                        int y2 = clear & 0x30 ? 3 * size : (clear & 0xC ? 2 * size : size);
                        GL_Scissor(x1, y1, x2 - x1, y2 - y1);
-                       GL_Clear(GL_DEPTH_BUFFER_BIT, NULL, 1.0f, 0);
+                       if (clear)
+                       {
+                               if (r_shadow_shadowmap2ddepthbuffer)
+                                       GL_Clear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT, clearcolor, 1.0f, 0);
+                               else
+                                       GL_Clear(GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
+                       }
                }
                GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
                break;
        case RENDERPATH_D3D9:
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
-               Vector4Set(clearcolor, 1,1,1,1);
-               // completely different meaning than in OpenGL path
-               r_shadow_shadowmap_parameters[1] = 0;
-               r_shadow_shadowmap_parameters[3] = -bias;
                // we invert the cull mode because we flip the projection matrix
                // NOTE: this actually does nothing because the DrawShadowMap code sets it to doublesided...
                GL_CullFace(r_refdef.view.cullface_front);
                // D3D considers it an error to use a scissor larger than the viewport...  clear just this view
                GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
-               if (r_shadow_shadowmapsampler)
-               {
-                       GL_ColorMask(0,0,0,0);
-                       if (clear)
-                               GL_Clear(GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
-               }
-               else
+               if (clear)
                {
-                       GL_ColorMask(1,1,1,1);
-                       if (clear)
+                       if (r_shadow_shadowmap2ddepthbuffer)
                                GL_Clear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT, clearcolor, 1.0f, 0);
+                       else
+                               GL_Clear(GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
                }
                break;
        }
@@ -2227,7 +2253,6 @@ init_done:
 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent, qboolean shadowmapping)
 {
        R_Mesh_ResetTextureState();
-       R_Mesh_ResetRenderTargets();
        if (transparent)
        {
                r_shadow_lightscissor[0] = r_refdef.view.viewport.x;
@@ -2287,7 +2312,10 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow
        // only draw light where this geometry was already rendered AND the
        // stencil is 128 (values other than this mean shadow)
        R_SetStencil(stenciltest, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_EQUAL, 128, 255);
-       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       if (rsurface.rtlight->specularscale > 0 && r_shadow_gloss.integer > 0)
+               R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       else
+               R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
 
        r_shadow_usingshadowmap2d = shadowmapping;
 
@@ -2302,124 +2330,136 @@ 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);
 }
 
-static 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;
-       vec_t radius;
-       vec_t s;
-       vec_t lightintensity;
-       vec_t photonscaling;
-       vec_t photonresidual;
-       float m[16];
-       float texlerp[2][3];
-       float splatcolor[16];
-       float pixelweight[8];
-       float w;
-       int c[4];
-       int pixelindex[8];
-       int corner;
-       int x, y, z, d;
-       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_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)
+       if (enable && r_shadow_bouncegrid_static.integer)
        {
                enable = false;
-               range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
-               range1 = r_shadow_bouncegrid_static.integer ? 0 : r_refdef.scene.numlights;
-               range2 = range + range1;
-               for (lightindex = 0;lightindex < range2;lightindex++)
+               range = (unsigned int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+               for (lightindex = 0;lightindex < range;lightindex++)
                {
-                       if (r_shadow_bouncegrid_static.integer)
-                       {
-                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                               if (!light || !(light->flags & flag))
-                                       continue;
-                               rtlight = &light->rtlight;
-                               // when static, we skip styled lights because they tend to change...
-                               if (rtlight->style > 0)
-                                       continue;
-                               VectorScale(rtlight->color, (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) * (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1), lightcolor);
-                       }
-                       else
-                       {
-                               if (lightindex < range)
-                               {
-                                       light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                                       rtlight = &light->rtlight;
-                               }
-                               else
-                                       rtlight = r_refdef.scene.lights[lightindex - range];
-                               // draw only visible lights (major speedup)
-                               if (!rtlight->draw)
-                                       continue;
-                               VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
-                       }
+                       light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                       if (!light || !(light->flags & flag))
+                               continue;
+                       rtlight = &light->rtlight;
+                       // when static, we skip styled lights because they tend to change...
+                       if (rtlight->style > 0)
+                               continue;
+                       VectorScale(rtlight->color, (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale), lightcolor);
                        if (!VectorLength2(lightcolor))
                                continue;
                        enable = true;
@@ -2427,59 +2467,79 @@ static 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
-       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.lightradiusscale              = r_shadow_bouncegrid_lightradiusscale.value;
-       settings.maxbounce                     = r_shadow_bouncegrid_maxbounce.integer;
-       settings.particlebounceintensity       = r_shadow_bouncegrid_particlebounceintensity.value;
-       settings.particleintensity             = r_shadow_bouncegrid_particleintensity.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_spacingx.value;
-       settings.spacing[1]                    = r_shadow_bouncegrid_spacingy.value;
-       settings.spacing[2]                    = r_shadow_bouncegrid_spacingz.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(1, 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);
@@ -2507,12 +2567,12 @@ static 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 < resolution[0] || r_shadow_bouncegrid_y.integer < resolution[1] || r_shadow_bouncegrid_z.integer < 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);
@@ -2541,157 +2601,586 @@ static 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;
+       // 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);
 
-       // store the new settings
-       r_shadow_bouncegridsettings = settings;
-
-       // 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;
-       if (settings.directionalshading)
-       {
-               m[10] *= 0.25f;
-               m[11] *= 0.25f;
-       }
-       Matrix4x4_FromArrayFloatD3D(&r_shadow_bouncegridmatrix, m);
-       numpixels = resolution[0]*resolution[1]*resolution[2];
-       if (settings.directionalshading)
-               numpixels *= 4;
-       r_shadow_bouncegriddirectional = settings.directionalshading;
        // 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;
-       if (settings.directionalshading)
-               memset(pixels, 128, numpixels * sizeof(unsigned char[4]));
-       else
-               memset(pixels, 0, numpixels * sizeof(unsigned char[4]));
-       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;
+               r_shadow_bouncegrid_state.numpixels = numpixels;
+       }
+
+       // 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 (settings.staticmode)
+               if (lightindex < range)
                {
                        light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                       if (!light || !(light->flags & flag))
+                       if (!light)
                                continue;
                        rtlight = &light->rtlight;
-                       // when static, we skip styled lights because they tend to change...
-                       if (rtlight->style > 0)
+                       VectorClear(rtlight->photoncolor);
+                       rtlight->photons = 0;
+                       if (!(light->flags & flag))
                                continue;
-                       VectorScale(rtlight->color, (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) * (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1), lightcolor);
+                       if (settings->staticmode)
+                       {
+                               // when static, we skip styled lights because they tend to change...
+                               if (rtlight->style > 0 && r_shadow_bouncegrid.integer != 2)
+                                       continue;
+                       }
                }
                else
                {
-                       if (lightindex < range)
-                       {
-                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                               rtlight = &light->rtlight;
-                       }
-                       else
-                               rtlight = r_refdef.scene.lights[lightindex - range];
-                       // draw only visible lights (major speedup)
-                       if (!rtlight->draw)
+                       rtlight = r_refdef.scene.lights[lightindex - range];
+                       VectorClear(rtlight->photoncolor);
+                       rtlight->photons = 0;
+               }
+               // draw only visible lights (major speedup)
+               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;
+               w = r_shadow_lightintensityscale.value * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
+               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;
-                       VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
                }
-               if (!VectorLength2(lightcolor))
+               // 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 && rtlight->style < MAX_LIGHTSTYLES) ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
+               VectorScale(rtlight->color, w, rtlight->photoncolor);
+               // 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
                // distribution, the seeded random is only consistent for a
                // consistent number of particles on this light...
-               radius = rtlight->radius * settings.lightradiusscale;
                s = rtlight->radius;
                lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
                if (lightindex >= range)
-                       lightintensity *= settings.dlightparticlemultiplier;
-               photoncount += 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);
+               }
        }
-       photonscaling = (float)settings.photons / max(1, photoncount);
-       photonresidual = 0.0f;
-       for (lightindex = 0;lightindex < range2;lightindex++)
+}
+
+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++)
        {
-               if (settings.staticmode)
+               for (z = 1;z < resolution[2]-1;z++)
                {
-                       light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                       if (!light || !(light->flags & flag))
-                               continue;
-                       rtlight = &light->rtlight;
-                       // when static, we skip styled lights because they tend to change...
-                       if (rtlight->style > 0)
-                               continue;
-                       VectorScale(rtlight->color, (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) * (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1), lightcolor);
+                       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);
+                               }
+                       }
                }
-               else
+       }
+}
+
+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++)
                {
-                       if (lightindex < range)
+                       for (y = 0;y < resolution[1]-1;y++)
                        {
-                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-                               rtlight = &light->rtlight;
+                               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
-                               rtlight = r_refdef.scene.lights[lightindex - range];
-                       // draw only visible lights (major speedup)
-                       if (!rtlight->draw)
+                               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)
+               {
+                       light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                       if (!light)
                                continue;
-                       VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
+                       rtlight = &light->rtlight;
                }
-               if (!VectorLength2(lightcolor))
-                       continue;
-               // shoot particles from this light
-               // use a calculation for the number of particles that will not
-               // vary with lightstyle, otherwise we get randomized particle
-               // distribution, the seeded random is only consistent for a
-               // consistent number of particles on this light...
-               radius = rtlight->radius * settings.lightradiusscale;
-               s = rtlight->radius;
-               lightintensity = VectorLength(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
-               if (lightindex >= range)
-                       lightintensity *= settings.dlightparticlemultiplier;
-               photonresidual += lightintensity * s * s * photonscaling;
-               shootparticles = (int)bound(0, photonresidual, MAXBOUNCEGRIDPARTICLESPERLIGHT);
+               else
+                       rtlight = r_refdef.scene.lights[lightindex - range];
+               // 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(lightcolor, s, baseshotcolor);
-               if (VectorLength2(baseshotcolor) == 0.0f)
-                       break;
-               r_refdef.stats.bouncegrid_lights++;
-               r_refdef.stats.bouncegrid_particles += shootparticles;
+               VectorScale(rtlight->photoncolor, s, baseshotcolor);
+               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)
@@ -2705,125 +3194,28 @@ static 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)
-                                       Collision_ClipLineToWorld(&cliptrace, cl.worldmodel, clipstart, clipend, hitsupercontentsmask, true);
+                               {
+                                       // 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, collision_extendmovelength.value, true, false, NULL, true, true);
+                               }
                                else
-                                       cliptrace = CL_TraceLine(clipstart, clipend, settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS, NULL, hitsupercontentsmask, true, false, NULL, true, true);
-                               if (cliptrace.fraction >= 1.0f)
-                                       break;
-                               r_refdef.stats.bouncegrid_hits++;
-                               if (bouncecount > 0)
                                {
-                                       r_refdef.stats.bouncegrid_splats++;
-                                       // figure out which texture pixel this is in
-                                       texlerp[1][0] = ((cliptrace.endpos[0] - mins[0]) * ispacing[0]);
-                                       texlerp[1][1] = ((cliptrace.endpos[1] - mins[1]) * ispacing[1]);
-                                       texlerp[1][2] = ((cliptrace.endpos[2] - mins[2]) * ispacing[2]);
-                                       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 first order spherical harmonics values (average, slopeX, slopeY, slopeZ)
-                                               if (settings.directionalshading)
-                                               {
-                                                       VectorSubtract(clipstart, cliptrace.endpos, clipdiff);
-                                                       VectorNormalize(clipdiff);
-                                                       splatcolor[ 0] = shotcolor[0] * clipdiff[2];
-                                                       splatcolor[ 1] = shotcolor[0] * clipdiff[1];
-                                                       splatcolor[ 2] = shotcolor[0] * clipdiff[0];
-                                                       splatcolor[ 3] = shotcolor[0];
-                                                       splatcolor[ 4] = shotcolor[1] * clipdiff[2];
-                                                       splatcolor[ 5] = shotcolor[1] * clipdiff[1];
-                                                       splatcolor[ 6] = shotcolor[1] * clipdiff[0];
-                                                       splatcolor[ 7] = shotcolor[1];
-                                                       splatcolor[ 8] = shotcolor[2] * clipdiff[2];
-                                                       splatcolor[ 9] = shotcolor[2] * clipdiff[1];
-                                                       splatcolor[10] = shotcolor[2] * clipdiff[0];
-                                                       splatcolor[11] = shotcolor[2];
-                                                       w = VectorLength(shotcolor);
-                                                       splatcolor[12] = clipdiff[2] * w;
-                                                       splatcolor[13] = clipdiff[1] * w;
-                                                       splatcolor[14] = clipdiff[0] * w;
-                                                       splatcolor[15] = 1.0f;
-                                               }
-                                               else
-                                               {
-                                                       splatcolor[ 0] = shotcolor[2];
-                                                       splatcolor[ 1] = shotcolor[1];
-                                                       splatcolor[ 2] = shotcolor[0];
-                                                       splatcolor[ 3] = 1.0f;
-                                               }
-                                               // 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 (corner = 0;corner < 8;corner++)
-                                               {
-                                                       // calculate address for first set of coefficients
-                                                       w = pixelweight[corner];
-                                                       pixel = pixels + 4 * pixelindex[corner];
-                                                       highpixel = highpixels + 4 * pixelindex[corner];
-                                                       // add to the high precision pixel color
-                                                       highpixel[0] += (splatcolor[ 0]*w);
-                                                       highpixel[1] += (splatcolor[ 1]*w);
-                                                       highpixel[2] += (splatcolor[ 2]*w);
-                                                       highpixel[3] += (splatcolor[ 3]*w);
-                                                       // flag the low precision pixel as needing to be updated
-                                                       pixel[3] = 255;
-                                                       if (settings.directionalshading)
-                                                       {
-                                                               // advance to second set of coefficients
-                                                               pixel += numpixels;
-                                                               highpixel += numpixels;
-                                                               // add to the high precision pixel color
-                                                               highpixel[0] += (splatcolor[ 4]*w);
-                                                               highpixel[1] += (splatcolor[ 5]*w);
-                                                               highpixel[2] += (splatcolor[ 6]*w);
-                                                               highpixel[3] += (splatcolor[ 7]*w);
-                                                               // flag the low precision pixel as needing to be updated
-                                                               pixel[3] = 255;
-                                                               // advance to third set of coefficients
-                                                               pixel += numpixels;
-                                                               highpixel += numpixels;
-                                                               // add to the high precision pixel color
-                                                               highpixel[0] += (splatcolor[ 8]*w);
-                                                               highpixel[1] += (splatcolor[ 9]*w);
-                                                               highpixel[2] += (splatcolor[10]*w);
-                                                               highpixel[3] += (splatcolor[11]*w);
-                                                               // flag the low precision pixel as needing to be updated
-                                                               pixel[3] = 255;
-                                                               // advance to fourth set of coefficients
-                                                               pixel += numpixels;
-                                                               highpixel += numpixels;
-                                                               // add to the high precision pixel color
-                                                               highpixel[0] += (splatcolor[12]*w);
-                                                               highpixel[1] += (splatcolor[13]*w);
-                                                               highpixel[2] += (splatcolor[14]*w);
-                                                               highpixel[3] += (splatcolor[15]*w);
-                                                               // flag the low precision pixel as needing to be updated
-                                                               pixel[3] = 255;
-                                                       }
-                                               }
-                                       }
+                                       // dynamic mode fires many rays and most will match the cache from the previous frame
+                                       cliptrace = CL_Cache_TraceLineSurfaces(clipstart, clipend, settings.staticmode ? MOVE_WORLDONLY : (settings.hitmodels ? MOVE_HITMODEL : MOVE_NOMONSTERS), hitsupercontentsmask);
+                               }
+                               if (bouncecount > 0 || settings.includedirectlighting)
+                               {
+                                       vec3_t hitpos;
+                                       VectorCopy(cliptrace.endpos, hitpos);
+                                       R_Shadow_BounceGrid_AddSplatPath(clipstart, hitpos, shotcolor);
                                }
+                               if (cliptrace.fraction >= 1.0f)
+                                       break;
+                               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)
@@ -2839,7 +3231,7 @@ static 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
@@ -2864,58 +3256,78 @@ static 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 (d = 0;d < 4;d++)
-       {
-               for (z = 1;z < resolution[2]-1;z++)
+}
+
+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)
+       {
+               // 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] = ((d*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 (settings.directionalshading)
-                                               {
-                                                       if (d == 3)
-                                                               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[0] = (unsigned char)bound(0, c[0], 255);
-                                               pixel[1] = (unsigned char)bound(0, c[1], 255);
-                                               pixel[2] = (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;
                }
-               if (!settings.directionalshading)
-                       break;
-       }
-       if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2])
-               R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]*(settings.directionalshading ? 4 : 1));
-       else
-       {
-               VectorCopy(resolution, r_shadow_bouncegridresolution);
-               if (r_shadow_bouncegridtexture)
-                       R_FreeTexture(r_shadow_bouncegridtexture);
-               r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*(settings.directionalshading ? 4 : 1), pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+               r_shadow_bouncegrid_state.numpixels = 0;
+               r_shadow_bouncegrid_state.directional = false;
+
+               if (!enable)
+                       return;
        }
-       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)
@@ -2973,7 +3385,7 @@ int bboxedges[12][2] =
 
 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 {
-       if (!r_shadow_scissor.integer)
+       if (!r_shadow_scissor.integer || r_shadow_usingdeferredprepass || r_trippy.integer)
        {
                r_shadow_lightscissor[0] = r_refdef.view.viewport.x;
                r_shadow_lightscissor[1] = r_refdef.view.viewport.y;
@@ -2987,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;
 }
 
@@ -3172,12 +3584,8 @@ static void R_Shadow_RenderLighting_VisibleLighting(int texturenumsurfaces, cons
 static void R_Shadow_RenderLighting_Light_GLSL(int texturenumsurfaces, const msurface_t **texturesurfacelist, const vec3_t lightcolor, float ambientscale, float diffusescale, float specularscale)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       R_SetupShader_Surface(lightcolor, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT, texturenumsurfaces, texturesurfacelist, NULL);
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
-               GL_DepthFunc(GL_EQUAL);
+       R_SetupShader_Surface(lightcolor, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT, texturenumsurfaces, texturesurfacelist, NULL, false);
        RSurf_DrawBatch();
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
-               GL_DepthFunc(GL_LEQUAL);
 }
 
 static void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numvertices, int numtriangles, const int *element3i, vec3_t diffusecolor2, vec3_t ambientcolor2)
@@ -3191,8 +3599,8 @@ static void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numve
        int *newe;
        const int *e;
        float *c;
-       int maxtriangles = 4096;
-       static int newelements[4096*3];
+       int maxtriangles = 1024;
+       int newelements[1024*3];
        R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, diffusecolor2, ambientcolor2);
        for (renders = 0;renders < 4;renders++)
        {
@@ -3345,8 +3753,8 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
        qboolean negated;
        float lightcolor[3];
        VectorCopy(rsurface.rtlight->currentcolor, lightcolor);
-       ambientscale = rsurface.rtlight->ambientscale;
-       diffusescale = rsurface.rtlight->diffusescale;
+       ambientscale = rsurface.rtlight->ambientscale + rsurface.texture->rtlightambient;
+       diffusescale = rsurface.rtlight->diffusescale * max(0, 1.0 - rsurface.texture->rtlightambient);
        specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
        if (!r_shadow_usenormalmap.integer)
        {
@@ -3360,29 +3768,7 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
        if(negated)
        {
                VectorNegate(lightcolor, lightcolor);
-               switch(vid.renderpath)
-               {
-               case RENDERPATH_GL11:
-               case RENDERPATH_GL13:
-               case RENDERPATH_GL20:
-               case RENDERPATH_GLES2:
-                       qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
-                       break;
-               case RENDERPATH_D3D9:
-#ifdef SUPPORTD3D
-                       IDirect3DDevice9_SetRenderState(vid_d3d9dev, D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT);
-#endif
-                       break;
-               case RENDERPATH_D3D10:
-                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
-               case RENDERPATH_D3D11:
-                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
-               case RENDERPATH_SOFT:
-                       DPSOFTRAST_BlendSubtract(true);
-                       break;
-               }
+               GL_BlendEquationSubtract(true);
        }
        RSurf_SetupDepthAndCulling();
        switch (r_shadow_rendermode)
@@ -3405,31 +3791,7 @@ void R_Shadow_RenderLighting(int texturenumsurfaces, const msurface_t **textures
                break;
        }
        if(negated)
-       {
-               switch(vid.renderpath)
-               {
-               case RENDERPATH_GL11:
-               case RENDERPATH_GL13:
-               case RENDERPATH_GL20:
-               case RENDERPATH_GLES2:
-                       qglBlendEquationEXT(GL_FUNC_ADD_EXT);
-                       break;
-               case RENDERPATH_D3D9:
-#ifdef SUPPORTD3D
-                       IDirect3DDevice9_SetRenderState(vid_d3d9dev, D3DRS_BLENDOP, D3DBLENDOP_ADD);
-#endif
-                       break;
-               case RENDERPATH_D3D10:
-                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
-               case RENDERPATH_D3D11:
-                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                       break;
-               case RENDERPATH_SOFT:
-                       DPSOFTRAST_BlendSubtract(false);
-                       break;
-               }
-       }
+               GL_BlendEquationSubtract(false);
 }
 
 void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, int shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
@@ -3573,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++;
@@ -3626,7 +3988,7 @@ void R_Shadow_UncompileWorldLights(void)
        }
 }
 
-void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
+static void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
 {
        int i, j;
        mplane_t plane;
@@ -3635,6 +3997,9 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
        // can hold
        rtlight->cached_numfrustumplanes = 0;
 
+       if (r_trippy.integer)
+               return;
+
        // haven't implemented a culling path for ortho rendering
        if (!r_refdef.view.useperspective)
        {
@@ -3792,7 +4157,7 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
 #endif
 }
 
-void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist, const unsigned char *trispvs, const unsigned char *surfacesides)
+static void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist, const unsigned char *trispvs, const unsigned char *surfacesides)
 {
        shadowmesh_t *mesh;
 
@@ -3807,11 +4172,8 @@ void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist, const
                {
                        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
@@ -3822,7 +4184,7 @@ void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist, const
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
+static void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
 {
        qboolean zpass = false;
        shadowmesh_t *mesh;
@@ -3847,11 +4209,8 @@ void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, co
                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
@@ -3897,7 +4256,7 @@ void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, co
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_Shadow_DrawEntityShadow(entity_render_t *ent)
+static void R_Shadow_DrawEntityShadow(entity_render_t *ent)
 {
        vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
        vec_t relativeshadowradius;
@@ -3933,7 +4292,7 @@ void R_Shadow_SetupEntityLight(const entity_render_t *ent)
        Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
 }
 
-void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *lighttrispvs)
+static void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *lighttrispvs)
 {
        if (!r_refdef.scene.worldmodel->DrawLight)
                return;
@@ -3950,7 +4309,7 @@ void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned c
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_Shadow_DrawEntityLight(entity_render_t *ent)
+static void R_Shadow_DrawEntityLight(entity_render_t *ent)
 {
        dp_model_t *model = ent->model;
        if (!model->DrawLight)
@@ -3963,7 +4322,7 @@ void R_Shadow_DrawEntityLight(entity_render_t *ent)
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_Shadow_PrepareLight(rtlight_t *rtlight)
+static void R_Shadow_PrepareLight(rtlight_t *rtlight)
 {
        int i;
        float f;
@@ -3982,8 +4341,21 @@ 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;
+       rtlight->cached_numlightentities_noselfshadow  = 0;
+       rtlight->cached_numshadowentities              = 0;
+       rtlight->cached_numshadowentities_noselfshadow = 0;
+       rtlight->cached_numsurfaces                    = 0;
+       rtlight->cached_lightentities                  = NULL;
+       rtlight->cached_lightentities_noselfshadow     = NULL;
+       rtlight->cached_shadowentities                 = NULL;
+       rtlight->cached_shadowentities_noselfshadow    = NULL;
+       rtlight->cached_shadowtrispvs                  = NULL;
+       rtlight->cached_lighttrispvs                   = NULL;
+       rtlight->cached_surfacelist                    = NULL;
 
        // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
        // skip lights that are basically invisible (color 0 0 0)
@@ -4004,7 +4376,7 @@ 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)
@@ -4031,6 +4403,10 @@ void R_Shadow_PrepareLight(rtlight_t *rtlight)
 
        R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
 
+       // don't allow lights to be drawn if using r_shadow_bouncegrid 2, except if we're using static bouncegrid where dynamic lights still need to draw
+       if (r_shadow_bouncegrid.integer == 2 && (rtlight->isstatic || !r_shadow_bouncegrid_static.integer))
+               return;
+
        if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
        {
                // compiled light, world available and can receive realtime lighting
@@ -4156,18 +4532,21 @@ 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
@@ -4197,7 +4576,7 @@ void R_Shadow_PrepareLight(rtlight_t *rtlight)
        }
 }
 
-void R_Shadow_DrawLight(rtlight_t *rtlight)
+static void R_Shadow_DrawLight(rtlight_t *rtlight)
 {
        int i;
        int numsurfaces;
@@ -4446,13 +4825,9 @@ static void R_Shadow_FreeDeferred(void)
        R_Mesh_DestroyFramebufferObject(r_shadow_prepasslightingdiffusefbo);
        r_shadow_prepasslightingdiffusefbo = 0;
 
-       if (r_shadow_prepassgeometrydepthtexture)
-               R_FreeTexture(r_shadow_prepassgeometrydepthtexture);
-       r_shadow_prepassgeometrydepthtexture = NULL;
-
-       if (r_shadow_prepassgeometrydepthcolortexture)
-               R_FreeTexture(r_shadow_prepassgeometrydepthcolortexture);
-       r_shadow_prepassgeometrydepthcolortexture = NULL;
+       if (r_shadow_prepassgeometrydepthbuffer)
+               R_FreeTexture(r_shadow_prepassgeometrydepthbuffer);
+       r_shadow_prepassgeometrydepthbuffer = NULL;
 
        if (r_shadow_prepassgeometrynormalmaptexture)
                R_FreeTexture(r_shadow_prepassgeometrynormalmaptexture);
@@ -4484,7 +4859,7 @@ void R_Shadow_DrawPrepass(void)
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_Color(1,1,1,1);
        GL_DepthTest(true);
-       R_Mesh_SetRenderTargets(r_shadow_prepassgeometryfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepassgeometrynormalmaptexture, r_shadow_prepassgeometrydepthcolortexture, NULL, NULL);
+       R_Mesh_SetRenderTargets(r_shadow_prepassgeometryfbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepassgeometrynormalmaptexture, NULL, NULL, NULL);
        Vector4Set(clearcolor, 0.5f,0.5f,0.5f,1.0f);
        GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
        if (r_timereport_active)
@@ -4511,7 +4886,7 @@ void R_Shadow_DrawPrepass(void)
        GL_ColorMask(1,1,1,1);
        GL_Color(1,1,1,1);
        GL_DepthTest(true);
-       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
        Vector4Set(clearcolor, 0, 0, 0, 0);
        GL_Clear(GL_COLOR_BUFFER_BIT, clearcolor, 1.0f, 0);
        if (r_timereport_active)
@@ -4542,8 +4917,6 @@ void R_Shadow_DrawPrepass(void)
                        if (r_refdef.scene.lights[lnum]->draw)
                                R_Shadow_DrawLight(r_refdef.scene.lights[lnum]);
 
-       R_Mesh_ResetRenderTargets();
-
        R_Shadow_RenderMode_End();
 
        if (r_timereport_active)
@@ -4551,7 +4924,7 @@ void R_Shadow_DrawPrepass(void)
 }
 
 void R_Shadow_DrawLightSprites(void);
-void R_Shadow_PrepareLights(void)
+void R_Shadow_PrepareLights(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        int flag;
        int lnum;
@@ -4559,16 +4932,21 @@ void R_Shadow_PrepareLights(void)
        dlight_t *light;
        size_t range;
        float f;
-       GLenum status;
 
        if (r_shadow_shadowmapmaxsize != bound(1, r_shadow_shadowmapping_maxsize.integer, (int)vid.maxtexturesize_2d / 4) ||
                (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL) != (r_shadow_shadowmapping.integer || r_shadow_deferred.integer) ||
                r_shadow_shadowmapvsdct != (r_shadow_shadowmapping_vsdct.integer != 0 && vid.renderpath == RENDERPATH_GL20) || 
                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;
+       r_shadow_fb_depthtexture = depthtexture;
+       r_shadow_fb_colortexture = colortexture;
+
        r_shadow_usingshadowmaportho = false;
 
        switch (vid.renderpath)
@@ -4578,7 +4956,7 @@ void R_Shadow_PrepareLights(void)
        case RENDERPATH_D3D10:
        case RENDERPATH_D3D11:
        case RENDERPATH_SOFT:
-       case RENDERPATH_GLES2:
+#ifndef USE_GLES2
                if (!r_shadow_deferred.integer || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_STENCIL || !vid.support.ext_framebuffer_object || vid.maxdrawbuffers < 2)
                {
                        r_shadow_usingdeferredprepass = false;
@@ -4595,77 +4973,36 @@ void R_Shadow_PrepareLights(void)
                        r_shadow_usingdeferredprepass = true;
                        r_shadow_prepass_width = vid.width;
                        r_shadow_prepass_height = vid.height;
-                       r_shadow_prepassgeometrydepthtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "prepassgeometrydepthmap", vid.width, vid.height, 24, false);
-                       switch (vid.renderpath)
-                       {
-                       case RENDERPATH_D3D9:
-                               r_shadow_prepassgeometrydepthcolortexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrydepthcolormap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
-                               break;
-                       default:
-                               break;
-                       }
-                       r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
-                       r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
-                       r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
+                       r_shadow_prepassgeometrydepthbuffer = R_LoadTextureRenderBuffer(r_shadow_texturepool, "prepassgeometrydepthbuffer", vid.width, vid.height, TEXTYPE_DEPTHBUFFER24);
+                       r_shadow_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER32F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
+                       r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER16F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
+                       r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_COLORBUFFER16F, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
 
                        // set up the geometry pass fbo (depth + normalmap)
-                       r_shadow_prepassgeometryfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepassgeometrynormalmaptexture, NULL, NULL, NULL);
-                       R_Mesh_SetRenderTargets(r_shadow_prepassgeometryfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepassgeometrynormalmaptexture, r_shadow_prepassgeometrydepthcolortexture, NULL, NULL);
-                       // render depth into one texture and normalmap into the other
-                       if (qglDrawBuffersARB)
-                       {
-                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
-                               qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
-                               {
-                                       Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
-                                       Cvar_SetValueQuick(&r_shadow_deferred, 0);
-                                       r_shadow_usingdeferredprepass = false;
-                               }
-                       }
+                       r_shadow_prepassgeometryfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthbuffer, r_shadow_prepassgeometrynormalmaptexture, NULL, NULL, NULL);
+                       R_Mesh_SetRenderTargets(r_shadow_prepassgeometryfbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepassgeometrynormalmaptexture, NULL, NULL, NULL);
+                       // render depth into a renderbuffer and other important properties into the normalmap texture
 
                        // set up the lighting pass fbo (diffuse + specular)
-                       r_shadow_prepasslightingdiffusespecularfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
-                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+                       r_shadow_prepasslightingdiffusespecularfbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusespecularfbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
                        // render diffuse into one texture and specular into another,
                        // with depth and normalmap bound as textures,
                        // with depth bound as attachment as well
-                       if (qglDrawBuffersARB)
-                       {
-                               qglDrawBuffersARB(2, r_shadow_prepasslightingdrawbuffers);CHECKGLERROR
-                               qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
-                               {
-                                       Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
-                                       Cvar_SetValueQuick(&r_shadow_deferred, 0);
-                                       r_shadow_usingdeferredprepass = false;
-                               }
-                       }
 
                        // set up the lighting pass fbo (diffuse)
-                       r_shadow_prepasslightingdiffusefbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
-                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
+                       r_shadow_prepasslightingdiffusefbo = R_Mesh_CreateFramebufferObject(r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
+                       R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthbuffer, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
                        // render diffuse into one texture,
                        // with depth and normalmap bound as textures,
                        // with depth bound as attachment as well
-                       if (qglDrawBuffersARB)
-                       {
-                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
-                               qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
-                               {
-                                       Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
-                                       Cvar_SetValueQuick(&r_shadow_deferred, 0);
-                                       r_shadow_usingdeferredprepass = false;
-                               }
-                       }
                }
+#endif
                break;
-       case RENDERPATH_GL13:
        case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GLES1:
+       case RENDERPATH_GLES2:
                r_shadow_usingdeferredprepass = false;
                break;
        }
@@ -4677,7 +5014,7 @@ void R_Shadow_PrepareLights(void)
        {
                lightindex = r_shadow_debuglight.integer;
                light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-               if (light && (light->flags & flag))
+               if (light)
                        R_Shadow_PrepareLight(&light->rtlight);
        }
        else
@@ -4700,15 +5037,13 @@ void R_Shadow_PrepareLights(void)
                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);
                }
        }
 
        if (r_editlights.integer)
                R_Shadow_DrawLightSprites();
-
-       R_Shadow_UpdateBounceGridTexture();
 }
 
 void R_Shadow_DrawLights(void)
@@ -4726,7 +5061,7 @@ void R_Shadow_DrawLights(void)
        {
                lightindex = r_shadow_debuglight.integer;
                light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-               if (light && (light->flags & flag))
+               if (light)
                        R_Shadow_DrawLight(&light->rtlight);
        }
        else
@@ -4746,26 +5081,19 @@ void R_Shadow_DrawLights(void)
        R_Shadow_RenderMode_End();
 }
 
-extern const float r_screenvertex3f[12];
-extern void R_SetupView(qboolean allowwaterclippingplane);
-extern void R_ResetViewRendering3D(void);
-extern void R_ResetViewRendering2D(void);
-extern cvar_t r_shadows;
-extern cvar_t r_shadows_darken;
-extern cvar_t r_shadows_drawafterrtlighting;
-extern cvar_t r_shadows_castfrombmodels;
-extern cvar_t r_shadows_throwdistance;
-extern cvar_t r_shadows_throwdirection;
-extern cvar_t r_shadows_focus;
-extern cvar_t r_shadows_shadowmapscale;
+#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;
        float scale, size, radius, dot1, dot2;
+       prvm_vec3_t prvmshadowdir, prvmshadowfocus;
        vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus, shadowmins, shadowmaxs;
        entity_render_t *ent;
 
+       r_shadow_nummodelshadows = 0;
        if (!r_refdef.scene.numentities)
                return;
 
@@ -4776,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:
@@ -4791,7 +5126,8 @@ void R_Shadow_PrepareModelShadows(void)
        scale = r_shadow_shadowmapping_precision.value * r_shadows_shadowmapscale.value;
        radius = 0.5f * size / scale;
 
-       Math_atov(r_shadows_throwdirection.string, shadowdir);
+       Math_atov(r_shadows_throwdirection.string, prvmshadowdir);
+       VectorCopy(prvmshadowdir, shadowdir);
        VectorNormalize(shadowdir);
        dot1 = DotProduct(r_refdef.view.forward, shadowdir);
        dot2 = DotProduct(r_refdef.view.up, shadowdir);
@@ -4801,7 +5137,8 @@ void R_Shadow_PrepareModelShadows(void)
                VectorMA(r_refdef.view.up, -dot2, shadowdir, shadowforward);
        VectorNormalize(shadowforward);
        CrossProduct(shadowdir, shadowforward, shadowright);
-       Math_atov(r_shadows_focus.string, shadowfocus);
+       Math_atov(r_shadows_focus.string, prvmshadowfocus);
+       VectorCopy(prvmshadowfocus, shadowfocus);
        VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin);
        VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin);
        VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin);
@@ -4823,12 +5160,17 @@ 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);
+               }
        }
 }
 
-void R_DrawModelShadowMaps(void)
+void R_DrawModelShadowMaps(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        int i;
        float relativethrowdistance, scale, size, radius, nearclip, farclip, bias, dot1, dot2;
@@ -4837,13 +5179,14 @@ void R_DrawModelShadowMaps(void)
        vec3_t relativelightdirection, relativeforward, relativeright;
        vec3_t relativeshadowmins, relativeshadowmaxs;
        vec3_t shadowdir, shadowforward, shadowright, shadoworigin, shadowfocus;
+       prvm_vec3_t prvmshadowdir, prvmshadowfocus;
        float m[12];
        matrix4x4_t shadowmatrix, cameramatrix, mvpmatrix, invmvpmatrix, scalematrix, texmatrix;
        r_viewport_t viewport;
-       GLuint fbo = 0;
+       GLuint shadowfbo = 0;
        float clearcolor[4];
 
-       if (!r_refdef.scene.numentities)
+       if (!r_shadow_nummodelshadows)
                return;
 
        switch (r_shadow_shadowmode)
@@ -4854,18 +5197,22 @@ void R_DrawModelShadowMaps(void)
                return;
        }
 
-       R_ResetViewRendering3D();
+       r_shadow_fb_fbo = fbo;
+       r_shadow_fb_depthtexture = depthtexture;
+       r_shadow_fb_colortexture = colortexture;
+
+       R_ResetViewRendering3D(fbo, depthtexture, colortexture);
        R_Shadow_RenderMode_Begin();
        R_Shadow_RenderMode_ActiveLight(NULL);
 
        switch (r_shadow_shadowmode)
        {
        case R_SHADOW_SHADOWMODE_SHADOWMAP2D:
-               if (!r_shadow_shadowmap2dtexture)
+               if (!r_shadow_shadowmap2ddepthtexture)
                        R_Shadow_MakeShadowMap(0, r_shadow_shadowmapmaxsize);
-               fbo = r_shadow_fbo2d;
-               r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2dtexture);
-               r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2dtexture);
+               shadowfbo = r_shadow_fbo2d;
+               r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2ddepthtexture);
+               r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2ddepthtexture);
                r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAP2D;
                break;
        default:
@@ -4877,16 +5224,18 @@ void R_DrawModelShadowMaps(void)
        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;
        r_shadow_shadowmap_parameters[2] = 1.0;
        r_shadow_shadowmap_parameters[3] = bound(0.0f, 1.0f - r_shadows_darken.value, 1.0f);
 
-       Math_atov(r_shadows_throwdirection.string, shadowdir);
+       Math_atov(r_shadows_throwdirection.string, prvmshadowdir);
+       VectorCopy(prvmshadowdir, shadowdir);
        VectorNormalize(shadowdir);
-       Math_atov(r_shadows_focus.string, shadowfocus);
+       Math_atov(r_shadows_focus.string, prvmshadowfocus);
+       VectorCopy(prvmshadowfocus, shadowfocus);
        VectorM(shadowfocus[0], r_refdef.view.right, shadoworigin);
        VectorMA(shadoworigin, shadowfocus[1], r_refdef.view.up, shadoworigin);
        VectorMA(shadoworigin, -shadowfocus[2], r_refdef.view.forward, shadoworigin);
@@ -4913,8 +5262,11 @@ void R_DrawModelShadowMaps(void)
 
        VectorMA(shadoworigin, (1.0f - fabs(dot1)) * radius, shadowforward, shadoworigin);
 
-       R_Mesh_SetRenderTargets(fbo, r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
-       R_SetupShader_DepthOrShadow();
+       if (r_shadow_shadowmap2ddepthbuffer)
+               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, 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);
@@ -4923,7 +5275,7 @@ void R_DrawModelShadowMaps(void)
        Vector4Set(clearcolor, 1,1,1,1);
        // in D3D9 we have to render to a color texture shadowmap
        // in GL we render directly to a depth texture only
-       if (r_shadow_shadowmap2dtexture)
+       if (r_shadow_shadowmap2ddepthbuffer)
                GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
        else
                GL_Clear(GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
@@ -4932,36 +5284,23 @@ void R_DrawModelShadowMaps(void)
        // 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_ResetRenderTargets();
-       R_SetupShader_ShowDepth();
-       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
@@ -4992,12 +5331,13 @@ void R_DrawModelShadowMaps(void)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
                break;
        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;
@@ -5022,7 +5362,7 @@ void R_DrawModelShadowMaps(void)
        }
 }
 
-void R_DrawModelShadows(void)
+void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        int i;
        float relativethrowdistance;
@@ -5031,11 +5371,16 @@ void R_DrawModelShadows(void)
        vec3_t relativelightdirection;
        vec3_t relativeshadowmins, relativeshadowmaxs;
        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_ResetViewRendering3D();
+       r_shadow_fb_fbo = fbo;
+       r_shadow_fb_depthtexture = depthtexture;
+       r_shadow_fb_colortexture = colortexture;
+
+       R_ResetViewRendering3D(fbo, depthtexture, colortexture);
        //GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
        //GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
        R_Shadow_RenderMode_Begin();
@@ -5049,66 +5394,64 @@ void R_DrawModelShadows(void)
        // get shadow dir
        if (r_shadows.integer == 2)
        {
-               Math_atov(r_shadows_throwdirection.string, shadowdir);
+               Math_atov(r_shadows_throwdirection.string, prvmshadowdir);
+               VectorCopy(prvmshadowdir, shadowdir);
                VectorNormalize(shadowdir);
        }
 
        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
@@ -5120,7 +5463,7 @@ void R_DrawModelShadows(void)
        //GL_ScissorTest(true);
        //R_EntityMatrix(&identitymatrix);
        //R_Mesh_ResetTextureState();
-       R_ResetViewRendering2D();
+       R_ResetViewRendering2D(fbo, depthtexture, colortexture);
 
        // set up a darkening blend on shadowed areas
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -5135,7 +5478,7 @@ void R_DrawModelShadows(void)
 
        // apply the blend to the shadowed areas
        R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic_NoTexture(false, true);
        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 
        // restore the viewport
@@ -5145,11 +5488,13 @@ void R_DrawModelShadows(void)
        //R_Shadow_RenderMode_End();
 }
 
-void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
+static void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
 {
        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;
@@ -5165,25 +5510,28 @@ void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
 
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL20:
-               case RENDERPATH_GL13:
                case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
+#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
+#endif
                        break;
                case RENDERPATH_D3D9:
                        Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
@@ -5204,47 +5552,71 @@ void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
 
 static float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
 
-void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
+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)
        {
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL20:
-               case RENDERPATH_GL13:
                case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
+#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
                        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);
@@ -5255,59 +5627,13 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                if(negated)
                {
                        VectorNegate(color, color);
-                       switch(vid.renderpath)
-                       {
-                       case RENDERPATH_GL11:
-                       case RENDERPATH_GL13:
-                       case RENDERPATH_GL20:
-                       case RENDERPATH_GLES2:
-                               qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
-                               break;
-                       case RENDERPATH_D3D9:
-#ifdef SUPPORTD3D
-                               IDirect3DDevice9_SetRenderState(vid_d3d9dev, D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT);
-#endif
-                               break;
-                       case RENDERPATH_D3D10:
-                               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                               break;
-                       case RENDERPATH_D3D11:
-                               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                               break;
-                       case RENDERPATH_SOFT:
-                               DPSOFTRAST_BlendSubtract(true);
-                               break;
-                       }
+                       GL_BlendEquationSubtract(true);
                }
                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)
-               {
-                       switch(vid.renderpath)
-                       {
-                       case RENDERPATH_GL11:
-                       case RENDERPATH_GL13:
-                       case RENDERPATH_GL20:
-                       case RENDERPATH_GLES2:
-                               qglBlendEquationEXT(GL_FUNC_ADD_EXT);
-                               break;
-                       case RENDERPATH_D3D9:
-#ifdef SUPPORTD3D
-                               IDirect3DDevice9_SetRenderState(vid_d3d9dev, D3DRS_BLENDOP, D3DBLENDOP_ADD);
-#endif
-                               break;
-                       case RENDERPATH_D3D10:
-                               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                               break;
-                       case RENDERPATH_D3D11:
-                               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
-                               break;
-                       case RENDERPATH_SOFT:
-                               DPSOFTRAST_BlendSubtract(false);
-                               break;
-                       }
-               }
+                       GL_BlendEquationSubtract(false);
        }
 }
 
@@ -5321,7 +5647,7 @@ void R_Shadow_DrawCoronas(void)
        size_t range;
        if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
                return;
-       if (r_waterstate.renderingscene)
+       if (r_fb.water.renderingscene)
                return;
        flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
        R_EntityMatrix(&identitymatrix);
@@ -5337,16 +5663,18 @@ void R_Shadow_DrawCoronas(void)
        case RENDERPATH_GL11:
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
                usequery = vid.support.arb_occlusion_query && r_coronas_occlusionquery.integer;
+#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);
@@ -5360,8 +5688,9 @@ void R_Shadow_DrawCoronas(void)
                        GL_PolygonOffset(0, 0);
                        GL_DepthTest(true);
                        R_Mesh_ResetTextureState();
-                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+                       R_SetupShader_Generic_NoTexture(false, false);
                }
+#endif
                break;
        case RENDERPATH_D3D9:
                usequery = false;
@@ -5435,20 +5764,18 @@ void R_Shadow_DrawCoronas(void)
 
 
 
-dlight_t *R_Shadow_NewWorldLight(void)
+static dlight_t *R_Shadow_NewWorldLight(void)
 {
        return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
 }
 
-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)
+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 = "";
 
@@ -5481,7 +5808,7 @@ void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, ve
        R_RTLight_Update(&light->rtlight, true, &matrix, light->color, light->style, light->cubemapname[0] ? light->cubemapname : NULL, light->shadow, light->corona, light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
 }
 
-void R_Shadow_FreeWorldLight(dlight_t *light)
+static void R_Shadow_FreeWorldLight(dlight_t *light)
 {
        if (r_shadow_selectedlight == light)
                r_shadow_selectedlight = NULL;
@@ -5503,7 +5830,7 @@ void R_Shadow_ClearWorldLights(void)
        r_shadow_selectedlight = NULL;
 }
 
-void R_Shadow_SelectLight(dlight_t *light)
+static void R_Shadow_SelectLight(dlight_t *light)
 {
        if (r_shadow_selectedlight)
                r_shadow_selectedlight->selected = false;
@@ -5512,7 +5839,7 @@ void R_Shadow_SelectLight(dlight_t *light)
                r_shadow_selectedlight->selected = true;
 }
 
-void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+static void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
        // this is never batched (there can be only one)
        float vertex3f[12];
@@ -5521,7 +5848,7 @@ void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const r
        R_DrawCustomSurface(r_editlights_sprcursor, &identitymatrix, MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
 }
 
-void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+static void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
        float intensity;
        float s;
@@ -5574,10 +5901,10 @@ void R_Shadow_DrawLightSprites(void)
        {
                light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
                if (light)
-                       R_MeshQueue_AddTransparent(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(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)
@@ -5585,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);
@@ -5600,7 +5927,7 @@ int R_Shadow_GetRTLightInfo(unsigned int lightindex, float *origin, float *radiu
        return 1;
 }
 
-void R_Shadow_SelectLightInView(void)
+static void R_Shadow_SelectLightInView(void)
 {
        float bestrating, rating, temp[3];
        dlight_t *best;
@@ -5622,7 +5949,7 @@ 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;
@@ -5650,8 +5977,8 @@ void R_Shadow_LoadWorldLights(void)
                n = 0;
                while (*s)
                {
-                       t = s;
                        /*
+                       t = s;
                        shadow = true;
                        for (;COM_Parse(t, true) && strcmp(
                        if (COM_Parse(t, true))
@@ -5855,6 +6182,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
        const char *data;
        float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
        char key[256], value[MAX_INPUTLINE];
+       char vabuf[1024];
 
        if (cl.worldmodel == NULL)
        {
@@ -5869,7 +6197,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                data = cl.worldmodel->brush.entities;
        if (!data)
                return;
-       for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
+       for (entnum = 0;COM_ParseToken_Simple(&data, false, false, true) && com_token[0] == '{';entnum++)
        {
                type = LIGHTTYPE_MINUSX;
                origin[0] = origin[1] = origin[2] = 0;
@@ -5887,7 +6215,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                islight = false;
                while (1)
                {
-                       if (!COM_ParseToken_Simple(&data, false, false))
+                       if (!COM_ParseToken_Simple(&data, false, false, true))
                                break; // error
                        if (com_token[0] == '}')
                                break; // end of entity
@@ -5897,7 +6225,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
-                       if (!COM_ParseToken_Simple(&data, false, false))
+                       if (!COM_ParseToken_Simple(&data, false, false, true))
                                break; // error
                        strlcpy(value, com_token, sizeof(value));
 
@@ -6058,20 +6386,20 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                }
                VectorAdd(origin, originhack, origin);
                if (radius >= 1)
-                       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
+                       R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va(vabuf, sizeof(vabuf), "cubemaps/%i", skin) : NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
        }
        if (entfiledata)
                Mem_Free(entfiledata);
 }
 
 
-void R_Shadow_SetCursorLocationForView(void)
+static void R_Shadow_SetCursorLocationForView(void)
 {
        vec_t dist, push;
        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;
@@ -6102,7 +6430,7 @@ void R_Shadow_UpdateWorldLightSelection(void)
                R_Shadow_SelectLight(NULL);
 }
 
-void R_Shadow_EditLights_Clear_f(void)
+static void R_Shadow_EditLights_Clear_f(void)
 {
        R_Shadow_ClearWorldLights();
 }
@@ -6113,35 +6441,39 @@ 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();
        }
 }
 
-void R_Shadow_EditLights_Save_f(void)
+static void R_Shadow_EditLights_Save_f(void)
 {
        if (!cl.worldmodel)
                return;
        R_Shadow_SaveWorldLights();
 }
 
-void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
+static void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
 {
        R_Shadow_ClearWorldLights();
        R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
 }
 
-void R_Shadow_EditLights_ImportLightsFile_f(void)
+static void R_Shadow_EditLights_ImportLightsFile_f(void)
 {
        R_Shadow_ClearWorldLights();
        R_Shadow_LoadLightsFile();
 }
 
-void R_Shadow_EditLights_Spawn_f(void)
+static void R_Shadow_EditLights_Spawn_f(void)
 {
        vec3_t color;
        if (!r_editlights.integer)
@@ -6158,7 +6490,7 @@ void R_Shadow_EditLights_Spawn_f(void)
        R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
 }
 
-void R_Shadow_EditLights_Edit_f(void)
+static void R_Shadow_EditLights_Edit_f(void)
 {
        vec3_t origin, angles, color;
        vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
@@ -6484,7 +6816,7 @@ void R_Shadow_EditLights_Edit_f(void)
        R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
 }
 
-void R_Shadow_EditLights_EditAll_f(void)
+static void R_Shadow_EditLights_EditAll_f(void)
 {
        size_t lightindex;
        dlight_t *light, *oldselected;
@@ -6516,10 +6848,37 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void)
        int lightnumber, lightcount;
        size_t lightindex, range;
        dlight_t *light;
-       float x, y;
        char temp[256];
+       float x, y;
+
        if (!r_editlights.integer)
                return;
+
+       // update cvars so QC can query them
+       if (r_shadow_selectedlight)
+       {
+               dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
+               Cvar_SetQuick(&r_editlights_current_origin, temp);
+               dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
+               Cvar_SetQuick(&r_editlights_current_angles, temp);
+               dpsnprintf(temp, sizeof(temp), "%f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
+               Cvar_SetQuick(&r_editlights_current_color, temp);
+               Cvar_SetValueQuick(&r_editlights_current_radius, r_shadow_selectedlight->radius);
+               Cvar_SetValueQuick(&r_editlights_current_corona, r_shadow_selectedlight->corona);
+               Cvar_SetValueQuick(&r_editlights_current_coronasize, r_shadow_selectedlight->coronasizescale);
+               Cvar_SetValueQuick(&r_editlights_current_style, r_shadow_selectedlight->style);
+               Cvar_SetValueQuick(&r_editlights_current_shadows, r_shadow_selectedlight->shadow);
+               Cvar_SetQuick(&r_editlights_current_cubemap, r_shadow_selectedlight->cubemapname);
+               Cvar_SetValueQuick(&r_editlights_current_ambient, r_shadow_selectedlight->ambientscale);
+               Cvar_SetValueQuick(&r_editlights_current_diffuse, r_shadow_selectedlight->diffusescale);
+               Cvar_SetValueQuick(&r_editlights_current_specular, r_shadow_selectedlight->specularscale);
+               Cvar_SetValueQuick(&r_editlights_current_normalmode, (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? 1 : 0);
+               Cvar_SetValueQuick(&r_editlights_current_realtimemode, (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? 1 : 0);
+       }
+
+       // draw properties on screen
+       if (!r_editlights_drawproperties.integer)
+               return;
        x = vid_conwidth.value - 240;
        y = 5;
        DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
@@ -6532,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;
@@ -6557,7 +6916,7 @@ void R_Shadow_EditLights_DrawSelectedLightProperties(void)
        dpsnprintf(temp, sizeof(temp), "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true, FONT_DEFAULT);y += 8;
 }
 
-void R_Shadow_EditLights_ToggleShadow_f(void)
+static void R_Shadow_EditLights_ToggleShadow_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6572,7 +6931,7 @@ void R_Shadow_EditLights_ToggleShadow_f(void)
        R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
 }
 
-void R_Shadow_EditLights_ToggleCorona_f(void)
+static void R_Shadow_EditLights_ToggleCorona_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6587,7 +6946,7 @@ void R_Shadow_EditLights_ToggleCorona_f(void)
        R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
 }
 
-void R_Shadow_EditLights_Remove_f(void)
+static void R_Shadow_EditLights_Remove_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6603,7 +6962,7 @@ void R_Shadow_EditLights_Remove_f(void)
        r_shadow_selectedlight = NULL;
 }
 
-void R_Shadow_EditLights_Help_f(void)
+static void R_Shadow_EditLights_Help_f(void)
 {
        Con_Print(
 "Documentation on r_editlights system:\n"
@@ -6647,7 +7006,7 @@ void R_Shadow_EditLights_Help_f(void)
 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
 "originscale x y z : multiply origin of light (1 1 1 does nothing)\n"
 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
-"cubemap basename : set filter cubemap of light (not yet supported)\n"
+"cubemap basename : set filter cubemap of light\n"
 "shadows 1/0 : turn on/off shadows\n"
 "corona n : set corona intensity\n"
 "coronasize n : set corona size (0-1)\n"
@@ -6660,7 +7019,7 @@ void R_Shadow_EditLights_Help_f(void)
        );
 }
 
-void R_Shadow_EditLights_CopyInfo_f(void)
+static void R_Shadow_EditLights_CopyInfo_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6689,7 +7048,7 @@ void R_Shadow_EditLights_CopyInfo_f(void)
        r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
 }
 
-void R_Shadow_EditLights_PasteInfo_f(void)
+static void R_Shadow_EditLights_PasteInfo_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6704,7 +7063,7 @@ void R_Shadow_EditLights_PasteInfo_f(void)
        R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_bufferlight.angles, r_shadow_bufferlight.color, r_shadow_bufferlight.radius, r_shadow_bufferlight.corona, r_shadow_bufferlight.style, r_shadow_bufferlight.shadow, r_shadow_bufferlight.cubemapname, r_shadow_bufferlight.coronasizescale, r_shadow_bufferlight.ambientscale, r_shadow_bufferlight.diffusescale, r_shadow_bufferlight.specularscale, r_shadow_bufferlight.flags);
 }
 
-void R_Shadow_EditLights_Lock_f(void)
+static void R_Shadow_EditLights_Lock_f(void)
 {
        if (!r_editlights.integer)
        {
@@ -6724,7 +7083,7 @@ void R_Shadow_EditLights_Lock_f(void)
        r_editlights_lockcursor = true;
 }
 
-void R_Shadow_EditLights_Init(void)
+static void R_Shadow_EditLights_Init(void)
 {
        Cvar_RegisterVariable(&r_editlights);
        Cvar_RegisterVariable(&r_editlights_cursordistance);
@@ -6732,6 +7091,21 @@ void R_Shadow_EditLights_Init(void)
        Cvar_RegisterVariable(&r_editlights_cursorpushoff);
        Cvar_RegisterVariable(&r_editlights_cursorgrid);
        Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
+       Cvar_RegisterVariable(&r_editlights_drawproperties);
+       Cvar_RegisterVariable(&r_editlights_current_origin);
+       Cvar_RegisterVariable(&r_editlights_current_angles);
+       Cvar_RegisterVariable(&r_editlights_current_color);
+       Cvar_RegisterVariable(&r_editlights_current_radius);
+       Cvar_RegisterVariable(&r_editlights_current_corona);
+       Cvar_RegisterVariable(&r_editlights_current_coronasize);
+       Cvar_RegisterVariable(&r_editlights_current_style);
+       Cvar_RegisterVariable(&r_editlights_current_shadows);
+       Cvar_RegisterVariable(&r_editlights_current_cubemap);
+       Cvar_RegisterVariable(&r_editlights_current_ambient);
+       Cvar_RegisterVariable(&r_editlights_current_diffuse);
+       Cvar_RegisterVariable(&r_editlights_current_specular);
+       Cvar_RegisterVariable(&r_editlights_current_normalmode);
+       Cvar_RegisterVariable(&r_editlights_current_realtimemode);
        Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
        Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
        Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f, "reloads rtlights file (or imports from .lights file or .ent file or the map itself)");
@@ -6759,7 +7133,7 @@ LIGHT SAMPLING
 =============================================================================
 */
 
-void R_LightPoint(vec3_t color, const vec3_t p, const int flags)
+void R_LightPoint(float *color, const vec3_t p, const int flags)
 {
        int i, numlights, flag;
        float f, relativepoint[3], dist, dist2, lightradius2;
@@ -6767,30 +7141,33 @@ void R_LightPoint(vec3_t color, const vec3_t p, const int flags)
        rtlight_t *light;
        dlight_t *dlight;
 
-       VectorClear(color);
-
        if (r_fullbright.integer)
        {
                VectorSet(color, 1, 1, 1);
                return;
        }
 
+       VectorClear(color);
+
        if (flags & LP_LIGHTMAP)
        {
-               if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+               if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->lit && r_refdef.scene.worldmodel->brush.LightPoint)
                {
+                       VectorClear(diffuse);
                        r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, color, diffuse, n);
-                       color[0] += r_refdef.scene.ambient + diffuse[0];
-                       color[1] += r_refdef.scene.ambient + diffuse[1];
-                       color[2] += r_refdef.scene.ambient + diffuse[2];
+                       VectorAdd(color, diffuse, color);
                }
                else
                        VectorSet(color, 1, 1, 1);
+               color[0] += r_refdef.scene.ambient;
+               color[1] += r_refdef.scene.ambient;
+               color[2] += r_refdef.scene.ambient;
        }
+
        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);
@@ -6810,7 +7187,7 @@ void R_LightPoint(vec3_t 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);
                }
        }
@@ -6831,7 +7208,7 @@ void R_LightPoint(vec3_t 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);
                }
        }
@@ -6864,15 +7241,17 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const
                VectorSet(ambient, r_refdef.scene.ambient, r_refdef.scene.ambient, r_refdef.scene.ambient);
                VectorClear(diffuse);
                VectorClear(lightdir);
-               if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+               if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->lit && r_refdef.scene.worldmodel->brush.LightPoint)
                        r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambient, diffuse, lightdir);
+               else
+                       VectorSet(ambient, 1, 1, 1);
                return;
        }
 
        memset(sample, 0, sizeof(sample));
        VectorSet(sample, r_refdef.scene.ambient, r_refdef.scene.ambient, r_refdef.scene.ambient);
 
-       if ((flags & LP_LIGHTMAP) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+       if ((flags & LP_LIGHTMAP) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->lit && r_refdef.scene.worldmodel->brush.LightPoint)
        {
                vec3_t tempambient;
                VectorClear(tempambient);
@@ -6894,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);
@@ -6913,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;
@@ -6927,6 +7306,7 @@ void R_CompleteLightPoint(vec3_t ambient, vec3_t diffuse, vec3_t lightdir, const
                        intensity *= VectorLength(color);
                        VectorMA(sample + 12, intensity, relativepoint, sample + 12);
                }
+               // FIXME: sample bouncegrid too!
        }
 
        if (flags & LP_DYNLIGHT)
@@ -6945,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;