]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
removed r_shadow_realtime_world_compilelight cvar and all code relating to it (no...
[xonotic/darkplaces.git] / r_shadow.c
index fba2ad3964cb4863205d466506a4363af24b2695..b2408c279ff2d3c8bf39ce62defd1d71ea32d005 100644 (file)
@@ -7,29 +7,31 @@ as far as the light's radius, if the light has a radius at all), capped at
 both front and back to avoid any problems (extrusion from dark faces also
 works but has a different set of problems)
 
-This is rendered using Carmack's Reverse technique, in which backfaces behind
-zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
-decrement the stencil, the result is a stencil value of zero where shadows
-did not intersect the visible geometry, suitable as a stencil mask for
-rendering lighting everywhere but shadow.
-
-In our case we use a biased stencil clear of 128 to avoid requiring the
-stencil wrap extension (but probably should support it), and to address
-Creative's patent on this sort of technology we also draw the frontfaces
-first, and backfaces second (decrement, increment).
+This is normally rendered using Carmack's Reverse technique, in which
+backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
+zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
+where shadows did not intersect the visible geometry, suitable as a stencil
+mask for rendering lighting everywhere but shadow.
+
+In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
+as decrement and the frontfaces as increment, and we redefine the DepthFunc to
+GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
+and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
+additionally we clear stencil to 128 to avoid the need for the unclamped
+incr/decr extension (not related to patent).
 
 Patent warning:
-This algorithm may be covered by Creative's patent (US Patent #6384822)
-on Carmack's Reverse paper (which I have not read), however that patent
-seems to be about drawing a stencil shadow from a model in an otherwise
-unshadowed scene, where as realtime lighting technology draws light where
-shadows do not lie.
+This algorithm may be covered by Creative's patent (US Patent #6384822),
+however that patent is quite specific about increment on backfaces and
+decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
+opposite this implementation and partially opposite Carmack's Reverse paper
+(which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
 
 
 
 Terminology: Stencil Light Volume (sometimes called Light Volumes)
 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
-areas in shadow it contanis the areas in light, this can only be built
+areas in shadow it contains the areas in light, this can only be built
 quickly for certain limited cases (such as portal visibility from a point),
 but is quite useful for some effects (sunlight coming from sky polygons is
 one possible example, translucent occluders is another example).
@@ -53,7 +55,8 @@ NormalMap) if supported by hardware; in our case there is support for cards
 which are incapable of DOT3, the quality is quite poor however.  Additionally
 it is desirable to have specular evaluation per pixel, per vertex
 normalization of specular halfangle vectors causes noticable distortion but
-is unavoidable on hardware without GL_ARB_fragment_program.
+is unavoidable on hardware without GL_ARB_fragment_program or
+GL_ARB_fragment_shader.
 
 
 
@@ -61,18 +64,29 @@ Terminology: Normalization CubeMap
 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
 encoded as RGB colors) for any possible direction, this technique allows per
 pixel calculation of incidence vector for per pixel lighting purposes, which
-would not otherwise be possible per pixel without GL_ARB_fragment_program.
+would not otherwise be possible per pixel without GL_ARB_fragment_program or
+GL_ARB_fragment_shader.
 
 
 
-Terminology: 2D Attenuation Texturing
+Terminology: 2D+1D Attenuation Texturing
 A very crude approximation of light attenuation with distance which results
 in cylindrical light shapes which fade vertically as a streak (some games
 such as Doom3 allow this to be rotated to be less noticable in specific
 cases), the technique is simply modulating lighting by two 2D textures (which
 can be the same) on different axes of projection (XY and Z, typically), this
-is the best technique available without 3D Attenuation Texturing or
-GL_ARB_fragment_program technology.
+is the second best technique available without 3D Attenuation Texturing,
+GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
+
+
+
+Terminology: 2D+1D Inverse Attenuation Texturing
+A clever method described in papers on the Abducted engine, this has a squared
+distance texture (bright on the outside, black in the middle), which is used
+twice using GL_ADD blending, the result of this is used in an inverse modulate
+(GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
+lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
+texturing).
 
 
 
@@ -91,8 +105,13 @@ diffuse lighting if 3D Attenuation Textures are already used.
 
 Terminology: Light Cubemap Filtering
 A technique for modeling non-uniform light distribution according to
-direction, for example projecting a stained glass window image onto a wall,
-this is done by texturing the lighting with a cubemap.
+direction, for example a lantern may use a cubemap to describe the light
+emission pattern of the cage around the lantern (as well as soot buildup
+discoloring the light in certain areas), often also used for softened grate
+shadows and light shining through a stained glass window (done crudely by
+texturing the lighting with a cubemap), another good example would be a disco
+light.  This technique is used heavily in many games (Doom3 does not support
+this however).
 
 
 
@@ -101,14 +120,18 @@ A technique for modeling shadowing of light passing through translucent
 surfaces, allowing stained glass windows and other effects to be done more
 elegantly than possible with Light Cubemap Filtering by applying an occluder
 texture to the lighting combined with a stencil light volume to limit the lit
-area (this allows evaluating multiple translucent occluders in a scene).
+area, this technique is used by Doom3 for spotlights and flashlights, among
+other things, this can also be used more generally to render light passing
+through multiple translucent occluders in a scene (using a light volume to
+describe the area beyond the occluder, and thus mask off rendering of all
+other areas).
 
 
 
 Terminology: Doom3 Lighting
 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
-CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
-the (currently upcoming) game Doom3.
+CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
+demonstrated by the game Doom3.
 */
 
 #include "quakedef.h"
@@ -119,12 +142,20 @@ the (currently upcoming) game Doom3.
 
 extern void R_Shadow_EditLights_Init(void);
 
-#define SHADOWSTAGE_NONE 0
-#define SHADOWSTAGE_STENCIL 1
-#define SHADOWSTAGE_LIGHT 2
-#define SHADOWSTAGE_STENCILTWOSIDE 3
+typedef enum r_shadowstage_e
+{
+       R_SHADOWSTAGE_NONE,
+       R_SHADOWSTAGE_STENCIL,
+       R_SHADOWSTAGE_STENCILTWOSIDE,
+       R_SHADOWSTAGE_LIGHT_VERTEX,
+       R_SHADOWSTAGE_LIGHT_DOT3,
+       R_SHADOWSTAGE_LIGHT_GLSL,
+       R_SHADOWSTAGE_VISIBLEVOLUMES,
+       R_SHADOWSTAGE_VISIBLELIGHTING,
+}
+r_shadowstage_t;
 
-int r_shadowstage = SHADOWSTAGE_NONE;
+r_shadowstage_t r_shadowstage = R_SHADOWSTAGE_NONE;
 
 mempool_t *r_shadow_mempool;
 
@@ -142,23 +173,17 @@ int *vertexupdate;
 int *vertexremap;
 int vertexupdatenum;
 
-int r_shadow_buffer_numclusterpvsbytes;
-qbyte *r_shadow_buffer_clusterpvs;
-int *r_shadow_buffer_clusterlist;
+int r_shadow_buffer_numleafpvsbytes;
+qbyte *r_shadow_buffer_leafpvs;
+int *r_shadow_buffer_leaflist;
 
 int r_shadow_buffer_numsurfacepvsbytes;
 qbyte *r_shadow_buffer_surfacepvs;
 int *r_shadow_buffer_surfacelist;
 
 rtexturepool_t *r_shadow_texturepool;
-rtexture_t *r_shadow_normalcubetexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
-rtexture_t *r_shadow_blankbumptexture;
-rtexture_t *r_shadow_blankglosstexture;
-rtexture_t *r_shadow_blankwhitetexture;
-rtexture_t *r_shadow_blankwhitecubetexture;
-rtexture_t *r_shadow_blankblacktexture;
 
 // lights are reloaded when this changes
 char r_shadow_mapname[MAX_QPATH];
@@ -168,7 +193,6 @@ rtexturepool_t *r_shadow_filters_texturepool;
 
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
-cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1"};
 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
@@ -179,31 +203,34 @@ cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1"};
-cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "0"};
+cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1"};
+cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0"};
 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0"};
 cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1"};
 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0"};
 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1"};
+cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1"};
+cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
-cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
+cvar_t r_shadow_visiblelighting = {0, "r_shadow_visiblelighting", "0"};
 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
 cvar_t r_shadow_glsl = {0, "r_shadow_glsl", "1"};
-cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "1"};
-cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "0.04"};
-cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "-0.04"};
+cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "0"};
+cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "-0.04"};
+cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "0.04"};
+cvar_t r_shadow_glsl_usehalffloat = {0, "r_shadow_glsl_usehalffloat", "0"};
+cvar_t r_shadow_glsl_surfacenormalize = {0, "r_shadow_glsl_surfacenormalize", "1"};
 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
 cvar_t r_editlights = {0, "r_editlights", "0"};
-cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
-cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
-cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
-cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
+cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024"};
+cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0"};
+cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4"};
+cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
-cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
-cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
 
 float r_shadow_attenpower, r_shadow_attenscale;
 
@@ -232,7 +259,9 @@ static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
 #define SHADERPERMUTATION_FOG (1<<1)
 #define SHADERPERMUTATION_CUBEFILTER (1<<2)
 #define SHADERPERMUTATION_OFFSETMAPPING (1<<3)
-#define SHADERPERMUTATION_COUNT (1<<4)
+#define SHADERPERMUTATION_SURFACENORMALIZE (1<<4)
+#define SHADERPERMUTATION_GEFORCEFX (1<<5)
+#define SHADERPERMUTATION_COUNT (1<<6)
 
 GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
 
@@ -277,15 +306,15 @@ const char *builtinshader_light_vert =
 "      // (we use unnormalized to ensure that it interpolates correctly and then\n"
 "      //  normalize it per pixel)\n"
 "      vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
-"      LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
+"      LightVector.x = -dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
+"      LightVector.y = -dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
 "      LightVector.z = -dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
 "\n"
 "#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
 "      // transform unnormalized eye direction into tangent space\n"
 "      vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
+"      EyeVector.x = -dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
+"      EyeVector.y = -dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
 "      EyeVector.z = -dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
 "#endif\n"
 "\n"
@@ -299,22 +328,29 @@ const char *builtinshader_light_frag =
 "// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
 "// written by Forest 'LordHavoc' Hale\n"
 "\n"
-"uniform vec3 LightColor;\n"
+"// use half floats on GEFORCEFX for math performance, otherwise don't\n"
+"#ifndef GEFORCEFX\n"
+"#define half float\n"
+"#define hvec2 vec2\n"
+"#define hvec3 vec3\n"
+"#define hvec4 vec4\n"
+"#endif\n"
 "\n"
+"uniform hvec3 LightColor;\n"
 "#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale;\n"
-"uniform float OffsetMapping_Bias;\n"
+"uniform half OffsetMapping_Scale;\n"
+"uniform half OffsetMapping_Bias;\n"
 "#endif\n"
 "#ifdef USESPECULAR\n"
-"uniform float SpecularPower;\n"
+"uniform half SpecularPower;\n"
 "#endif\n"
 "#ifdef USEFOG\n"
-"uniform float FogRangeRecip;\n"
+"uniform half FogRangeRecip;\n"
 "#endif\n"
-"uniform float AmbientScale;\n"
-"uniform float DiffuseScale;\n"
+"uniform half AmbientScale;\n"
+"uniform half DiffuseScale;\n"
 "#ifdef USESPECULAR\n"
-"uniform float SpecularScale;\n"
+"uniform half SpecularScale;\n"
 "#endif\n"
 "\n"
 "uniform sampler2D Texture_Normal;\n"
@@ -346,59 +382,57 @@ const char *builtinshader_light_frag =
 "      //\n"
 "      // pow(1-(x*x+y*y+z*z), 4) is far more realistic but needs large lights to\n"
 "      // provide significant illumination, large = slow = pain.\n"
-"      float colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
+"      half colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
 "\n"
 "#ifdef USEFOG\n"
 "      // apply fog\n"
-"      colorscale *= texture2D(Texture_FogMask, vec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
+"      colorscale *= texture2D(Texture_FogMask, hvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
 "#endif\n"
 "\n"
 "#ifdef USEOFFSETMAPPING\n"
 "      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-"      vec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      vec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
+"      hvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
+"      hvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
 "      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
 "      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
 "#define TexCoord TexCoordOffset\n"
 "#endif\n"
 "\n"
-"      // get the texels - with a blendmap we'd need to blend multiple here\n"
-"      vec3 surfacenormal = -1.0 + 2.0 * vec3(texture2D(Texture_Normal, TexCoord));\n"
-"      vec3 colortexel = vec3(texture2D(Texture_Color, TexCoord));\n"
-"#ifdef USESPECULAR\n"
-"      vec3 glosstexel = vec3(texture2D(Texture_Gloss, TexCoord));\n"
+"      // get the surface normal\n"
+"#ifdef SURFACENORMALIZE\n"
+"      hvec3 surfacenormal = normalize(hvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"#else\n"
+"      hvec3 surfacenormal = -1.0 + 2.0 * hvec3(texture2D(Texture_Normal, TexCoord));\n"
 "#endif\n"
 "\n"
 "      // calculate shading\n"
-"      vec3 diffusenormal = normalize(LightVector);\n"
-"      vec3 color = colortexel * (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      hvec3 diffusenormal = hvec3(normalize(LightVector));\n"
+"      hvec3 color = hvec3(texture2D(Texture_Color, TexCoord)) * (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      color += glosstexel * (SpecularScale * pow(max(dot(surfacenormal, normalize(diffusenormal + normalize(EyeVector))), 0.0), SpecularPower));\n"
+"      hvec3 specularnormal = hvec3(normalize(diffusenormal + hvec3(normalize(EyeVector))));\n"
+"      color += hvec3(texture2D(Texture_Gloss, TexCoord)) * (SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower));\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
 "      // apply light cubemap filter\n"
