]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
reworked collision cache to only be used by bouncegrid and only in
[xonotic/darkplaces.git] / r_shadow.c
index bef3f2c339f97e8a3784380d5e697770b99a6554..f48621e52d1cc7a707729f48c4a7a8640f6cb3cc 100644 (file)
@@ -261,13 +261,20 @@ 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];
 
 // 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};
+#ifndef USE_GLES2
+static const GLenum r_shadow_prepasslightingdrawbuffers[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
+#endif
 
 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"};
@@ -321,22 +328,23 @@ cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to
 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_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_directionalshading = {CVAR_SAVE, "r_shadow_bouncegrid_directionalshading", "0", "use diffuse shading rather than ambient, 3D texture becomes 8x as many pixels to hold the additional data"};
 cvar_t r_shadow_bouncegrid_dlightparticlemultiplier = {CVAR_SAVE, "r_shadow_bouncegrid_dlightparticlemultiplier", "0", "if set to a high value like 16 this can make dlights look great, but 0 is recommended for performance reasons"};
 cvar_t r_shadow_bouncegrid_hitmodels = {CVAR_SAVE, "r_shadow_bouncegrid_hitmodels", "0", "enables hitting character model geometry (SLOW)"};
+cvar_t r_shadow_bouncegrid_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_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_lightradiusscale", "4", "particles stop at this fraction of light radius (can be more than 1)"};
+cvar_t r_shadow_bouncegrid_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_maxbounce", "2", "maximum number of bounces for a particle (minimum is 0)"};
+cvar_t r_shadow_bouncegrid_particlebounceintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particlebounceintensity", "1", "amount of energy carried over after each bounce, this is a multiplier of texture color and the result is clamped to 1 or less, to prevent adding energy on each bounce"};
+cvar_t r_shadow_bouncegrid_particleintensity = {CVAR_SAVE, "r_shadow_bouncegrid_particleintensity", "1", "brightness of particles contributing to bouncegrid texture"};
 cvar_t r_shadow_bouncegrid_photons = {CVAR_SAVE, "r_shadow_bouncegrid_photons", "2000", "total photons to shoot per update, divided proportionately between lights"};
-cvar_t r_shadow_bouncegrid_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_spacing = {CVAR_SAVE, "r_shadow_bouncegrid_spacing", "64", "unit size of bouncegrid pixel"};
+cvar_t r_shadow_bouncegrid_stablerandom = {CVAR_SAVE, "r_shadow_bouncegrid_stablerandom", "1", "make particle distribution consistent from frame to frame"};
 cvar_t r_shadow_bouncegrid_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_static_lightradiusscale = {CVAR_SAVE, "r_shadow_bouncegrid_static_lightradiusscale", "10", "particles stop at this fraction of light radius (can be more than 1) when in static mode"};
+cvar_t r_shadow_bouncegrid_static_maxbounce = {CVAR_SAVE, "r_shadow_bouncegrid_static_maxbounce", "5", "maximum number of bounces for a particle (minimum is 0) in static mode"};
+cvar_t r_shadow_bouncegrid_static_photons = {CVAR_SAVE, "r_shadow_bouncegrid_static_photons", "25000", "photons value to use when in static mode"};
 cvar_t r_shadow_bouncegrid_updateinterval = {CVAR_SAVE, "r_shadow_bouncegrid_updateinterval", "0", "update bouncegrid texture once per this many seconds, useful values are 0, 0.05, or 1000000"};
 cvar_t r_shadow_bouncegrid_x = {CVAR_SAVE, "r_shadow_bouncegrid_x", "64", "maximum texture size of bouncegrid on X axis"};
 cvar_t r_shadow_bouncegrid_y = {CVAR_SAVE, "r_shadow_bouncegrid_y", "64", "maximum texture size of bouncegrid on Y axis"};
@@ -359,6 +367,7 @@ typedef struct r_shadow_bouncegrid_settings_s
        qboolean staticmode;
        qboolean bounceanglediffuse;
        qboolean directionalshading;
+       qboolean includedirectlighting;
        float dlightparticlemultiplier;
        qboolean hitmodels;
        float lightradiusscale;
@@ -480,10 +489,9 @@ 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;
                }
