]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
movement packet loss tracking
[xonotic/darkplaces.git] / r_shadow.c
index 57e108401260f615167a478d4ce81bc3bcb983d7..89c2560b88e8aa09943a2059d3a87d8511d6ab52 100644 (file)
@@ -203,6 +203,7 @@ qboolean r_shadow_shadowmapsampler;
 int r_shadow_shadowmappcf;
 int r_shadow_shadowmapborder;
 int r_shadow_lightscissor[4];
+qboolean r_shadow_usingdeferredprepass;
 
 int maxshadowtriangles;
 int *shadowelements;
@@ -253,15 +254,29 @@ rtexture_t *r_shadow_shadowmapvsdcttexture;
 int r_shadow_shadowmapsize; // changes for each light based on distance
 int r_shadow_shadowmaplod; // changes for each light based on distance
 
+GLuint r_shadow_prepassgeometryfbo;
+GLuint r_shadow_prepasslightingfbo;
+int r_shadow_prepass_width;
+int r_shadow_prepass_height;
+rtexture_t *r_shadow_prepassgeometrydepthtexture;
+rtexture_t *r_shadow_prepassgeometrynormalmaptexture;
+rtexture_t *r_shadow_prepasslightingdiffusetexture;
+rtexture_t *r_shadow_prepasslightingspeculartexture;
+
 // 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};
+
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
+cvar_t r_shadow_deferred = {CVAR_SAVE, "r_shadow_deferred", "0", "uses image-based lighting instead of geometry-based lighting, the method used renders a depth image and a normalmap image, renders lights into separate diffuse and specular images, and then combines this into the normal rendering, requires r_shadow_shadowmapping"};
+cvar_t r_shadow_deferred_8bitrange = {CVAR_SAVE, "r_shadow_deferred_8bitrange", "2", "dynamic range of image-based lighting when using 32bit color (does not apply to fp)"};
+//cvar_t r_shadow_deferred_fp = {CVAR_SAVE, "r_shadow_deferred_fp", "0", "use 16bit (1) or 32bit (2) floating point for accumulation of image-based lighting"};
 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.125", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
@@ -383,7 +398,7 @@ void R_Shadow_SetShadowMode(void)
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-               if(r_shadow_shadowmapping.integer && vid.support.ext_framebuffer_object)
+               if ((r_shadow_shadowmapping.integer || r_shadow_deferred.integer) && vid.support.ext_framebuffer_object)
                {
                        if(r_shadow_shadowmapfilterquality < 0)
                        {
@@ -550,8 +565,12 @@ void r_shadow_start(void)
        r_shadow_buffer_shadowtrispvs = NULL;
        r_shadow_buffer_numlighttrispvsbytes = 0;
        r_shadow_buffer_lighttrispvs = NULL;
+
+       r_shadow_usingdeferredprepass = false;
+       r_shadow_prepass_width = r_shadow_prepass_height = 0;
 }
 
+static void R_Shadow_FreeDeferred(void);
 void r_shadow_shutdown(void)
 {
        CHECKGLERROR
@@ -559,6 +578,11 @@ void r_shadow_shutdown(void)
 
        R_Shadow_FreeShadowMaps();
 
+       r_shadow_usingdeferredprepass = false;
+       if (r_shadow_prepass_width)
+               R_Shadow_FreeDeferred();
+       r_shadow_prepass_width = r_shadow_prepass_height = 0;
+
        CHECKGLERROR
        numcubemaps = 0;
        r_shadow_attenuationgradienttexture = NULL;
@@ -645,6 +669,9 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
        Cvar_RegisterVariable(&r_shadow_usenormalmap);
        Cvar_RegisterVariable(&r_shadow_debuglight);
+       Cvar_RegisterVariable(&r_shadow_deferred);
+       Cvar_RegisterVariable(&r_shadow_deferred_8bitrange);
+//     Cvar_RegisterVariable(&r_shadow_deferred_fp);
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
        Cvar_RegisterVariable(&r_shadow_glossintensity);
@@ -1289,6 +1316,12 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
                tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
                Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow_zfail, NULL, NULL, NULL, shadowvertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
        }
+       else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_VISIBLEVOLUMES)
+       {
+               tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
+               R_Mesh_VertexPointer(shadowvertex3f, 0, 0);
+               R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, 0);
+       }
        else
        {
                // decide which type of shadow to generate and set stencil mode
@@ -2064,10 +2097,11 @@ void R_Shadow_RenderMode_ShadowMap(int side, qboolean clear, int size)
                        qglDrawBuffer(GL_NONE);CHECKGLERROR
                        qglReadBuffer(GL_NONE);CHECKGLERROR
                        status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && r_shadow_shadowmapping.integer)
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && (r_shadow_shadowmapping.integer || r_shadow_deferred.integer))
                        {
                                Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
                                Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                               Cvar_SetValueQuick(&r_shadow_deferred, 0);
                        }
 #endif
                }
@@ -2098,10 +2132,11 @@ void R_Shadow_RenderMode_ShadowMap(int side, qboolean clear, int size)
                        qglDrawBuffer(GL_NONE);CHECKGLERROR
                        qglReadBuffer(GL_NONE);CHECKGLERROR
                        status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && r_shadow_shadowmapping.integer)
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && (r_shadow_shadowmapping.integer || r_shadow_deferred.integer))
                        {
                                Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
                                Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                               Cvar_SetValueQuick(&r_shadow_deferred, 0);
                        }
 #endif
                }
@@ -2130,10 +2165,11 @@ void R_Shadow_RenderMode_ShadowMap(int side, qboolean clear, int size)
                        qglDrawBuffer(GL_NONE);CHECKGLERROR
                        qglReadBuffer(GL_NONE);CHECKGLERROR
                        status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
-                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && r_shadow_shadowmapping.integer)
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT && (r_shadow_shadowmapping.integer || r_shadow_deferred.integer))
                        {
                                Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
                                Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                               Cvar_SetValueQuick(&r_shadow_deferred, 0);
                        }
  #endif
                }
@@ -2181,6 +2217,34 @@ init_done:
        CHECKGLERROR
 }
 
+void R_Shadow_RenderMode_SetShadowMapTexture(void)
+{
+       if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAP2D)
+       {
+               r_shadow_usingshadowmap2d = true;
+               R_Mesh_TexBind(GL20TU_SHADOWMAP2D, R_GetTexture(r_shadow_shadowmap2dtexture));
+               CHECKGLERROR
+       }
+       else if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPRECTANGLE)
+       {
+               r_shadow_usingshadowmaprect = true;
+               R_Mesh_TexBindAll(GL20TU_SHADOWMAPRECT, 0, 0, 0, R_GetTexture(r_shadow_shadowmaprectangletexture));
+               CHECKGLERROR
+       }
+       else if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPCUBESIDE)
+       {
+               r_shadow_usingshadowmapcube = true;
+               R_Mesh_TexBindAll(GL20TU_SHADOWMAPCUBE, 0, 0, R_GetTexture(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]), 0);
+               CHECKGLERROR
+       }
+
+       if (r_shadow_shadowmapvsdct && (r_shadow_usingshadowmap2d || r_shadow_usingshadowmaprect))
+       {
+               R_Mesh_TexBindAll(GL20TU_CUBEPROJECTION, 0, 0, R_GetTexture(r_shadow_shadowmapvsdcttexture), 0);
+               CHECKGLERROR
+       }
+}
+
 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent, qboolean shadowmapping)
 {
        if (transparent)
@@ -2212,38 +2276,85 @@ void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent, qb
                GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
                CHECKGLERROR
                if (shadowmapping)
-               {
-                       if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAP2D)
-                       {
-                               r_shadow_usingshadowmap2d = true;
-                               R_Mesh_TexBind(GL20TU_SHADOWMAP2D, R_GetTexture(r_shadow_shadowmap2dtexture));
-                               CHECKGLERROR
-                       }
-                       else if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPRECTANGLE)
-                       {
-                               r_shadow_usingshadowmaprect = true;
-                               R_Mesh_TexBindAll(GL20TU_SHADOWMAPRECT, 0, 0, 0, R_GetTexture(r_shadow_shadowmaprectangletexture));
-                               CHECKGLERROR
-                       }
-                       else if (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPCUBESIDE)
-                       {
-                               r_shadow_usingshadowmapcube = true;
-                               R_Mesh_TexBindAll(GL20TU_SHADOWMAPCUBE, 0, 0, R_GetTexture(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]), 0);
-                               CHECKGLERROR
-                       }
-
-                       if (r_shadow_shadowmapvsdct && (r_shadow_usingshadowmap2d || r_shadow_usingshadowmaprect))
-                       {
-                               R_Mesh_TexBindAll(GL20TU_CUBEPROJECTION, 0, 0, R_GetTexture(r_shadow_shadowmapvsdcttexture), 0);
-                               CHECKGLERROR
-                       }
-               }
+                       R_Shadow_RenderMode_SetShadowMapTexture();
        }
        R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