-"      color *= vec3(textureCube(Texture_Cube, CubeVector));\n"
+"      color *= hvec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
-"      // calculate fragment color\n"
-"      gl_FragColor = vec4(LightColor * color * colorscale, 1);\n"
+"      // calculate fragment color (apply light color and attenuation/fog scaling)\n"
+"      gl_FragColor = hvec4(color * LightColor * colorscale, 1);\n"
 "}\n"
 ;
 
 void r_shadow_start(void)
 {
        int i;
+       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
+       if (gl_support_half_float)
+               Cvar_SetValue("r_shadow_glsl_usehalffloat", 1);
        // allocate vertex processing arrays
        numcubemaps = 0;
-       r_shadow_normalcubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
-       r_shadow_blankbumptexture = NULL;
-       r_shadow_blankglosstexture = NULL;
-       r_shadow_blankwhitetexture = NULL;
-       r_shadow_blankwhitecubetexture = NULL;
-       r_shadow_blankblacktexture = NULL;
        r_shadow_texturepool = NULL;
        r_shadow_filters_texturepool = NULL;
        R_Shadow_ValidateCvars();
@@ -414,9 +448,9 @@ void r_shadow_start(void)
        shadowmark = NULL;
        shadowmarklist = NULL;
        shadowmarkcount = 0;
-       r_shadow_buffer_numclusterpvsbytes = 0;
-       r_shadow_buffer_clusterpvs = NULL;
-       r_shadow_buffer_clusterlist = NULL;
+       r_shadow_buffer_numleafpvsbytes = 0;
+       r_shadow_buffer_leafpvs = NULL;
+       r_shadow_buffer_leaflist = NULL;
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
@@ -427,10 +461,10 @@ void r_shadow_start(void)
                char *vertstring, *fragstring;
                int vertstrings_count;
                int fragstrings_count;
-               const char *vertstrings_list[SHADERPERMUTATION_COUNT];
-               const char *fragstrings_list[SHADERPERMUTATION_COUNT];
-               vertstring = FS_LoadFile("glsl/light.vert", tempmempool, false);
-               fragstring = FS_LoadFile("glsl/light.frag", tempmempool, false);
+               const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
+               const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
+               vertstring = (char *)FS_LoadFile("glsl/light.vert", tempmempool, false);
+               fragstring = (char *)FS_LoadFile("glsl/light.frag", tempmempool, false);
                for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
                {
                        vertstrings_count = 0;
@@ -455,12 +489,22 @@ void r_shadow_start(void)
                                vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
                                fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
                        }
+                       if (i & SHADERPERMUTATION_SURFACENORMALIZE)
+                       {
+                               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
+                               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
+                       }
+                       if (i & SHADERPERMUTATION_GEFORCEFX)
+                       {
+                               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
+                               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
+                       }
                        vertstrings_list[vertstrings_count++] = vertstring ? vertstring : builtinshader_light_vert;
                        fragstrings_list[fragstrings_count++] = fragstring ? fragstring : builtinshader_light_frag;
                        r_shadow_program_light[i] = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
                        if (!r_shadow_program_light[i])
                        {
-                               Con_Printf("permutation %s %s %s %s failed for shader %s, some features may not work properly!\n", i & 1 ? "specular" : "", i & 2 ? "fog" : "", i & 4 ? "cubefilter" : "", i & 8 ? "offsetmapping" : "", "glsl/light");
+                               Con_Printf("permutation %s %s %s %s %s %s failed for shader %s, some features may not work properly!\n", i & 1 ? "specular" : "", i & 2 ? "fog" : "", i & 4 ? "cubefilter" : "", i & 8 ? "offsetmapping" : "", i & 16 ? "surfacenormalize" : "", i & 32 ? "geforcefx" : "", "glsl/light");
                                continue;
                        }
                        qglUseProgramObjectARB(r_shadow_program_light[i]);
@@ -500,14 +544,8 @@ void r_shadow_shutdown(void)
                }
        }
        numcubemaps = 0;
-       r_shadow_normalcubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
-       r_shadow_blankbumptexture = NULL;
-       r_shadow_blankglosstexture = NULL;
-       r_shadow_blankwhitetexture = NULL;
-       r_shadow_blankwhitecubetexture = NULL;
-       r_shadow_blankblacktexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
        R_FreeTexturePool(&r_shadow_filters_texturepool);
        maxshadowelements = 0;
@@ -531,13 +569,13 @@ void r_shadow_shutdown(void)
                Mem_Free(shadowmarklist);
        shadowmarklist = NULL;
        shadowmarkcount = 0;
-       r_shadow_buffer_numclusterpvsbytes = 0;
-       if (r_shadow_buffer_clusterpvs)
-               Mem_Free(r_shadow_buffer_clusterpvs);
-       r_shadow_buffer_clusterpvs = NULL;
-       if (r_shadow_buffer_clusterlist)
-               Mem_Free(r_shadow_buffer_clusterlist);
-       r_shadow_buffer_clusterlist = NULL;
+       r_shadow_buffer_numleafpvsbytes = 0;
+       if (r_shadow_buffer_leafpvs)
+               Mem_Free(r_shadow_buffer_leafpvs);
+       r_shadow_buffer_leafpvs = NULL;
+       if (r_shadow_buffer_leaflist)
+               Mem_Free(r_shadow_buffer_leaflist);
+       r_shadow_buffer_leaflist = NULL;
        r_shadow_buffer_numsurfacepvsbytes = 0;
        if (r_shadow_buffer_surfacepvs)
                Mem_Free(r_shadow_buffer_surfacepvs);
@@ -569,19 +607,25 @@ void R_Shadow_Help_f(void)
 "r_shadow_projectdistance : shadow volume projection distance\n"
 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
+"r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
 "r_shadow_realtime_world : use high quality world lighting mode\n"
 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
+"r_shadow_realtime_world_compile : compile surface/visibility information\n"
+"r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
 "r_shadow_glsl : use OpenGL Shading Language for lighting\n"
 "r_shadow_glsl_offsetmapping : enables Offset Mapping bumpmap enhancement\n"
 "r_shadow_glsl_offsetmapping_scale : controls depth of Offset Mapping\n"
 "r_shadow_glsl_offsetmapping_bias : should be negative half of scale\n"
+"r_shadow_glsl_usehalffloat : use lower quality lighting\n"
+"r_shadow_glsl_surfacenormalize : makes bumpmapping slightly higher quality\n"
 "r_shadow_scissor : use scissor optimization\n"
 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
+"r_shadow_visiblelighting : useful for performance testing; bright = slow!\n"
 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
 "Commands:\n"
 "r_shadow_help : this help\n"
@@ -592,7 +636,6 @@ void R_Shadow_Init(void)
 {
        Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
        Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
-       Cvar_RegisterVariable(&r_shadow_cull);
        Cvar_RegisterVariable(&r_shadow_debuglight);
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
@@ -604,21 +647,26 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_projectdistance);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
+       Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
        Cvar_RegisterVariable(&r_shadow_realtime_world);
        Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
        Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
        Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
+       Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
+       Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
        Cvar_RegisterVariable(&r_shadow_scissor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
-       Cvar_RegisterVariable(&r_shadow_staticworldlights);
        Cvar_RegisterVariable(&r_shadow_texture3d);
+       Cvar_RegisterVariable(&r_shadow_visiblelighting);
        Cvar_RegisterVariable(&r_shadow_visiblevolumes);
        Cvar_RegisterVariable(&r_shadow_glsl);
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping);
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_scale);
        Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_bias);
+       Cvar_RegisterVariable(&r_shadow_glsl_usehalffloat);
+       Cvar_RegisterVariable(&r_shadow_glsl_surfacenormalize);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
        {
@@ -640,9 +688,9 @@ void R_Shadow_Init(void)
        shadowmark = NULL;
        shadowmarklist = NULL;
        shadowmarkcount = 0;
-       r_shadow_buffer_numclusterpvsbytes = 0;
-       r_shadow_buffer_clusterpvs = NULL;
-       r_shadow_buffer_clusterlist = NULL;
+       r_shadow_buffer_numleafpvsbytes = 0;
+       r_shadow_buffer_leafpvs = NULL;
+       r_shadow_buffer_leaflist = NULL;
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
@@ -682,24 +730,20 @@ int *R_Shadow_ResizeShadowElements(int numtris)
        return shadowelements;
 }
 
-void R_Shadow_EnlargeClusterBuffer(int numclusters)
+static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
 {
-       int numclusterpvsbytes = (((numclusters + 7) >> 3) + 255) & ~255;
-       if (r_shadow_buffer_numclusterpvsbytes < numclusterpvsbytes)
+       int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
+       int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
+       if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
        {
-               if (r_shadow_buffer_clusterpvs)
-                       Mem_Free(r_shadow_buffer_clusterpvs);
-               if (r_shadow_buffer_clusterlist)
-                       Mem_Free(r_shadow_buffer_clusterlist);
-               r_shadow_buffer_numclusterpvsbytes = numclusterpvsbytes;
-               r_shadow_buffer_clusterpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes);
-               r_shadow_buffer_clusterlist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes * 8 * sizeof(*r_shadow_buffer_clusterlist));
+               if (r_shadow_buffer_leafpvs)
+                       Mem_Free(r_shadow_buffer_leafpvs);
+               if (r_shadow_buffer_leaflist)
+                       Mem_Free(r_shadow_buffer_leaflist);
+               r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
+               r_shadow_buffer_leafpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
+               r_shadow_buffer_leaflist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
        }