@@ -742,19 +750,20 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_bouncegrid_directionalshading);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_dlightparticlemultiplier);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_hitmodels);
+       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_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_spacing);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_photons);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_static_directionalshading);
-       Cvar_RegisterVariable(&r_shadow_bouncegrid_stablerandom);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_lightradiusscale);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_maxbounce);
+       Cvar_RegisterVariable(&r_shadow_bouncegrid_static_photons);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_updateinterval);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_x);
        Cvar_RegisterVariable(&r_shadow_bouncegrid_y);
@@ -1753,7 +1762,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 +1959,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 +1988,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 +2020,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 +2035,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);
@@ -2047,7 +2057,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);
        r_shadow_rendermode = mode;
        switch(mode)
        {
@@ -2109,6 +2119,7 @@ static void R_Shadow_MakeShadowMap(int side, int size)
                return;
        }
 
+#ifndef USE_GLES2
        // render depth into the fbo, do not render color at all
        // validate the fbo now
        if (qglDrawBuffer)
@@ -2116,14 +2127,15 @@ static void R_Shadow_MakeShadowMap(int side, int size)
                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))
+               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER);CHECKGLERROR
+               if (status != GL_FRAMEBUFFER_COMPLETE && (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);
                }
        }
+#endif
 }
 
 void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
@@ -2131,7 +2143,7 @@ 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;
@@ -2151,21 +2163,15 @@ void R_Shadow_RenderMode_ShadowMap(int side, int clear, int size)
                R_Shadow_MakeVSDCT();
        if (!r_shadow_shadowmap2dtexture)
                R_Shadow_MakeShadowMap(side, r_shadow_shadowmapmaxsize);
-       if (r_shadow_shadowmap2dtexture) fbo = r_shadow_fbo2d;
+       if (r_shadow_shadowmap2dtexture) fbo2d = 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);
        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();
-       }
-       else
-               R_SetupShader_ShowDepth();
+       R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
+       R_SetupShader_DepthOrShadow(true);
        GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value);
        GL_DepthMask(true);
        GL_DepthTest(true);
@@ -2181,6 +2187,7 @@ init_done:
        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
@@ -2227,7 +2234,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 +2293,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_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, r_shadow_prepasslightingspeculartexture, NULL, NULL);
+       else
+               R_Mesh_SetRenderTargets(r_shadow_prepasslightingdiffusefbo, r_shadow_prepassgeometrydepthtexture, r_shadow_prepasslightingdiffusetexture, NULL, NULL, NULL);
 
        r_shadow_usingshadowmap2d = shadowmapping;
 
@@ -2306,7 +2315,7 @@ void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadow
        R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
-static void R_Shadow_UpdateBounceGridTexture(void)
+void R_Shadow_UpdateBounceGridTexture(void)
 {
 #define MAXBOUNCEGRIDPARTICLESPERLIGHT 1048576
        dlight_t *light;
@@ -2344,6 +2353,9 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        vec3_t size;
        vec3_t spacing;
        vec3_t lightcolor;
+       vec3_t steppos;
+       vec3_t stepdelta;
+       vec3_t cullmins, cullmaxs;
        vec_t radius;
        vec_t s;
        vec_t lightintensity;
@@ -2351,13 +2363,18 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        vec_t photonresidual;
        float m[16];
        float texlerp[2][3];
-       float splatcolor[16];
+       float splatcolor[32];
        float pixelweight[8];
        float w;
        int c[4];
        int pixelindex[8];
        int corner;
-       int x, y, z, d;
+       int pixelsperband;
+       int pixelband;
+       int pixelbands;
+       int numsteps;
+       int step;
+       int x, y, z;
        rtlight_t *rtlight;
        r_shadow_bouncegrid_settings_t settings;
        qboolean enable = r_shadow_bouncegrid.integer != 0 && r_refdef.scene.worldmodel;
@@ -2377,6 +2394,7 @@ static void R_Shadow_UpdateBounceGridTexture(void)
                // 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:
@@ -2387,39 +2405,20 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        r_shadow_bouncegridintensity = r_shadow_bouncegrid_intensity.value;
 
        // 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++)
+               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;
@@ -2446,25 +2445,27 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        }
 
        // build up a complete collection of the desired settings, so that memcmp can be used to compare parameters
+       memset(&settings, 0, sizeof(settings));
        settings.staticmode                    = r_shadow_bouncegrid_static.integer != 0;
        settings.bounceanglediffuse            = r_shadow_bouncegrid_bounceanglediffuse.integer != 0;
        settings.directionalshading            = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_directionalshading.integer != 0 : r_shadow_bouncegrid_directionalshading.integer != 0) && allowdirectionalshading;
        settings.dlightparticlemultiplier      = r_shadow_bouncegrid_dlightparticlemultiplier.value;
        settings.hitmodels                     = r_shadow_bouncegrid_hitmodels.integer != 0;