-       //GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        CHECKGLERROR
 }
 
+static const unsigned short bboxelements[36] =
+{
+       5, 1, 3, 5, 3, 7,
+       6, 2, 0, 6, 0, 4,
+       7, 3, 2, 7, 2, 6,
+       4, 0, 1, 4, 1, 5,
+       4, 5, 7, 4, 7, 6,
+       1, 0, 2, 1, 2, 3,
+};
+
+static const float bboxpoints[8][3] =
+{
+       {-1,-1,-1},
+       { 1,-1,-1},
+       {-1, 1,-1},
+       { 1, 1,-1},
+       {-1,-1, 1},
+       { 1,-1, 1},
+       {-1, 1, 1},
+       { 1, 1, 1},
+};
+
+void R_Shadow_RenderMode_DrawDeferredLight(qboolean stenciltest, qboolean shadowmapping)
+{
+       int i;
+       float vertex3f[8*3];
+       const matrix4x4_t *matrix = &rsurface.rtlight->matrix_lighttoworld;
+       CHECKGLERROR
+       R_Shadow_RenderMode_Reset();
+       r_shadow_rendermode = r_shadow_lightingrendermode;
+       // do global setup needed for the chosen lighting mode
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
+       {
+               R_Mesh_Matrix(&identitymatrix);
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               if (stenciltest)
+               {
+                       qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+                       // only draw light where this geometry was already rendered AND the
+                       // stencil is 128 (values other than this mean shadow)
+                       qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
+               }
+               qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_prepasslightingfbo);CHECKGLERROR
+               R_Mesh_TexBindAll(GL20TU_SCREENDEPTH, 0, 0, 0, R_GetTexture(r_shadow_prepassgeometrydepthtexture));
+               R_Mesh_TexBindAll(GL20TU_SCREENNORMALMAP, 0, 0, 0, R_GetTexture(r_shadow_prepassgeometrynormalmaptexture));
+               R_Mesh_TexBindAll(GL20TU_CUBE, 0, 0, R_GetTexture(rsurface.rtlight->currentcubemap), 0); // light filter
+               if (shadowmapping)
+                       R_Shadow_RenderMode_SetShadowMapTexture();
+               R_SetupDeferredLightShader(rsurface.rtlight);
+               //R_Mesh_TexBind(GL20TU_FOGMASK, R_GetTexture(r_texture_fogattenuation));
+               R_Mesh_TexBind(GL20TU_ATTENUATION, R_GetTexture(r_shadow_attenuationgradienttexture));
+
+               for (i = 0;i < 8;i++)
+                       Matrix4x4_Transform(matrix, bboxpoints[i], vertex3f + i*3);
+               CHECKGLERROR
+               //qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);CHECKGLERROR
+               R_Mesh_VertexPointer(vertex3f, 0, 0);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               GL_ColorMask(1,1,1,1);
+               //GL_Color(0.25f,0.05f,0.02f,1.0f);
+               //R_SetupGenericShader(false);
+               GL_DepthMask(false);
+               GL_DepthRange(0, 1);
+               GL_PolygonOffset(0, 0);
+               GL_DepthTest(true);
+               qglDepthFunc(GL_GREATER);CHECKGLERROR
+               GL_CullFace(r_refdef.view.cullface_back);
+               //GL_AlphaTest(false);
+               //qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+               R_Mesh_Draw(0, 8, 0, 12, NULL, bboxelements, 0, 0);
+       }
+}
+
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
 {
        CHECKGLERROR
@@ -2590,22 +2701,20 @@ static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int nu
        }
 }
 
-static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int element3i_bufferobject, int element3s_bufferobject, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int element3i_bufferobject, int element3s_bufferobject)
 {
        // used to display how many times a surface is lit for level design purposes
        R_Mesh_Draw(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject);
 }
 
-static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int element3i_bufferobject, int element3s_bufferobject, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int element3i_bufferobject, int element3s_bufferobject, const vec3_t lightcolor, float ambientscale, float diffusescale, float specularscale)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
+       R_SetupSurfaceShader(lightcolor, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
        if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND))
                R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
        else
                R_Mesh_ColorPointer(NULL, 0, 0);
-       R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
-       R_Mesh_TexMatrix(1, &rsurface.texture->currentbackgroundtexmatrix);
        R_Mesh_TexBind(GL20TU_NORMAL, R_GetTexture(rsurface.texture->currentskinframe->nmap));
        R_Mesh_TexBind(GL20TU_COLOR, R_GetTexture(rsurface.texture->basetexture));
        R_Mesh_TexBind(GL20TU_GLOSS, R_GetTexture(rsurface.texture->glosstexture));
@@ -2651,7 +2760,7 @@ static void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numve
        const int *e;
        float *c;
        int maxtriangles = 4096;
-       int newelements[4096*3];
+       static int newelements[4096*3];
        R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
        for (renders = 0;renders < 64;renders++)
        {
@@ -2728,76 +2837,53 @@ static void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numve
        }
 }
 