-}
-
-void R_Shadow_EnlargeSurfaceBuffer(int numsurfaces)
-{
-       int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
        if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
        {
                if (r_shadow_buffer_surfacepvs)
@@ -738,10 +782,10 @@ void R_Shadow_PrepareShadowMark(int numtris)
 
 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
 {
-       int i, j, tris = 0, vr[3], t, outvertices = 0;
-       float f, temp[3];
-       const int *e, *n;
-       const float *v;
+       int i, j;
+       int outtriangles = 0, outvertices = 0;
+       const int *element;
+       const float *vertex;
 
        if (maxvertexupdate < innumvertices)
        {
@@ -767,22 +811,22 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
 
        for (i = 0;i < numshadowmarktris;i++)
        {
-               t = shadowmarktris[i];
-               e = inelement3i + t * 3;
+               element = inelement3i + shadowmarktris[i] * 3;
                // make sure the vertices are created
                for (j = 0;j < 3;j++)
                {
-                       if (vertexupdate[e[j]] != vertexupdatenum)
+                       if (vertexupdate[element[j]] != vertexupdatenum)
                        {
-                               vertexupdate[e[j]] = vertexupdatenum;
-                               vertexremap[e[j]] = outvertices;
-                               v = invertex3f + e[j] * 3;
+                               float ratio, direction[3];
+                               vertexupdate[element[j]] = vertexupdatenum;
+                               vertexremap[element[j]] = outvertices;
+                               vertex = invertex3f + element[j] * 3;
                                // project one copy of the vertex to the sphere radius of the light
                                // (FIXME: would projecting it to the light box be better?)
-                               VectorSubtract(v, projectorigin, temp);
-                               f = projectdistance / VectorLength(temp);
-                               VectorCopy(v, outvertex3f);
-                               VectorMA(projectorigin, f, temp, (outvertex3f + 3));
+                               VectorSubtract(vertex, projectorigin, direction);
+                               ratio = projectdistance / VectorLength(direction);
+                               VectorCopy(vertex, outvertex3f);
+                               VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
                                outvertex3f += 6;
                                outvertices += 2;
                        }
@@ -791,62 +835,70 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
 
        for (i = 0;i < numshadowmarktris;i++)
        {
-               t = shadowmarktris[i];
-               e = inelement3i + t * 3;
-               n = inneighbor3i + t * 3;
+               int remappedelement[3];
+               int markindex;
+               const int *neighbortriangle;
+
+               markindex = shadowmarktris[i] * 3;
+               element = inelement3i + markindex;
+               neighbortriangle = inneighbor3i + markindex;
                // output the front and back triangles
-               outelement3i[0] = vertexremap[e[0]];
-               outelement3i[1] = vertexremap[e[1]];
-               outelement3i[2] = vertexremap[e[2]];
-               outelement3i[3] = vertexremap[e[2]] + 1;
-               outelement3i[4] = vertexremap[e[1]] + 1;
-               outelement3i[5] = vertexremap[e[0]] + 1;
+               outelement3i[0] = vertexremap[element[0]];
+               outelement3i[1] = vertexremap[element[1]];
+               outelement3i[2] = vertexremap[element[2]];
+               outelement3i[3] = vertexremap[element[2]] + 1;
+               outelement3i[4] = vertexremap[element[1]] + 1;
+               outelement3i[5] = vertexremap[element[0]] + 1;
+
                outelement3i += 6;
-               tris += 2;
+               outtriangles += 2;
                // output the sides (facing outward from this triangle)
-               if (shadowmark[n[0]] != shadowmarkcount)
+               if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
                {
-                       vr[0] = vertexremap[e[0]];
-                       vr[1] = vertexremap[e[1]];
-                       outelement3i[0] = vr[1];
-                       outelement3i[1] = vr[0];
-                       outelement3i[2] = vr[0] + 1;
-                       outelement3i[3] = vr[1];
-                       outelement3i[4] = vr[0] + 1;
-                       outelement3i[5] = vr[1] + 1;
+                       remappedelement[0] = vertexremap[element[0]];
+                       remappedelement[1] = vertexremap[element[1]];
+                       outelement3i[0] = remappedelement[1];
+                       outelement3i[1] = remappedelement[0];
+                       outelement3i[2] = remappedelement[0] + 1;
+                       outelement3i[3] = remappedelement[1];
+                       outelement3i[4] = remappedelement[0] + 1;
+                       outelement3i[5] = remappedelement[1] + 1;
+
                        outelement3i += 6;
-                       tris += 2;
+                       outtriangles += 2;
                }
-               if (shadowmark[n[1]] != shadowmarkcount)
+               if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
                {
-                       vr[1] = vertexremap[e[1]];
-                       vr[2] = vertexremap[e[2]];
-                       outelement3i[0] = vr[2];
-                       outelement3i[1] = vr[1];
-                       outelement3i[2] = vr[1] + 1;
-                       outelement3i[3] = vr[2];
-                       outelement3i[4] = vr[1] + 1;
-                       outelement3i[5] = vr[2] + 1;
+                       remappedelement[1] = vertexremap[element[1]];
+                       remappedelement[2] = vertexremap[element[2]];
+                       outelement3i[0] = remappedelement[2];
+                       outelement3i[1] = remappedelement[1];
+                       outelement3i[2] = remappedelement[1] + 1;
+                       outelement3i[3] = remappedelement[2];
+                       outelement3i[4] = remappedelement[1] + 1;
+                       outelement3i[5] = remappedelement[2] + 1;
+
                        outelement3i += 6;
-                       tris += 2;
+                       outtriangles += 2;
                }
-               if (shadowmark[n[2]] != shadowmarkcount)
+               if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
                {
-                       vr[0] = vertexremap[e[0]];
-                       vr[2] = vertexremap[e[2]];
-                       outelement3i[0] = vr[0];
-                       outelement3i[1] = vr[2];
-                       outelement3i[2] = vr[2] + 1;
-                       outelement3i[3] = vr[0];
-                       outelement3i[4] = vr[2] + 1;
-                       outelement3i[5] = vr[0] + 1;
+                       remappedelement[0] = vertexremap[element[0]];
+                       remappedelement[2] = vertexremap[element[2]];
+                       outelement3i[0] = remappedelement[0];
+                       outelement3i[1] = remappedelement[2];
+                       outelement3i[2] = remappedelement[2] + 1;
+                       outelement3i[3] = remappedelement[0];
+                       outelement3i[4] = remappedelement[2] + 1;
+                       outelement3i[5] = remappedelement[0] + 1;
+
                        outelement3i += 6;
-                       tris += 2;
+                       outtriangles += 2;
                }
        }
        if (outnumvertices)
                *outnumvertices = outvertices;
-       return tris;
+       return outtriangles;
 }
 
 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
@@ -866,7 +918,7 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
 }
 
-void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, vec3_t lightmins, vec3_t lightmaxs, vec3_t surfacemins, vec3_t surfacemaxs)
+void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
 {
        int t, tend;
        const int *e;
@@ -916,19 +968,19 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        m.pointer_vertex = vertex3f;
        R_Mesh_State(&m);
        GL_LockArrays(0, numvertices);
-       if (r_shadowstage == SHADOWSTAGE_STENCIL)
+       if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
        {
-               // increment stencil if backface is behind depthbuffer
+               // decrement stencil if backface is behind depthbuffer
                qglCullFace(GL_BACK); // quake is backwards, this culls front faces
-               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
-               R_Mesh_Draw(numvertices, numtriangles, element3i);
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               R_Mesh_Draw(0, numvertices, numtriangles, element3i);
                c_rt_shadowmeshes++;
                c_rt_shadowtris += numtriangles;
-               // decrement stencil if frontface is behind depthbuffer
+               // increment stencil if frontface is behind depthbuffer
                qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
        }
-       R_Mesh_Draw(numvertices, numtriangles, element3i);
+       R_Mesh_Draw(0, numvertices, numtriangles, element3i);
        c_rt_shadowmeshes++;
        c_rt_shadowtris += numtriangles;
        GL_LockArrays(0, 0);
@@ -936,99 +988,16 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
 
 static void R_Shadow_MakeTextures(void)
 {
-       int x, y, z, d, side;
-       float v[3], s, t, intensity;
+       int x, y, z, d;
+       float v[3], intensity;
        qbyte *data;
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_texturepool = R_AllocTexturePool();
        r_shadow_attenpower = r_shadow_lightattenuationpower.value;
        r_shadow_attenscale = r_shadow_lightattenuationscale.value;
-#define NORMSIZE 64
 #define ATTEN2DSIZE 64
 #define ATTEN3DSIZE 32
-       data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
-       data[0] = 128; // normal X
-       data[1] = 128; // normal Y
-       data[2] = 255; // normal Z
-       data[3] = 128; // height
-       r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
-       data[0] = 255;
-       data[1] = 255;
-       data[2] = 255;
-       data[3] = 255;
-       r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
-       data[0] = 255;
-       data[1] = 255;
-       data[2] = 255;
-       data[3] = 255;
-       r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
-       data[0] = 0;
-       data[1] = 0;
-       data[2] = 0;
-       data[3] = 255;
-       r_shadow_blankblacktexture = R_LoadTexture2D(r_shadow_texturepool, "blankblack", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
-       r_shadow_blankwhitecubetexture = NULL;
-       r_shadow_normalcubetexture = NULL;
-       if (gl_texturecubemap)
-       {
-               data[ 0] = 255;data[ 1] = 255;data[ 2] = 255;data[ 3] = 255;
-               data[ 4] = 255;data[ 5] = 255;data[ 6] = 255;data[ 7] = 255;
-               data[ 8] = 255;data[ 9] = 255;data[10] = 255;data[11] = 255;
-               data[12] = 255;data[13] = 255;data[14] = 255;data[15] = 255;
-               data[16] = 255;data[17] = 255;data[18] = 255;data[19] = 255;
-               data[20] = 255;data[21] = 255;data[22] = 255;data[23] = 255;
-               r_shadow_blankwhitecubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "blankwhitecube", 1, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
-               for (side = 0;side < 6;side++)
-               {
-                       for (y = 0;y < NORMSIZE;y++)
-                       {
-                               for (x = 0;x < NORMSIZE;x++)
-                               {
-                                       s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
-                                       t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
-                                       switch(side)
-                                       {
-                                       case 0:
-                                               v[0] = 1;
-                                               v[1] = -t;
-                                               v[2] = -s;
-                                               break;
-                                       case 1:
-                                               v[0] = -1;
-                                               v[1] = -t;
-                                               v[2] = s;
-                                               break;
-                                       case 2:
-                                               v[0] = s;
-                                               v[1] = 1;
-                                               v[2] = t;
-                                               break;
-                                       case 3:
-                                               v[0] = s;
-                                               v[1] = -1;
-                                               v[2] = -t;
-                                               break;
-                                       case 4:
-                                               v[0] = s;
-                                               v[1] = -t;
-                                               v[2] = 1;
-                                               break;
-                                       case 5:
-                                               v[0] = -s;
-                                               v[1] = -t;
-                                               v[2] = -1;
-                                               break;
-                                       }
-                                       intensity = 127.0f / sqrt(DotProduct(v, v));
-                                       data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
-                                       data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
-                                       data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
-                                       data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
-                               }
-                       }
-               }
-               r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
-       }
+       data = Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
        for (y = 0;y < ATTEN2DSIZE;y++)
        {
                for (x = 0;x < ATTEN2DSIZE;x++)
@@ -1082,6 +1051,29 @@ void R_Shadow_ValidateCvars(void)
                Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
 }
 
+// light currently being rendered
+rtlight_t *r_shadow_rtlight;
+// light filter cubemap being used by the light
+static rtexture_t *r_shadow_lightcubemap;
+
+// this is the location of the eye in entity space
+static vec3_t r_shadow_entityeyeorigin;
+// this is the location of the light in entity space
+static vec3_t r_shadow_entitylightorigin;
+// this transforms entity coordinates to light filter cubemap coordinates
+// (also often used for other purposes)
+static matrix4x4_t r_shadow_entitytolight;
+// based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
+// of attenuation texturing in full 3D (Z result often ignored)
+static matrix4x4_t r_shadow_entitytoattenuationxyz;
+// this transforms only the Z to S, and T is always 0.5
+static matrix4x4_t r_shadow_entitytoattenuationz;
+// rtlight->color * r_dlightstylevalue[rtlight->style] / 256 * r_shadow_lightintensityscale.value * ent->colormod * ent->alpha
+static vec3_t r_shadow_entitylightcolor;
+
+static int r_shadow_lightpermutation;
+static int r_shadow_lightprog;
+
 void R_Shadow_Stage_Begin(void)
 {
        rmeshstate_t m;
@@ -1103,14 +1095,34 @@ void R_Shadow_Stage_Begin(void)
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_CULL_FACE);
        GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
-       r_shadowstage = SHADOWSTAGE_NONE;
+       r_shadowstage = R_SHADOWSTAGE_NONE;
+}
+
+void R_Shadow_Stage_ActiveLight(rtlight_t *rtlight)
+{
+       r_shadow_rtlight = rtlight;
 }
 
-void R_Shadow_Stage_ShadowVolumes(void)
+void R_Shadow_Stage_Reset(void)
 {
        rmeshstate_t m;
+       if (gl_support_stenciltwoside)
+               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
+       if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
+       {
+               qglUseProgramObjectARB(0);
+               // HACK HACK HACK: work around for stupid NVIDIA bug that causes GL_OUT_OF_MEMORY and/or software rendering in 6xxx drivers
+               qglBegin(GL_TRIANGLES);
+               qglEnd();
+               CHECKGLERROR
+       }
        memset(&m, 0, sizeof(m));
        R_Mesh_State(&m);
+}
+
+void R_Shadow_Stage_StencilShadowVolumes(void)
+{
+       R_Shadow_Stage_Reset();
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(0, 0, 0, 0);
        GL_BlendFunc(GL_ONE, GL_ZERO);
@@ -1130,19 +1142,19 @@ void R_Shadow_Stage_ShadowVolumes(void)
        qglStencilFunc(GL_ALWAYS, 128, ~0);
        if (gl_ext_stenciltwoside.integer)
        {
-               r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
+               r_shadowstage = R_SHADOWSTAGE_STENCILTWOSIDE;
                qglDisable(GL_CULL_FACE);
                qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
                qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
                qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
                qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
                qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
        }
        else
        {
-               r_shadowstage = SHADOWSTAGE_STENCIL;
+               r_shadowstage = R_SHADOWSTAGE_STENCIL;
                qglEnable(GL_CULL_FACE);
                qglStencilMask(~0);
                // this is changed by every shadow render so its value here is unimportant
@@ -1150,20 +1162,12 @@ void R_Shadow_Stage_ShadowVolumes(void)
        }
        GL_Clear(GL_STENCIL_BUFFER_BIT);
        c_rt_clears++;
-       // LordHavoc note: many shadow volumes reside entirely inside the world
-       // (that is to say they are entirely bounded by their lit surfaces),
-       // which can be optimized by handling things as an inverted light volume,
-       // with the shadow boundaries of the world being simulated by an altered
-       // (129) bias to stencil clearing on such lights
-       // FIXME: generate inverted light volumes for use as shadow volumes and
-       // optimize for them as noted above
 }
 
-void R_Shadow_Stage_Light(int shadowtest)
+void R_Shadow_Stage_Lighting(int stenciltest)
 {
        rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
+       R_Shadow_Stage_Reset();
        GL_BlendFunc(GL_ONE, GL_ONE);
        GL_DepthMask(false);
        GL_DepthTest(true);
@@ -1174,26 +1178,122 @@ void R_Shadow_Stage_Light(int shadowtest)
        qglDepthFunc(GL_EQUAL);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_CULL_FACE);
-       if (shadowtest)
+       if (r_shadowstage == R_SHADOWSTAGE_STENCIL || r_shadowstage == R_SHADOWSTAGE_STENCILTWOSIDE)
                qglEnable(GL_STENCIL_TEST);
        else
                qglDisable(GL_STENCIL_TEST);
-       if (gl_support_stenciltwoside)
-               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        // only draw light where this geometry was already rendered AND the
        // stencil is 128 (values other than this mean shadow)
        qglStencilFunc(GL_EQUAL, 128, ~0);
-       r_shadowstage = SHADOWSTAGE_LIGHT;
-       c_rt_lights++;
+       if (r_shadow_glsl.integer && r_shadow_program_light[0])
+       {
+               r_shadowstage = R_SHADOWSTAGE_LIGHT_GLSL;
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = varray_vertex3f;
+               m.pointer_texcoord[0] = varray_texcoord2f[0];
+               m.pointer_texcoord3f[1] = varray_svector3f;
+               m.pointer_texcoord3f[2] = varray_tvector3f;
+               m.pointer_texcoord3f[3] = varray_normal3f;
+               m.tex[0] = R_GetTexture(r_texture_blanknormalmap); // normal
+               m.tex[1] = R_GetTexture(r_texture_white); // diffuse
+               m.tex[2] = R_GetTexture(r_texture_white); // gloss
+               m.texcubemap[3] = R_GetTexture(r_shadow_lightcubemap); // light filter
+               // TODO: support fog (after renderer is converted to texture fog)
+               m.tex[4] = R_GetTexture(r_texture_white); // fog
+               //m.texmatrix[3] = r_shadow_entitytolight; // light filter matrix
+               R_Mesh_State(&m);
+               GL_BlendFunc(GL_ONE, GL_ONE);
+               GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
+               CHECKGLERROR
+               r_shadow_lightpermutation = 0;
+               // only add a feature to the permutation if that permutation exists
+               // (otherwise it might end up not using a shader at all, which looks
+               // worse than using less features)
+               if (r_shadow_rtlight->specularscale && r_shadow_gloss.integer >= 1 && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SPECULAR])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_SPECULAR;
+               //if (fog && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_FOG])
+               //      r_shadow_lightpermutation |= SHADERPERMUTATION_FOG;
+               if (r_shadow_lightcubemap != r_texture_whitecube && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_CUBEFILTER])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_CUBEFILTER;
+               if (r_shadow_glsl_offsetmapping.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_OFFSETMAPPING])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_OFFSETMAPPING;
+               if (r_shadow_glsl_surfacenormalize.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SURFACENORMALIZE])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_SURFACENORMALIZE;
+               if (r_shadow_glsl_usehalffloat.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_GEFORCEFX])
+                       r_shadow_lightpermutation |= SHADERPERMUTATION_GEFORCEFX;
+               r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
+               qglUseProgramObjectARB(r_shadow_lightprog);CHECKGLERROR
+               // TODO: support fog (after renderer is converted to texture fog)
+               if (r_shadow_lightpermutation & SHADERPERMUTATION_FOG)
+               {
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "FogRangeRecip"), 0);CHECKGLERROR
+               }
+               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "AmbientScale"), r_shadow_rtlight->ambientscale);CHECKGLERROR
+               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "DiffuseScale"), r_shadow_rtlight->diffusescale);CHECKGLERROR
+               if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
+               {
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularPower"), 8);CHECKGLERROR
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), r_shadow_rtlight->specularscale);CHECKGLERROR
+               }
+               //qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
+               //qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), relativelightorigin[0], relativelightorigin[1], relativelightorigin[2]);CHECKGLERROR
+               //if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
+               //{
+               //      qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), relativeeyeorigin[0], relativeeyeorigin[1], relativeeyeorigin[2]);CHECKGLERROR
+               //}
+               if (r_shadow_lightpermutation & SHADERPERMUTATION_OFFSETMAPPING)
+               {
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
+               }
+       }
+       else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
+               r_shadowstage = R_SHADOWSTAGE_LIGHT_DOT3;
+       else
+               r_shadowstage = R_SHADOWSTAGE_LIGHT_VERTEX;
+}
+
+void R_Shadow_Stage_VisibleShadowVolumes(void)
+{
+       R_Shadow_Stage_Reset();
+       GL_BlendFunc(GL_ONE, GL_ONE);
+       GL_DepthMask(false);
+       GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
+       qglPolygonOffset(0, 0);
+       GL_Color(0.0, 0.0125, 0.1, 1);
+       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
+       qglDepthFunc(GL_GEQUAL);
+       qglCullFace(GL_FRONT); // this culls back
+       qglDisable(GL_CULL_FACE);
+       qglDisable(GL_STENCIL_TEST);
+       r_shadowstage = R_SHADOWSTAGE_VISIBLEVOLUMES;
+}
+
+void R_Shadow_Stage_VisibleLighting(int stenciltest)
+{
+       R_Shadow_Stage_Reset();
+       GL_BlendFunc(GL_ONE, GL_ONE);
+       GL_DepthMask(false);
+       GL_DepthTest(r_shadow_visiblelighting.integer < 2);
+       qglPolygonOffset(0, 0);
+       GL_Color(0.1, 0.0125, 0, 1);
+       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
+       qglDepthFunc(GL_EQUAL);
+       qglCullFace(GL_FRONT); // this culls back
+       qglEnable(GL_CULL_FACE);
+       if (stenciltest)
+               qglEnable(GL_STENCIL_TEST);
+       else
+               qglDisable(GL_STENCIL_TEST);
+       r_shadowstage = R_SHADOWSTAGE_VISIBLELIGHTING;
 }
 
 void R_Shadow_Stage_End(void)
 {
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
+       R_Shadow_Stage_Reset();
+       R_Shadow_Stage_ActiveLight(NULL);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(true);
        GL_DepthTest(true);
@@ -1210,190 +1310,109 @@ void R_Shadow_Stage_End(void)
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
        qglStencilFunc(GL_ALWAYS, 128, ~0);
-       r_shadowstage = SHADOWSTAGE_NONE;
+       r_shadowstage = R_SHADOWSTAGE_NONE;
 }
 