-       settings.lightradiusscale              = r_shadow_bouncegrid_lightradiusscale.value;
-       settings.maxbounce                     = r_shadow_bouncegrid_maxbounce.integer;
+       settings.includedirectlighting         = r_shadow_bouncegrid_includedirectlighting.integer != 0 || r_shadow_bouncegrid.integer == 2;
+       settings.lightradiusscale              = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_lightradiusscale.value : r_shadow_bouncegrid_lightradiusscale.value);
+       settings.maxbounce                     = (r_shadow_bouncegrid_static.integer != 0 ? r_shadow_bouncegrid_static_maxbounce.integer : r_shadow_bouncegrid_maxbounce.integer);
        settings.particlebounceintensity       = r_shadow_bouncegrid_particlebounceintensity.value;
-       settings.particleintensity             = r_shadow_bouncegrid_particleintensity.value;
+       settings.particleintensity             = r_shadow_bouncegrid_particleintensity.value * 16384.0f * (settings.directionalshading ? 4.0f : 1.0f) / (r_shadow_bouncegrid_spacing.value * r_shadow_bouncegrid_spacing.value);
        settings.photons                       = r_shadow_bouncegrid_static.integer ? r_shadow_bouncegrid_static_photons.integer : r_shadow_bouncegrid_photons.integer;
-       settings.spacing[0]                    = r_shadow_bouncegrid_spacingx.value;
-       settings.spacing[1]                    = r_shadow_bouncegrid_spacingy.value;
-       settings.spacing[2]                    = r_shadow_bouncegrid_spacingz.value;
+       settings.spacing[0]                    = r_shadow_bouncegrid_spacing.value;
+       settings.spacing[1]                    = r_shadow_bouncegrid_spacing.value;
+       settings.spacing[2]                    = r_shadow_bouncegrid_spacing.value;
        settings.stablerandom                  = r_shadow_bouncegrid_stablerandom.integer;
 
        // 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.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);
@@ -2507,7 +2508,7 @@ 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_x.integer * r_shadow_bouncegrid_y.integer * r_shadow_bouncegrid_z.integer < resolution[0] * resolution[1] * resolution[2]))
        {
                // we know the resolution we want
                c[0] = r_shadow_bouncegrid_x.integer;
@@ -2548,6 +2549,10 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        // store the new settings
        r_shadow_bouncegridsettings = settings;
 
+       pixelbands = settings.directionalshading ? 8 : 1;
+       pixelsperband = resolution[0]*resolution[1]*resolution[2];
+       numpixels = pixelsperband*pixelbands;
+
        // we're going to update the bouncegrid, update the matrix...
        memset(m, 0, sizeof(m));
        m[0] = 1.0f / size[0];
@@ -2557,16 +2562,7 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        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)
        {
@@ -2581,16 +2577,20 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        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]));
+       x = pixelsperband*4;
+       for (pixelband = 0;pixelband < pixelbands;pixelband++)
+       {
+               if (pixelband == 1)
+                       memset(pixels + pixelband * x, 128, x);
+               else
+                       memset(pixels + pixelband * x, 0, x);
+       }
        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;
+               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;// | SUPERCONTENTS_LIQUIDSMASK;
        else
-               hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK;
+               hitsupercontentsmask = SUPERCONTENTS_SOLID;// | SUPERCONTENTS_LIQUIDSMASK;
        maxbounce = settings.maxbounce;
        // clear variables that produce warnings otherwise
        memset(splatcolor, 0, sizeof(splatcolor));
@@ -2601,95 +2601,89 @@ static void R_Shadow_UpdateBounceGridTexture(void)
        photoncount = 0;
        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)
-                               continue;
-                       VectorScale(rtlight->currentcolor, rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale, lightcolor);
+                       rtlight = r_refdef.scene.lights[lightindex - range];
+                       VectorClear(rtlight->photoncolor);
+                       rtlight->photons = 0;
                }
-               if (!VectorLength2(lightcolor))
+               // 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;
+               if (R_CullBox(cullmins, cullmaxs))
                        continue;