-static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolor, float ambientscale, float diffusescale)
 {
        // OpenGL 1.1 path (anything)
        float ambientcolorbase[3], diffusecolorbase[3];
        float ambientcolorpants[3], diffusecolorpants[3];
        float ambientcolorshirt[3], diffusecolorshirt[3];
-       rmeshstate_t m;
-       VectorScale(lightcolorbase, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorbase);
-       VectorScale(lightcolorbase, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorbase);
-       VectorScale(lightcolorpants, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorpants);
-       VectorScale(lightcolorpants, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorpants);
-       VectorScale(lightcolorshirt, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorshirt);
-       VectorScale(lightcolorshirt, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorshirt);
+       const float *surfacecolor = rsurface.texture->dlightcolor;
+       const float *surfacepants = rsurface.colormap_pantscolor;
+       const float *surfaceshirt = rsurface.colormap_shirtcolor;
+       rtexture_t *basetexture = rsurface.texture->basetexture;
+       rtexture_t *pantstexture = rsurface.texture->currentskinframe->pants;
+       rtexture_t *shirttexture = rsurface.texture->currentskinframe->shirt;
+       qboolean dopants = pantstexture && VectorLength2(surfacepants) >= (1.0f / 1048576.0f);
+       qboolean doshirt = shirttexture && VectorLength2(surfaceshirt) >= (1.0f / 1048576.0f);
+       ambientscale *= 2 * r_refdef.view.colorscale;
+       diffusescale *= 2 * r_refdef.view.colorscale;
+       ambientcolorbase[0] = lightcolor[0] * ambientscale * surfacecolor[0];ambientcolorbase[1] = lightcolor[1] * ambientscale * surfacecolor[1];ambientcolorbase[2] = lightcolor[2] * ambientscale * surfacecolor[2];
+       diffusecolorbase[0] = lightcolor[0] * diffusescale * surfacecolor[0];diffusecolorbase[1] = lightcolor[1] * diffusescale * surfacecolor[1];diffusecolorbase[2] = lightcolor[2] * diffusescale * surfacecolor[2];
+       ambientcolorpants[0] = ambientcolorbase[0] * surfacepants[0];ambientcolorpants[1] = ambientcolorbase[1] * surfacepants[1];ambientcolorpants[2] = ambientcolorbase[2] * surfacepants[2];
+       diffusecolorpants[0] = diffusecolorbase[0] * surfacepants[0];diffusecolorpants[1] = diffusecolorbase[1] * surfacepants[1];diffusecolorpants[2] = diffusecolorbase[2] * surfacepants[2];
+       ambientcolorshirt[0] = ambientcolorbase[0] * surfaceshirt[0];ambientcolorshirt[1] = ambientcolorbase[1] * surfaceshirt[1];ambientcolorshirt[2] = ambientcolorbase[2] * surfaceshirt[2];
+       diffusecolorshirt[0] = diffusecolorbase[0] * surfaceshirt[0];diffusecolorshirt[1] = diffusecolorbase[1] * surfaceshirt[1];diffusecolorshirt[2] = diffusecolorbase[2] * surfaceshirt[2];
+       R_Mesh_TexBind(0, R_GetTexture(basetexture));
+       R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
+       R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
+       R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
        switch(r_shadow_rendermode)
        {
        case R_SHADOW_RENDERMODE_LIGHT_VERTEX3DATTEN:
-               memset(&m, 0, sizeof(m));
-               m.tex[0] = R_GetTexture(basetexture);
-               m.texmatrix[0] = rsurface.texture->currenttexmatrix;
-               m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
-               m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
-               m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
-               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-               m.texmatrix[1] = rsurface.entitytoattenuationxyz;
-               m.pointer_texcoord3f[1] = rsurface.vertex3f;
-               m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
-               m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
-               R_Mesh_TextureState(&m);
+               R_Mesh_TexBindAll(1, 0, R_GetTexture(r_shadow_attenuation3dtexture), 0, 0);
+               R_Mesh_TexMatrix(1, &rsurface.entitytoattenuationxyz);
+               R_Mesh_TexCombine(1, GL_MODULATE, GL_MODULATE, 1, 1);
+               R_Mesh_TexCoordPointer(1, 3, rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
                break;
        case R_SHADOW_RENDERMODE_LIGHT_VERTEX2D1DATTEN:
-               memset(&m, 0, sizeof(m));
-               m.tex[0] = R_GetTexture(basetexture);
-               m.texmatrix[0] = rsurface.texture->currenttexmatrix;
-               m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
-               m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
-               m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
-               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-               m.texmatrix[1] = rsurface.entitytoattenuationxyz;
-               m.pointer_texcoord3f[1] = rsurface.vertex3f;
-               m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
-               m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
-               m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-               m.texmatrix[2] = rsurface.entitytoattenuationz;
-               m.pointer_texcoord3f[2] = rsurface.vertex3f;
-               m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
-               m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
-               R_Mesh_TextureState(&m);
-               break;
+               R_Mesh_TexBind(2, R_GetTexture(r_shadow_attenuation2dtexture));
+               R_Mesh_TexMatrix(2, &rsurface.entitytoattenuationz);
+               R_Mesh_TexCombine(2, GL_MODULATE, GL_MODULATE, 1, 1);
+               R_Mesh_TexCoordPointer(2, 3, rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
+               // fall through
        case R_SHADOW_RENDERMODE_LIGHT_VERTEX2DATTEN:
-               memset(&m, 0, sizeof(m));
-               m.tex[0] = R_GetTexture(basetexture);
-               m.texmatrix[0] = rsurface.texture->currenttexmatrix;
-               m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
-               m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
-               m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
-               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-               m.texmatrix[1] = rsurface.entitytoattenuationxyz;
-               m.pointer_texcoord3f[1] = rsurface.vertex3f;
-               m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
-               m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
-               R_Mesh_TextureState(&m);
+               R_Mesh_TexBind(1, R_GetTexture(r_shadow_attenuation2dtexture));
+               R_Mesh_TexMatrix(1, &rsurface.entitytoattenuationxyz);
+               R_Mesh_TexCombine(1, GL_MODULATE, GL_MODULATE, 1, 1);
+               R_Mesh_TexCoordPointer(1, 3, rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
                break;
        case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-               memset(&m, 0, sizeof(m));
-               m.tex[0] = R_GetTexture(basetexture);
-               m.texmatrix[0] = rsurface.texture->currenttexmatrix;
-               m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
-               m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
-               m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
-               R_Mesh_TextureState(&m);
                break;
        default:
                break;
@@ -2821,12 +2907,8 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int firsttriangle
 {
        float ambientscale, diffusescale, specularscale;
        qboolean negated;
-       vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
-       rtexture_t *nmap;
-       // calculate colors to render this texture with
-       lightcolorbase[0] = rsurface.rtlight->currentcolor[0] * rsurface.texture->dlightcolor[0];
-       lightcolorbase[1] = rsurface.rtlight->currentcolor[1] * rsurface.texture->dlightcolor[1];
-       lightcolorbase[2] = rsurface.rtlight->currentcolor[2] * rsurface.texture->dlightcolor[2];
+       float lightcolor[3];
+       VectorCopy(rsurface.rtlight->currentcolor, lightcolor);
        ambientscale = rsurface.rtlight->ambientscale;
        diffusescale = rsurface.rtlight->diffusescale;
        specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
@@ -2836,79 +2918,33 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int firsttriangle
                diffusescale = 0;
                specularscale = 0;
        }
-       if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
+       if ((ambientscale + diffusescale) * VectorLength2(lightcolor) + specularscale * VectorLength2(lightcolor) < (1.0f / 1048576.0f))
                return;
-       negated = (lightcolorbase[0] + lightcolorbase[1] + lightcolorbase[2] < 0) && vid.support.ext_blend_subtract;
+       negated = (lightcolor[0] + lightcolor[1] + lightcolor[2] < 0) && vid.support.ext_blend_subtract;
        if(negated)
        {
-               VectorNegate(lightcolorbase, lightcolorbase);
+               VectorNegate(lightcolor, lightcolor);
                qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
        }
        RSurf_SetupDepthAndCulling();
-       nmap = rsurface.texture->currentskinframe->nmap;
-       if (gl_lightmaps.integer)
-               nmap = r_texture_blanknormalmap;
-       if (rsurface.texture->colormapping && !gl_lightmaps.integer)
-       {
-               qboolean dopants = rsurface.texture->currentskinframe->pants != NULL && VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f);
-               qboolean doshirt = rsurface.texture->currentskinframe->shirt != NULL && VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
-               if (dopants)
-               {
-                       lightcolorpants[0] = lightcolorbase[0] * rsurface.colormap_pantscolor[0];
-                       lightcolorpants[1] = lightcolorbase[1] * rsurface.colormap_pantscolor[1];
-                       lightcolorpants[2] = lightcolorbase[2] * rsurface.colormap_pantscolor[2];
-               }
-               else
-                       VectorClear(lightcolorpants);
-               if (doshirt)
-               {
-                       lightcolorshirt[0] = lightcolorbase[0] * rsurface.colormap_shirtcolor[0];
-                       lightcolorshirt[1] = lightcolorbase[1] * rsurface.colormap_shirtcolor[1];
-                       lightcolorshirt[2] = lightcolorbase[2] * rsurface.colormap_shirtcolor[2];
-               }
-               else
-                       VectorClear(lightcolorshirt);
-               switch (r_shadow_rendermode)
-               {
-               case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
-                       GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
-                       break;
-               case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
-                       break;
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX3DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX2D1DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX2DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i + firsttriangle * 3, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
-                       break;
-               default:
-                       Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
-                       break;
-               }
-       }
-       else
+       switch (r_shadow_rendermode)
        {
-               switch (r_shadow_rendermode)
-               {
-               case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
-                       GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
-                       break;
-               case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
-                       break;
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX3DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX2D1DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX2DATTEN:
-               case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i + firsttriangle * 3, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
-                       break;
-               default:
-                       Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
-                       break;
-               }
+       case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
+               GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
+               R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject);
+               break;
+       case R_SHADOW_RENDERMODE_LIGHT_GLSL:
+               R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, firsttriangle, numtriangles, element3i, element3s, element3i_bufferobject, element3s_bufferobject, lightcolor, ambientscale, diffusescale, specularscale);
+               break;
+       case R_SHADOW_RENDERMODE_LIGHT_VERTEX3DATTEN:
+       case R_SHADOW_RENDERMODE_LIGHT_VERTEX2D1DATTEN:
+       case R_SHADOW_RENDERMODE_LIGHT_VERTEX2DATTEN:
+       case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
+               R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i + firsttriangle * 3, lightcolor, ambientscale, diffusescale);
+               break;
+       default:
+               Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
+               break;
        }
        if(negated)
                qglBlendEquationEXT(GL_FUNC_ADD_EXT);
@@ -2989,8 +3025,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
        {
                // this variable must be set for the CompileShadowVolume/CompileShadowMap code
                r_shadow_compilingrtlight = rtlight;
-               R_Shadow_EnlargeLeafSurfaceTrisBuffer(model->brush.num_leafs, model->num_surfaces, model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles, model->surfmesh.num_triangles);
-               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs);
+               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, 0, NULL);
                numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
                numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
                numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
@@ -3112,9 +3147,9 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
        int i, j;
        mplane_t plane;
        // reset the count of frustum planes
-       // see rsurface.rtlight_frustumplanes definition for how much this array
+       // see rtlight->cached_frustumplanes definition for how much this array
        // can hold
-       rsurface.rtlight_numfrustumplanes = 0;
+       rtlight->cached_numfrustumplanes = 0;
 
        // haven't implemented a culling path for ortho rendering
        if (!r_refdef.view.useperspective)
@@ -3125,7 +3160,7 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
                                break;
                if (i == 4)
                        for (i = 0;i < 4;i++)
-                               rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
+                               rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = r_refdef.view.frustum[i];
                return;
        }
 
@@ -3147,11 +3182,11 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
                if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
                        continue;
                // copy the plane
-               rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
+               rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = r_refdef.view.frustum[i];
        }
        // if all the standard frustum planes were accepted, the light is onscreen
        // otherwise we need to generate some more planes below...