-int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
+qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 {
        int i, ix1, iy1, ix2, iy2;
-       float x1, y1, x2, y2, x, y, f;
-       vec3_t smins, smaxs;
+       float x1, y1, x2, y2;
        vec4_t v, v2;
-       if (!r_shadow_scissor.integer)
-               return false;
-       // if view is inside the box, just say yes it's visible
+       rmesh_t mesh;
+       mplane_t planes[11];
+       float vertex3f[256*3];
+
+       // if view is inside the light box, just say yes it's visible
        if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
        {
                GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
                return false;
        }
-       for (i = 0;i < 3;i++)
+
+       // create a temporary brush describing the area the light can affect in worldspace
+       VectorNegate(frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -frustum[0].dist;
+       VectorNegate(frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -frustum[1].dist;
+       VectorNegate(frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -frustum[2].dist;
+       VectorNegate(frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -frustum[3].dist;
+       VectorNegate(frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -frustum[4].dist;
+       VectorSet   (planes[ 5].normal,  1, 0, 0);         planes[ 5].dist =  maxs[0];
+       VectorSet   (planes[ 6].normal, -1, 0, 0);         planes[ 6].dist = -mins[0];
+       VectorSet   (planes[ 7].normal, 0,  1, 0);         planes[ 7].dist =  maxs[1];
+       VectorSet   (planes[ 8].normal, 0, -1, 0);         planes[ 8].dist = -mins[1];
+       VectorSet   (planes[ 9].normal, 0, 0,  1);         planes[ 9].dist =  maxs[2];
+       VectorSet   (planes[10].normal, 0, 0, -1);         planes[10].dist = -mins[2];
+
+       // turn the brush into a mesh
+       memset(&mesh, 0, sizeof(rmesh_t));
+       mesh.maxvertices = 256;
+       mesh.vertex3f = vertex3f;
+       mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
+       R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
+
+       // if that mesh is empty, the light is not visible at all
+       if (!mesh.numvertices)
+               return true;
+
+       if (!r_shadow_scissor.integer)
+               return false;
+
+       // if that mesh is not empty, check what area of the screen it covers
+       x1 = y1 = x2 = y2 = 0;
+       v[3] = 1.0f;
+       for (i = 0;i < mesh.numvertices;i++)
        {
-               if (r_viewforward[i] >= 0)
+               VectorCopy(mesh.vertex3f + i * 3, v);
+               GL_TransformToScreen(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)
                {
-                       v[i] = mins[i];
-                       v2[i] = maxs[i];
+                       if (x1 > v2[0]) x1 = v2[0];
+                       if (x2 < v2[0]) x2 = v2[0];
+                       if (y1 > v2[1]) y1 = v2[1];
+                       if (y2 < v2[1]) y2 = v2[1];
                }
                else
                {
-                       v[i] = maxs[i];
-                       v2[i] = mins[i];
-               }
-       }
-       f = DotProduct(r_viewforward, r_vieworigin) + 1;
-       if (DotProduct(r_viewforward, v2) <= f)
-       {
-               // entirely behind nearclip plane
-               return true;
-       }
-       if (DotProduct(r_viewforward, v) >= f)
-       {
-               // entirely infront of nearclip plane
-               x1 = y1 = x2 = y2 = 0;
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = (i & 1) ? mins[0] : maxs[0];
-                       v[1] = (i & 2) ? mins[1] : maxs[1];
-                       v[2] = (i & 4) ? mins[2] : maxs[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(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]);
-                       x = v2[0];
-                       y = v2[1];
-                       if (i)
-                       {
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-                       else
-                       {
-                               x1 = x2 = x;
-                               y1 = y2 = y;
-                       }
+                       x1 = x2 = v2[0];
+                       y1 = y2 = v2[1];
                }
        }
-       else
-       {
-               // clipped by nearclip plane
-               // this is nasty and crude...
-               // create viewspace bbox
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
-                       v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
-                       v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
-                       v2[0] = -DotProduct(v, r_viewleft);
-                       v2[1] = DotProduct(v, r_viewup);
-                       v2[2] = DotProduct(v, r_viewforward);
-                       if (i)
-                       {
-                               if (smins[0] > v2[0]) smins[0] = v2[0];
-                               if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
-                               if (smins[1] > v2[1]) smins[1] = v2[1];
-                               if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
-                               if (smins[2] > v2[2]) smins[2] = v2[2];
-                               if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
-                       }
-                       else
-                       {
-                               smins[0] = smaxs[0] = v2[0];
-                               smins[1] = smaxs[1] = v2[1];
-                               smins[2] = smaxs[2] = v2[2];
-                       }
-               }
-               // now we have a bbox in viewspace
-               // clip it to the view plane
-               if (smins[2] < 1)
-                       smins[2] = 1;
-               // return true if that culled the box
-               if (smins[2] >= smaxs[2])
-                       return true;
-               // ok some of it is infront of the view, transform each corner back to
-               // worldspace and then to screenspace and make screen rect
-               // initialize these variables just to avoid compiler warnings
-               x1 = y1 = x2 = y2 = 0;
-               for (i = 0;i < 8;i++)
-               {
-                       v2[0] = (i & 1) ? smins[0] : smaxs[0];
-                       v2[1] = (i & 2) ? smins[1] : smaxs[1];
-                       v2[2] = (i & 4) ? smins[2] : smaxs[2];
-                       v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
-                       v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
-                       v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(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]);
-                       x = v2[0];
-                       y = v2[1];
-                       if (i)
-                       {
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-                       else
-                       {
-                               x1 = x2 = x;
-                               y1 = y2 = y;
-                       }
-               }
-               /*
-               // this code doesn't handle boxes with any points behind view properly
-               x1 = 1000;x2 = -1000;
-               y1 = 1000;y2 = -1000;
-               for (i = 0;i < 8;i++)
-               {
-                       v[0] = (i & 1) ? mins[0] : maxs[0];
-                       v[1] = (i & 2) ? mins[1] : maxs[1];
-                       v[2] = (i & 4) ? mins[2] : maxs[2];
-                       v[3] = 1.0f;
-                       GL_TransformToScreen(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 (v2[2] > 0)
-                       {
-                               x = v2[0];
-                               y = v2[1];
 
-                               if (x1 > x) x1 = x;
-                               if (x2 < x) x2 = x;
-                               if (y1 > y) y1 = y;
-                               if (y2 < y) y2 = y;
-                       }
-               }
-               */
-       }
+       // now convert the scissor rectangle to integer screen coordinates
        ix1 = x1 - 1.0f;
        iy1 = y1 - 1.0f;
        ix2 = x2 + 1.0f;
        iy2 = y2 + 1.0f;
        //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
+
+       // clamp it to the screen
        if (ix1 < r_view_x) ix1 = r_view_x;
        if (iy1 < r_view_y) iy1 = r_view_y;
        if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
        if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
+
+       // if it is inside out, it's not visible
        if (ix2 <= ix1 || iy2 <= iy1)
                return true;
-       // set up the scissor rectangle
-       GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
+
+       // the light area is visible, set up the scissor rectangle
+       GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
        //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
        //qglEnable(GL_SCISSOR_TEST);
        c_rt_scissored++;
        return false;
 }
 
-static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
+static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
 {
        float *color4f = varray_color4f;
        float dist, dot, intensity, v[3], n[3];
        for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
+               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
                if ((dist = DotProduct(v, v)) < 1)
                {
-                       Matrix4x4_Transform3x3(m, normal3f, n);
+                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
                        if ((dot = DotProduct(n, v)) > 0)
                        {
                                dist = sqrt(dist);
@@ -1416,16 +1435,16 @@ static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *
        }
 }
 
-static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
+static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
 {
        float *color4f = varray_color4f;
        float dist, dot, intensity, v[3], n[3];
        for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
+               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
                if ((dist = fabs(v[2])) < 1)
                {
-                       Matrix4x4_Transform3x3(m, normal3f, n);
+                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
                        if ((dot = DotProduct(n, v)) > 0)
                        {
                                intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
@@ -1447,14 +1466,14 @@ static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *ve
        }
 }
 
-static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
+static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
 {
        float *color4f = varray_color4f;
        float dot, intensity, v[3], n[3];
        for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
-               Matrix4x4_Transform3x3(m, normal3f, n);
+               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
                if ((dot = DotProduct(n, v)) > 0)
                {
                        intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
@@ -1469,13 +1488,13 @@ static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const fl
        }
 }
 
-static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
+static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor)
 {
        float *color4f = varray_color4f;
        float dist, intensity, v[3];
        for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
+               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
                if ((dist = DotProduct(v, v)) < 1)
                {
                        dist = sqrt(dist);
@@ -1491,13 +1510,13 @@ static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float
        }
 }
 
-static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
+static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor)
 {
        float *color4f = varray_color4f;
        float dist, intensity, v[3];
        for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
+               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
                if ((dist = fabs(v[2])) < 1)
                {
                        intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
@@ -1565,9 +1584,9 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numve
        for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
                VectorSubtract(vertex3f, relativelightorigin, lightdir);
-               VectorNormalizeFast(lightdir);
+               VectorNormalize(lightdir);
                VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
-               VectorNormalizeFast(eyedir);
+               VectorNormalize(eyedir);
                VectorAdd(lightdir, eyedir, halfdir);
                // the cubemap normalizes this for us
                out3f[0] = DotProduct(svector3f, halfdir);
@@ -1576,116 +1595,172 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numve
        }
 }
 
-void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale)
+void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *lightcolorbase, const float *lightcolorpants, const float *lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *bumptexture, rtexture_t *glosstexture)
 {
        int renders;
-       float color[3], color2[3], colorscale;
+       float color[3], color2[3], colorscale, specularscale;
        rmeshstate_t m;
-       // FIXME: support EF_NODEPTHTEST
-       GL_DepthMask(false);
-       GL_DepthTest(true);
-       if (!bumptexture)
-               bumptexture = r_shadow_blankbumptexture;
-       specularscale *= r_shadow_glossintensity.value;
-       if (!glosstexture)
+       // FIXME: support MATERIALFLAG_NODEPTHTEST
+       if (r_shadowstage == R_SHADOWSTAGE_VISIBLELIGHTING)
        {
-               if (r_shadow_gloss.integer >= 2)
+               int passes = 0;
+               if (r_shadow_glsl.integer && r_shadow_program_light[0])
                {
-                       glosstexture = r_shadow_blankglosstexture;
-                       specularscale *= r_shadow_gloss2intensity.value;
+                       // GLSL shader path (GFFX5200, Radeon 9500)
+                       // TODO: add direct pants/shirt rendering
+                       if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       passes++;
                }
-               else
-               {
-                       glosstexture = r_shadow_blankblacktexture;
-                       specularscale = 0;
-               }
-       }
-       if (r_shadow_gloss.integer < 1)
-               specularscale = 0;
-       if (!lightcubemap)
-               lightcubemap = r_shadow_blankwhitecubetexture;
-       if (ambientscale + diffusescale + specularscale < 0.01)
-               return;
-       if (r_shadow_glsl.integer && r_shadow_program_light[0])
-       {
-               unsigned int perm, prog;
-               // GLSL shader path (GFFX5200, Radeon 9500)
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = vertex3f;
-               m.pointer_texcoord[0] = texcoord2f;
-               m.pointer_texcoord3f[1] = svector3f;
-               m.pointer_texcoord3f[2] = tvector3f;
-               m.pointer_texcoord3f[3] = normal3f;
-               m.tex[0] = R_GetTexture(bumptexture);
-               m.tex[1] = R_GetTexture(basetexture);
-               m.tex[2] = R_GetTexture(glosstexture);
-               m.texcubemap[3] = R_GetTexture(lightcubemap);
-               // TODO: support fog (after renderer is converted to texture fog)
-               m.tex[4] = R_GetTexture(r_shadow_blankwhitetexture);
-               m.texmatrix[3] = *matrix_modeltolight;
-               R_Mesh_State(&m);
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
-               CHECKGLERROR
-               perm = 0;
-               // only add a feature to the permutation if that permutation exists
-               // (otherwise it might end up not using a shader at all, which looks
-               // worse than using less features)
-               if (specularscale && r_shadow_program_light[perm | SHADERPERMUTATION_SPECULAR])
-                       perm |= SHADERPERMUTATION_SPECULAR;
-               //if (fog && r_shadow_program_light[perm | SHADERPERMUTATION_FOG])
-               //      perm |= SHADERPERMUTATION_FOG;
-               if (lightcubemap && r_shadow_program_light[perm | SHADERPERMUTATION_CUBEFILTER])
-                       perm |= SHADERPERMUTATION_CUBEFILTER;
-               if (r_shadow_glsl_offsetmapping.integer && r_shadow_program_light[perm | SHADERPERMUTATION_OFFSETMAPPING])
-                       perm |= SHADERPERMUTATION_OFFSETMAPPING;
-               prog = r_shadow_program_light[perm];
-               qglUseProgramObjectARB(prog);CHECKGLERROR
-               // TODO: support fog (after renderer is converted to texture fog)
-               if (perm & SHADERPERMUTATION_FOG)
+               else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
                {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "FogRangeRecip"), 0);CHECKGLERROR
+                       // TODO: add direct pants/shirt rendering
+                       if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       if (r_shadow_rtlight->ambientscale)
+                       {
+                               colorscale = r_shadow_rtlight->ambientscale;
+                               if (r_shadow_texture3d.integer && r_shadow_lightcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
+                               {
+                               }
+                               else if (r_shadow_texture3d.integer && r_shadow_lightcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
+                               {
+                               }
+                               else if (r_textureunits.integer >= 4 && r_shadow_lightcubemap != r_texture_whitecube)
+                               {
+                               }
+                               else if (r_textureunits.integer >= 3 && r_shadow_lightcubemap == r_texture_whitecube)
+                               {
+                               }
+                               else
+                                       passes++;
+                               VectorScale(lightcolorbase, colorscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                                       passes++;
+                       }
+                       if (r_shadow_rtlight->diffusescale)
+                       {
+                               colorscale = r_shadow_rtlight->diffusescale;
+                               if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
+                               {
+                                       // 3/2 3D combine path (Geforce3, Radeon 8500)
+                                       passes++;
+                               }
+                               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube)
+                               {
+                                       // 1/2/2 3D combine path (original Radeon)
+                                       passes += 2;
+                               }
+                               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube)
+                               {
+                                       // 2/2 3D combine path (original Radeon)
+                                       passes++;
+                               }
+                               else if (r_textureunits.integer >= 4)
+                               {
+                                       // 4/2 2D combine path (Geforce3, Radeon 8500)
+                                       passes++;
+                               }
+                               else
+                               {
+                                       // 2/2/2 2D combine path (any dot3 card)
+                                       passes += 2;
+                               }
+                               VectorScale(lightcolorbase, colorscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                                       passes++;
+                       }
+                       if (specularscale && glosstexture != r_texture_black)
+                       {
+                               //if (gl_support_blendsquare)
+                               {
+                                       colorscale = specularscale;
+                                       if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
+                                               passes += 4;
+                                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
+                                               passes += 3;
+                                       else
+                                               passes += 4;
+                                       VectorScale(lightcolorbase, colorscale, color2);
+                                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                                               passes++;
+                               }
+                       }
                }
-               qglUniform1fARB(qglGetUniformLocationARB(prog, "AmbientScale"), ambientscale);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(prog, "DiffuseScale"), diffusescale);CHECKGLERROR
-               if (perm & SHADERPERMUTATION_SPECULAR)
+               else
                {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "SpecularPower"), 8);CHECKGLERROR
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "SpecularScale"), specularscale);CHECKGLERROR
+                       // TODO: add direct pants/shirt rendering
+                       if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+                       if (r_shadow_rtlight->ambientscale)
+                       {
+                               VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                                       passes++;
+                       }
+                       if (r_shadow_rtlight->diffusescale)
+                       {
+                               VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale, color2);
+                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                                       passes++;
+                       }
                }