+               if (r_refdef.scene.worldmodel
+                && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs
+                && !r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, cullmins, cullmaxs))
+                       continue;
+               w = r_shadow_lightintensityscale.value * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale);
+               if (w * VectorLength2(rtlight->color) == 0.0f)
+                       continue;
+               w *= (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1);
+               VectorScale(rtlight->color, w, rtlight->photoncolor);
+               //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);
+               rtlight->photons = max(0.0f, lightintensity * s * s);
+               photoncount += rtlight->photons;
        }
        photonscaling = (float)settings.photons / max(1, photoncount);
        photonresidual = 0.0f;
        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)
-                               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);
-               }
-               if (!VectorLength2(lightcolor))
+                       rtlight = r_refdef.scene.lights[lightindex - range];
+               // skip a light with no photons
+               if (rtlight->photons == 0.0f)
                        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;
+               // skip a light with no photon color)
+               if (VectorLength2(rtlight->photoncolor) == 0.0f)
+                       continue;
+               photonresidual += rtlight->photons * photonscaling;
                shootparticles = (int)bound(0, photonresidual, MAXBOUNCEGRIDPARTICLESPERLIGHT);
                if (!shootparticles)
                        continue;
                photonresidual -= shootparticles;
+               radius = rtlight->radius * settings.lightradiusscale;
                s = settings.particleintensity / shootparticles;
-               VectorScale(lightcolor, s, baseshotcolor);
-               if (VectorLength2(baseshotcolor) == 0.0f)
-                       break;
+               VectorScale(rtlight->photoncolor, s, baseshotcolor);
                r_refdef.stats.bouncegrid_lights++;
                r_refdef.stats.bouncegrid_particles += shootparticles;
                for (shotparticles = 0;shotparticles < shootparticles;shotparticles++)
@@ -2709,121 +2703,123 @@ static void R_Shadow_UpdateBounceGridTexture(void)
                                //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, 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)
