]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
new commands: prvm_edictget, prvm_globalget - read values from edicts/globals into...
[xonotic/darkplaces.git] / r_shadow.c
index f0c09fa2b034bb3fc4915cd74200fd9f5c31fb0e..7fde55273a142d3bc4234c18f21997419c633de7 100644 (file)
@@ -140,25 +140,48 @@ demonstrated by the game Doom3.
 #include "portals.h"
 #include "image.h"
 
+#define R_SHADOW_SHADOWMAP_NUMCUBEMAPS 8
+
 extern void R_Shadow_EditLights_Init(void);
 
 typedef enum r_shadow_rendermode_e
 {
        R_SHADOW_RENDERMODE_NONE,
-       R_SHADOW_RENDERMODE_STENCIL,
-       R_SHADOW_RENDERMODE_SEPARATESTENCIL,
-       R_SHADOW_RENDERMODE_STENCILTWOSIDE,
+       R_SHADOW_RENDERMODE_ZPASS_STENCIL,
+       R_SHADOW_RENDERMODE_ZPASS_SEPARATESTENCIL,
+       R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE,
+       R_SHADOW_RENDERMODE_ZFAIL_STENCIL,
+       R_SHADOW_RENDERMODE_ZFAIL_SEPARATESTENCIL,
+       R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE,
        R_SHADOW_RENDERMODE_LIGHT_VERTEX,
        R_SHADOW_RENDERMODE_LIGHT_DOT3,
        R_SHADOW_RENDERMODE_LIGHT_GLSL,
        R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
        R_SHADOW_RENDERMODE_VISIBLELIGHTING,
+       R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE,
+       R_SHADOW_RENDERMODE_SHADOWMAP2D,
+       R_SHADOW_RENDERMODE_SHADOWMAPCUBESIDE,
 }
 r_shadow_rendermode_t;
 
 r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
-r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
+r_shadow_rendermode_t r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_NONE;
+r_shadow_rendermode_t r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_NONE;
+qboolean r_shadow_usingshadowmaprect;
+qboolean r_shadow_usingshadowmap2d;
+qboolean r_shadow_usingshadowmapcube;
+float r_shadow_shadowmap_bias;
+float r_shadow_shadowmap_texturescale[2];
+float r_shadow_shadowmap_parameters[4];
+int r_shadow_drawbuffer;
+int r_shadow_readbuffer;
+GLuint r_shadow_fborectangle;
+GLuint r_shadow_fbocubeside[R_SHADOW_SHADOWMAP_NUMCUBEMAPS][6];
+GLuint r_shadow_fbo2d;
+int r_shadow_shadowmode;
+int r_shadow_shadowmapmaxsize;
+int r_shadow_lightscissor[4];
 
 int maxshadowtriangles;
 int *shadowelements;
@@ -196,6 +219,12 @@ rtexture_t *r_shadow_attenuationgradienttexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
 rtexture_t *r_shadow_lightcorona;
+rtexture_t *r_shadow_shadowmaprectangletexture;
+rtexture_t *r_shadow_shadowmap2dtexture;
+rtexture_t *r_shadow_shadowmapcubeprojectiontexture;
+rtexture_t *r_shadow_shadowmapcubetexture[R_SHADOW_SHADOWMAP_NUMCUBEMAPS];
+int r_shadow_shadowmapsize; // changes for each light based on distance
+int r_shadow_shadowmaplod; // changes for each light based on distance
 
 // lights are reloaded when this changes
 char r_shadow_mapname[MAX_QPATH];
@@ -211,7 +240,7 @@ cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (sp
 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"};
 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
-cvar_t r_shadow_glossexact = {0, "r_shadow_glossexact", "1", "use exact reflection math for gloss (slightly slower, but should look a tad better)"};
+cvar_t r_shadow_glossexact = {0, "r_shadow_glossexact", "0", "use exact reflection math for gloss (slightly slower, but should look a tad better)"};
 cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
 cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
@@ -231,6 +260,15 @@ cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_comp
 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
+cvar_t r_shadow_shadowmapping = {CVAR_SAVE, "r_shadow_shadowmapping", "0", "enables use of shadowmapping (depth texture sampling) instead of stencil shadow volumes, requires gl_fbo 1"};
+cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "0", "shadowmap filter modes: 0 = no filtering, 1 = bilinear, 2 = bilinear small blur (fast), 3 = bilinear large blur (slow)"};
+cvar_t r_shadow_shadowmapping_minsize = {CVAR_SAVE, "r_shadow_shadowmapping_minsize", "32", "shadowmap size limit"};
+cvar_t r_shadow_shadowmapping_maxsize = {CVAR_SAVE, "r_shadow_shadowmapping_maxsize", "1024", "shadowmap size limit"};
+cvar_t r_shadow_shadowmapping_lod_bias = {CVAR_SAVE, "r_shadow_shadowmapping_lod_bias", "16", "shadowmap size bias"};
+cvar_t r_shadow_shadowmapping_lod_scale = {CVAR_SAVE, "r_shadow_shadowmapping_lod_scale", "128", "shadowmap size scaling parameter"};
+cvar_t r_shadow_shadowmapping_bordersize = {CVAR_SAVE, "r_shadow_shadowmapping_bordersize", "6", "shadowmap size bias for filtering"};
+cvar_t r_shadow_shadowmapping_nearclip = {CVAR_SAVE, "r_shadow_shadowmapping_nearclip", "1", "shadowmap nearclip in world units"};
+cvar_t r_shadow_shadowmapping_bias = {CVAR_SAVE, "r_shadow_shadowmapping_bias", "0.03", "shadowmap bias parameter (this is multiplied by nearclip * 1024 / lodsize)"};
 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
@@ -297,6 +335,51 @@ cachepic_t *r_editlights_sprcubemaplight;
 cachepic_t *r_editlights_sprcubemapnoshadowlight;
 cachepic_t *r_editlights_sprselection;
 