-       if (rsurface.rtlight_numfrustumplanes < 4)
+       if (rtlight->cached_numfrustumplanes < 4)
        {
                // at least one of the stock frustum planes failed, so we need to
                // create one or two custom planes to enclose the light origin
@@ -3182,12 +3217,12 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
                        // we have created a valid plane, compute extra info
                        PlaneClassify(&plane);
                        // copy the plane
-                       rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
+                       rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = plane;
 #if 1
                        // if we've found 5 frustum planes then we have constructed a
                        // proper split-side case and do not need to keep searching for
                        // planes to enclose the light origin
-                       if (rsurface.rtlight_numfrustumplanes == 5)
+                       if (rtlight->cached_numfrustumplanes == 5)
                                break;
 #endif
                }
@@ -3195,9 +3230,9 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
 #endif
 
 #if 0
-       for (i = 0;i < rsurface.rtlight_numfrustumplanes;i++)
+       for (i = 0;i < rtlight->cached_numfrustumplanes;i++)
        {
-               plane = rsurface.rtlight_frustumplanes[i];
+               plane = rtlight->cached_frustumplanes[i];
                Con_Printf("light %p plane #%i %f %f %f : %f (%f %f %f %f %f)\n", rtlight, i, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist, PlaneDiff(r_refdef.view.frustumcorner[0], &plane), PlaneDiff(r_refdef.view.frustumcorner[1], &plane), PlaneDiff(r_refdef.view.frustumcorner[2], &plane), PlaneDiff(r_refdef.view.frustumcorner[3], &plane), PlaneDiff(rtlight->shadoworigin, &plane));
        }
 #endif
@@ -3217,7 +3252,7 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
                        VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
                        plane.dist = VectorNormalizeLength(plane.normal);
                        plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
-                       rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
+                       rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = plane;
                }
        }
 #endif
@@ -3228,8 +3263,8 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
        {
                VectorClear(plane.normal);
                plane.normal[i >> 1] = (i & 1) ? -1 : 1;
-               plane.dist = (i & 1) ? -rsurface.rtlight_cullmaxs[i >> 1] : rsurface.rtlight_cullmins[i >> 1];
-               rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
+               plane.dist = (i & 1) ? -rtlight->cached_cullmaxs[i >> 1] : rtlight->cached_cullmins[i >> 1];
+               rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = plane;
        }
 #endif
 
@@ -3240,33 +3275,33 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
        vec_t bestdist;
        // reduce all plane distances to tightly fit the rtlight cull box, which
        // is in worldspace