+                                       // 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)
+                               {
+                                       // calculate second order spherical harmonics values (average, slopeX, slopeY, slopeZ)
+                                       // accumulate average shotcolor
+                                       w = VectorLength(shotcolor);
+                                       splatcolor[ 0] = shotcolor[0];
+                                       splatcolor[ 1] = shotcolor[1];
+                                       splatcolor[ 2] = shotcolor[2];
+                                       splatcolor[ 3] = 0.0f;
+                                       if (pixelbands > 1)
                                        {
-                                               // 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++)
+                                               VectorSubtract(clipstart, cliptrace.endpos, clipdiff);
+                                               VectorNormalize(clipdiff);
+                                               // store bentnormal in case the shader has a use for it
+                                               splatcolor[ 4] = clipdiff[0] * w;
+                                               splatcolor[ 5] = clipdiff[1] * w;
+                                               splatcolor[ 6] = clipdiff[2] * w;
+                                               splatcolor[ 7] = w;
+                                               // accumulate directional contributions (+X, +Y, +Z, -X, -Y, -Z)
+                                               splatcolor[ 8] = shotcolor[0] * max(0.0f, clipdiff[0]);
+                                               splatcolor[ 9] = shotcolor[0] * max(0.0f, clipdiff[1]);
+                                               splatcolor[10] = shotcolor[0] * max(0.0f, clipdiff[2]);
+                                               splatcolor[11] = 0.0f;
+                                               splatcolor[12] = shotcolor[1] * max(0.0f, clipdiff[0]);
+                                               splatcolor[13] = shotcolor[1] * max(0.0f, clipdiff[1]);
+                                               splatcolor[14] = shotcolor[1] * max(0.0f, clipdiff[2]);
+                                               splatcolor[15] = 0.0f;
+                                               splatcolor[16] = shotcolor[2] * max(0.0f, clipdiff[0]);
+                                               splatcolor[17] = shotcolor[2] * max(0.0f, clipdiff[1]);
+                                               splatcolor[18] = shotcolor[2] * max(0.0f, clipdiff[2]);
+                                               splatcolor[19] = 0.0f;
+                                               splatcolor[20] = shotcolor[0] * max(0.0f, -clipdiff[0]);
+                                               splatcolor[21] = shotcolor[0] * max(0.0f, -clipdiff[1]);
+                                               splatcolor[22] = shotcolor[0] * max(0.0f, -clipdiff[2]);
+                                               splatcolor[23] = 0.0f;
+                                               splatcolor[24] = shotcolor[1] * max(0.0f, -clipdiff[0]);
+                                               splatcolor[25] = shotcolor[1] * max(0.0f, -clipdiff[1]);
+                                               splatcolor[26] = shotcolor[1] * max(0.0f, -clipdiff[2]);
+                                               splatcolor[27] = 0.0f;
+                                               splatcolor[28] = shotcolor[2] * max(0.0f, -clipdiff[0]);
+                                               splatcolor[29] = shotcolor[2] * max(0.0f, -clipdiff[1]);
+                                               splatcolor[30] = shotcolor[2] * max(0.0f, -clipdiff[2]);
+                                               splatcolor[31] = 0.0f;
+                                       }
+                                       // calculate the number of steps we need to traverse this distance
+                                       VectorSubtract(cliptrace.endpos, clipstart, stepdelta);
+                                       numsteps = (int)(VectorLength(stepdelta) * ispacing[0]);
+                                       numsteps = bound(1, numsteps, 1024);
+                                       w = 1.0f / numsteps;
+                                       VectorScale(stepdelta, w, stepdelta);
+                                       VectorMA(clipstart, 0.5f, stepdelta, steppos);
+                                       for (step = 0;step < numsteps;step++)
+                                       {
+                                               r_refdef.stats.bouncegrid_splats++;
+                                               // figure out which texture pixel this is in
+                                               texlerp[1][0] = ((steppos[0] - mins[0]) * ispacing[0]) - 0.5f;
+                                               texlerp[1][1] = ((steppos[1] - mins[1]) * ispacing[1]) - 0.5f;
+                                               texlerp[1][2] = ((steppos[2] - mins[2]) * ispacing[2]) - 0.5f;
+                                               tex[0] = (int)floor(texlerp[1][0]);
+                                               tex[1] = (int)floor(texlerp[1][1]);
+                                               tex[2] = (int)floor(texlerp[1][2]);
+                                               if (tex[0] >= 1 && tex[1] >= 1 && tex[2] >= 1 && tex[0] < resolution[0] - 2 && tex[1] < resolution[1] - 2 && tex[2] < resolution[2] - 2)
                                                {
-                                                       // 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)
+                                                       // it is within bounds...  do the real work now
+                                                       // calculate the lerp factors
+                                                       texlerp[1][0] -= tex[0];
+                                                       texlerp[1][1] -= tex[1];
+                                                       texlerp[1][2] -= tex[2];
+                                                       texlerp[0][0] = 1.0f - texlerp[1][0];
+                                                       texlerp[0][1] = 1.0f - texlerp[1][1];
+                                                       texlerp[0][2] = 1.0f - texlerp[1][2];
+                                                       // calculate individual pixel indexes and weights
+                                                       pixelindex[0] = (((tex[2]  )*resolution[1]+tex[1]  )*resolution[0]+tex[0]  );pixelweight[0] = (texlerp[0][0]*texlerp[0][1]*texlerp[0][2]);
+                                                       pixelindex[1] = (((tex[2]  )*resolution[1]+tex[1]  )*resolution[0]+tex[0]+1);pixelweight[1] = (texlerp[1][0]*texlerp[0][1]*texlerp[0][2]);
+                                                       pixelindex[2] = (((tex[2]  )*resolution[1]+tex[1]+1)*resolution[0]+tex[0]  );pixelweight[2] = (texlerp[0][0]*texlerp[1][1]*texlerp[0][2]);
+                                                       pixelindex[3] = (((tex[2]  )*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[3] = (texlerp[1][0]*texlerp[1][1]*texlerp[0][2]);
+                                                       pixelindex[4] = (((tex[2]+1)*resolution[1]+tex[1]  )*resolution[0]+tex[0]  );pixelweight[4] = (texlerp[0][0]*texlerp[0][1]*texlerp[1][2]);
+                                                       pixelindex[5] = (((tex[2]+1)*resolution[1]+tex[1]  )*resolution[0]+tex[0]+1);pixelweight[5] = (texlerp[1][0]*texlerp[0][1]*texlerp[1][2]);
+                                                       pixelindex[6] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0]  );pixelweight[6] = (texlerp[0][0]*texlerp[1][1]*texlerp[1][2]);
+                                                       pixelindex[7] = (((tex[2]+1)*resolution[1]+tex[1]+1)*resolution[0]+tex[0]+1);pixelweight[7] = (texlerp[1][0]*texlerp[1][1]*texlerp[1][2]);
+                                                       // update the 8 pixels...
+                                                       for (pixelband = 0;pixelband < pixelbands;pixelband++)
                                                        {
-                                                               // 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;
+                                                               for (corner = 0;corner < 8;corner++)
+                                                               {
+                                                                       // calculate address for pixel
+                                                                       w = pixelweight[corner];
+                                                                       pixel = pixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4;
+                                                                       highpixel = highpixels + 4 * pixelindex[corner] + pixelband * pixelsperband * 4;
+                                                                       // add to the high precision pixel color
+                                                                       highpixel[0] += (splatcolor[pixelband*4+0]*w);
+                                                                       highpixel[1] += (splatcolor[pixelband*4+1]*w);
+                                                                       highpixel[2] += (splatcolor[pixelband*4+2]*w);
+                                                                       highpixel[3] += (splatcolor[pixelband*4+3]*w);
+                                                                       // flag the low precision pixel as needing to be updated
+                                                                       pixel[3] = 255;
+                                                                       // advance to next band of coefficients
+                                                                       //pixel += pixelsperband*4;
+                                                                       //highpixel += pixelsperband*4;
+                                                               }
                                                        }
                                                }
+                                               VectorAdd(steppos, stepdelta, steppos);
                                        }
                                }
