]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
added indirection cube map for shadowmapping and revised filtering modes
[xonotic/darkplaces.git] / r_shadow.c
index 08c71f396a5423c6301b515f2723b893a184aade..d992a762555d548d1435095155e104f7b4d4a9bd 100644 (file)
@@ -140,6 +140,8 @@ 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
@@ -156,6 +158,9 @@ typedef enum r_shadow_rendermode_e
        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;
 
@@ -163,6 +168,16 @@ 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_zpass = R_SHADOW_RENDERMODE_NONE;
 r_shadow_rendermode_t r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_NONE;
+float r_shadow_shadowmap_texturescale[4];
+float r_shadow_shadowmap_parameters[4];
+int r_shadow_drawbuffer;
+int r_shadow_readbuffer;
+GLuint r_shadow_fborectangle;
+int r_shadow_shadowmode;
+int r_shadow_shadowmapmaxsize;
+int r_shadow_shadowmapfilter;
+int r_shadow_shadowmapborder;
+int r_shadow_lightscissor[4];
 
 int maxshadowtriangles;
 int *shadowelements;
@@ -200,6 +215,10 @@ 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_shadowmapcubeprojectiontexture[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];
@@ -215,7 +234,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"};
@@ -235,6 +254,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", "512", "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", "4", "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)"};
@@ -301,6 +329,34 @@ 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_shadowmapfilter = r_shadow_shadowmapping_filterquality.integer;
+    r_shadow_shadowmapborder = bound(0, r_shadow_shadowmapping_bordersize.integer, 16);
+       r_shadow_shadowmaplod = -1;
+
+       CHECKGLERROR
+       if (r_shadow_fborectangle)
+               qglDeleteFramebuffersEXT(1, &r_shadow_fborectangle);
+       r_shadow_fborectangle = 0;
+       CHECKGLERROR
+
+       if (r_shadow_shadowmaprectangletexture)
+               R_FreeTexture(r_shadow_shadowmaprectangletexture);
+       r_shadow_shadowmaprectangletexture = NULL;
+
+    for (i = 0;i < R_SHADOW_SHADOWMAP_NUMCUBEMAPS;i++)
+        if (r_shadow_shadowmapcubeprojectiontexture[i])
+            R_FreeTexture(r_shadow_shadowmapcubeprojectiontexture[i]);
+    memset(r_shadow_shadowmapcubeprojectiontexture, 0, sizeof(r_shadow_shadowmapcubeprojectiontexture));
+
+       CHECKGLERROR
+}
+
 void r_shadow_start(void)
 {
        // allocate vertex processing arrays
@@ -308,6 +364,17 @@ void r_shadow_start(void)
        r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
+       r_shadow_shadowmode = 0;
+       r_shadow_shadowmaprectangletexture = NULL;
+    memset(r_shadow_shadowmapcubeprojectiontexture, 0, sizeof(r_shadow_shadowmapcubeprojectiontexture));
+       r_shadow_shadowmapmaxsize = 0;
+       r_shadow_shadowmapsize = 0;
+       r_shadow_shadowmaplod = 0;
+    r_shadow_shadowmapfilter = 0;
+       r_shadow_fborectangle = 0;
+
+       R_Shadow_FreeShadowMaps();
+
        r_shadow_texturepool = NULL;
        r_shadow_filters_texturepool = NULL;
        R_Shadow_ValidateCvars();
@@ -340,7 +407,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;
@@ -467,6 +539,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);
@@ -1087,6 +1168,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;
@@ -1179,6 +1288,8 @@ void R_Shadow_ValidateCvars(void)
 
 void R_Shadow_RenderMode_Begin(void)
 {
+       GLint drawbuffer;
+       GLint readbuffer;
        R_Shadow_ValidateCvars();
 
        if (!r_shadow_attenuation2dtexture
@@ -1196,7 +1307,7 @@ 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.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
 
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 
@@ -1222,6 +1333,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)
@@ -1236,6 +1353,14 @@ void R_Shadow_RenderMode_Reset(void)
        {
                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);
@@ -1252,6 +1377,7 @@ 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);
+       CHECKGLERROR
 }
 
 void R_Shadow_ClearStencil(void)
@@ -1311,7 +1437,76 @@ void R_Shadow_RenderMode_StencilShadowVolumes(qboolean zpass)
        }
 }
 