+void R_Shadow_FreeShadowMaps(void)
+{
+       int i;
+
+       r_shadow_shadowmapmaxsize = bound(1, r_shadow_shadowmapping_maxsize.integer, 2048);
+       r_shadow_shadowmode = r_shadow_shadowmapping.integer;
+       r_shadow_shadowmaplod = -1;
+
+       CHECKGLERROR
+       if (r_shadow_fborectangle)
+               qglDeleteFramebuffersEXT(1, &r_shadow_fborectangle);
+       r_shadow_fborectangle = 0;
+       CHECKGLERROR
+
+       if (r_shadow_fbo2d)
+               qglDeleteFramebuffersEXT(1, &r_shadow_fbo2d);
+       r_shadow_fbo2d = 0;
+       CHECKGLERROR
+
+       for (i = 0;i < R_SHADOW_SHADOWMAP_NUMCUBEMAPS;i++)
+               if (r_shadow_fbocubeside[i])
+                       qglDeleteFramebuffersEXT(6, r_shadow_fbocubeside[i]);
+       memset(r_shadow_fbocubeside, 0, sizeof(r_shadow_fbocubeside));
+       CHECKGLERROR
+
+       if (r_shadow_shadowmaprectangletexture)
+               R_FreeTexture(r_shadow_shadowmaprectangletexture);
+       r_shadow_shadowmaprectangletexture = NULL;
+
+       if (r_shadow_shadowmap2dtexture)
+               R_FreeTexture(r_shadow_shadowmap2dtexture);
+       r_shadow_shadowmap2dtexture = NULL;
+
+       if (r_shadow_shadowmapcubeprojectiontexture)
+               R_FreeTexture(r_shadow_shadowmapcubeprojectiontexture);
+       r_shadow_shadowmapcubeprojectiontexture = NULL;
+
+       for (i = 0;i < R_SHADOW_SHADOWMAP_NUMCUBEMAPS;i++)
+               if (r_shadow_shadowmapcubetexture[i])
+                       R_FreeTexture(r_shadow_shadowmapcubetexture[i]);
+       memset(r_shadow_shadowmapcubetexture, 0, sizeof(r_shadow_shadowmapcubetexture));
+
+       CHECKGLERROR
+}
+
 void r_shadow_start(void)
 {
        // allocate vertex processing arrays
@@ -304,6 +387,19 @@ void r_shadow_start(void)
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
+       r_shadow_shadowmaprectangletexture = NULL;
+       memset(r_shadow_shadowmapcubetexture, 0, sizeof(r_shadow_shadowmapcubetexture));
+       r_shadow_shadowmapcubeprojectiontexture = NULL;
+       r_shadow_shadowmap2dtexture = NULL;
+       r_shadow_shadowmapmaxsize = 0;
+       r_shadow_shadowmapsize = 0;
+       r_shadow_shadowmaplod = 0;
+       r_shadow_fborectangle = 0;
+       memset(r_shadow_fbocubeside, 0, sizeof(r_shadow_fbocubeside));
+       r_shadow_fbo2d = 0;
+
+       R_Shadow_FreeShadowMaps();
+
        r_shadow_texturepool = NULL;
        r_shadow_filters_texturepool = NULL;
        R_Shadow_ValidateCvars();
@@ -336,7 +432,12 @@ void r_shadow_start(void)
 
 void r_shadow_shutdown(void)
 {
+       CHECKGLERROR
        R_Shadow_UncompileWorldLights();
+
+       R_Shadow_FreeShadowMaps();
+
+       CHECKGLERROR
        numcubemaps = 0;
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
@@ -463,6 +564,15 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
        Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
        Cvar_RegisterVariable(&r_shadow_scissor);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_filterquality);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_maxsize);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_minsize);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_lod_bias);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_lod_scale);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_bordersize);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_nearclip);
+       Cvar_RegisterVariable(&r_shadow_shadowmapping_bias);
        Cvar_RegisterVariable(&r_shadow_culltriangles);
        Cvar_RegisterVariable(&r_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_polygonoffset);
@@ -812,7 +922,6 @@ static int R_Shadow_ConstructShadowVolume_ZFail(int innumvertices, int innumtris
        return outtriangles;
 }
 