+                               if (cliptrace.fraction >= 1.0f)
+                                       break;
+                               r_refdef.stats.bouncegrid_hits++;
                                if (bouncecount >= maxbounce)
                                        break;
                                // scale down shot color by bounce intensity and texture color (or 50% if no texture reported)
@@ -2867,22 +2863,21 @@ 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 (pixelband = 0;pixelband < pixelbands;pixelband++)
        {
                for (z = 1;z < resolution[2]-1;z++)
                {
                        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)
+                               for (x = 1, pixelindex[0] = ((pixelband*resolution[2]+z)*resolution[1]+y)*resolution[0]+x, pixel = pixels + 4*pixelindex[0], highpixel = highpixels + 4*pixelindex[0];x < resolution[0]-1;x++, pixel += 4, highpixel += 4)
                                {
                                        // only convert pixels that were hit by photons
                                        if (pixel[3] == 255)
                                        {
                                                // normalize the bentnormal...
-                                               if (settings.directionalshading)
+                                               if (pixelband == 1)
                                                {
-                                                       if (d == 3)
-                                                               VectorNormalize(highpixel);
+                                                       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);
@@ -2895,25 +2890,24 @@ static void R_Shadow_UpdateBounceGridTexture(void)
                                                        c[2] = (int)(highpixel[2]*256.0f);
                                                        c[3] = (int)(highpixel[3]*256.0f);
                                                }
-                                               pixel[0] = (unsigned char)bound(0, c[0], 255);
+                                               pixel[2] = (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[0] = (unsigned char)bound(0, c[2], 255);
                                                pixel[3] = (unsigned char)bound(0, c[3], 255);
                                        }
                                }
                        }
                }
-               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));
+       if (r_shadow_bouncegridtexture && r_shadow_bouncegridresolution[0] == resolution[0] && r_shadow_bouncegridresolution[1] == resolution[1] && r_shadow_bouncegridresolution[2] == resolution[2] && r_shadow_bouncegriddirectional == settings.directionalshading)
+               R_UpdateTexture(r_shadow_bouncegridtexture, pixels, 0, 0, 0, resolution[0], resolution[1], resolution[2]*pixelbands);
        else
        {
                VectorCopy(resolution, r_shadow_bouncegridresolution);
+               r_shadow_bouncegriddirectional = settings.directionalshading;
                if (r_shadow_bouncegridtexture)
                        R_FreeTexture(r_shadow_bouncegridtexture);
-               r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*(settings.directionalshading ? 4 : 1), pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
+               r_shadow_bouncegridtexture = R_LoadTexture3D(r_shadow_texturepool, "bouncegrid", resolution[0], resolution[1], resolution[2]*pixelbands, pixels, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCELINEAR, 0, NULL);
        }
        r_shadow_bouncegridtime = realtime;
 }
@@ -2973,7 +2967,7 @@ int bboxedges[12][2] =
 
 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 {
-       if (!r_shadow_scissor.integer || r_shadow_usingdeferredprepass)
+       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;
@@ -3172,12 +3166,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)
@@ -3345,8 +3335,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 +3350,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 +3373,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)
@@ -3635,6 +3579,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)
        {
@@ -4542,8 +4489,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 +4496,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;
@@ -4569,6 +4514,10 @@ void R_Shadow_PrepareLights(void)
                r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
                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 +4527,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;
@@ -4599,14 +4548,14 @@ void R_Shadow_PrepareLights(void)
                        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);