-       VectorSet(points[0], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
-       VectorSet(points[1], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
-       VectorSet(points[2], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
-       VectorSet(points[3], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
-       VectorSet(points[4], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
-       VectorSet(points[5], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
-       VectorSet(points[6], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
-       VectorSet(points[7], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
-       oldnum = rsurface.rtlight_numfrustumplanes;
-       rsurface.rtlight_numfrustumplanes = 0;
+       VectorSet(points[0], rtlight->cached_cullmins[0], rtlight->cached_cullmins[1], rtlight->cached_cullmins[2]);
+       VectorSet(points[1], rtlight->cached_cullmaxs[0], rtlight->cached_cullmins[1], rtlight->cached_cullmins[2]);
+       VectorSet(points[2], rtlight->cached_cullmins[0], rtlight->cached_cullmaxs[1], rtlight->cached_cullmins[2]);
+       VectorSet(points[3], rtlight->cached_cullmaxs[0], rtlight->cached_cullmaxs[1], rtlight->cached_cullmins[2]);
+       VectorSet(points[4], rtlight->cached_cullmins[0], rtlight->cached_cullmins[1], rtlight->cached_cullmaxs[2]);
+       VectorSet(points[5], rtlight->cached_cullmaxs[0], rtlight->cached_cullmins[1], rtlight->cached_cullmaxs[2]);
+       VectorSet(points[6], rtlight->cached_cullmins[0], rtlight->cached_cullmaxs[1], rtlight->cached_cullmaxs[2]);
+       VectorSet(points[7], rtlight->cached_cullmaxs[0], rtlight->cached_cullmaxs[1], rtlight->cached_cullmaxs[2]);
+       oldnum = rtlight->cached_numfrustumplanes;
+       rtlight->cached_numfrustumplanes = 0;
        for (j = 0;j < oldnum;j++)
        {
                // find the nearest point on the box to this plane
-               bestdist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[0]);
+               bestdist = DotProduct(rtlight->cached_frustumplanes[j].normal, points[0]);
                for (i = 1;i < 8;i++)
                {
-                       dist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[i]);
+                       dist = DotProduct(rtlight->cached_frustumplanes[j].normal, points[i]);
                        if (bestdist > dist)
                                bestdist = dist;
                }
-               Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, rsurface.rtlight_frustumplanes[j].normal[0], rsurface.rtlight_frustumplanes[j].normal[1], rsurface.rtlight_frustumplanes[j].normal[2], rsurface.rtlight_frustumplanes[j].dist, bestdist);
+               Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, rtlight->cached_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, rtlight->cached_frustumplanes[j].normal[0], rtlight->cached_frustumplanes[j].normal[1], rtlight->cached_frustumplanes[j].normal[2], rtlight->cached_frustumplanes[j].dist, bestdist);
                // if the nearest point is near or behind the plane, we want this
                // plane, otherwise the plane is useless as it won't cull anything
-               if (rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125)
+               if (rtlight->cached_frustumplanes[j].dist < bestdist + 0.03125)
                {
-                       PlaneClassify(&rsurface.rtlight_frustumplanes[j]);
-                       rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = rsurface.rtlight_frustumplanes[j];
+                       PlaneClassify(&rtlight->cached_frustumplanes[j]);
+                       rtlight->cached_frustumplanes[rtlight->cached_numfrustumplanes++] = rtlight->cached_frustumplanes[j];
                }
        }
        }
@@ -3295,14 +3330,14 @@ void R_Shadow_DrawWorldShadow_ShadowMap(int numsurfaces, int *surfacelist, const
         CHECKGLERROR
     }
        else if (r_refdef.scene.worldentity->model)
-               r_refdef.scene.worldmodel->DrawShadowMap(r_shadow_shadowmapside, r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, surfacesides, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
+               r_refdef.scene.worldmodel->DrawShadowMap(r_shadow_shadowmapside, r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, surfacesides, rsurface.rtlight->cached_cullmins, rsurface.rtlight->cached_cullmaxs);
 
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
 void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
 {
-       qboolean zpass;
+       qboolean zpass = false;
        shadowmesh_t *mesh;
        int t, tend;
        int surfacelistindex;
@@ -3313,8 +3348,11 @@ void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, co
        if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
        {
                CHECKGLERROR
-               zpass = R_Shadow_UseZPass(r_refdef.scene.worldmodel->normalmins, r_refdef.scene.worldmodel->normalmaxs);
-               R_Shadow_RenderMode_StencilShadowVolumes(zpass);
+               if (r_shadow_rendermode != R_SHADOW_RENDERMODE_VISIBLEVOLUMES)
+               {
+                       zpass = R_Shadow_UseZPass(r_refdef.scene.worldmodel->normalmins, r_refdef.scene.worldmodel->normalmaxs);
+                       R_Shadow_RenderMode_StencilShadowVolumes(zpass);
+               }
                mesh = zpass ? rsurface.rtlight->static_meshchain_shadow_zpass : rsurface.rtlight->static_meshchain_shadow_zfail;
                for (;mesh;mesh = mesh->next)
                {
@@ -3359,7 +3397,7 @@ void R_Shadow_DrawWorldShadow_ShadowVolume(int numsurfaces, int *surfacelist, co
                R_Shadow_VolumeFromList(r_refdef.scene.worldmodel->brush.shadowmesh->numverts, r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles, r_refdef.scene.worldmodel->brush.shadowmesh->vertex3f, r_refdef.scene.worldmodel->brush.shadowmesh->element3i, r_refdef.scene.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.scene.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist, r_refdef.scene.worldmodel->normalmins, r_refdef.scene.worldmodel->normalmaxs);
        }
        else if (numsurfaces)
-               r_refdef.scene.worldmodel->DrawShadowVolume(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
+               r_refdef.scene.worldmodel->DrawShadowVolume(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight->cached_cullmins, rsurface.rtlight->cached_cullmaxs);
 
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
@@ -3368,7 +3406,7 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent)
 {
        vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
        vec_t relativeshadowradius;
-       RSurf_ActiveModelEntity(ent, false, false);
+       RSurf_ActiveModelEntity(ent, false, false, false);
        Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, relativeshadoworigin);
        relativeshadowradius = rsurface.rtlight->radius / ent->scale;
        relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
@@ -3389,20 +3427,12 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent)
 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
 {
        // set up properties for rendering light onto this entity
-       RSurf_ActiveModelEntity(ent, true, true);
+       RSurf_ActiveModelEntity(ent, true, true, false);
        GL_AlphaTest(false);
        Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
        Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
        Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
        Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
-       switch(r_shadow_lightingrendermode)
-       {
-       case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-               R_Mesh_TexMatrix(3, &rsurface.entitytolight);
-               break;
-       default:
-               break;
-       }
 }
 
 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
@@ -3417,14 +3447,6 @@ void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned c
        Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
        Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
        VectorCopy(rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
-       switch(r_shadow_lightingrendermode)
-       {
-       case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-               R_Mesh_TexMatrix(3, &rsurface.entitytolight);
-               break;
-       default:
-               break;
-       }
 
        r_refdef.scene.worldmodel->DrawLight(r_refdef.scene.worldentity, numsurfaces, surfacelist, trispvs);
 
@@ -3444,7 +3466,7 @@ void R_Shadow_DrawEntityLight(entity_render_t *ent)
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
+void R_Shadow_PrepareLight(rtlight_t *rtlight)
 {
        int i;
        float f;
@@ -3456,14 +3478,11 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        int numshadowentities;
        int numshadowentities_noselfshadow;
        static entity_render_t *lightentities[MAX_EDICTS];
+       static entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
        static entity_render_t *shadowentities[MAX_EDICTS];
-       static unsigned char entitysides[MAX_EDICTS];
-       int lightentities_noselfshadow;
-       int shadowentities_noselfshadow;
-       vec3_t nearestpoint;
-       vec_t distance;
-       qboolean castshadows;
-       int lodlinear;
+       static entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
+
+       rtlight->draw = false;
 
        // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
        // skip lights that are basically invisible (color 0 0 0)
@@ -3503,8 +3522,10 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
                return;
 
-       VectorCopy(rtlight->cullmins, rsurface.rtlight_cullmins);
-       VectorCopy(rtlight->cullmaxs, rsurface.rtlight_cullmaxs);
+       VectorCopy(rtlight->cullmins, rtlight->cached_cullmins);
+       VectorCopy(rtlight->cullmaxs, rtlight->cached_cullmaxs);
+
+       R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
 
        if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
        {
@@ -3523,8 +3544,8 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        {
                // dynamic light, world available and can receive realtime lighting
                // calculate lit surfaces and leafs
-               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);
-               r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.worldentity, rtlight->shadoworigin, rtlight->radius, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs);
+               r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cached_cullmins, rtlight->cached_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs, r_shadow_buffer_visitingleafpvs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes);
+               R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
                leaflist = r_shadow_buffer_leaflist;
                leafpvs = r_shadow_buffer_leafpvs;
                surfacelist = r_shadow_buffer_surfacelist;
@@ -3532,7 +3553,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                shadowtrispvs = r_shadow_buffer_shadowtrispvs;
                lighttrispvs = r_shadow_buffer_lighttrispvs;
                // if the reduced leaf bounds are offscreen, skip it
-               if (R_CullBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
+               if (R_CullBox(rtlight->cached_cullmins, rtlight->cached_cullmaxs))
                        return;
        }
        else
@@ -3556,82 +3577,72 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (i == numleafs)
                        return;
        }
-       // set up a scissor rectangle for this light
-       if (R_Shadow_ScissorForBBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
-               return;
-
-       R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
 
        // make a list of lit entities and shadow casting entities
        numlightentities = 0;
        numlightentities_noselfshadow = 0;
-       lightentities_noselfshadow = sizeof(lightentities)/sizeof(lightentities[0]) - 1;
        numshadowentities = 0;
        numshadowentities_noselfshadow = 0;
-       shadowentities_noselfshadow = sizeof(shadowentities)/sizeof(shadowentities[0]) - 1;
 
        // add dynamic entities that are lit by the light
-       if (r_drawentities.integer)
+       for (i = 0;i < r_refdef.scene.numentities;i++)
        {
-               for (i = 0;i < r_refdef.scene.numentities;i++)
+               dp_model_t *model;
+               entity_render_t *ent = r_refdef.scene.entities[i];
+               vec3_t org;
+               if (!BoxesOverlap(ent->mins, ent->maxs, rtlight->cached_cullmins, rtlight->cached_cullmaxs))
+                       continue;
+               // skip the object entirely if it is not within the valid
+               // shadow-casting region (which includes the lit region)
+               if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rtlight->cached_numfrustumplanes, rtlight->cached_frustumplanes))
+                       continue;
+               if (!(model = ent->model))
+                       continue;
+               if (r_refdef.viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
                {
-                       dp_model_t *model;
-                       entity_render_t *ent = r_refdef.scene.entities[i];
-                       vec3_t org;
-                       if (!BoxesOverlap(ent->mins, ent->maxs, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
-                               continue;
-                       // skip the object entirely if it is not within the valid
-                       // shadow-casting region (which includes the lit region)
-                       if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rsurface.rtlight_numfrustumplanes, rsurface.rtlight_frustumplanes))
+                       // this entity wants to receive light, is visible, and is
+                       // inside the light box
+                       // TODO: check if the surfaces in the model can receive light
+                       // so now check if it's in a leaf seen by the light
+                       if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
                                continue;
-                       if (!(model = ent->model))
-                               continue;
-                       if (r_refdef.viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
+                       if (ent->flags & RENDER_NOSELFSHADOW)
+                               lightentities_noselfshadow[numlightentities_noselfshadow++] = ent;
+                       else
+                               lightentities[numlightentities++] = ent;
+                       // since it is lit, it probably also casts a shadow...
+                       // about the VectorDistance2 - light emitting entities should not cast their own shadow
+                       Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+                       if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
                        {
-                               // this entity wants to receive light, is visible, and is
-                               // inside the light box
-                               // TODO: check if the surfaces in the model can receive light
-                               // so now check if it's in a leaf seen by the light
-                               if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
-                                       continue;
-                               if (ent->flags & RENDER_NOSELFSHADOW)
-                                       lightentities[lightentities_noselfshadow - numlightentities_noselfshadow++] = ent;
+                               // note: exterior models without the RENDER_NOSELFSHADOW
+                               // flag still create a RENDER_NOSELFSHADOW shadow but
+                               // are lit normally, this means that they are
+                               // self-shadowing but do not shadow other
+                               // RENDER_NOSELFSHADOW entities such as the gun
+                               // (very weird, but keeps the player shadow off the gun)
+                               if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
+                                       shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
                                else
-                                       lightentities[numlightentities++] = ent;
-                               // since it is lit, it probably also casts a shadow...
-                               // about the VectorDistance2 - light emitting entities should not cast their own shadow
-                               Matrix4x4_OriginFromMatrix(&ent->matrix, org);
-                               if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
-                               {
-                                       // note: exterior models without the RENDER_NOSELFSHADOW
-                                       // flag still create a RENDER_NOSELFSHADOW shadow but
-                                       // are lit normally, this means that they are
-                                       // self-shadowing but do not shadow other
-                                       // RENDER_NOSELFSHADOW entities such as the gun
-                                       // (very weird, but keeps the player shadow off the gun)
-                                       if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
-                                               shadowentities[shadowentities_noselfshadow - numshadowentities_noselfshadow++] = ent;
-                                       else
-                                               shadowentities[numshadowentities++] = ent;
-                               }
+                                       shadowentities[numshadowentities++] = ent;
                        }
-                       else if (ent->flags & RENDER_SHADOW)
+               }
+               else if (ent->flags & RENDER_SHADOW)
+               {
+                       // this entity is not receiving light, but may still need to
+                       // cast a shadow...
+                       // TODO: check if the surfaces in the model can cast shadow
+                       // now check if it is in a leaf seen by the light
+                       if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
+                               continue;
+                       // about the VectorDistance2 - light emitting entities should not cast their own shadow
+                       Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+                       if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
                        {
-                               // this entity is not receiving light, but may still need to
-                               // cast a shadow...
-                               // TODO: check if the surfaces in the model can cast shadow
-                               // now check if it is in a leaf seen by the light
-                               if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
-                                       continue;
-                               // about the VectorDistance2 - light emitting entities should not cast their own shadow
-                               Matrix4x4_OriginFromMatrix(&ent->matrix, org);
-                               if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
-                               {
-                                       if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
-                                               shadowentities[shadowentities_noselfshadow - numshadowentities_noselfshadow++] = ent;
-                                       else
-                                               shadowentities[numshadowentities++] = ent;
-                               }
+                               if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
+                                       shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
+                               else
+                                       shadowentities[numshadowentities++] = ent;
                        }
                }
        }
@@ -3640,14 +3651,100 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        if (numsurfaces + numlightentities + numlightentities_noselfshadow == 0)
                return;
 
+       // count this light in the r_speeds
+       r_refdef.stats.lights++;
+
+       // flag it as worth drawing later
+       rtlight->draw = true;
+
+       // cache all the animated entities that cast a shadow but are not visible
+       for (i = 0;i < numshadowentities;i++)
+               if (!shadowentities[i]->animcache_vertex3f)
+                       R_AnimCache_GetEntity(shadowentities[i], false, false);
+       for (i = 0;i < numshadowentities_noselfshadow;i++)
+               if (!shadowentities_noselfshadow[i]->animcache_vertex3f)
+                       R_AnimCache_GetEntity(shadowentities_noselfshadow[i], false, false);
+
+       // allocate some temporary memory for rendering this light later in the frame
+       // reusable buffers need to be copied, static data can be used as-is
+       rtlight->cached_numlightentities               = numlightentities;
+       rtlight->cached_numlightentities_noselfshadow  = numlightentities_noselfshadow;
+       rtlight->cached_numshadowentities              = numshadowentities;
+       rtlight->cached_numshadowentities_noselfshadow = numshadowentities_noselfshadow;
+       rtlight->cached_numsurfaces                    = numsurfaces;
+       rtlight->cached_lightentities                  = (entity_render_t**)R_FrameData_Store(numlightentities*sizeof(entity_render_t*), (void*)lightentities);
+       rtlight->cached_lightentities_noselfshadow     = (entity_render_t**)R_FrameData_Store(numlightentities_noselfshadow*sizeof(entity_render_t*), (void*)lightentities_noselfshadow);
+       rtlight->cached_shadowentities                 = (entity_render_t**)R_FrameData_Store(numshadowentities*sizeof(entity_render_t*), (void*)shadowentities);
+       rtlight->cached_shadowentities_noselfshadow    = (entity_render_t**)R_FrameData_Store(numshadowentities_noselfshadow*sizeof(entity_render_t *), (void*)shadowentities_noselfshadow);
+       if (shadowtrispvs == r_shadow_buffer_shadowtrispvs)
+       {
+               int numshadowtrispvsbytes = (((r_refdef.scene.worldmodel->brush.shadowmesh ? r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles : r_refdef.scene.worldmodel->surfmesh.num_triangles) + 7) >> 3);
+               int numlighttrispvsbytes = ((r_refdef.scene.worldmodel->surfmesh.num_triangles + 7) >> 3);
+               rtlight->cached_shadowtrispvs                  =   (unsigned char *)R_FrameData_Store(numshadowtrispvsbytes, shadowtrispvs);
+               rtlight->cached_lighttrispvs                   =   (unsigned char *)R_FrameData_Store(numlighttrispvsbytes, lighttrispvs);
+               rtlight->cached_surfacelist                    =              (int*)R_FrameData_Store(numsurfaces*sizeof(int), (void*)surfacelist);
+       }
+       else
+       {
+               // compiled light data
+               rtlight->cached_shadowtrispvs = shadowtrispvs;
+               rtlight->cached_lighttrispvs = lighttrispvs;
+               rtlight->cached_surfacelist = surfacelist;
+       }
+}
+
+void R_Shadow_DrawLight(rtlight_t *rtlight)
+{
+       int i;
+       int numsurfaces;
+       unsigned char *shadowtrispvs, *lighttrispvs, *surfacesides;
+       int numlightentities;
+       int numlightentities_noselfshadow;
+       int numshadowentities;
+       int numshadowentities_noselfshadow;
+       entity_render_t **lightentities;
+       entity_render_t **lightentities_noselfshadow;
+       entity_render_t **shadowentities;
+       entity_render_t **shadowentities_noselfshadow;
+       int *surfacelist;
+       static unsigned char entitysides[MAX_EDICTS];
+       static unsigned char entitysides_noselfshadow[MAX_EDICTS];
+       vec3_t nearestpoint;
+       vec_t distance;
+       qboolean castshadows;
+       int lodlinear;
+
+       // check if we cached this light this frame (meaning it is worth drawing)
+       if (!rtlight->draw)
+               return;
+
+       // if R_FrameData_Store ran out of space we skip anything dependent on it
+       if (r_framedata_failed)
+               return;
+
+       numlightentities = rtlight->cached_numlightentities;
+       numlightentities_noselfshadow = rtlight->cached_numlightentities_noselfshadow;
+       numshadowentities = rtlight->cached_numshadowentities;
+       numshadowentities_noselfshadow = rtlight->cached_numshadowentities_noselfshadow;
+       numsurfaces = rtlight->cached_numsurfaces;
+       lightentities = rtlight->cached_lightentities;
+       lightentities_noselfshadow = rtlight->cached_lightentities_noselfshadow;
+       shadowentities = rtlight->cached_shadowentities;
+       shadowentities_noselfshadow = rtlight->cached_shadowentities_noselfshadow;
+       shadowtrispvs = rtlight->cached_shadowtrispvs;
+       lighttrispvs = rtlight->cached_lighttrispvs;
+       surfacelist = rtlight->cached_surfacelist;
+
+       // set up a scissor rectangle for this light
+       if (R_Shadow_ScissorForBBox(rtlight->cached_cullmins, rtlight->cached_cullmaxs))
+               return;
+
        // don't let sound skip if going slow
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
        // make this the active rtlight for rendering purposes
        R_Shadow_RenderMode_ActiveLight(rtlight);
-       // count this light in the r_speeds
-       r_refdef.stats.lights++;
 
        if (r_showshadowvolumes.integer && r_refdef.view.showdebug && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.scene.rtworldshadows : r_refdef.scene.rtdlightshadows))
        {
@@ -3659,7 +3756,8 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                for (i = 0;i < numshadowentities;i++)
                        R_Shadow_DrawEntityShadow(shadowentities[i]);
                for (i = 0;i < numshadowentities_noselfshadow;i++)
-                       R_Shadow_DrawEntityShadow(shadowentities[shadowentities_noselfshadow - i]);
+                       R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
+               R_Shadow_RenderMode_VisibleLighting(false, false);
        }
 
        if (r_showlighting.integer && r_refdef.view.showdebug && numsurfaces + numlightentities + numlightentities_noselfshadow)
@@ -3672,7 +3770,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                for (i = 0;i < numlightentities;i++)
                        R_Shadow_DrawEntityLight(lightentities[i]);
                for (i = 0;i < numlightentities_noselfshadow;i++)
-                       R_Shadow_DrawEntityLight(lightentities[lightentities_noselfshadow - i]);
+                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
        }
 
        castshadows = numsurfaces + numshadowentities + numshadowentities_noselfshadow > 0 && rtlight->shadow && (rtlight->isstatic ? r_refdef.scene.rtworldshadows : r_refdef.scene.rtdlightshadows);
@@ -3708,6 +3806,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        
                borderbias = r_shadow_shadowmapborder / (float)(size - r_shadow_shadowmapborder);
 
+               surfacesides = NULL;
                if (numsurfaces)
                {
                        if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
@@ -3717,6 +3816,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        }
                        else
                        {
+                               surfacesides = r_shadow_buffer_surfacesides;
                                for(i = 0;i < numsurfaces;i++)
                                {
                                        msurface_t *surface = r_refdef.scene.worldmodel->data_surfaces + surfacelist[i];
@@ -3732,7 +3832,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                                receivermask |= R_Shadow_CalcEntitySideMask(lightentities[i], &rtlight->matrix_worldtolight, &radiustolight, borderbias);
                        if (receivermask < 0x3F)
                                for(i = 0; i < numlightentities_noselfshadow;i++)
-                                       receivermask |= R_Shadow_CalcEntitySideMask(lightentities[lightentities_noselfshadow - i], &rtlight->matrix_worldtolight, &radiustolight, borderbias);
+                                       receivermask |= R_Shadow_CalcEntitySideMask(lightentities_noselfshadow[i], &rtlight->matrix_worldtolight, &radiustolight, borderbias);
                }
 
                receivermask &= R_Shadow_CullFrustumSides(rtlight, size, r_shadow_shadowmapborder);
@@ -3742,7 +3842,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        for (i = 0;i < numshadowentities;i++)
                                castermask |= (entitysides[i] = R_Shadow_CalcEntitySideMask(shadowentities[i], &rtlight->matrix_worldtolight, &radiustolight, borderbias));
                        for (i = 0;i < numshadowentities_noselfshadow;i++)
-                               castermask |= (entitysides[shadowentities_noselfshadow - i] = R_Shadow_CalcEntitySideMask(shadowentities[shadowentities_noselfshadow - i], &rtlight->matrix_worldtolight, &radiustolight, borderbias)); 
+                               castermask |= (entitysides_noselfshadow[i] = R_Shadow_CalcEntitySideMask(shadowentities_noselfshadow[i], &rtlight->matrix_worldtolight, &radiustolight, borderbias)); 
                }
 
                //Con_Printf("distance %f lodlinear %i (lod %i) size %i\n", distance, lodlinear, r_shadow_shadowmaplod, size);
@@ -3764,17 +3864,17 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        // draw lighting in the unmasked areas
                        R_Shadow_RenderMode_Lighting(false, false, true);
                        for (i = 0;i < numlightentities_noselfshadow;i++)
-                               R_Shadow_DrawEntityLight(lightentities[lightentities_noselfshadow - i]);
+                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
                }
 
                // render shadow casters into 6 sided depth texture
-               if (numshadowentities_noselfshadow) 
+               if (numshadowentities_noselfshadow)
                {
                        for (side = 0;side < 6;side++) if ((receivermask & castermask) & (1 << side))
                        {
                                R_Shadow_RenderMode_ShadowMap(side, false, size);
-                               for (i = 0;i < numshadowentities_noselfshadow;i++) if (entitysides[shadowentities_noselfshadow - i] & (1 << side))
-                                       R_Shadow_DrawEntityShadow(shadowentities[shadowentities_noselfshadow - i]);
+                               for (i = 0;i < numshadowentities_noselfshadow;i++) if (entitysides_noselfshadow[i] & (1 << side))
+                                       R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
                        }
                }
 
@@ -3794,42 +3894,25 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                GL_Scissor(r_shadow_lightscissor[0], r_shadow_lightscissor[1], r_shadow_lightscissor[2], r_shadow_lightscissor[3]);
                R_Shadow_ClearStencil();
 
-               if (numsurfaces + numshadowentities)
-               {
-                       if (numsurfaces)
-                               R_Shadow_DrawWorldShadow_ShadowVolume(numsurfaces, surfacelist, shadowtrispvs);
-                       for (i = 0;i < numshadowentities;i++)
-                               R_Shadow_DrawEntityShadow(shadowentities[i]);
-               }
+               if (numsurfaces)
+                       R_Shadow_DrawWorldShadow_ShadowVolume(numsurfaces, surfacelist, shadowtrispvs);
+               for (i = 0;i < numshadowentities;i++)
+                       R_Shadow_DrawEntityShadow(shadowentities[i]);
 
-               if (numlightentities_noselfshadow)
-               {
-                       // draw lighting in the unmasked areas
-                       R_Shadow_RenderMode_Lighting(true, false, false);
-                       for (i = 0;i < numlightentities_noselfshadow;i++)
-                               R_Shadow_DrawEntityLight(lightentities[lightentities_noselfshadow - i]);
+               // draw lighting in the unmasked areas
+               R_Shadow_RenderMode_Lighting(true, false, false);
+               for (i = 0;i < numlightentities_noselfshadow;i++)
+                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
 
-                       // optionally draw the illuminated areas
-                       // for performance analysis by level designers
-                       if (r_showlighting.integer && r_refdef.view.showdebug)
-                       {
-                               R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
-                               for (i = 0;i < numlightentities_noselfshadow;i++)
-                                       R_Shadow_DrawEntityLight(lightentities[lightentities_noselfshadow - i]);
-                       }
-                       for (i = 0;i < numshadowentities_noselfshadow;i++)
-                               R_Shadow_DrawEntityShadow(shadowentities[shadowentities_noselfshadow - i]);
-               }
+               for (i = 0;i < numshadowentities_noselfshadow;i++)
+                       R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
 
-               if (numsurfaces + numlightentities)
-               {
-                       // draw lighting in the unmasked areas
-                       R_Shadow_RenderMode_Lighting(true, false, false);
-                       if (numsurfaces)
-                               R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
-                       for (i = 0;i < numlightentities;i++)
-                               R_Shadow_DrawEntityLight(lightentities[i]);
-               }
+               // draw lighting in the unmasked areas
+               R_Shadow_RenderMode_Lighting(true, false, false);
+               if (numsurfaces)
+                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i]);
        }
        else
        {
@@ -3840,12 +3923,130 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                for (i = 0;i < numlightentities;i++)
                        R_Shadow_DrawEntityLight(lightentities[i]);
                for (i = 0;i < numlightentities_noselfshadow;i++)
-                       R_Shadow_DrawEntityLight(lightentities[lightentities_noselfshadow - i]);
+                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
+       }
+
+       if (r_shadow_usingdeferredprepass)
+       {
+               // when rendering deferred lighting, we simply rasterize the box
+               if (castshadows && (r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAP2D || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPRECTANGLE || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_SHADOWMAPCUBESIDE))
+                       R_Shadow_RenderMode_DrawDeferredLight(false, true);
+               else if (castshadows && vid.stencil)
+                       R_Shadow_RenderMode_DrawDeferredLight(true, false);
+               else
+                       R_Shadow_RenderMode_DrawDeferredLight(false, false);
        }
 }
 