-               qglUniform3fARB(qglGetUniformLocationARB(prog, "LightColor"), lightcolor[0], lightcolor[1], lightcolor[2]);CHECKGLERROR
-               qglUniform3fARB(qglGetUniformLocationARB(prog, "LightPosition"), relativelightorigin[0], relativelightorigin[1], relativelightorigin[2]);CHECKGLERROR
-               if (perm & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
+               if (passes)
                {
-                       qglUniform3fARB(qglGetUniformLocationARB(prog, "EyePosition"), relativeeyeorigin[0], relativeeyeorigin[1], relativeeyeorigin[2]);CHECKGLERROR
+                       GL_Color(0.1*passes, 0.025*passes, 0, 1);
+                       memset(&m, 0, sizeof(m));
+                       m.pointer_vertex = vertex3f;
+                       R_Mesh_State(&m);
+                       GL_LockArrays(firstvertex, numvertices);
+                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
+                       GL_LockArrays(0, 0);
                }
-               if (perm & SHADERPERMUTATION_OFFSETMAPPING)
+               return;
+       }
+       else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
+       {
+               // GLSL shader path (GFFX5200, Radeon 9500)
+               // TODO: add direct pants/shirt rendering
+               if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               R_Mesh_VertexPointer(vertex3f);
+               R_Mesh_TexCoordPointer(0, 2, texcoord2f);
+               R_Mesh_TexCoordPointer(1, 3, svector3f);
+               R_Mesh_TexCoordPointer(2, 3, tvector3f);
+               R_Mesh_TexCoordPointer(3, 3, normal3f);
+               R_Mesh_TexBind(0, R_GetTexture(bumptexture));
+               R_Mesh_TexBind(1, R_GetTexture(basetexture));
+               R_Mesh_TexBind(2, R_GetTexture(glosstexture));
+               if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
                {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
+                       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), specularscale);CHECKGLERROR
                }
-               CHECKGLERROR
-               GL_LockArrays(0, numverts);
-               R_Mesh_Draw(numverts, numtriangles, elements);
+               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
+               GL_LockArrays(firstvertex, numvertices);
+               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                c_rt_lightmeshes++;
                c_rt_lighttris += numtriangles;
                GL_LockArrays(0, 0);
-               qglUseProgramObjectARB(0);
-               // HACK HACK HACK: work around for stupid NVIDIA bug that causes GL_OUT_OF_MEMORY and/or software rendering
-               qglBegin(GL_TRIANGLES);
-               qglEnd();
-               CHECKGLERROR
        }
-       else if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
+       else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_DOT3)
        {
-               if (!bumptexture)
-                       bumptexture = r_shadow_blankbumptexture;
-               if (!glosstexture)
-                       glosstexture = r_shadow_blankglosstexture;
-               if (ambientscale)
+               // TODO: add direct pants/shirt rendering
+               if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               if (r_shadow_rtlight->ambientscale)
                {
                        GL_Color(1,1,1,1);
-                       colorscale = ambientscale;
+                       colorscale = r_shadow_rtlight->ambientscale;
                        // colorscale accounts for how much we multiply the brightness
                        // during combine.
                        //
@@ -1693,7 +1768,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                        // performed to get more brightness than otherwise possible.
                        //
                        // Limit mult to 64 for sanity sake.
-                       if (r_shadow_texture3d.integer && lightcubemap && r_textureunits.integer >= 4)
+                       if (r_shadow_texture3d.integer && r_shadow_lightcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
                        {
                                // 3 3D combine path (Geforce3, Radeon 8500)
                                memset(&m, 0, sizeof(m));
@@ -1701,24 +1776,24 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(basetexture);
                                m.pointer_texcoord[1] = texcoord2f;
-                               m.texcubemap[2] = R_GetTexture(lightcubemap);
+                               m.texcubemap[2] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltolight;
+                               m.texmatrix[2] = r_shadow_entitytolight;
 #else
                                m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltolight);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                GL_BlendFunc(GL_ONE, GL_ONE);
                        }
-                       else if (r_shadow_texture3d.integer && !lightcubemap && r_textureunits.integer >= 2)
+                       else if (r_shadow_texture3d.integer && r_shadow_lightcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
                        {
                                // 2 3D combine path (Geforce3, original Radeon)
                                memset(&m, 0, sizeof(m));
@@ -1726,16 +1801,16 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(basetexture);
                                m.pointer_texcoord[1] = texcoord2f;
                                GL_BlendFunc(GL_ONE, GL_ONE);
                        }
-                       else if (r_textureunits.integer >= 4 && lightcubemap)
+                       else if (r_textureunits.integer >= 4 && r_shadow_lightcubemap != r_texture_whitecube)
                        {
                                // 4 2D combine path (Geforce3, Radeon 8500)
                                memset(&m, 0, sizeof(m));
@@ -1743,35 +1818,35 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                m.tex[2] = R_GetTexture(basetexture);
                                m.pointer_texcoord[2] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[3] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[3] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[3] = vertex3f;
-                                       m.texmatrix[3] = *matrix_modeltolight;
+                                       m.texmatrix[3] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[3] = varray_texcoord3f[3];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_ONE, GL_ONE);
                        }
-                       else if (r_textureunits.integer >= 3 && !lightcubemap)
+                       else if (r_textureunits.integer >= 3 && r_shadow_lightcubemap == r_texture_whitecube)
                        {
                                // 3 2D combine path (Geforce3, original Radeon)
                                memset(&m, 0, sizeof(m));
@@ -1779,18 +1854,18 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                m.tex[2] = R_GetTexture(basetexture);
                                m.pointer_texcoord[2] = texcoord2f;
@@ -1804,24 +1879,24 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1830,15 +1905,15 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.pointer_vertex = vertex3f;
                                m.tex[0] = R_GetTexture(basetexture);
                                m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
+                                       m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
@@ -1846,21 +1921,21 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                        // this final code is shared
                        R_Mesh_State(&m);
                        GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
-                       VectorScale(lightcolor, colorscale, color2);
-                       GL_LockArrays(0, numverts);
+                       VectorScale(lightcolorbase, colorscale, color2);
+                       GL_LockArrays(firstvertex, numvertices);
                        for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
                        {
                                GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
                        }
                        GL_LockArrays(0, 0);
                }
-               if (diffusescale)
+               if (r_shadow_rtlight->diffusescale)
                {
                        GL_Color(1,1,1,1);
-                       colorscale = diffusescale;
+                       colorscale = r_shadow_rtlight->diffusescale;
                        // colorscale accounts for how much we multiply the brightness
                        // during combine.
                        //
@@ -1876,23 +1951,23 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(bumptexture);
                                m.texcombinergb[0] = GL_REPLACE;
                                m.pointer_texcoord[0] = texcoord2f;
-                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
                                m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1901,20 +1976,20 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.pointer_vertex = vertex3f;
                                m.tex[0] = R_GetTexture(basetexture);
                                m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
+                                       m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                        }
-                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
+                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube)
                        {
                                // 1/2/2 3D combine path (original Radeon)
                                memset(&m, 0, sizeof(m));
@@ -1922,16 +1997,16 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1941,14 +2016,14 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(bumptexture);
                                m.texcombinergb[0] = GL_REPLACE;
                                m.pointer_texcoord[0] = texcoord2f;
-                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
                                R_Mesh_State(&m);
                                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1957,20 +2032,20 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.pointer_vertex = vertex3f;
                                m.tex[0] = R_GetTexture(basetexture);
                                m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
+                                       m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                        }
-                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
+                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube)
                        {
                                // 2/2 3D combine path (original Radeon)
                                memset(&m, 0, sizeof(m));
@@ -1978,15 +2053,15 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(bumptexture);
                                m.texcombinergb[0] = GL_REPLACE;
                                m.pointer_texcoord[0] = texcoord2f;
-                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -1998,10 +2073,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                        }
@@ -2013,31 +2088,31 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(bumptexture);
                                m.texcombinergb[0] = GL_REPLACE;
                                m.pointer_texcoord[0] = texcoord2f;