+                               r_shadow_prepassgeometrydepthcolortexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrydepthcolormap", vid.width, vid.height, NULL, r_fb.textype, 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_prepassgeometrynormalmaptexture = R_LoadTexture2D(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
+                       r_shadow_prepasslightingdiffusetexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, r_fb.textype, TEXF_RENDERTARGET | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, -1, NULL);
+                       r_shadow_prepasslightingspeculartexture = R_LoadTexture2D(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, r_fb.textype, 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);
@@ -4614,10 +4563,10 @@ void R_Shadow_PrepareLights(void)
                        // render depth into one texture and normalmap into the other
                        if (qglDrawBuffersARB)
                        {
-                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
+                               qglDrawBuffer(GL_COLOR_ATTACHMENT0);CHECKGLERROR
                                qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER);CHECKGLERROR
+                               if (status != GL_FRAMEBUFFER_COMPLETE)
                                {
                                        Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
                                        Cvar_SetValueQuick(&r_shadow_deferred, 0);
@@ -4635,8 +4584,8 @@ void R_Shadow_PrepareLights(void)
                        {
                                qglDrawBuffersARB(2, r_shadow_prepasslightingdrawbuffers);CHECKGLERROR
                                qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER);CHECKGLERROR
+                               if (status != GL_FRAMEBUFFER_COMPLETE)
                                {
                                        Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
                                        Cvar_SetValueQuick(&r_shadow_deferred, 0);
@@ -4652,10 +4601,10 @@ void R_Shadow_PrepareLights(void)
                        // with depth bound as attachment as well
                        if (qglDrawBuffersARB)
                        {
-                               qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
+                               qglDrawBuffer(GL_COLOR_ATTACHMENT0);CHECKGLERROR
                                qglReadBuffer(GL_NONE);CHECKGLERROR
-                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                               if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                               status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER);CHECKGLERROR
+                               if (status != GL_FRAMEBUFFER_COMPLETE)
                                {
                                        Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
                                        Cvar_SetValueQuick(&r_shadow_deferred, 0);
@@ -4663,9 +4612,12 @@ void R_Shadow_PrepareLights(void)
                                }
                        }
                }
+#endif
                break;
-       case RENDERPATH_GL13:
        case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GLES1:
+       case RENDERPATH_GLES2:
                r_shadow_usingdeferredprepass = false;
                break;
        }
@@ -4673,22 +4625,25 @@ void R_Shadow_PrepareLights(void)
        R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.scene.worldmodel->brush.num_leafs, r_refdef.scene.worldmodel->num_surfaces, r_refdef.scene.worldmodel->brush.shadowmesh ? r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles : r_refdef.scene.worldmodel->surfmesh.num_triangles, r_refdef.scene.worldmodel->surfmesh.num_triangles);
 
        flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
-       if (r_shadow_debuglight.integer >= 0)
-       {
-               lightindex = r_shadow_debuglight.integer;
-               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-               if (light && (light->flags & flag))
-                       R_Shadow_PrepareLight(&light->rtlight);
-       }
-       else
+       if (r_shadow_bouncegrid.integer != 2)
        {
-               range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
-               for (lightindex = 0;lightindex < range;lightindex++)
+               if (r_shadow_debuglight.integer >= 0)
                {
+                       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
+               {
+                       range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+                       for (lightindex = 0;lightindex < range;lightindex++)
+                       {
+                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                               if (light && (light->flags & flag))
+                                       R_Shadow_PrepareLight(&light->rtlight);
+                       }
+               }
        }
        if (r_refdef.scene.rtdlight)
        {
@@ -4707,8 +4662,6 @@ void R_Shadow_PrepareLights(void)
 
        if (r_editlights.integer)
                R_Shadow_DrawLightSprites();
-
-       R_Shadow_UpdateBounceGridTexture();
 }
 
 void R_Shadow_DrawLights(void)
@@ -4721,23 +4674,26 @@ void R_Shadow_DrawLights(void)
 
        R_Shadow_RenderMode_Begin();
 
-       flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
-       if (r_shadow_debuglight.integer >= 0)
-       {
-               lightindex = r_shadow_debuglight.integer;
-               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
-               if (light && (light->flags & flag))
-                       R_Shadow_DrawLight(&light->rtlight);
-       }
-       else
+       if (r_shadow_bouncegrid.integer != 2)
        {
-               range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
-               for (lightindex = 0;lightindex < range;lightindex++)
+               flag = r_refdef.scene.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
+               if (r_shadow_debuglight.integer >= 0)
                {
+                       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
+               {
+                       range = Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray); // checked
+                       for (lightindex = 0;lightindex < range;lightindex++)
+                       {
+                               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+                               if (light && (light->flags & flag))
+                                       R_Shadow_DrawLight(&light->rtlight);
+                       }
+               }
        }
        if (r_refdef.scene.rtdlight)
                for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++)