+static void R_Shadow_FreeDeferred(void)
+{
+       if (r_shadow_prepassgeometryfbo)
+               qglDeleteFramebuffersEXT(1, &r_shadow_prepassgeometryfbo);CHECKGLERROR
+       r_shadow_prepassgeometryfbo = 0;
+
+       if (r_shadow_prepasslightingfbo)
+               qglDeleteFramebuffersEXT(1, &r_shadow_prepasslightingfbo);CHECKGLERROR
+       r_shadow_prepasslightingfbo = 0;
+
+       if (r_shadow_prepassgeometrydepthtexture)
+               R_FreeTexture(r_shadow_prepassgeometrydepthtexture);
+       r_shadow_prepassgeometrydepthtexture = NULL;
+
+       if (r_shadow_prepassgeometrynormalmaptexture)
+               R_FreeTexture(r_shadow_prepassgeometrynormalmaptexture);
+       r_shadow_prepassgeometrynormalmaptexture = NULL;
+
+       if (r_shadow_prepasslightingdiffusetexture)
+               R_FreeTexture(r_shadow_prepasslightingdiffusetexture);
+       r_shadow_prepasslightingdiffusetexture = NULL;
+
+       if (r_shadow_prepasslightingspeculartexture)
+               R_FreeTexture(r_shadow_prepasslightingspeculartexture);
+       r_shadow_prepasslightingspeculartexture = NULL;
+}
+
+void R_Shadow_DrawPrepass(void)
+{
+       int i;
+       int flag;
+       int lnum;
+       size_t lightindex;
+       dlight_t *light;
+       size_t range;
+       entity_render_t *ent;
+
+       GL_AlphaTest(false);
+       R_Mesh_ColorPointer(NULL, 0, 0);
+       R_Mesh_ResetTextureState();
+       GL_DepthMask(true);
+       GL_ColorMask(1,1,1,1);
+       GL_BlendFunc(GL_ONE, GL_ZERO);
+       GL_Color(1,1,1,1);
+       GL_DepthTest(true);
+       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_prepassgeometryfbo);CHECKGLERROR
+       qglClearColor(0.5f,0.5f,0.5f,1.0f);CHECKGLERROR
+       GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);CHECKGLERROR
+
+       if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawPrepass)
+               r_refdef.scene.worldmodel->DrawPrepass(r_refdef.scene.worldentity);
+       if (r_timereport_active)
+               R_TimeReport("prepassgeometry");
+
+       for (i = 0;i < r_refdef.scene.numentities;i++)
+       {
+               if (!r_refdef.viewcache.entityvisible[i])
+                       continue;
+               ent = r_refdef.scene.entities[i];
+               if (ent->model && ent->model->DrawPrepass != NULL)
+                       ent->model->DrawPrepass(ent);
+       }
+
+       GL_DepthMask(false);
+       GL_ColorMask(1,1,1,1);
+       GL_Color(1,1,1,1);
+       GL_DepthTest(true);
+       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_prepasslightingfbo);CHECKGLERROR
+       qglClearColor(0.0f,0.0f,0.0f,0.0f);CHECKGLERROR
+       GL_Clear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
+       if (r_refdef.fogenabled)
+               qglClearColor(r_refdef.fogcolor[0],r_refdef.fogcolor[1],r_refdef.fogcolor[2],0);CHECKGLERROR
+
+       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
+       {
+               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++)
+                       R_Shadow_DrawLight(r_refdef.scene.lights[lnum]);
+
+       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);CHECKGLERROR
+       if (r_refdef.fogenabled)
+               qglClearColor(r_refdef.fogcolor[0],r_refdef.fogcolor[1],r_refdef.fogcolor[2],0);CHECKGLERROR
+
+       R_Shadow_RenderMode_End();
+
+       if (r_timereport_active)
+               R_TimeReport("prepasslights");
+}
+
 void R_Shadow_DrawLightSprites(void);