-#if 0
 static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, const float *projectdirection, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
 {
        int i, j, k;
@@ -835,15 +944,16 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
 
                markindex = shadowmarktris[i] * 3;
                neighbortriangle = inneighbor3i + markindex;
-               side[0] = shadowmark[neighbortriangle[0]] != shadowmarkcount;
-               side[1] = shadowmark[neighbortriangle[1]] != shadowmarkcount;
-               side[2] = shadowmark[neighbortriangle[2]] != shadowmarkcount;
+               side[0] = shadowmark[neighbortriangle[0]] == shadowmarkcount;
+               side[1] = shadowmark[neighbortriangle[1]] == shadowmarkcount;
+               side[2] = shadowmark[neighbortriangle[2]] == shadowmarkcount;
                if (side[0] + side[1] + side[2] == 0)
                        continue;
 
                side[3] = side[0];
                element = inelement3i + markindex;
 
+               // create the vertices
                for (j = 0;j < 3;j++)
                {
                        if (side[j] + side[j+1] == 0)
@@ -853,7 +963,7 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
                        {
                                vertexupdate[k] = vertexupdatenum;
                                vertexremap[k] = outvertices;
-                               vertex = invertex3f + element[j] * 3;
+                               vertex = invertex3f + k * 3;
                                VectorCopy(vertex, outvertex3f);
                                if (projectdirection)
                                {
@@ -874,7 +984,7 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
                }
 
                // output the sides (facing outward from this triangle)
-               if (side[0])
+               if (!side[0])
                {
                        remappedelement[0] = vertexremap[element[0]];
                        remappedelement[1] = vertexremap[element[1]];
@@ -888,7 +998,7 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
                        outelement3i += 6;
                        outtriangles += 2;
                }
-               if (side[1])
+               if (!side[1])
                {
                        remappedelement[1] = vertexremap[element[1]];
                        remappedelement[2] = vertexremap[element[2]];
@@ -902,7 +1012,7 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
                        outelement3i += 6;
                        outtriangles += 2;
                }
-               if (side[2])
+               if (!side[2])
                {
                        remappedelement[0] = vertexremap[element[0]];
                        remappedelement[2] = vertexremap[element[2]];
@@ -921,7 +1031,6 @@ static int R_Shadow_ConstructShadowVolume_ZPass(int innumvertices, int innumtris
                *outnumvertices = outvertices;
        return outtriangles;
 }
-#endif
 
 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
 {
@@ -1039,18 +1148,36 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        if (r_shadow_compilingrtlight)
        {
                // if we're compiling an rtlight, capture the mesh
+               //tris = R_Shadow_ConstructShadowVolume_ZPass(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_zpass, NULL, NULL, NULL, shadowvertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
                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
        {
-               tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
+               // decide which type of shadow to generate and set stencil mode
+               R_Shadow_RenderMode_StencilShadowVolumes(R_Shadow_UseZPass(trismins, trismaxs));
+               // generate the sides or a solid volume, depending on type
+               if (r_shadow_rendermode >= R_SHADOW_RENDERMODE_ZPASS_STENCIL && r_shadow_rendermode <= R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE)
+                       tris = R_Shadow_ConstructShadowVolume_ZPass(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
+               else
+                       tris = R_Shadow_ConstructShadowVolume_ZFail(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
                r_refdef.stats.lights_dynamicshadowtriangles += tris;
                r_refdef.stats.lights_shadowtriangles += tris;
                CHECKGLERROR
                R_Mesh_VertexPointer(shadowvertex3f, 0, 0);
                GL_LockArrays(0, outverts);
-               if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
+               if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL)
+               {
+                       // increment stencil if frontface is infront of depthbuffer
+                       GL_CullFace(r_refdef.view.cullface_front);
+                       qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR);CHECKGLERROR
+                       R_Mesh_Draw(0, outverts, 0, tris, shadowelements, NULL, 0, 0);
+                       // decrement stencil if backface is infront of depthbuffer
+                       GL_CullFace(r_refdef.view.cullface_back);
+                       qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);CHECKGLERROR
+               }
+               else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZFAIL_STENCIL)
                {
                        // decrement stencil if backface is behind depthbuffer
                        GL_CullFace(r_refdef.view.cullface_front);
@@ -1066,6 +1193,34 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        }
 }
 
+void R_Shadow_ShadowMapFromList(int numverts, int numtris, const float *vertex3f, int vertex3f_bufferobject, int vertex3f_bufferoffset, const int *elements, int nummarktris, const int *marktris)
+{
+       int i, tris = nummarktris;
+       int *outelement3i;
+       const int *element;
+       if (!numverts || !nummarktris)
+               return;
+       // make sure shadowelements is big enough for this mesh
+       if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
+               R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
+
+       // gather up the (sparse) triangles into one array
+       outelement3i = shadowelements;
+       for (i = 0;i < nummarktris;i++)
+       {
+               element = elements + marktris[i] * 3;
+               outelement3i[0] = element[0];
+               outelement3i[1] = element[1];
+               outelement3i[2] = element[2];
+               outelement3i += 3;
+       }
+
+       r_refdef.stats.lights_dynamicshadowtriangles += tris;
+       r_refdef.stats.lights_shadowtriangles += tris;
+       R_Mesh_VertexPointer(vertex3f, vertex3f_bufferobject, vertex3f_bufferoffset);
+       R_Mesh_Draw(0, numverts, 0, tris, shadowelements, NULL, 0, 0);
+}
+
 static void R_Shadow_MakeTextures_MakeCorona(void)
 {
        float dx, dy;
@@ -1158,6 +1313,8 @@ void R_Shadow_ValidateCvars(void)
 
 void R_Shadow_RenderMode_Begin(void)
 {
+       GLint drawbuffer;
+       GLint readbuffer;
        R_Shadow_ValidateCvars();
 
        if (!r_shadow_attenuation2dtexture
@@ -1175,16 +1332,25 @@ void R_Shadow_RenderMode_Begin(void)
        GL_DepthTest(true);
        GL_DepthMask(false);
        GL_Color(0, 0, 0, 1);
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height);
 
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 
        if (gl_ext_separatestencil.integer)
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_SEPARATESTENCIL;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_SEPARATESTENCIL;
+       }
        else if (gl_ext_stenciltwoside.integer)
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE;
+       }
        else
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCIL;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCIL;
+       }
 
        if (r_glsl.integer && gl_support_fragment_shader)
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
@@ -1192,6 +1358,12 @@ void R_Shadow_RenderMode_Begin(void)
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
        else
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
+
+       CHECKGLERROR
+       qglGetIntegerv(GL_DRAW_BUFFER, &drawbuffer);CHECKGLERROR
+       qglGetIntegerv(GL_READ_BUFFER, &readbuffer);CHECKGLERROR
+       r_shadow_drawbuffer = drawbuffer;
+       r_shadow_readbuffer = readbuffer;
 }
 
 void R_Shadow_RenderMode_ActiveLight(const rtlight_t *rtlight)
@@ -1202,10 +1374,18 @@ void R_Shadow_RenderMode_ActiveLight(const rtlight_t *rtlight)
 void R_Shadow_RenderMode_Reset(void)
 {
        CHECKGLERROR
-       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE || r_shadow_rendermode == R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE)
        {
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
        }
+       if (gl_support_ext_framebuffer_object)
+       {
+               qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);CHECKGLERROR
+       }
+       qglDrawBuffer(r_shadow_drawbuffer);CHECKGLERROR
+       qglReadBuffer(r_shadow_readbuffer);CHECKGLERROR
+       R_SetViewport(&r_refdef.view.viewport);
+       GL_Scissor(r_shadow_lightscissor[0], r_shadow_lightscissor[1], r_shadow_lightscissor[2], r_shadow_lightscissor[3]);
        R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        GL_DepthRange(0, 1);
@@ -1222,6 +1402,10 @@ void R_Shadow_RenderMode_Reset(void)
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        R_SetupGenericShader(false);
+       r_shadow_usingshadowmaprect = false;
+       r_shadow_usingshadowmapcube = false;
+       r_shadow_usingshadowmap2d = false;
+       CHECKGLERROR
 }
 
 void R_Shadow_ClearStencil(void)
@@ -1231,8 +1415,11 @@ void R_Shadow_ClearStencil(void)
        r_refdef.stats.lights_clears++;
 }
 