-                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
                                m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[2] = varray_texcoord2f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[3] = vertex3f;
-                               m.texmatrix[3] = *matrix_modeltoattenuationz;
+                               m.texmatrix[3] = r_shadow_entitytoattenuationz;
 #else
                                m.pointer_texcoord[3] = varray_texcoord2f[3];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -2046,15 +2121,15 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.pointer_vertex = vertex3f;
                                m.tex[0] = R_GetTexture(basetexture);
                                m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
+                                       m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
@@ -2067,24 +2142,24 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                R_Mesh_State(&m);
                                GL_ColorMask(0,0,0,1);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -2094,14 +2169,14 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[0] = R_GetTexture(bumptexture);
                                m.texcombinergb[0] = GL_REPLACE;
                                m.pointer_texcoord[0] = texcoord2f;
-                               m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
+                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
                                R_Mesh_State(&m);
                                GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -2110,15 +2185,15 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.pointer_vertex = vertex3f;
                                m.tex[0] = R_GetTexture(basetexture);
                                m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
+                               if (r_shadow_lightcubemap != r_texture_whitecube)
                                {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                       m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
+                                       m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                }
                                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
@@ -2126,41 +2201,41 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                        // this final code is shared
                        R_Mesh_State(&m);
                        GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
-                       VectorScale(lightcolor, colorscale, color2);
-                       GL_LockArrays(0, numverts);
+                       VectorScale(lightcolorbase, colorscale, color2);
+                       GL_LockArrays(firstvertex, numvertices);
                        for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
                        {
                                GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
                        }
                        GL_LockArrays(0, 0);
                }
-               if (specularscale && glosstexture != r_shadow_blankblacktexture)
+               if (specularscale && glosstexture != r_texture_black)
                {
                        // FIXME: detect blendsquare!
                        //if (gl_support_blendsquare)
                        {
                                colorscale = specularscale;
                                GL_Color(1,1,1,1);
-                               if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
+                               if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
                                {
                                        // 2/0/0/1/2 3D combine blendsquare path
                                        memset(&m, 0, sizeof(m));
                                        m.pointer_vertex = vertex3f;
                                        m.tex[0] = R_GetTexture(bumptexture);
                                        m.pointer_texcoord[0] = texcoord2f;
-                                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                                       m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                        m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
                                        R_Mesh_State(&m);
                                        GL_ColorMask(0,0,0,1);
                                        // this squares the result
                                        GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(0, numverts);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       GL_LockArrays(firstvertex, numvertices);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        GL_LockArrays(0, 0);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
@@ -2168,17 +2243,17 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        memset(&m, 0, sizeof(m));
                                        m.pointer_vertex = vertex3f;
                                        R_Mesh_State(&m);
-                                       GL_LockArrays(0, numverts);
+                                       GL_LockArrays(firstvertex, numvertices);
                                        // square alpha in framebuffer a few times to make it shiny
                                        GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                                        // these comments are a test run through this math for intensity 0.5
                                        // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
                                        // 0.25 * 0.25 = 0.0625 (this is another pass)
                                        // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
                                        GL_LockArrays(0, 0);
@@ -2188,15 +2263,15 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[0] = vertex3f;
-                                       m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                                       m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                        m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                        R_Mesh_State(&m);
                                        GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                                       GL_LockArrays(0, numverts);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       GL_LockArrays(firstvertex, numvertices);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        GL_LockArrays(0, 0);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
@@ -2205,36 +2280,36 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.pointer_vertex = vertex3f;
                                        m.tex[0] = R_GetTexture(glosstexture);
                                        m.pointer_texcoord[0] = texcoord2f;
-                                       if (lightcubemap)
+                                       if (r_shadow_lightcubemap != r_texture_whitecube)
                                        {
-                                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                               m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                                m.pointer_texcoord3f[1] = vertex3f;
-                                               m.texmatrix[1] = *matrix_modeltolight;
+                                               m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                        }
                                        GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                                }
-                               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
+                               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
                                {
                                        // 2/0/0/2 3D combine blendsquare path
                                        memset(&m, 0, sizeof(m));
                                        m.pointer_vertex = vertex3f;
                                        m.tex[0] = R_GetTexture(bumptexture);
                                        m.pointer_texcoord[0] = texcoord2f;
-                                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                                       m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                        m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
                                        R_Mesh_State(&m);
                                        GL_ColorMask(0,0,0,1);
                                        // this squares the result
                                        GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(0, numverts);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       GL_LockArrays(firstvertex, numvertices);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        GL_LockArrays(0, 0);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
@@ -2242,17 +2317,17 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        memset(&m, 0, sizeof(m));
                                        m.pointer_vertex = vertex3f;
                                        R_Mesh_State(&m);
-                                       GL_LockArrays(0, numverts);
+                                       GL_LockArrays(firstvertex, numvertices);
                                        // square alpha in framebuffer a few times to make it shiny
                                        GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                                        // these comments are a test run through this math for intensity 0.5
                                        // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
                                        // 0.25 * 0.25 = 0.0625 (this is another pass)
                                        // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
                                        GL_LockArrays(0, 0);
@@ -2264,10 +2339,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltoattenuationxyz;
+                                       m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
 #else
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                        GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                                }
@@ -2278,16 +2353,16 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.pointer_vertex = vertex3f;
                                        m.tex[0] = R_GetTexture(bumptexture);
                                        m.pointer_texcoord[0] = texcoord2f;
-                                       m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
+                                       m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                                        m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
                                        m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
+                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
                                        R_Mesh_State(&m);
                                        GL_ColorMask(0,0,0,1);
                                        // this squares the result
                                        GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(0, numverts);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       GL_LockArrays(firstvertex, numvertices);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        GL_LockArrays(0, 0);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
@@ -2295,17 +2370,17 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        memset(&m, 0, sizeof(m));
                                        m.pointer_vertex = vertex3f;
                                        R_Mesh_State(&m);
-                                       GL_LockArrays(0, numverts);
+                                       GL_LockArrays(firstvertex, numvertices);
                                        // square alpha in framebuffer a few times to make it shiny
                                        GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
                                        // these comments are a test run through this math for intensity 0.5
                                        // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
                                        // 0.25 * 0.25 = 0.0625 (this is another pass)
                                        // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
                                        GL_LockArrays(0, 0);
@@ -2315,23 +2390,23 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[0] = vertex3f;
-                                       m.texmatrix[0] = *matrix_modeltoattenuationxyz;
+                                       m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
 #else
                                        m.pointer_texcoord[0] = varray_texcoord2f[0];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                        m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltoattenuationz;
+                                       m.texmatrix[1] = r_shadow_entitytoattenuationz;
 #else
                                        m.pointer_texcoord[1] = varray_texcoord2f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                        R_Mesh_State(&m);
                                        GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                                       GL_LockArrays(0, numverts);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       GL_LockArrays(firstvertex, numvertices);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        GL_LockArrays(0, 0);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
@@ -2340,27 +2415,27 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.pointer_vertex = vertex3f;
                                        m.tex[0] = R_GetTexture(glosstexture);
                                        m.pointer_texcoord[0] = texcoord2f;
-                                       if (lightcubemap)
+                                       if (r_shadow_lightcubemap != r_texture_whitecube)
                                        {
-                                               m.texcubemap[1] = R_GetTexture(lightcubemap);
+                                               m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
 #ifdef USETEXMATRIX
                                                m.pointer_texcoord3f[1] = vertex3f;
-                                               m.texmatrix[1] = *matrix_modeltolight;
+                                               m.texmatrix[1] = r_shadow_entitytolight;
 #else
                                                m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
+                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
 #endif
                                        }
                                        GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
                                }
                                R_Mesh_State(&m);
                                GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
-                               VectorScale(lightcolor, colorscale, color2);
-                               GL_LockArrays(0, numverts);
+                               VectorScale(lightcolorbase, colorscale, color2);
+                               GL_LockArrays(firstvertex, numvertices);
                                for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
                                {
                                        GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
-                                       R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                        c_rt_lightmeshes++;
                                        c_rt_lighttris += numtriangles;
                                }
@@ -2368,12 +2443,17 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                        }
                }
        }
-       else
+       else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_VERTEX)
        {
-               if (ambientscale)
+               // TODO: add direct pants/shirt rendering
+               if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, r_texture_black, r_texture_black, bumptexture, NULL);
+               if (r_shadow_rtlight->ambientscale)
                {
                        GL_BlendFunc(GL_ONE, GL_ONE);
-                       VectorScale(lightcolorambientscale, color2);
+                       VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale, color2);
                        memset(&m, 0, sizeof(m));
                        m.pointer_vertex = vertex3f;
                        m.tex[0] = R_GetTexture(basetexture);
@@ -2384,10 +2464,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                if (r_textureunits.integer >= 3)
                                {
@@ -2395,10 +2475,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[2] = vertex3f;
-                                       m.texmatrix[2] = *matrix_modeltoattenuationz;
+                                       m.texmatrix[2] = r_shadow_entitytoattenuationz;
 #else
                                        m.pointer_texcoord[2] = varray_texcoord2f[2];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                }
                        }
@@ -2415,20 +2495,20 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                if (r_textureunits.integer >= 3)
                                        GL_Color(color[0], color[1], color[2], 1);
                                else if (r_textureunits.integer >= 2)
-                                       R_Shadow_VertexNoShadingWithZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
+                                       R_Shadow_VertexNoShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, color);
                                else
-                                       R_Shadow_VertexNoShadingWithXYZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Shadow_VertexNoShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, color);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
                        }
                }
-               if (diffusescale)
+               if (r_shadow_rtlight->diffusescale)
                {
                        GL_BlendFunc(GL_ONE, GL_ONE);
-                       VectorScale(lightcolordiffusescale, color2);
+                       VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale, color2);
                        memset(&m, 0, sizeof(m));
                        m.pointer_vertex = vertex3f;
                        m.pointer_color = varray_color4f;
@@ -2440,10 +2520,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
+                               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
 #else
                                m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
+                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
 #endif
                                if (r_textureunits.integer >= 3)
                                {
@@ -2451,10 +2531,10 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                        m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
 #ifdef USETEXMATRIX
                                        m.pointer_texcoord3f[2] = vertex3f;
-                                       m.texmatrix[2] = *matrix_modeltoattenuationz;
+                                       m.texmatrix[2] = r_shadow_entitytoattenuationz;
 #else
                                        m.pointer_texcoord[2] = varray_texcoord2f[2];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
+                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
 #endif
                                }
                        }
@@ -2465,13 +2545,13 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements
                                color[1] = bound(0, color2[1], 1);
                                color[2] = bound(0, color2[2], 1);
                                if (r_textureunits.integer >= 3)
-                                       R_Shadow_VertexShading(numverts, vertex3f, normal3f, color, matrix_modeltolight);
+                                       R_Shadow_VertexShading(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
                                else if (r_textureunits.integer >= 2)
-                                       R_Shadow_VertexShadingWithZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
+                                       R_Shadow_VertexShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
                                else
-                                       R_Shadow_VertexShadingWithXYZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
-                               GL_LockArrays(0, numverts);
-                               R_Mesh_Draw(numverts, numtriangles, elements);
+                                       R_Shadow_VertexShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
+                               GL_LockArrays(firstvertex, numvertices);
+                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
                                GL_LockArrays(0, 0);
                                c_rt_lightmeshes++;
                                c_rt_lighttris += numtriangles;
@@ -2519,8 +2599,6 @@ void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int i
        for (k = 0;k < 3;k++)
                for (j = 0;j < 4;j++)
                        rtlight->matrix_worldtolight.m[k][j] *= scale;
-       Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
-       Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
 
        rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
        rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
@@ -2532,16 +2610,19 @@ void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int i
 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
 void R_RTLight_Compile(rtlight_t *rtlight)
 {
-       int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
-       entity_render_t *ent = &cl_entities[0].render;
-       model_t *model = ent->model;
+       int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
+       entity_render_t *ent = r_refdef.worldentity;
+       model_t *model = r_refdef.worldmodel;
+       qbyte *data;
 
        // compile the light
        rtlight->compiled = true;
-       rtlight->static_numclusters = 0;
-       rtlight->static_numclusterpvsbytes = 0;
-       rtlight->static_clusterlist = NULL;
-       rtlight->static_clusterpvs = NULL;
+       rtlight->static_numleafs = 0;
+       rtlight->static_numleafpvsbytes = 0;
+       rtlight->static_leaflist = NULL;
+       rtlight->static_leafpvs = NULL;
+       rtlight->static_numsurfaces = 0;
+       rtlight->static_surfacelist = NULL;
        rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
        rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
        rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
@@ -2553,30 +2634,28 @@ void R_RTLight_Compile(rtlight_t *rtlight)
        {
                // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
                r_shadow_compilingrtlight = rtlight;
-               R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
-               R_Shadow_EnlargeSurfaceBuffer(model->nummodelsurfaces);
-               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
-               rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
-               rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
-               if (numclusters)
-               {
-                       rtlight->static_numclusters = numclusters;
-                       rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
-                       memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
-                       memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
-               }
+               R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
+               model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
+               data = Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
+               rtlight->static_numleafs = numleafs;
+               rtlight->static_numleafpvsbytes = numleafpvsbytes;
+               rtlight->static_leaflist = (void *)data;data += sizeof(int) * numleafs;
+               rtlight->static_leafpvs = (void *)data;data += numleafpvsbytes;
+               rtlight->static_numsurfaces = numsurfaces;
+               rtlight->static_surfacelist = (void *)data;data += sizeof(int) * numsurfaces;
+               if (numleafs)
+                       memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
+               if (numleafpvsbytes)
+                       memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
+               if (numsurfaces)
+                       memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
                if (model->DrawShadowVolume && rtlight->shadow)
                {
                        rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
-                       model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
+                       model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist, rtlight->cullmins, rtlight->cullmaxs);
                        rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
                }
-               if (model->DrawLight)
-               {
-                       rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
-                       model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, 0, 0, 0, numsurfaces, r_shadow_buffer_surfacelist);
-                       rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
-               }
                // switch back to rendering when DrawShadowVolume or DrawLight is called
                r_shadow_compilingrtlight = NULL;
        }