-void R_ShadowVolumeLighting(qboolean visible)
+void R_Shadow_PrepareLights(void)
 {
        int flag;
        int lnum;
@@ -3853,9 +4054,10 @@ void R_ShadowVolumeLighting(qboolean visible)
        dlight_t *light;
        size_t range;
        float f;
+       GLenum status;
 
        if (r_shadow_shadowmapmaxsize != bound(1, r_shadow_shadowmapping_maxsize.integer, (int)vid.maxtexturesize_2d / 4) ||
-               (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL) != r_shadow_shadowmapping.integer ||
+               (r_shadow_shadowmode != R_SHADOW_SHADOWMODE_STENCIL) != (r_shadow_shadowmapping.integer || r_shadow_deferred.integer) ||
                r_shadow_shadowmapvsdct != (r_shadow_shadowmapping_vsdct.integer != 0) || 
                r_shadow_shadowmaptexturetype != r_shadow_shadowmapping_texturetype.integer ||
                r_shadow_shadowmapfilterquality != r_shadow_shadowmapping_filterquality.integer || 
@@ -3863,10 +4065,73 @@ void R_ShadowVolumeLighting(qboolean visible)
                r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
                R_Shadow_FreeShadowMaps();
 
-       if (r_editlights.integer)
-               R_Shadow_DrawLightSprites();
+       switch (vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+               if (!r_shadow_deferred.integer || r_shadow_shadowmode == R_SHADOW_SHADOWMODE_STENCIL || !vid.support.ext_framebuffer_object || !vid.support.arb_texture_rectangle || vid.maxdrawbuffers < 2)
+               {
+                       r_shadow_usingdeferredprepass = false;
+                       if (r_shadow_prepass_width)
+                               R_Shadow_FreeDeferred();
+                       r_shadow_prepass_width = r_shadow_prepass_height = 0;
+                       break;
+               }
 
-       R_Shadow_RenderMode_Begin();
+               if (r_shadow_prepass_width != vid.width || r_shadow_prepass_height != vid.height)
+               {
+                       R_Shadow_FreeDeferred();
+
+                       r_shadow_usingdeferredprepass = true;
+                       r_shadow_prepass_width = vid.width;
+                       r_shadow_prepass_height = vid.height;
+                       r_shadow_prepassgeometrydepthtexture = R_LoadTextureShadowMapRectangle(r_shadow_texturepool, "prepassgeometrydepthmap", vid.width, vid.height, 24, false);
+                       r_shadow_prepassgeometrynormalmaptexture = R_LoadTextureRectangle(r_shadow_texturepool, "prepassgeometrynormalmap", vid.width, vid.height, NULL, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL);
+                       r_shadow_prepasslightingdiffusetexture = R_LoadTextureRectangle(r_shadow_texturepool, "prepasslightingdiffuse", vid.width, vid.height, NULL, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL);
+                       r_shadow_prepasslightingspeculartexture = R_LoadTextureRectangle(r_shadow_texturepool, "prepasslightingspecular", vid.width, vid.height, NULL, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_FORCENEAREST, NULL);
+
+                       // set up the geometry pass fbo (depth + normalmap)
+                       qglGenFramebuffersEXT(1, &r_shadow_prepassgeometryfbo);CHECKGLERROR
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_prepassgeometryfbo);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_prepassgeometrydepthtexture), 0);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_prepassgeometrynormalmaptexture), 0);CHECKGLERROR
+                       // render depth into one texture and normalmap into the other
+                       qglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);CHECKGLERROR
+                       qglReadBuffer(GL_NONE);CHECKGLERROR
+                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                       {
+                               Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
+                               Cvar_SetValueQuick(&r_shadow_deferred, 0);
+                               r_shadow_usingdeferredprepass = false;
+                       }
+
+                       // set up the lighting pass fbo (diffuse + specular)
+                       qglGenFramebuffersEXT(1, &r_shadow_prepasslightingfbo);CHECKGLERROR
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_prepasslightingfbo);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_prepassgeometrydepthtexture), 0);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_prepasslightingdiffusetexture), 0);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_prepasslightingspeculartexture), 0);CHECKGLERROR
+                       // render diffuse into one texture and specular into another,
+                       // with depth and normalmap bound as textures,
+                       // with depth bound as attachment as well
+                       qglDrawBuffersARB(2, r_shadow_prepasslightingdrawbuffers);CHECKGLERROR
+                       qglReadBuffer(GL_NONE);CHECKGLERROR
+                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                       {
+                               Con_Printf("R_PrepareRTLights: glCheckFramebufferStatusEXT returned %i\n", status);
+                               Cvar_SetValueQuick(&r_shadow_deferred, 0);
+                               r_shadow_usingdeferredprepass = false;
+                       }
+               }
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               r_shadow_usingdeferredprepass = false;
+               break;
+       }
+
+       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)
@@ -3874,7 +4139,7 @@ void R_ShadowVolumeLighting(qboolean visible)
                lightindex = r_shadow_debuglight.integer;
                light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
                if (light && (light->flags & flag))