-void R_Shadow_RenderMode_StencilShadowVolumes(void)
+void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass)
 {
+       r_shadow_rendermode_t mode = zpass ? r_shadow_shadowingrendermode_zpass : r_shadow_shadowingrendermode_zfail;
+       if (r_shadow_rendermode == mode)
+               return;
        CHECKGLERROR
        R_Shadow_RenderMode_Reset();
        GL_ColorMask(0, 0, 0, 0);
@@ -1240,15 +1427,32 @@ void R_Shadow_RenderMode_StencilShadowVolumes(void)
        R_SetupDepthOrShadowShader();
        qglDepthFunc(GL_LESS);CHECKGLERROR
        qglEnable(GL_STENCIL_TEST);CHECKGLERROR
-       r_shadow_rendermode = r_shadow_shadowingrendermode;
-       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
+       r_shadow_rendermode = mode;
+       switch(mode)
        {
+       default:
+               break;
+       case R_SHADOW_RENDERMODE_ZPASS_SEPARATESTENCIL:
+               GL_CullFace(GL_NONE);
+               qglStencilOpSeparate(r_refdef.view.cullface_front, GL_KEEP, GL_KEEP, GL_INCR);CHECKGLERROR
+               qglStencilOpSeparate(r_refdef.view.cullface_back, GL_KEEP, GL_KEEP, GL_DECR);CHECKGLERROR
+               break;
+       case R_SHADOW_RENDERMODE_ZFAIL_SEPARATESTENCIL:
                GL_CullFace(GL_NONE);
                qglStencilOpSeparate(r_refdef.view.cullface_front, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
                qglStencilOpSeparate(r_refdef.view.cullface_back, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
-       }
-       else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
-       {
+               break;
+       case R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE:
+               GL_CullFace(GL_NONE);
+               qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
+               qglActiveStencilFaceEXT(r_refdef.view.cullface_front);CHECKGLERROR
+               qglStencilMask(~0);CHECKGLERROR
+               qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);CHECKGLERROR
+               qglActiveStencilFaceEXT(r_refdef.view.cullface_back);CHECKGLERROR
+               qglStencilMask(~0);CHECKGLERROR
+               qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR);CHECKGLERROR
+               break;
+       case R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE:
                GL_CullFace(GL_NONE);
                qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
                qglActiveStencilFaceEXT(r_refdef.view.cullface_front);CHECKGLERROR
@@ -1257,10 +1461,162 @@ void R_Shadow_RenderMode_StencilShadowVolumes(void)
                qglActiveStencilFaceEXT(r_refdef.view.cullface_back);CHECKGLERROR
                qglStencilMask(~0);CHECKGLERROR
                qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
+               break;
+       }
+}
+
+void R_Shadow_RenderMode_ShadowMap(int side, qboolean clear, int size)
+{
+       int i;
+       int status;
+       int maxsize;
+       float nearclip, farclip;
+       r_viewport_t viewport;
+       CHECKGLERROR
+       maxsize = bound(1, r_shadow_shadowmapping_maxsize.integer, 2048);
+       nearclip = r_shadow_shadowmapping_nearclip.value / rsurface.rtlight->radius;
+       farclip = 1.0f;
+       r_shadow_shadowmap_bias = r_shadow_shadowmapping_bias.value * nearclip * (1024.0f / size);// * rsurface.rtlight->radius;
+       r_shadow_shadowmap_parameters[0] = 1.0f - r_shadow_shadowmapping_bordersize.value / size;
+       r_shadow_shadowmap_parameters[1] = 1.0f - r_shadow_shadowmapping_bordersize.value / size;
+       r_shadow_shadowmap_parameters[2] = -(farclip + nearclip) / (farclip - nearclip);
+       r_shadow_shadowmap_parameters[3] = -2.0f * nearclip * farclip / (farclip - nearclip);
+       if (r_shadow_shadowmapping.integer == 1)
+       {
+               // complex unrolled cube approach (more flexible)
+               //if (!r_shadow_shadowmapcubeprojectiontexture)
+               //      r_shadow_shadowmapcubeprojectiontexture = R_LoadTextureCubeProjection(r_shadow_texturepool, "shadowmapcubeprojection");
+               if (!r_shadow_shadowmap2dtexture)
+               {
+#if 1
+                       r_shadow_shadowmap2dtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", size*2, size*4);
+                       qglGenFramebuffersEXT(1, &r_shadow_fbo2d);CHECKGLERROR
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fbo2d);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, R_GetTexture(r_shadow_shadowmap2dtexture), 0);CHECKGLERROR
+#endif
+               }
+               CHECKGLERROR
+               R_Shadow_RenderMode_Reset();
+               if (r_shadow_shadowmap2dtexture)
+               {
+                       // render depth into the fbo, do not render color at all
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fbo2d);CHECKGLERROR
+                       qglDrawBuffer(GL_NONE);CHECKGLERROR
+                       qglReadBuffer(GL_NONE);CHECKGLERROR
+                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                       {
+                               Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
+                               Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                       }
+                       R_SetupDepthOrShadowShader();
+               }
+               else
+               {
+                       R_SetupShowDepthShader();
+                       qglClearColor(1,1,1,1);CHECKGLERROR
+               }
+               R_Viewport_InitRectSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, r_shadow_shadowmapping_bordersize.integer, nearclip, farclip, NULL);
+               r_shadow_shadowmap_texturescale[0] = (float)size / R_TextureWidth(r_shadow_shadowmap2dtexture);
+               r_shadow_shadowmap_texturescale[1] = (float)size / R_TextureHeight(r_shadow_shadowmap2dtexture);
+               r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAP2D;
+       }
+       else if (r_shadow_shadowmapping.integer == 2)
+       {
+               // complex unrolled cube approach (more flexible)
+               //if (!r_shadow_shadowmapcubeprojectiontexture)
+               //      r_shadow_shadowmapcubeprojectiontexture = R_LoadTextureCubeProjection(r_shadow_texturepool, "shadowmapcubeprojection");
+               if (!r_shadow_shadowmaprectangletexture)
+               {
+#if 1
+                       r_shadow_shadowmaprectangletexture = R_LoadTextureShadowMapRectangle(r_shadow_texturepool, "shadowmap", size*2, size*4);
+                       qglGenFramebuffersEXT(1, &r_shadow_fborectangle);CHECKGLERROR
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fborectangle);CHECKGLERROR
+                       qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_shadowmaprectangletexture), 0);CHECKGLERROR
+#endif
+               }
+               CHECKGLERROR
+               R_Shadow_RenderMode_Reset();
+               if (r_shadow_shadowmaprectangletexture)
+               {
+                       // render depth into the fbo, do not render color at all
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fborectangle);CHECKGLERROR
+                       qglDrawBuffer(GL_NONE);CHECKGLERROR
+                       qglReadBuffer(GL_NONE);CHECKGLERROR
+                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                       {
+                               Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
+                               Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                       }
+                       R_SetupDepthOrShadowShader();
+               }
+               else
+               {
+                       R_SetupShowDepthShader();
+                       qglClearColor(1,1,1,1);CHECKGLERROR
+               }
+               R_Viewport_InitRectSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, r_shadow_shadowmapping_bordersize.integer, nearclip, farclip, NULL);
+               r_shadow_shadowmap_texturescale[0] = size;
+               r_shadow_shadowmap_texturescale[1] = size;
+               r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE;
        }