-void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
+void R_Shadow_RenderMode_ShadowMap(int side, qboolean clear, int size)
+{
+       int status;
+       int maxsize;
+       float nearclip, farclip, bias;
+       r_viewport_t viewport;
+       CHECKGLERROR
+       maxsize = r_shadow_shadowmapmaxsize;
+       nearclip = r_shadow_shadowmapping_nearclip.value / rsurface.rtlight->radius;
+       farclip = 1.0f;
+       bias = r_shadow_shadowmapping_bias.value * nearclip * (1024.0f / size);// * rsurface.rtlight->radius;
+       // complex unrolled cube approach (more flexible)
+       if (!r_shadow_shadowmapcubeprojectiontexture[r_shadow_shadowmaplod])
+               r_shadow_shadowmapcubeprojectiontexture[r_shadow_shadowmaplod] = R_LoadTextureCubeProjection(r_shadow_texturepool, "shadowmapcubeprojection", size, r_shadow_shadowmapborder);
+       if (!r_shadow_shadowmaprectangletexture)
+       {
+#if 1
+               r_shadow_shadowmaprectangletexture = R_LoadTextureShadowMapRectangle(r_shadow_texturepool, "shadowmap", maxsize*2, maxsize*3, r_shadow_shadowmapfilter == 1 || r_shadow_shadowmapfilter == 2);
+               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_shadowmapborder, nearclip, farclip, NULL);
+       r_shadow_shadowmap_texturescale[0] = 2*size;
+       r_shadow_shadowmap_texturescale[1] = 3*size;
+       r_shadow_shadowmap_texturescale[2] = 0.5f + 0.5f * (farclip + nearclip) / (farclip - nearclip);
+       r_shadow_shadowmap_texturescale[3] = -nearclip * farclip / (farclip - nearclip) - 0.5f * bias;
+       // compat for ALU cubemap calcs
+    r_shadow_shadowmap_parameters[0] = 0.5f * (size - r_shadow_shadowmapborder);
+       r_shadow_shadowmap_parameters[1] = size;
+       r_shadow_shadowmap_parameters[2] = r_shadow_shadowmap_texturescale[2];
+       r_shadow_shadowmap_parameters[3] = r_shadow_shadowmap_texturescale[3];
+       r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAPRECTANGLE;
+       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, qboolean shadowmapping)
 {
        CHECKGLERROR
        R_Shadow_RenderMode_Reset();
@@ -1333,10 +1528,19 @@ 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)
+               {
+                       R_Mesh_TexBindRectangle(GL20TU_SHADOWMAPRECT, R_GetTexture(r_shadow_shadowmaprectangletexture));
+                       CHECKGLERROR
+                       R_Mesh_TexBindCubeMap(GL20TU_CUBEPROJECTION, R_GetTexture(r_shadow_shadowmapcubeprojectiontexture[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)
@@ -1378,7 +1582,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.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 }
 
@@ -1415,15 +1619,17 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        int sign[8];
        float f;
 
+       r_shadow_lightscissor[0] = r_refdef.view.viewport.x;
+       r_shadow_lightscissor[1] = r_refdef.view.viewport.y;
+       r_shadow_lightscissor[2] = r_refdef.view.viewport.width;
+       r_shadow_lightscissor[3] = r_refdef.view.viewport.height;
+
        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;
 
@@ -1471,7 +1677,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)
                {
@@ -1489,25 +1695,27 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 
        // now convert the scissor rectangle to integer screen coordinates
        ix1 = (int)(x1 - 1.0f);
-       iy1 = (int)(y1 - 1.0f);
+       iy1 = vid.height - (int)(y2 - 1.0f);
        ix2 = (int)(x2 + 1.0f);
-       iy2 = (int)(y2 + 1.0f);
+       iy2 = vid.height - (int)(y1 + 1.0f);
        //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
 
        // clamp it to the screen
-       if (ix1 < r_refdef.view.x) ix1 = r_refdef.view.x;
-       if (iy1 < r_refdef.view.y) iy1 = r_refdef.view.y;
-       if (ix2 > r_refdef.view.x + r_refdef.view.width) ix2 = r_refdef.view.x + r_refdef.view.width;
-       if (iy2 > r_refdef.view.y + r_refdef.view.height) iy2 = r_refdef.view.y + r_refdef.view.height;
+       if (ix1 < r_refdef.view.viewport.x) ix1 = r_refdef.view.viewport.x;
+       if (iy1 < r_refdef.view.viewport.y) iy1 = r_refdef.view.viewport.y;
+       if (ix2 > r_refdef.view.viewport.x + r_refdef.view.viewport.width) ix2 = r_refdef.view.viewport.x + r_refdef.view.viewport.width;
+       if (iy2 > r_refdef.view.viewport.y + r_refdef.view.viewport.height) iy2 = r_refdef.view.viewport.y + r_refdef.view.viewport.height;
 
        // if it is inside out, it's not visible
        if (ix2 <= ix1 || iy2 <= iy1)
                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] = iy1;
+       r_shadow_lightscissor[2] = ix2 - ix1;
+       r_shadow_lightscissor[3] = iy2 - iy1;
+
        r_refdef.stats.lights_scissored++;
        return false;
 }