@@ -4746,19 +4702,6 @@ 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;
-
 void R_Shadow_PrepareModelShadows(void)
 {
        int i;
@@ -4828,7 +4771,7 @@ void R_Shadow_PrepareModelShadows(void)
        }
 }
 
-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;
@@ -4840,7 +4783,7 @@ void R_DrawModelShadowMaps(void)
        float m[12];
        matrix4x4_t shadowmatrix, cameramatrix, mvpmatrix, invmvpmatrix, scalematrix, texmatrix;
        r_viewport_t viewport;
-       GLuint fbo = 0;
+       GLuint fbo2d = 0;
        float clearcolor[4];
 
        if (!r_refdef.scene.numentities)
@@ -4854,7 +4797,11 @@ 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);
 
@@ -4913,8 +4860,8 @@ 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();
+       R_Mesh_SetRenderTargets(fbo2d, r_shadow_shadowmap2dtexture, r_shadow_shadowmap2dcolortexture, NULL, NULL, NULL);
+       R_SetupShader_DepthOrShadow(true);
        GL_PolygonOffset(r_shadow_shadowmapping_polygonfactor.value, r_shadow_shadowmapping_polygonoffset.value);
        GL_DepthMask(true);
        GL_DepthTest(true);
@@ -4934,8 +4881,8 @@ void R_DrawModelShadowMaps(void)
 
 #if 0
        // debugging
-       R_Mesh_ResetRenderTargets();
-       R_SetupShader_ShowDepth();
+       R_Mesh_SetRenderTargets(r_shadow_fb_fbo, r_shadow_fb_depthtexture, r_shadow_fb_colortexture, NULL, NULL, NULL);
+       R_SetupShader_ShowDepth(true);
        GL_ColorMask(1,1,1,1);
        GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, clearcolor, 1.0f, 0);
 #endif
@@ -4992,6 +4939,7 @@ void R_DrawModelShadowMaps(void)
        case RENDERPATH_GL13:
        case RENDERPATH_GL20:
        case RENDERPATH_SOFT:
+       case RENDERPATH_GLES1:
        case RENDERPATH_GLES2:
                break;
        case RENDERPATH_D3D9:
@@ -5022,7 +4970,7 @@ void R_DrawModelShadowMaps(void)
        }
 }
 
-void R_DrawModelShadows(void)
+void R_DrawModelShadows(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture)
 {
        int i;
        float relativethrowdistance;
@@ -5035,7 +4983,11 @@ void R_DrawModelShadows(void)
        if (!r_refdef.scene.numentities || !vid.stencil || (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();
@@ -5120,7 +5072,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 +5087,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
@@ -5165,10 +5117,12 @@ 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:
+#ifdef GL_SAMPLES_PASSED_ARB
                        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);
@@ -5184,6 +5138,7 @@ void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
                        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__);
@@ -5213,14 +5168,17 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
        {
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL20:
-               case RENDERPATH_GL13:
                case RENDERPATH_GL11:
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL20:
+               case RENDERPATH_GLES1:
                case RENDERPATH_GLES2:
+#ifdef GL_SAMPLES_PASSED_ARB
                        CHECKGLERROR
                        qglGetQueryObjectivARB(rtlight->corona_queryindex_visiblepixels, GL_QUERY_RESULT_ARB, &visiblepixels);
                        qglGetQueryObjectivARB(rtlight->corona_queryindex_allpixels, GL_QUERY_RESULT_ARB, &allpixels);
                        CHECKGLERROR
+#endif
                        break;
                case RENDERPATH_D3D9:
                        Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
@@ -5255,59 +5213,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);
                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 +5233,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,8 +5249,10 @@ 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;
+#ifdef GL_SAMPLES_PASSED_ARB
                if (usequery)
                {
                        GL_ColorMask(0,0,0,0);
@@ -5360,8 +5274,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;
@@ -6767,26 +6682,29 @@ 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;
@@ -6864,15 +6782,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);
@@ -6927,6 +6847,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)