+       else if (r_shadow_shadowmapping.integer == 3)
+       {
+               // simple cube approach
+               if (!r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod])
+               {
+#if 1
+                       r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod] = R_LoadTextureShadowMapCube(r_shadow_texturepool, "shadowmapcube", size);
+                       qglGenFramebuffersEXT(6, r_shadow_fbocubeside[r_shadow_shadowmaplod]);CHECKGLERROR
+                       for (i = 0;i < 6;i++)
+                       {
+                               qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fbocubeside[r_shadow_shadowmaplod][i]);CHECKGLERROR
+                               qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, R_GetTexture(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]), 0);CHECKGLERROR
+                       }
+#endif
+               }
+               CHECKGLERROR
+               R_Shadow_RenderMode_Reset();
+               if (r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod])
+               {
+                       // render depth into the fbo, do not render color at all
+                       qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fbocubeside[r_shadow_shadowmaplod][side]);CHECKGLERROR
+                       qglDrawBuffer(GL_NONE);CHECKGLERROR
+                       qglReadBuffer(GL_NONE);CHECKGLERROR
+                       status = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);CHECKGLERROR
+                       if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+                       {
+                               Con_Printf("R_Shadow_RenderMode_ShadowMap: glCheckFramebufferStatusEXT returned %i\n", status);
+                               Cvar_SetValueQuick(&r_shadow_shadowmapping, 0);
+                       }
+                       R_SetupDepthOrShadowShader();
+               }
+               else
+               {
+                       R_SetupShowDepthShader();
+                       qglClearColor(1,1,1,1);CHECKGLERROR
+               }
+               R_Viewport_InitCubeSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, nearclip, farclip, NULL);
+               r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);
+               r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureWidth(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);
+               r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAPCUBESIDE;
+       }
+       CHECKGLERROR
+       R_SetViewport(&viewport);
+       GL_PolygonOffset(0, 0);
+       GL_CullFace(GL_NONE); // quake is backwards
+       GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
+       GL_DepthMask(true);
+       GL_DepthTest(true);
+       qglClearDepth(1);CHECKGLERROR
+       CHECKGLERROR
+       if (clear)
+               qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |  GL_STENCIL_BUFFER_BIT);
+       CHECKGLERROR
 }
 
-void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
+void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent, qboolean shadowmapping)
 {
        CHECKGLERROR
        R_Shadow_RenderMode_Reset();
@@ -1282,10 +1638,33 @@ void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
        {
                R_Mesh_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap)); // light filter
                GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
+               CHECKGLERROR
+               if (shadowmapping)
+               {
+                       if (r_shadow_shadowmapping.integer == 1)
+                       {
+                               r_shadow_usingshadowmap2d = true;
+                               R_Mesh_TexBind(GL20TU_SHADOWMAP2D, R_GetTexture(r_shadow_shadowmap2dtexture));
+                               CHECKGLERROR
+                       }
+                       else if (r_shadow_shadowmapping.integer == 2)
+                       {
+                               r_shadow_usingshadowmaprect = true;
+                               R_Mesh_TexBindRectangle(GL20TU_SHADOWMAPRECT, R_GetTexture(r_shadow_shadowmaprectangletexture));
+                               CHECKGLERROR
+                       }
+                       else if (r_shadow_shadowmapping.integer == 3)
+                       {
+                               r_shadow_usingshadowmapcube = true;
+                               R_Mesh_TexBindCubeMap(GL20TU_SHADOWMAPCUBE, R_GetTexture(r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]));
+                               CHECKGLERROR
+                       }
+               }
        }
        else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_VERTEX)
                R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+       CHECKGLERROR
 }
 
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
@@ -1327,7 +1706,7 @@ void R_Shadow_RenderMode_End(void)
        R_Shadow_RenderMode_Reset();
        R_Shadow_RenderMode_ActiveLight(NULL);
        GL_DepthMask(true);
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height);
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 }
 
@@ -1364,15 +1743,17 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        int sign[8];
        float f;
 
+       r_shadow_lightscissor[0] = r_refdef.view.x;
+       r_shadow_lightscissor[1] = vid.height - r_refdef.view.y - r_refdef.view.height;
+       r_shadow_lightscissor[2] = r_refdef.view.width;
+       r_shadow_lightscissor[3] = r_refdef.view.height;
+
        if (!r_shadow_scissor.integer)
                return false;
 
        // if view is inside the light box, just say yes it's visible
        if (BoxesOverlap(r_refdef.view.origin, r_refdef.view.origin, mins, maxs))
-       {
-               GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
                return false;
-       }
 
        x1 = y1 = x2 = y2 = 0;
 
@@ -1420,7 +1801,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        for (i = 0;i < numvertices;i++)
        {
                VectorCopy(vertex[i], v);
-               GL_TransformToScreen(v, v2);
+               R_Viewport_TransformToScreen(&r_refdef.view.viewport, v, v2);
                //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
                if (i)
                {
@@ -1454,9 +1835,11 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
                return true;
 
        // the light area is visible, set up the scissor rectangle
-       GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
-       //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
-       //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
+       r_shadow_lightscissor[0] = ix1;
+       r_shadow_lightscissor[1] = vid.height - iy2;
+       r_shadow_lightscissor[2] = ix2 - ix1;
+       r_shadow_lightscissor[3] = iy2 - iy1;
+
        r_refdef.stats.lights_scissored++;
        return false;
 }
@@ -1686,6 +2069,7 @@ static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices,
        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));
@@ -1694,6 +2078,7 @@ static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices,
                R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL, R_GetTexture(rsurface.texture->backgroundcurrentskinframe->nmap));
                R_Mesh_TexBind(GL20TU_SECONDARY_COLOR, R_GetTexture(rsurface.texture->backgroundbasetexture));
                R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS, R_GetTexture(rsurface.texture->backgroundglosstexture));
+               R_Mesh_TexBind(GL20TU_SECONDARY_GLOW, R_GetTexture(rsurface.texture->backgroundcurrentskinframe->glow));
        }
        //R_Mesh_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap));
        R_Mesh_TexBind(GL20TU_FOGMASK, R_GetTexture(r_texture_fogattenuation));
@@ -2543,7 +2928,7 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int firsttriangle
        }
 }
 