-                       R_DrawRTLight(&light->rtlight, visible);
+                       R_Shadow_PrepareLight(&light->rtlight);
        }
        else
        {
@@ -3883,13 +4148,13 @@ void R_ShadowVolumeLighting(qboolean visible)
                {
                        light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
                        if (light && (light->flags & flag))
-                               R_DrawRTLight(&light->rtlight, visible);
+                               R_Shadow_PrepareLight(&light->rtlight);
                }
        }
        if (r_refdef.scene.rtdlight)
        {
                for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++)
-                       R_DrawRTLight(r_refdef.scene.lights[lnum], visible);
+                       R_Shadow_PrepareLight(r_refdef.scene.lights[lnum]);
        }
        else if(gl_flashblend.integer)
        {
@@ -3901,6 +4166,42 @@ void R_ShadowVolumeLighting(qboolean visible)
                }
        }
 
+       if (r_editlights.integer)
+               R_Shadow_DrawLightSprites();
+}
+
+void R_Shadow_DrawLights(void)
+{
+       int flag;
+       int lnum;
+       size_t lightindex;
+       dlight_t *light;
+       size_t range;
+
+       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
+       {
+               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++)
+                       R_Shadow_DrawLight(r_refdef.scene.lights[lnum]);
+
        R_Shadow_RenderMode_End();
 }
 
@@ -3924,7 +4225,7 @@ void R_DrawModelShadows(void)
        vec3_t relativeshadowmins, relativeshadowmaxs;
        vec3_t tmp, shadowdir;
 
-       if (!r_drawentities.integer || !vid.stencil)
+       if (!r_refdef.scene.numentities || !vid.stencil)
                return;
 
        CHECKGLERROR
@@ -3990,7 +4291,7 @@ void R_DrawModelShadows(void)
                        }
 
                        VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
-                       RSurf_ActiveModelEntity(ent, false, false);
+                       RSurf_ActiveModelEntity(ent, false, false, false);
                        ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs);
                        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                }
@@ -4108,13 +4409,13 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                }
                R_CalcSprite_Vertex3f(vertex3f, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale);
                RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, RENDER_NODEPTHTEST, 0, color[0], color[1], color[2], 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false);
-               R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false);
+               R_DrawCustomSurface(r_shadow_lightcorona, &identitymatrix, MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
                if(negated)
                        qglBlendEquationEXT(GL_FUNC_ADD_EXT);
        }
 }
 
-void R_DrawCoronas(void)
+void R_Shadow_DrawCoronas(void)
 {
        int i, flag;
        qboolean usequery;
@@ -4435,7 +4736,7 @@ void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const r
        float vertex3f[12];
        R_CalcSprite_Vertex3f(vertex3f, r_editlights_cursorlocation, r_refdef.view.right, r_refdef.view.up, EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, EDLIGHTSPRSIZE);
        RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, 0, 0, 1, 1, 1, 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false);
-       R_DrawCustomSurface(r_editlights_sprcursor, &identitymatrix, MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false);
+       R_DrawCustomSurface(r_editlights_sprcursor, &identitymatrix, MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
 }
 
 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
@@ -4471,13 +4772,13 @@ void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, co
                skinframe = r_editlights_sprlight;
 
        RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, 0, 0, spritecolor[0], spritecolor[1], spritecolor[2], 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false);
-       R_DrawCustomSurface(skinframe, &identitymatrix, MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false);
+       R_DrawCustomSurface(skinframe, &identitymatrix, MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
 
        // draw selection sprite if light is selected
        if (light->selected)
        {
                RSurf_ActiveCustomEntity(&identitymatrix, &identitymatrix, 0, 0, 1, 1, 1, 1, 4, vertex3f, spritetexcoord2f, NULL, NULL, NULL, NULL, 2, polygonelement3i, polygonelement3s, false, false);
-               R_DrawCustomSurface(r_editlights_sprselection, &identitymatrix, MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false);
+               R_DrawCustomSurface(r_editlights_sprselection, &identitymatrix, MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOCULLFACE, 0, 4, 0, 2, false, false);
                // VorteX todo: add normalmode/realtime mode light overlay sprites?
        }
 }
@@ -5639,7 +5940,7 @@ void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffu
 
        if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
        {
-               ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_refdef.scene.ambient * (2.0f / 128.0f);
+               ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_refdef.scene.ambient;
                r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
        }
        else