@@ -2598,19 +2677,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                }
        }
 
-       lightmeshes = 0;
-       lighttris = 0;
-       if (rtlight->static_meshchain_light)
-       {
-               shadowmesh_t *mesh;
-               for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
-               {
-                       lightmeshes++;
-                       lighttris += mesh->numtriangles;
-               }
-       }
-
-       Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
+       Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes);
 }
 
 void R_RTLight_Uncompile(rtlight_t *rtlight)
@@ -2620,17 +2687,15 @@ void R_RTLight_Uncompile(rtlight_t *rtlight)
                if (rtlight->static_meshchain_shadow)
                        Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
                rtlight->static_meshchain_shadow = NULL;
-               if (rtlight->static_meshchain_light)
-                       Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
-               rtlight->static_meshchain_light = NULL;
-               if (rtlight->static_clusterlist)
-                       Mem_Free(rtlight->static_clusterlist);
-               rtlight->static_clusterlist = NULL;
-               if (rtlight->static_clusterpvs)
-                       Mem_Free(rtlight->static_clusterpvs);
-               rtlight->static_clusterpvs = NULL;
-               rtlight->static_numclusters = 0;
-               rtlight->static_numclusterpvsbytes = 0;
+               // these allocations are grouped
+               if (rtlight->static_leaflist)
+                       Mem_Free(rtlight->static_leaflist);
+               rtlight->static_numleafs = 0;
+               rtlight->static_numleafpvsbytes = 0;
+               rtlight->static_leaflist = NULL;
+               rtlight->static_leafpvs = NULL;
+               rtlight->static_numsurfaces = 0;
+               rtlight->static_surfacelist = NULL;
                rtlight->compiled = false;
        }
 }
@@ -2642,28 +2707,107 @@ void R_Shadow_UncompileWorldLights(void)
                R_RTLight_Uncompile(&light->rtlight);
 }
 