-void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, qboolean shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
+void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, int shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
 {
        matrix4x4_t tempmatrix = *matrix;
        Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
@@ -2887,22 +3272,43 @@ void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
 
 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
 {
+       qboolean zpass;
        shadowmesh_t *mesh;
        int t, tend;
        int surfacelistindex;
        msurface_t *surface;
 
        RSurf_ActiveWorldEntity();
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE || r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPCUBESIDE || r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAP2D)
+       {
+               if (r_refdef.scene.worldentity->model)
+                       r_refdef.scene.worldmodel->DrawShadowMap(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+               return;
+       }
+
        if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
        {
                CHECKGLERROR
-               mesh = rsurface.rtlight->static_meshchain_shadow_zfail;
+               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)
                {
                        r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
                        R_Mesh_VertexPointer(mesh->vertex3f, mesh->vbo, mesh->vbooffset_vertex3f);
                        GL_LockArrays(0, mesh->numverts);
-                       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
+                       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZPASS_STENCIL)
+                       {
+                               // increment stencil if frontface is infront of depthbuffer
+                               GL_CullFace(r_refdef.view.cullface_back);
+                               qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);CHECKGLERROR
+                               R_Mesh_Draw(0, mesh->numverts, 0, mesh->numtriangles, mesh->element3i, mesh->element3s, mesh->ebo3i, mesh->ebo3s);
+                               // decrement stencil if backface is infront of depthbuffer
+                               GL_CullFace(r_refdef.view.cullface_front);
+                               qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR);CHECKGLERROR
+                       }
+                       else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_ZFAIL_STENCIL)
                        {
                                // decrement stencil if backface is behind depthbuffer
                                GL_CullFace(r_refdef.view.cullface_front);
@@ -2931,6 +3337,8 @@ void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned
        }
        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);
+
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
@@ -2946,7 +3354,11 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent)
        relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
        relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
        relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
-       ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE || r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAPCUBESIDE || r_shadow_rendermode == R_SHADOW_RENDERMODE_SHADOWMAP2D)
+               ent->model->DrawShadowMap(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs);
+       else
+               ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->sortedmodelsurfaces, relativeshadowmins, relativeshadowmaxs);
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
@@ -2978,6 +3390,8 @@ void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned c
                R_Mesh_TexMatrix(3, &rsurface.entitytolight);
 
        r_refdef.scene.worldmodel->DrawLight(r_refdef.scene.worldentity, numsurfaces, surfacelist, trispvs);
+
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
 void R_Shadow_DrawEntityLight(entity_render_t *ent)
@@ -2988,9 +3402,60 @@ void R_Shadow_DrawEntityLight(entity_render_t *ent)
 
        R_Shadow_SetupEntityLight(ent);
 
-       model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
+       model->DrawLight(ent, model->nummodelsurfaces, model->sortedmodelsurfaces, NULL);
+
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
+/*
+{{  0,   0, 0}, "px",  true,  true,  true},
+{{  0,  90, 0}, "py", false,  true, false},
+{{  0, 180, 0}, "nx", false, false,  true},
+{{  0, 270, 0}, "ny",  true, false, false},
+{{-90, 180, 0}, "pz", false, false,  true},
+{{ 90, 180, 0}, "nz", false, false,  true}
+*/
+
+static const double shadowviewmat16[6][4][4] =
+{
+       {
+               {-1,  0,  0, 0},
+               { 0, -1,  0, 0},
+               { 0,  0,  1, 0},
+               { 0,  0,  0, 1},
+       },
+       {
+               { 0, -1,  0, 0},
+               {-1,  0,  0, 0},
+               { 0,  0,  1, 0},
+               { 0,  0,  0, 1},
+       },
+       {
+               {-1,  0,  0, 0},
+               { 0, -1,  0, 0},
+               { 0,  0,  1, 0},
+               { 0,  0,  0, 1},
+       },
+       {
+               { 0, -1,  0, 0},
+               {-1,  0,  0, 0},
+               { 0,  0,  1, 0},
+               { 0,  0,  0, 1},
+       },
+       {
+               { 0,  0,  1, 0},
+               { 0, -1,  0, 0},
+               { 1,  0,  0, 0},
+               { 0,  0,  0, 1},
+       },
+       {
+               { 0,  0, -1, 0},
+               { 0, -1,  0, 0},
+               {-1,  0,  0, 0},
+               { 0,  0,  0, 1},
+       },
+};
+
 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
 {
        int i;
@@ -3006,6 +3471,10 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        static entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
        static entity_render_t *shadowentities[MAX_EDICTS];
        static entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
+       vec3_t nearestpoint;
+       vec_t distance;
+       qboolean castshadows;
+       int lodlinear;
 
        // 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)
@@ -3193,12 +3662,102 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                        R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
        }
 