@@ -1737,6 +1945,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));
@@ -1745,6 +1954,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));
@@ -2594,7 +2804,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);
@@ -2945,6 +3155,14 @@ void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned
        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
@@ -2995,6 +3213,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)
@@ -3010,7 +3230,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)
@@ -3042,6 +3266,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)
@@ -3052,9 +3278,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;
@@ -3070,6 +3347,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)
@@ -3257,10 +3538,83 @@ 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_shadowmode && 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 = bound(1, r_shadow_shadowmapping_maxsize.integer >> r_shadow_shadowmaplod, 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]);
+               }
+
+               // 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
+               GL_Scissor(r_shadow_lightscissor[0], r_shadow_lightscissor[1], r_shadow_lightscissor[2], r_shadow_lightscissor[3]);
                R_Shadow_ClearStencil();
                if (numsurfaces)
                        R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
@@ -3269,7 +3623,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]);
 
@@ -3288,22 +3642,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
@@ -3311,26 +3654,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]);
-                       }
                }
        }
 }
@@ -3344,6 +3674,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_shadowmapfilter != r_shadow_shadowmapping_filterquality.integer || r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
+               R_Shadow_FreeShadowMaps();
+
        if (r_editlights.integer)
                R_Shadow_DrawLightSprites();
 
@@ -3374,9 +3707,16 @@ void R_ShadowVolumeLighting(qboolean visible)
        R_Shadow_RenderMode_End();
 }
 
+extern const float r_screenvertex3f[12];
 extern void R_SetupView(qboolean allowwaterclippingplane);
+extern void R_ResetViewRendering3D(void);
+extern void R_ResetViewRendering2D(void);
 extern cvar_t r_shadows;
+extern cvar_t r_shadows_darken;
+extern cvar_t r_shadows_drawafterrtlighting;
+extern cvar_t r_shadows_castfrombmodels;
 extern cvar_t r_shadows_throwdistance;
+extern cvar_t r_shadows_throwdirection;
 void R_DrawModelShadows(void)
 {
        int i;
@@ -3385,31 +3725,28 @@ void R_DrawModelShadows(void)
        vec3_t relativelightorigin;
        vec3_t relativelightdirection;
        vec3_t relativeshadowmins, relativeshadowmaxs;
-       vec3_t tmp;
-       float vertex3f[12];
+       vec3_t tmp, shadowdir;
 
        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);
-
-       r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
+       R_ResetViewRendering3D();
+       //GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
+       //GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       R_Shadow_RenderMode_Begin();
+       R_Shadow_RenderMode_ActiveLight(NULL);
+       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;
+       R_Shadow_RenderMode_StencilShadowVolumes(false);
 
-       if (gl_ext_separatestencil.integer)
-       {
-               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_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCILTWOSIDE;
-               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCILTWOSIDE;
-       }
-       else
+       // get shadow dir
+       if (r_shadows.integer == 2)
        {
-               r_shadow_shadowingrendermode_zpass = R_SHADOW_RENDERMODE_ZPASS_STENCIL;
-               r_shadow_shadowingrendermode_zfail = R_SHADOW_RENDERMODE_ZFAIL_STENCIL;
+               Math_atov(r_shadows_throwdirection.string, shadowdir);
+               VectorNormalize(shadowdir);
        }
 
        R_Shadow_ClearStencil();
@@ -3417,19 +3754,15 @@ void R_DrawModelShadows(void)
        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)
@@ -3461,38 +3794,34 @@ 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
                }
        }
 
        // not really the right mode, but this will disable any silly stencil features
-       R_Shadow_RenderMode_VisibleLighting(true, true);
-
-       // vertex coordinates for a quad that covers the screen exactly
-       vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
-       vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
-       vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
-       vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+       R_Shadow_RenderMode_End();
 
        // 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);
-       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);
-       R_Mesh_ResetTextureState();
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
+       //GL_Scissor(r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, 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);
+       //R_Mesh_ResetTextureState();
+       R_ResetViewRendering2D();
+       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
        R_Mesh_ColorPointer(NULL, 0, 0);
+       R_SetupGenericShader(false);
 
-       // 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_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
-       qglDepthFunc(GL_ALWAYS);CHECKGLERROR
+       //GL_DepthRange(0, 1);
+       //GL_DepthTest(false);
+       //GL_DepthMask(false);
+       //GL_PolygonOffset(0, 0);CHECKGLERROR
+       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
        qglStencilMask(~0);CHECKGLERROR
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
@@ -3501,34 +3830,42 @@ 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();
+       //R_Shadow_RenderMode_End();
 }
 
 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)
@@ -3545,7 +3882,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
@@ -3584,11 +3921,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);
@@ -3630,7 +3967,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;