-void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
+void R_Shadow_DrawEntityShadow(entity_render_t *ent, rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
-       int i, shadow, usestencil;
-       entity_render_t *ent;
+       vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
+       vec_t relativeshadowradius;
+       if (ent == r_refdef.worldentity)
+       {
+               if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
+               {
+                       shadowmesh_t *mesh;
+                       R_Mesh_Matrix(&ent->matrix);
+                       for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+                       {
+                               R_Mesh_VertexPointer(mesh->vertex3f);
+                               GL_LockArrays(0, mesh->numverts);
+                               if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
+                               {
+                                       // decrement stencil if backface is behind depthbuffer
+                                       qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+                                       R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
+                                       c_rtcached_shadowmeshes++;
+                                       c_rtcached_shadowtris += mesh->numtriangles;
+                                       // increment stencil if frontface is behind depthbuffer
+                                       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+                               }
+                               R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
+                               c_rtcached_shadowmeshes++;
+                               c_rtcached_shadowtris += mesh->numtriangles;
+                               GL_LockArrays(0, 0);
+                       }
+               }
+               else if (numsurfaces)
+               {
+                       R_Mesh_Matrix(&ent->matrix);
+                       ent->model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, surfacelist, rtlight->cullmins, rtlight->cullmaxs);
+               }
+       }
+       else
+       {
+               Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativeshadoworigin);
+               relativeshadowradius = rtlight->radius / ent->scale;
+               relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
+               relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
+               relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
+               relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
+               relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
+               relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
+               R_Mesh_Matrix(&ent->matrix);
+               ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+       }
+}
+
+void R_Shadow_DrawEntityLight(entity_render_t *ent, rtlight_t *rtlight, vec3_t lightcolorbase, int numsurfaces, int *surfacelist)
+{
+       // set up properties for rendering light onto this entity
+       r_shadow_entitylightcolor[0] = lightcolorbase[0] * ent->colormod[0] * ent->alpha;
+       r_shadow_entitylightcolor[1] = lightcolorbase[1] * ent->colormod[1] * ent->alpha;
+       r_shadow_entitylightcolor[2] = lightcolorbase[2] * ent->colormod[2] * ent->alpha;
+       Matrix4x4_Concat(&r_shadow_entitytolight, &rtlight->matrix_worldtolight, &ent->matrix);
+       Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
+       Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
+       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, r_shadow_entitylightorigin);
+       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
+       R_Mesh_Matrix(&ent->matrix);
+       if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
+       {
+               R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_lightcubemap));
+               R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
+               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);CHECKGLERROR
+               if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
+               {
+                       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);CHECKGLERROR
+               }
+       }
+       if (ent == r_refdef.worldentity)
+               ent->model->DrawLight(ent, r_shadow_entitylightcolor, numsurfaces, surfacelist);
+       else
+               ent->model->DrawLight(ent, r_shadow_entitylightcolor, ent->model->nummodelsurfaces, ent->model->surfacelist);
+}
+
+void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
+{
+       int i, usestencil;
        float f;
-       vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2;
-       rtexture_t *cubemaptexture;
-       matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
-       int numclusters, numsurfaces;
-       int *clusterlist, *surfacelist;
-       qbyte *clusterpvs;
-       vec3_t cullmins, cullmaxs;
-       shadowmesh_t *mesh;
-       rmeshstate_t m;
+       vec3_t lightcolor;
+       int numleafs, numsurfaces;
+       int *leaflist, *surfacelist;
+       qbyte *leafpvs;
+       int numlightentities;
+       int numshadowentities;
+       entity_render_t *lightentities[MAX_EDICTS];
+       entity_render_t *shadowentities[MAX_EDICTS];
 
        // skip lights that don't light (corona only lights)
-       if (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale < 0.01)
+       if (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale < (1.0f / 32768.0f))
                return;
 
        f = (rtlight->style >= 0 ? d_lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
        VectorScale(rtlight->color, f, lightcolor);
-       if (VectorLength2(lightcolor) < 0.01)
+       if (VectorLength2(lightcolor) < (1.0f / 32768.0f))
                return;
        /*
        if (rtlight->selected)
@@ -2676,225 +2820,150 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
        // loading is done before visibility checks because loading should happen
        // all at once at the start of a level, not when it stalls gameplay.
        // (especially important to benchmarks)
-       if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
+       // compile light
+       if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
                R_RTLight_Compile(rtlight);
-       if (rtlight->cubemapname[0])
-               cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
-       else
-               cubemaptexture = NULL;
-
-       cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
-       cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
-       cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
-       cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
-       cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
-       cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
-       if (rtlight->style >= 0 && d_lightstylevalue[rtlight->style] <= 0)
+       // load cubemap
+       r_shadow_lightcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
+
+       // if the light box is offscreen, skip it
+       if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
                return;
-       numclusters = 0;
-       clusterlist = NULL;
-       clusterpvs = NULL;
-       numsurfaces = 0;
-       surfacelist = NULL;
-       if (rtlight->compiled && r_shadow_staticworldlights.integer)
+
+       if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
        {
                // compiled light, world available and can receive realtime lighting
-               // retrieve cluster information
-               numclusters = rtlight->static_numclusters;
-               clusterlist = rtlight->static_clusterlist;
-               clusterpvs = rtlight->static_clusterpvs;
-               VectorCopy(rtlight->cullmins, cullmins);
-               VectorCopy(rtlight->cullmaxs, cullmaxs);
+               // retrieve leaf information
+               numleafs = rtlight->static_numleafs;
+               leaflist = rtlight->static_leaflist;
+               leafpvs = rtlight->static_leafpvs;
+               numsurfaces = rtlight->static_numsurfaces;
+               surfacelist = rtlight->static_surfacelist;
        }
        else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
        {
                // dynamic light, world available and can receive realtime lighting
-               // if the light box is offscreen, skip it right away
-               if (R_CullBox(cullmins, cullmaxs))
-                       return;
-               // calculate lit surfaces and clusters
-               R_Shadow_EnlargeClusterBuffer(r_refdef.worldmodel->brush.num_pvsclusters);
-               R_Shadow_EnlargeSurfaceBuffer(r_refdef.worldmodel->nummodelsurfaces);
-               r_refdef.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
-               clusterlist = r_shadow_buffer_clusterlist;
-               clusterpvs = r_shadow_buffer_clusterpvs;
+               // calculate lit surfaces and leafs
+               R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
+               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               leaflist = r_shadow_buffer_leaflist;
+               leafpvs = r_shadow_buffer_leafpvs;
                surfacelist = r_shadow_buffer_surfacelist;
+               // if the reduced leaf bounds are offscreen, skip it
+               if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
+                       return;
        }
-       // if the reduced cluster bounds are offscreen, skip it
-       if (R_CullBox(cullmins, cullmaxs))
-               return;
-       // check if light is illuminating any visible clusters
-       if (numclusters)
+       else
+       {
+               // no world
+               numleafs = 0;
+               leaflist = NULL;
+               leafpvs = NULL;
+               numsurfaces = 0;
+               surfacelist = NULL;
+       }
+       // check if light is illuminating any visible leafs
+       if (numleafs)
        {
-               for (i = 0;i < numclusters;i++)
-                       if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
+               for (i = 0;i < numleafs;i++)
+                       if (r_worldleafvisible[leaflist[i]])
                                break;
-               if (i == numclusters)
+               if (i == numleafs)
                        return;
        }
        // set up a scissor rectangle for this light
-       if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
+       if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
                return;
 
-       shadow = rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows);
-       usestencil = false;
-
-       if (shadow && (gl_stencil || visiblevolumes))
+       numlightentities = 0;
+       if (numsurfaces)
+               lightentities[numlightentities++] = r_refdef.worldentity;
+       numshadowentities = 0;
+       if (numsurfaces)
+               shadowentities[numshadowentities++] = r_refdef.worldentity;
+       if (r_drawentities.integer)
        {
-               if (!visiblevolumes)
-               {
-                       R_Shadow_Stage_ShadowVolumes();
-                       usestencil = true;
-               }
-               ent = &cl_entities[0].render;
-               if (r_shadow_staticworldlights.integer && rtlight->compiled)
-               {
-                       memset(&m, 0, sizeof(m));
-                       R_Mesh_Matrix(&ent->matrix);
-                       for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
-                       {
-                               m.pointer_vertex = mesh->vertex3f;
-                               R_Mesh_State(&m);
-                               GL_LockArrays(0, mesh->numverts);
-                               if (r_shadowstage == SHADOWSTAGE_STENCIL)
-                               {
-                                       // increment stencil if backface is behind depthbuffer
-                                       qglCullFace(GL_BACK); // quake is backwards, this culls front faces
-                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
-                                       R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
-                                       c_rtcached_shadowmeshes++;
-                                       c_rtcached_shadowtris += mesh->numtriangles;
-                                       // decrement stencil if frontface is behind depthbuffer
-                                       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
-                               }
-                               R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
-                               c_rtcached_shadowmeshes++;
-                               c_rtcached_shadowtris += mesh->numtriangles;
-                               GL_LockArrays(0, 0);
-                       }
-               }
-               else if (numsurfaces)
+               for (i = 0;i < r_refdef.numentities;i++)
                {
-                       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
-                       ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
-               }
-               if (r_drawentities.integer)
-               {
-                       for (i = 0;i < r_refdef.numentities;i++)
+                       entity_render_t *ent = r_refdef.entities[i];
+                       if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
+                        && ent->model
+                        && !(ent->flags & RENDER_TRANSPARENT)
+                        && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
                        {
-                               ent = r_refdef.entities[i];
-                               // rough checks
-                               if (r_shadow_cull.integer)
-                               {
-                                       if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
-                                               continue;
-                                       if (r_refdef.worldmodel != NULL && r_refdef.worldmodel->brush.BoxTouchingPVS != NULL && !r_refdef.worldmodel->brush.BoxTouchingPVS(r_refdef.worldmodel, clusterpvs, ent->mins, ent->maxs))
-                                               continue;
-                               }
-                               if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
-                                       continue;
-                               Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
-                               // light emitting entities should not cast their own shadow
-                               if (VectorLength2(relativelightorigin) < 0.1)
-                                       continue;
-                               ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist);
+                               // about the VectorDistance2 - light emitting entities should not cast their own shadow
+                               if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
+                                       shadowentities[numshadowentities++] = ent;
+                               if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
+                                       lightentities[numlightentities++] = ent;
                        }
                }
        }
 
-       if (!visiblevolumes)
+       // return if there's nothing at all to light
+       if (!numlightentities)
+               return;
+
+       R_Shadow_Stage_ActiveLight(rtlight);
+       c_rt_lights++;
+
+       usestencil = false;
+       if (numshadowentities && (!visible || r_shadow_visiblelighting.integer == 1) && gl_stencil && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
+       {
+               usestencil = true;
+               R_Shadow_Stage_StencilShadowVolumes();
+               for (i = 0;i < numshadowentities;i++)
+                       R_Shadow_DrawEntityShadow(shadowentities[i], rtlight, numsurfaces, surfacelist);
+       }
+
+       if (numlightentities && !visible)
        {
-               R_Shadow_Stage_Light(usestencil);
+               R_Shadow_Stage_Lighting(usestencil);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i], rtlight, lightcolor, numsurfaces, surfacelist);
+       }
 
-               ent = &cl_entities[0].render;
-               if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
-               {
-                       lightcolor2[0] = lightcolor[0] * ent->colormod[0] * ent->alpha;
-                       lightcolor2[1] = lightcolor[1] * ent->colormod[1] * ent->alpha;
-                       lightcolor2[2] = lightcolor[2] * ent->colormod[2] * ent->alpha;
-                       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
-                       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
-                       Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
-                       Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
-                       Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
-                       if (r_shadow_staticworldlights.integer && rtlight->compiled)
-                       {
-                               R_Mesh_Matrix(&ent->matrix);
-                               for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
-                                       R_Shadow_RenderLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, mesh->map_specular, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale);
-                       }
-                       else
-                               ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, numsurfaces, surfacelist);
-               }
-               if (r_drawentities.integer)
-               {
-                       for (i = 0;i < r_refdef.numentities;i++)
-                       {
-                               ent = r_refdef.entities[i];
-                               // can't draw transparent entity lighting here because
-                               // transparent meshes are deferred for later
-                               if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & (RENDER_LIGHT | RENDER_TRANSPARENT)) == RENDER_LIGHT)
-                               {
-                                       lightcolor2[0] = lightcolor[0] * ent->colormod[0] * ent->alpha;
-                                       lightcolor2[1] = lightcolor[1] * ent->colormod[1] * ent->alpha;
-                                       lightcolor2[2] = lightcolor[2] * ent->colormod[2] * ent->alpha;
-                                       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
-                                       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
-                                       Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
-                                       Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
-                                       Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
-                                       ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, ent->model->nummodelsurfaces, ent->model->surfacelist);
-                               }
-                       }
-               }
+       if (numshadowentities && visible && r_shadow_visiblevolumes.integer > 0 && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
+       {
+               R_Shadow_Stage_VisibleShadowVolumes();
+               for (i = 0;i < numshadowentities;i++)
+                       R_Shadow_DrawEntityShadow(shadowentities[i], rtlight, numsurfaces, surfacelist);
+       }
+
+       if (numlightentities && visible && r_shadow_visiblelighting.integer > 0)
+       {
+               R_Shadow_Stage_VisibleLighting(usestencil);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i], rtlight, lightcolor, numsurfaces, surfacelist);
        }
 }
 
-void R_ShadowVolumeLighting(int visiblevolumes)
+void R_ShadowVolumeLighting(qboolean visible)
 {
        int lnum, flag;
        dlight_t *light;
-       rmeshstate_t m;
 
        if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
                R_Shadow_EditLights_Reload_f();
 
-       if (visiblevolumes)
-       {
-               memset(&m, 0, sizeof(m));
-               R_Mesh_State(&m);
+       R_Shadow_Stage_Begin();
 
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               GL_DepthMask(false);
-               GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
-               qglDisable(GL_CULL_FACE);
-               GL_Color(0.0, 0.0125, 0.1, 1);
-       }
-       else
-               R_Shadow_Stage_Begin();
        flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
        if (r_shadow_debuglight.integer >= 0)
        {
                for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
                        if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
-                               R_DrawRTLight(&light->rtlight, visiblevolumes);
+                               R_DrawRTLight(&light->rtlight, visible);
        }
        else
                for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
                        if (light->flags & flag)
-                               R_DrawRTLight(&light->rtlight, visiblevolumes);
+                               R_DrawRTLight(&light->rtlight, visible);
        if (r_rtdlight)
                for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
-                       R_DrawRTLight(&light->rtlight, visiblevolumes);
+                       R_DrawRTLight(&light->rtlight, visible);
 
-       if (visiblevolumes)
-       {
-               qglEnable(GL_CULL_FACE);
-               GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
-       }
-       else
-               R_Shadow_Stage_End();
+       R_Shadow_Stage_End();
 }
 
 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
@@ -3002,10 +3071,12 @@ rtexture_t *R_Shadow_Cubemap(const char *basename)
                if (!strcasecmp(cubemaps[i].basename, basename))
                        return cubemaps[i].texture;
        if (i >= MAX_CUBEMAPS)
-               return NULL;
+               return r_texture_whitecube;
        numcubemaps++;
        strcpy(cubemaps[i].basename, basename);
        cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
+       if (!cubemaps[i].texture)
+               cubemaps[i].texture = r_texture_whitecube;
        return cubemaps[i].texture;
 }
 
@@ -3044,7 +3115,7 @@ void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, ve
        light->corona = corona;
        if (!cubemapname)
                cubemapname = "";
-       strlcpy(light->cubemapname, cubemapname, strlen(light->cubemapname));
+       strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
        light->coronasizescale = coronasizescale;
        light->ambientscale = ambientscale;
        light->diffusescale = diffusescale;
@@ -3111,7 +3182,7 @@ void R_Shadow_DrawLightSprites(void)
        for (i = 0;i < 5;i++)
        {
                lighttextures[i] = NULL;
-               if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
+               if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1), true)))
                        lighttextures[i] = pic->tex;
        }
 
@@ -3133,7 +3204,7 @@ void R_Shadow_SelectLightInView(void)
                if (rating >= 0.95)
                {
                        rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
-                       if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
+                       if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_vieworigin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
                        {
                                bestrating = rating;
                                best = light;
@@ -3155,7 +3226,7 @@ void R_Shadow_LoadWorldLights(void)
        }
        FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
        strlcat (name, ".rtlights", sizeof (name));
-       lightsstring = FS_LoadFile(name, tempmempool, false);
+       lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3221,8 +3292,6 @@ void R_Shadow_LoadWorldLights(void)
                                Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2] coronasizescale ambientscale diffusescale specularscale flags)\n", a, n + 1);
                                break;
                        }
-                       VectorScale(color, r_editlights_rtlightscolorscale.value, color);
-                       radius *= r_editlights_rtlightssizescale.value;
                        R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
                        if (*s == '\r')
                                s++;
@@ -3239,7 +3308,7 @@ void R_Shadow_LoadWorldLights(void)
 void R_Shadow_SaveWorldLights(void)
 {
        dlight_t *light;
-       int bufchars, bufmaxchars;
+       size_t bufchars, bufmaxchars;
        char *buf, *oldbuf;
        char name[MAX_QPATH];
        char line[1024];
@@ -3257,16 +3326,16 @@ void R_Shadow_SaveWorldLights(void)
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
                if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
                else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
                else
-                       sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style);
-               if (bufchars + (int) strlen(line) > bufmaxchars)
+                       sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style);
+               if (bufchars + strlen(line) > bufmaxchars)
                {
                        bufmaxchars = bufchars + strlen(line) + 2048;
                        oldbuf = buf;
-                       buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
+                       buf = Mem_Alloc(tempmempool, bufmaxchars);
                        if (oldbuf)
                        {
                                if (bufchars)
@@ -3281,7 +3350,7 @@ void R_Shadow_SaveWorldLights(void)
                }
        }
        if (bufchars)
-               FS_WriteFile(name, buf, bufchars);
+               FS_WriteFile(name, buf, (fs_offset_t)bufchars);
        if (buf)
                Mem_Free(buf);
 }
@@ -3298,7 +3367,7 @@ void R_Shadow_LoadLightsFile(void)
        }
        FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
        strlcat (name, ".lights", sizeof (name));
-       lightsstring = FS_LoadFile(name, tempmempool, false);
+       lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3354,7 +3423,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
        // try to load a .ent file first
        FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
        strlcat (key, ".ent", sizeof (key));
-       data = entfiledata = FS_LoadFile(key, tempmempool, true);
+       data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true);
        // and if that is not found, fall back to the bsp file entity string
        if (!data)
                data = r_refdef.worldmodel->brush.entities;
@@ -3461,7 +3530,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                        {
                                                originhack[0] = 0;
                                                originhack[1] = 0;
-                                               originhack[2] = 48;
+                                               originhack[2] = 0;
                                                overridecolor[0] = 1;
                                                overridecolor[1] = 0.5;
                                                overridecolor[2] = 0.1;
@@ -3470,7 +3539,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                        {
                                                originhack[0] = 0;
                                                originhack[1] = 0;
-                                               originhack[2] = 40;
+                                               originhack[2] = 0;
                                                overridecolor[0] = 1;
                                                overridecolor[1] = 0.5;
                                                overridecolor[2] = 0.1;
@@ -3479,7 +3548,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                        {
                                                originhack[0] = 0;
                                                originhack[1] = 0;
-                                               originhack[2] = 40;
+                                               originhack[2] = 0;
                                                overridecolor[0] = 1;
                                                overridecolor[1] = 0.5;
                                                overridecolor[2] = 0.1;
@@ -3488,7 +3557,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                        {
                                                originhack[0] = 0;
                                                originhack[1] = 0;
-                                               originhack[2] = 40;
+                                               originhack[2] = 0;
                                                overridecolor[0] = 1;
                                                overridecolor[1] = 0.5;
                                                overridecolor[2] = 0.1;
@@ -3497,6 +3566,12 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                        }
                        else if (!strcmp("style", key))
                                style = atoi(value);
+                       else if (!strcmp("skin", key))
+                               skin = (int)atof(value);
+                       else if (!strcmp("pflags", key))
+                               pflags = (int)atof(value);
+                       else if (!strcmp("effects", key))
+                               effects = (int)atof(value);
                        else if (r_refdef.worldmodel->type == mod_brushq3)
                        {
                                if (!strcmp("scale", key))
@@ -3504,12 +3579,6 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                                if (!strcmp("fade", key))
                                        fadescale = atof(value);
                        }
-                       else if (!strcmp("skin", key))
-                               skin = (int)atof(value);
-                       else if (!strcmp("pflags", key))
-                               pflags = (int)atof(value);
-                       else if (!strcmp("effects", key))
-                               effects = (int)atof(value);
                }
                if (!islight)
                        continue;
@@ -3558,19 +3627,24 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
 
 void R_Shadow_SetCursorLocationForView(void)
 {
-       vec_t dist, push, frac;
-       vec3_t dest, endpos, normal;
+       vec_t dist, push;
+       vec3_t dest, endpos;
+       trace_t trace;
        VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
-       frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
-       if (frac < 1)
+       trace = CL_TraceBox(r_vieworigin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
+       if (trace.fraction < 1)
        {
-               dist = frac * r_editlights_cursordistance.value;
+               dist = trace.fraction * r_editlights_cursordistance.value;
                push = r_editlights_cursorpushback.value;
                if (push > dist)
                        push = dist;
                push = -push;
-               VectorMA(endpos, push, r_viewforward, endpos);
-               VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
+               VectorMA(trace.endpos, push, r_viewforward, endpos);
+               VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
+       }
+       else
+       {
+               VectorClear( endpos );
        }
        r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
        r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
@@ -3813,6 +3887,36 @@ void R_Shadow_EditLights_Edit_f(void)
                }
                radius = atof(Cmd_Argv(2));
        }
+       else if (!strcmp(Cmd_Argv(1), "colorscale"))
+       {
+               if (Cmd_Argc() == 3)
+               {
+                       double scale = atof(Cmd_Argv(2));
+                       color[0] *= scale;
+                       color[1] *= scale;
+                       color[2] *= scale;
+               }
+               else
+               {
+                       if (Cmd_Argc() != 5)
+                       {
+                               Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
+                               return;
+                       }
+                       color[0] *= atof(Cmd_Argv(2));
+                       color[1] *= atof(Cmd_Argv(3));
+                       color[2] *= atof(Cmd_Argv(4));
+               }
+       }
+       else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
+       {
+               if (Cmd_Argc() != 3)
+               {
+                       Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
+                       return;
+               }
+               radius *= atof(Cmd_Argv(2));
+       }
        else if (!strcmp(Cmd_Argv(1), "style"))
        {
                if (Cmd_Argc() != 3)
@@ -4039,8 +4143,6 @@ void R_Shadow_EditLights_Help_f(void)
 "r_editlights_cursorpushoff : push cursor off surface this far\n"
 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
-"r_editlights_rtlightssizescale : imported rtlight size scaling\n"
-"r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
 "Commands:\n"
 "r_editlights_help : this help\n"
 "r_editlights_clear : remove all lights\n"
@@ -4067,6 +4169,10 @@ void R_Shadow_EditLights_Help_f(void)
 "anglesz z: set z component of light angles\n"
 "color r g b : set color of light (can be brighter than 1 1 1)\n"
 "radius radius : set radius (size) of light\n"
+"colorscale grey : multiply color of light (1 does nothing)\n"
+"colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
+"radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
+"sizescale scale : multiply radius (size) of light (1 does nothing)\n"
 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
 "cubemap basename : set filter cubemap of light (not yet supported)\n"
 "shadows 1/0 : turn on/off shadows\n"
@@ -4133,8 +4239,6 @@ void R_Shadow_EditLights_Init(void)
        Cvar_RegisterVariable(&r_editlights_cursorpushoff);
        Cvar_RegisterVariable(&r_editlights_cursorgrid);
        Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
-       Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
-       Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
        Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
        Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
        Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);