-       if (gl_stencil && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.scene.rtworldshadows : r_refdef.scene.rtdlightshadows))
+       if (r_showlighting.integer && r_refdef.view.showdebug && numsurfaces + numlightentities + numlightentities_noselfshadow)
+       {
+               // optionally draw the illuminated areas
+               // for performance analysis by level designers
+               R_Shadow_RenderMode_VisibleLighting(false, false);
+               if (numsurfaces)
+                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i]);
+               for (i = 0;i < numlightentities_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);
+
+       nearestpoint[0] = bound(rtlight->cullmins[0], r_refdef.view.origin[0], rtlight->cullmaxs[0]);
+       nearestpoint[1] = bound(rtlight->cullmins[1], r_refdef.view.origin[1], rtlight->cullmaxs[1]);
+       nearestpoint[2] = bound(rtlight->cullmins[2], r_refdef.view.origin[2], rtlight->cullmaxs[2]);
+       distance = VectorDistance(nearestpoint, r_refdef.view.origin);
+       lodlinear = (int)(r_shadow_shadowmapping_lod_bias.value + r_shadow_shadowmapping_lod_scale.value * rtlight->radius / max(1.0f, distance));
+       lodlinear = bound(r_shadow_shadowmapping_minsize.integer, lodlinear, r_shadow_shadowmapping_maxsize.integer);
+
+       if (castshadows && r_shadow_shadowmapping.integer >= 1 && r_shadow_shadowmapping.integer <= 3 && r_glsl.integer && gl_support_fragment_shader)
+       {
+               int side;
+               int size;
+
+               r_shadow_shadowmaplod = 0;
+               for (i = 1;i < R_SHADOW_SHADOWMAP_NUMCUBEMAPS;i++)
+                       if ((r_shadow_shadowmapping_maxsize.integer >> i) > lodlinear)
+                               r_shadow_shadowmaplod = i;
+
+               size = lodlinear;
+               if (r_shadow_shadowmapping.integer == 3)
+                       size = bound(1, r_shadow_shadowmapping_maxsize.integer, 2048) >> r_shadow_shadowmaplod;
+               size = bound(1, size, 2048);
+
+               //Con_Printf("distance %f lodlinear %i (lod %i) size %i\n", distance, lodlinear, r_shadow_shadowmaplod, size);
+
+               // render shadow casters into 6 sided depth texture
+               for (side = 0;side < 6;side++)
+               {
+                       R_Shadow_RenderMode_ShadowMap(side, true, size);
+                       if (numsurfaces)
+                               R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
+                       for (i = 0;i < numshadowentities;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities[i]);
+               }
+
+               if (numlightentities_noselfshadow)
+               {
+                       // render lighting using the depth texture as shadowmap
+                       // draw lighting in the unmasked areas
+                       R_Shadow_RenderMode_Lighting(false, false, true);
+                       for (i = 0;i < numlightentities_noselfshadow;i++)
+                               R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
+               }
+
+               // render shadow casters into 6 sided depth texture
+               for (side = 0;side < 6;side++)
+               {
+                       R_Shadow_RenderMode_ShadowMap(side, false, size);
+                       for (i = 0;i < numshadowentities_noselfshadow;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
+#if 0
+                       if (r_shadow_shadowmapping.integer == 1)
+                       {
+                               int w = R_TextureWidth(r_shadow_shadowmap2dtexture);
+                               int h = R_TextureHeight(r_shadow_shadowmap2dtexture);
+                               static int once = true;
+                               if (once)
+                               {
+                                       unsigned char *blah = Z_Malloc(w*h*4);
+                                       qglReadPixels(0, 0, w, h, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, blah);CHECKGLERROR
+                                       FS_WriteFile("testshadowmap.bin", blah, w*h*4);
+                                       Z_Free(blah);
+                               }
+                               once = false;
+                       }
+#endif
+               }
+
+               // render lighting using the depth texture as shadowmap
+               // draw lighting in the unmasked areas
+               R_Shadow_RenderMode_Lighting(false, false, true);
+               // draw lighting in the unmasked areas
+               if (numsurfaces)
+                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i]);
+       }
+       else if (castshadows && gl_stencil)
        {
                // draw stencil shadow volumes to mask off pixels that are in shadow
                // so that they won't receive lighting
                R_Shadow_ClearStencil();
-               R_Shadow_RenderMode_StencilShadowVolumes();
                if (numsurfaces)
                        R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
                for (i = 0;i < numshadowentities;i++)
@@ -3206,7 +3765,7 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (numlightentities_noselfshadow)
                {
                        // draw lighting in the unmasked areas
-                       R_Shadow_RenderMode_Lighting(true, false);
+                       R_Shadow_RenderMode_Lighting(true, false, false);
                        for (i = 0;i < numlightentities_noselfshadow;i++)
                                R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
 
@@ -3218,8 +3777,6 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                                for (i = 0;i < numlightentities_noselfshadow;i++)
                                        R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
                        }
-
-                       R_Shadow_RenderMode_StencilShadowVolumes();
                }
                for (i = 0;i < numshadowentities_noselfshadow;i++)
                        R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
@@ -3227,22 +3784,11 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (numsurfaces + numlightentities)
                {
                        // draw lighting in the unmasked areas
-                       R_Shadow_RenderMode_Lighting(true, false);
+                       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]);
-
-                       // 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);
-                               if (numsurfaces)
-                                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
-                               for (i = 0;i < numlightentities;i++)
-                                       R_Shadow_DrawEntityLight(lightentities[i]);
-                       }
                }
        }
        else
@@ -3250,26 +3796,13 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                if (numsurfaces + numlightentities)
                {
                        // draw lighting in the unmasked areas
-                       R_Shadow_RenderMode_Lighting(false, false);
+                       R_Shadow_RenderMode_Lighting(false, false, false);
                        if (numsurfaces)
                                R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
                        for (i = 0;i < numlightentities;i++)
                                R_Shadow_DrawEntityLight(lightentities[i]);
                        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(false, false);
-                               if (numsurfaces)
-                                       R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
-                               for (i = 0;i < numlightentities;i++)
-                                       R_Shadow_DrawEntityLight(lightentities[i]);
-                               for (i = 0;i < numlightentities_noselfshadow;i++)
-                                       R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
-                       }
                }
        }
 }
@@ -3283,6 +3816,9 @@ void R_ShadowVolumeLighting(qboolean visible)
        dlight_t *light;
        size_t range;
 
+       if (r_shadow_shadowmapmaxsize != bound(1, r_shadow_shadowmapping_maxsize.integer, 2048) || r_shadow_shadowmode != r_shadow_shadowmapping.integer)
+               R_Shadow_FreeShadowMaps();
+
        if (r_editlights.integer)
                R_Shadow_DrawLightSprites();
 
@@ -3315,7 +3851,11 @@ void R_ShadowVolumeLighting(qboolean visible)
 
 extern void R_SetupView(qboolean allowwaterclippingplane);
 extern cvar_t r_shadows;
+extern cvar_t r_shadows_darken;
+extern cvar_t r_shadows_drawafterrtlightning;
+extern cvar_t r_shadows_castfrombmodels;
 extern cvar_t r_shadows_throwdistance;
+extern cvar_t r_shadows_throwdirection;
 void R_DrawModelShadows(void)
 {
        int i;
@@ -3324,43 +3864,55 @@ void R_DrawModelShadows(void)
        vec3_t relativelightorigin;
        vec3_t relativelightdirection;
        vec3_t relativeshadowmins, relativeshadowmaxs;
-       vec3_t tmp;
+       vec3_t tmp, shadowdir;
        float vertex3f[12];
+       r_viewport_t viewport;
 
        if (!r_drawentities.integer || !gl_stencil)
                return;
 
        CHECKGLERROR
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height);
 
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 
        if (gl_ext_separatestencil.integer)
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_SEPARATESTENCIL;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_SEPARATESTENCIL;
+       }
        else if (gl_ext_stenciltwoside.integer)
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE;
+       }
        else
-               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
+       {
+               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCIL;
+               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCIL;
+       }
+
+       // get shadow dir
+       if (r_shadows.integer == 2)
+       {
+               Math_atov(r_shadows_throwdirection.string, shadowdir);
+               VectorNormalize(shadowdir);
+       }
 
        R_Shadow_ClearStencil();
-       R_Shadow_RenderMode_StencilShadowVolumes();
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
        {
                ent = r_refdef.scene.entities[i];
-               // cast shadows from anything that is not a submodel of the map
-               if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
+
+               // cast shadows from anything of the map (submodels are optional)
+               if (ent->model && ent->model->DrawShadowVolume != NULL && (!ent->model->brush.submodel || r_shadows_castfrombmodels.integer) && (ent->flags & RENDER_SHADOW))
                {
                        relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
                        VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
                        VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
-
-                       if(r_shadows.integer == 2)
-                       {
-                               // 2: simpler mode, throw shadows always DOWN
-                               VectorSet(tmp, 0, 0, -1);
-                               Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection);
-                       }
+                       if (r_shadows.integer == 2) // 2: simpler mode, throw shadows always in same direction
+                               Matrix4x4_Transform3x3(&ent->inversematrix, shadowdir, relativelightdirection);
                        else
                        {
                                if(ent->entitynumber != 0)
@@ -3392,7 +3944,8 @@ void R_DrawModelShadows(void)
 
                        VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
                        RSurf_ActiveModelEntity(ent, false, false);
-                       ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+                       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
                }
        }
 
@@ -3406,8 +3959,9 @@ void R_DrawModelShadows(void)
        vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
 
        // set up ortho view for rendering this pass
-       GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, 0, 0, 1, 1, -10, 100, NULL);
+       R_SetViewport(&viewport);
+       GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_ScissorTest(true);
        R_Mesh_Matrix(&identitymatrix);
@@ -3415,13 +3969,13 @@ void R_DrawModelShadows(void)
        R_Mesh_VertexPointer(vertex3f, 0, 0);
        R_Mesh_ColorPointer(NULL, 0, 0);
 
-       // set up a 50% darkening blend on shadowed areas
+       // set up a darkening blend on shadowed areas
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_DepthRange(0, 1);
        GL_DepthTest(false);
        GL_DepthMask(false);
        GL_PolygonOffset(0, 0);CHECKGLERROR
-       GL_Color(0, 0, 0, 0.5);
+       GL_Color(0, 0, 0, r_shadows_darken.value);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        qglDepthFunc(GL_ALWAYS);CHECKGLERROR
        qglEnable(GL_STENCIL_TEST);CHECKGLERROR
@@ -3432,8 +3986,8 @@ void R_DrawModelShadows(void)
        // apply the blend to the shadowed areas
        R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
 
-       // restoring the perspective view is done by R_RenderScene
-       //R_SetupView(true);
+       // restore the viewport
+       R_SetViewport(&r_refdef.view.viewport);
 
        // restore other state to normal
        R_Shadow_RenderMode_End();
@@ -3441,25 +3995,33 @@ void R_DrawModelShadows(void)
 
 void R_BeginCoronaQuery(rtlight_t *rtlight, float scale, qboolean usequery)
 {
+       float zdist;
+       vec3_t centerorigin;
        // if it's too close, skip it
        if (VectorLength(rtlight->color) < (1.0f / 256.0f))
                return;
-       if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f)
-               return;
+       zdist = (DotProduct(rtlight->shadoworigin, r_refdef.view.forward) - DotProduct(r_refdef.view.origin, r_refdef.view.forward));
+       if (zdist < 32)
+               return;
        if (usequery && r_numqueries + 2 <= r_maxqueries)
        {
                rtlight->corona_queryindex_allpixels = r_queries[r_numqueries++];
                rtlight->corona_queryindex_visiblepixels = r_queries[r_numqueries++];
+               VectorMA(r_refdef.view.origin, zdist, r_refdef.view.forward, centerorigin);
+
                CHECKGLERROR
+               // NOTE: we can't disable depth testing using R_DrawSprite's depthdisable argument, which calls GL_DepthTest, as that's broken in the ATI drivers
                qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_allpixels);
-               R_DrawSprite(GL_ONE, GL_ZERO, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, 1, 1, 1, 1);
+               qglDepthFunc(GL_ALWAYS);
+               R_DrawSprite(GL_ONE, GL_ZERO, r_shadow_lightcorona, NULL, false, false, centerorigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, 1, 1, 1, 1);
                qglEndQueryARB(GL_SAMPLES_PASSED_ARB);
+               qglDepthFunc(GL_LEQUAL);
                qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, rtlight->corona_queryindex_visiblepixels);
                R_DrawSprite(GL_ONE, GL_ZERO, r_shadow_lightcorona, NULL, false, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, 1, 1, 1, 1);
                qglEndQueryARB(GL_SAMPLES_PASSED_ARB);
                CHECKGLERROR
        }
-       rtlight->corona_visibility = 1;
+       rtlight->corona_visibility = bound(0, (zdist - 32) / 32, 1);
 }
 
 void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
@@ -3476,7 +4038,7 @@ void R_DrawCorona(rtlight_t *rtlight, float cscale, float scale)
                //Con_Printf("%i of %i pixels\n", (int)visiblepixels, (int)allpixels);
                if (visiblepixels < 1 || allpixels < 1)
                        return;
-               rtlight->corona_visibility *= (float)visiblepixels / (float)allpixels;
+               rtlight->corona_visibility *= bound(0, (float)visiblepixels / (float)allpixels, 1);
                cscale *= rtlight->corona_visibility;
        }
        else
@@ -3515,11 +4077,11 @@ void R_DrawCoronas(void)
        if (usequery)
        {
                GL_ColorMask(0,0,0,0);
-               if (r_maxqueries < range + r_refdef.scene.numlights)
+               if (r_maxqueries < (range + r_refdef.scene.numlights) * 2)
                if (r_maxqueries < R_MAX_OCCLUSION_QUERIES)
                {
                        i = r_maxqueries;
-                       r_maxqueries = (range + r_refdef.scene.numlights) * 2;
+                       r_maxqueries = (range + r_refdef.scene.numlights) * 4;
                        r_maxqueries = min(r_maxqueries, R_MAX_OCCLUSION_QUERIES);
                        CHECKGLERROR
                        qglGenQueriesARB(r_maxqueries - i, r_queries + i);
@@ -3561,7 +4123,7 @@ void R_DrawCoronas(void)
        // now draw the coronas using the query data for intensity info
        for (lightindex = 0;lightindex < range;lightindex++)
        {
-               light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
+               light = (dlight_t *) Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
                if (!light)
                        continue;
                rtlight = &light->rtlight;