]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
optimizations to surface rendering setup
[xonotic/darkplaces.git] / r_shadow.c
index b8127bd009970e640c256879a289e970d2e17ecc..6383a67b7913993222b735486a03ecedeeb5e536 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,18 +142,29 @@ 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
-
-int r_shadowstage = SHADOWSTAGE_NONE;
+typedef enum r_shadow_rendermode_e
+{
+       R_SHADOW_RENDERMODE_NONE,
+       R_SHADOW_RENDERMODE_STENCIL,
+       R_SHADOW_RENDERMODE_STENCILTWOSIDE,
+       R_SHADOW_RENDERMODE_LIGHT_VERTEX,
+       R_SHADOW_RENDERMODE_LIGHT_DOT3,
+       R_SHADOW_RENDERMODE_LIGHT_GLSL,
+       R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
+       R_SHADOW_RENDERMODE_VISIBLELIGHTING,
+}
+r_shadow_rendermode_t;
 
-mempool_t *r_shadow_mempool;
+r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
+r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
+r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
 
-int maxshadowelements;
+int maxshadowtriangles;
 int *shadowelements;
 
+int maxshadowvertices;
+float *shadowvertex3f;
+
 int maxshadowmark;
 int numshadowmark;
 int *shadowmark;
@@ -143,18 +177,16 @@ int *vertexremap;
 int vertexupdatenum;
 
 int r_shadow_buffer_numleafpvsbytes;
-qbyte *r_shadow_buffer_leafpvs;
+unsigned char *r_shadow_buffer_leafpvs;
 int *r_shadow_buffer_leaflist;
 
 int r_shadow_buffer_numsurfacepvsbytes;
-qbyte *r_shadow_buffer_surfacepvs;
+unsigned char *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_blankwhitecubetexture;
 
 // lights are reloaded when this changes
 char r_shadow_mapname[MAX_QPATH];
@@ -162,48 +194,37 @@ char r_shadow_mapname[MAX_QPATH];
 // used only for light filters (cubemaps)
 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"};
-cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
-cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
-cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
-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", "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_compilelight = {0, "r_shadow_realtime_world_compilelight", "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_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 gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
-cvar_t r_editlights = {0, "r_editlights", "0"};
-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"};
+cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
+cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
+cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
+cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
+cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
+cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
+cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
+cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
+cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
+cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
+cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
+cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
+cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
+cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal culling optimizations on dynamic lights (slow!  you probably don't want this!)"};
+cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"};
+cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1", "enables shadows from dynamic lights when using full world lighting"};
+cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"};
+cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
+cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
+cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1", "enables compilation of shadows from world lights for higher performance rendering"};
+cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
+cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
+cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
+cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
+cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
+cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
+cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
+cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
+cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
+cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
+cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
 
 float r_shadow_attenpower, r_shadow_attenscale;
 
@@ -213,8 +234,6 @@ dlight_t *r_shadow_selectedlight;
 dlight_t r_shadow_bufferlight;
 vec3_t r_editlights_cursorlocation;
 
-rtexture_t *lighttextures[5];
-
 extern int con_vislines;
 
 typedef struct cubemapinfo_s
@@ -228,14 +247,6 @@ cubemapinfo_t;
 static int numcubemaps;
 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
 
-#define SHADERPERMUTATION_SPECULAR (1<<0)
-#define SHADERPERMUTATION_FOG (1<<1)
-#define SHADERPERMUTATION_CUBEFILTER (1<<2)
-#define SHADERPERMUTATION_OFFSETMAPPING (1<<3)
-#define SHADERPERMUTATION_COUNT (1<<4)
-
-GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
-
 void R_Shadow_UncompileWorldLights(void);
 void R_Shadow_ClearWorldLights(void);
 void R_Shadow_SaveWorldLights(void);
@@ -247,160 +258,20 @@ void R_Shadow_ValidateCvars(void);
 static void R_Shadow_MakeTextures(void);
 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
 
-const char *builtinshader_light_vert =
-"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
-"// written by Forest 'LordHavoc' Hale\n"
-"\n"
-"uniform vec3 LightPosition;\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"varying vec3 CubeVector;\n"
-"varying vec3 LightVector;\n"
-"\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
-"uniform vec3 EyePosition;\n"
-"varying vec3 EyeVector;\n"
-"#endif\n"
-"\n"
-"// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      // copy the surface texcoord\n"
-"      TexCoord = gl_MultiTexCoord0.st;\n"
-"\n"
-"      // transform vertex position into light attenuation/cubemap space\n"
-"      // (-1 to +1 across the light box)\n"
-"      CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
-"\n"
-"      // transform unnormalized light direction into tangent space\n"
-"      // (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.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.z = -dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      // transform vertex to camera space, using ftransform to match non-VS\n"
-"      // rendering\n"
-"      gl_Position = ftransform();\n"
-"}\n"
-;
-
-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"
-"\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale;\n"
-"uniform float OffsetMapping_Bias;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"uniform float SpecularPower;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform float FogRangeRecip;\n"
-"#endif\n"
-"uniform float AmbientScale;\n"
-"uniform float DiffuseScale;\n"
-"#ifdef USESPECULAR\n"
-"uniform float SpecularScale;\n"
-"#endif\n"
-"\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Color;\n"
-"#ifdef USESPECULAR\n"
-"uniform sampler2D Texture_Gloss;\n"
-"#endif\n"
-"#ifdef USECUBEFILTER\n"
-"uniform samplerCube Texture_Cube;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform sampler2D Texture_FogMask;\n"
-"#endif\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"varying vec3 CubeVector;\n"
-"varying vec3 LightVector;\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
-"varying vec3 EyeVector;\n"
-"#endif\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      // attenuation\n"
-"      //\n"
-"      // the attenuation is (1-(x*x+y*y+z*z)) which gives a large bright\n"
-"      // center and sharp falloff at the edge, this is about the most efficient\n"
-"      // we can get away with as far as providing illumination.\n"
-"      //\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"
-"\n"
-"#ifdef USEFOG\n"
-"      // apply fog\n"
-"      colorscale *= texture2D(Texture_FogMask, vec2(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"
-"      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"
-"#endif\n"
-"\n"
-"      // calculate shading\n"
-"      vec3 diffusenormal = normalize(LightVector);\n"
-"      vec3 color = colortexel * (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"
-"#endif\n"
-"\n"
-"#ifdef USECUBEFILTER\n"
-"      // apply light cubemap filter\n"
-"      color *= vec3(textureCube(Texture_Cube, CubeVector));\n"
-"#endif\n"
-"\n"
-"      // calculate fragment color\n"
-"      gl_FragColor = vec4(LightColor * color * colorscale, 1);\n"
-"}\n"
-;
-
 void r_shadow_start(void)
 {
-       int i;
        // allocate vertex processing arrays
        numcubemaps = 0;
-       r_shadow_normalcubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
-       r_shadow_blankwhitecubetexture = NULL;
        r_shadow_texturepool = NULL;
        r_shadow_filters_texturepool = NULL;
        R_Shadow_ValidateCvars();
        R_Shadow_MakeTextures();
-       maxshadowelements = 0;
+       maxshadowtriangles = 0;
        shadowelements = NULL;
+       maxshadowvertices = 0;
+       shadowvertex3f = NULL;
        maxvertexupdate = 0;
        vertexupdate = NULL;
        vertexremap = NULL;
@@ -416,96 +287,23 @@ void r_shadow_start(void)
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               r_shadow_program_light[i] = 0;
-       if (gl_support_fragment_shader)
-       {
-               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);
-               for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               {
-                       vertstrings_count = 0;
-                       fragstrings_count = 0;
-                       if (i & SHADERPERMUTATION_SPECULAR)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
-                               fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
-                       }
-                       if (i & SHADERPERMUTATION_FOG)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
-                               fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
-                       }
-                       if (i & SHADERPERMUTATION_CUBEFILTER)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
-                               fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
-                       }
-                       if (i & SHADERPERMUTATION_OFFSETMAPPING)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
-                               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\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");
-                               continue;
-                       }
-                       qglUseProgramObjectARB(r_shadow_program_light[i]);
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Normal"), 0);CHECKGLERROR
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Color"), 1);CHECKGLERROR
-                       if (i & SHADERPERMUTATION_SPECULAR)
-                       {
-                               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Gloss"), 2);CHECKGLERROR
-                       }
-                       if (i & SHADERPERMUTATION_CUBEFILTER)
-                       {
-                               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Cube"), 3);CHECKGLERROR
-                       }
-                       if (i & SHADERPERMUTATION_FOG)
-                       {
-                               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_FogMask"), 4);CHECKGLERROR
-                       }
-               }
-               qglUseProgramObjectARB(0);
-               if (fragstring)
-                       Mem_Free(fragstring);
-               if (vertstring)
-                       Mem_Free(vertstring);
-       }
 }
 
 void r_shadow_shutdown(void)
 {
-       int i;
        R_Shadow_UncompileWorldLights();
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-       {
-               if (r_shadow_program_light[i])
-               {
-                       GL_Backend_FreeProgram(r_shadow_program_light[i]);
-                       r_shadow_program_light[i] = 0;
-               }
-       }
        numcubemaps = 0;
-       r_shadow_normalcubetexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
-       r_shadow_blankwhitecubetexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
        R_FreeTexturePool(&r_shadow_filters_texturepool);
-       maxshadowelements = 0;
+       maxshadowtriangles = 0;
        if (shadowelements)
                Mem_Free(shadowelements);
        shadowelements = NULL;
+       if (shadowvertex3f)
+               Mem_Free(shadowvertex3f);
+       shadowvertex3f = NULL;
        maxvertexupdate = 0;
        if (vertexupdate)
                Mem_Free(vertexupdate);
@@ -567,19 +365,13 @@ void R_Shadow_Help_f(void)
 "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_compilelight : compile lighting geometry\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_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"
+"r_showlighting : useful for performance testing; bright = slow!\n"
+"r_showshadowvolumes : useful for performance testing; bright = slow!\n"
 "Commands:\n"
 "r_shadow_help : this help\n"
        );
@@ -589,7 +381,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);
@@ -607,31 +398,24 @@ void R_Shadow_Init(void)
        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_compilelight);
        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_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(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
        {
                Cvar_SetValue("r_shadow_gloss", 2);
                Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
        }
-       Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
+       Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
        R_Shadow_EditLights_Init();
-       r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
        r_shadow_worldlightchain = NULL;
-       maxshadowelements = 0;
+       maxshadowtriangles = 0;
        shadowelements = NULL;
+       maxshadowvertices = 0;
+       shadowvertex3f = NULL;
        maxvertexupdate = 0;
        vertexupdate = NULL;
        vertexremap = NULL;
@@ -650,7 +434,7 @@ void R_Shadow_Init(void)
        R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
 }
 
-static matrix4x4_t matrix_attenuationxyz =
+matrix4x4_t matrix_attenuationxyz =
 {
        {
                {0.5, 0.0, 0.0, 0.5},
@@ -660,7 +444,7 @@ static matrix4x4_t matrix_attenuationxyz =
        }
 };
 
-static matrix4x4_t matrix_attenuationz =
+matrix4x4_t matrix_attenuationz =
 {
        {
                {0.0, 0.0, 0.5, 0.5},
@@ -670,17 +454,24 @@ static matrix4x4_t matrix_attenuationz =
        }
 };
 
-int *R_Shadow_ResizeShadowElements(int numtris)
+void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
 {
        // make sure shadowelements is big enough for this volume
-       if (maxshadowelements < numtris * 24)
+       if (maxshadowtriangles < numtriangles)
        {
-               maxshadowelements = numtris * 24;
+               maxshadowtriangles = numtriangles;
                if (shadowelements)
                        Mem_Free(shadowelements);
-               shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
+               shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
+       }
+       // make sure shadowvertex3f is big enough for this volume
+       if (maxshadowvertices < numvertices)
+       {
+               maxshadowvertices = numvertices;
+               if (shadowvertex3f)
+                       Mem_Free(shadowvertex3f);
+               shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
        }
-       return shadowelements;
 }
 
 static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
@@ -694,8 +485,8 @@ static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
                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));
+               r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
+               r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
        }
        if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
        {
@@ -704,8 +495,8 @@ static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
                if (r_shadow_buffer_surfacelist)
                        Mem_Free(r_shadow_buffer_surfacelist);
                r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
-               r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
-               r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
+               r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
+               r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
        }
 }
 
@@ -719,8 +510,8 @@ void R_Shadow_PrepareShadowMark(int numtris)
                        Mem_Free(shadowmark);
                if (shadowmarklist)
                        Mem_Free(shadowmarklist);
-               shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
-               shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
+               shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
+               shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
                shadowmarkcount = 0;
        }
        shadowmarkcount++;
@@ -747,8 +538,8 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
                        Mem_Free(vertexupdate);
                if (vertexremap)
                        Mem_Free(vertexremap);
-               vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
-               vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
+               vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
+               vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
                vertexupdatenum = 0;
        }
        vertexupdatenum++;
@@ -865,10 +656,11 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        if (!numverts || !nummarktris)
                return;
        // make sure shadowelements is big enough for this volume
-       if (maxshadowelements < nummarktris * 24)
-               R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
-       tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
-       R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
+       if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
+               R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
+       tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdistance, nummarktris, marktris);
+       renderstats.lights_dynamicshadowtriangles += tris;
+       R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
 }
 
 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)
@@ -914,106 +706,40 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        if (r_shadow_compilingrtlight)
        {
                // if we're compiling an rtlight, capture the mesh
-               Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
+               Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
                return;
        }
+       renderstats.lights_shadowtriangles += numtriangles;
        memset(&m, 0, sizeof(m));
        m.pointer_vertex = vertex3f;
        R_Mesh_State(&m);
        GL_LockArrays(0, numvertices);
-       if (r_shadowstage == SHADOWSTAGE_STENCIL)
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_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);
+               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(0, numvertices, numtriangles, element3i);
-       c_rt_shadowmeshes++;
-       c_rt_shadowtris += numtriangles;
        GL_LockArrays(0, 0);
 }
 
 static void R_Shadow_MakeTextures(void)
 {
-       int x, y, z, d, side;
-       float v[3], s, t, intensity;
-       qbyte *data;
+       int x, y, z, d;
+       float v[3], intensity;
+       unsigned char *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)));
-       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 = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
        for (y = 0;y < ATTEN2DSIZE;y++)
        {
                for (x = 0;x < ATTEN2DSIZE;x++)
@@ -1067,7 +793,23 @@ void R_Shadow_ValidateCvars(void)
                Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
 }
 
-void R_Shadow_Stage_Begin(void)
+// light currently being rendered
+rtlight_t *r_shadow_rtlight;
+
+// this is the location of the eye in entity space
+vec3_t r_shadow_entityeyeorigin;
+// this is the location of the light in entity space
+vec3_t r_shadow_entitylightorigin;
+// this transforms entity coordinates to light filter cubemap coordinates
+// (also often used for other purposes)
+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)
+matrix4x4_t r_shadow_entitytoattenuationxyz;
+// this transforms only the Z to S, and T is always 0.5
+matrix4x4_t r_shadow_entitytoattenuationz;
+
+void R_Shadow_RenderMode_Begin(void)
 {
        rmeshstate_t m;
 
@@ -1080,454 +822,383 @@ void R_Shadow_Stage_Begin(void)
                R_Shadow_MakeTextures();
 
        memset(&m, 0, sizeof(m));
+       R_Mesh_State(&m);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(false);
        GL_DepthTest(true);
-       R_Mesh_State(&m);
        GL_Color(0, 0, 0, 1);
        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_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
+
+       if (gl_ext_stenciltwoside.integer)
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
+       else
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
+
+       if (r_glsl.integer && gl_support_fragment_shader)
+               r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
+       else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
+               r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
+       else
+               r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
+}
+
+void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
+{
+       r_shadow_rtlight = rtlight;
 }
 
-void R_Shadow_Stage_ShadowVolumes(void)
+void R_Shadow_RenderMode_Reset(void)
 {
        rmeshstate_t m;
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
+       {
+               qglUseProgramObjectARB(0);
+               // HACK HACK HACK: work around for bug in NVIDIAI 6xxx drivers that causes GL_OUT_OF_MEMORY and/or software rendering
+               qglBegin(GL_TRIANGLES);
+               qglEnd();
+               CHECKGLERROR
+       }
+       else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
+               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        memset(&m, 0, sizeof(m));
        R_Mesh_State(&m);
+}
+
+void R_Shadow_RenderMode_StencilShadowVolumes(void)
+{
+       R_Shadow_RenderMode_Reset();
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(0, 0, 0, 0);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(false);
        GL_DepthTest(true);
-       qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
-       //if (r_shadow_shadow_polygonoffset.value != 0)
-       //{
-       //      qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
-       //      qglEnable(GL_POLYGON_OFFSET_FILL);
-       //}
-       //else
-       //      qglDisable(GL_POLYGON_OFFSET_FILL);
+       qglPolygonOffset(r_shadowpolygonfactor, r_shadowpolygonoffset);
        qglDepthFunc(GL_LESS);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_STENCIL_TEST);
        qglStencilFunc(GL_ALWAYS, 128, ~0);
-       if (gl_ext_stenciltwoside.integer)
+       r_shadow_rendermode = r_shadow_shadowingrendermode;
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
        {
-               r_shadowstage = 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;
                qglEnable(GL_CULL_FACE);
                qglStencilMask(~0);
                // this is changed by every shadow render so its value here is unimportant
                qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        }
        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
+       renderstats.lights_clears++;
 }
 
-void R_Shadow_Stage_Light(int shadowtest)
+void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
 {
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
-       GL_BlendFunc(GL_ONE, GL_ONE);
+       R_Shadow_RenderMode_Reset();
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        GL_DepthMask(false);
        GL_DepthTest(true);
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        //qglDisable(GL_POLYGON_OFFSET_FILL);
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
-       qglDepthFunc(GL_EQUAL);
+       if (transparent)
+               qglDepthFunc(GL_LEQUAL);
+       else
+               qglDepthFunc(GL_EQUAL);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
        qglEnable(GL_CULL_FACE);
-       if (shadowtest)
+       if (stenciltest)
                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++;
+       r_shadow_rendermode = r_shadow_lightingrendermode;
+       // do global setup needed for the chosen lighting mode
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
+       {
+               R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
+               R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
+               R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
+               R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap)); // light filter
+               R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
+               R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
+               R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
+               //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
+               CHECKGLERROR
+       }
 }
 
-void R_Shadow_Stage_End(void)
+void R_Shadow_RenderMode_VisibleShadowVolumes(void)
 {
-       rmeshstate_t m;
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
+       R_Shadow_RenderMode_Reset();
+       GL_BlendFunc(GL_ONE, GL_ONE);
+       GL_DepthMask(false);
+       GL_DepthTest(!r_showdisabledepthtest.integer);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
+       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_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
+}
+
+void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
+{
+       R_Shadow_RenderMode_Reset();
+       GL_BlendFunc(GL_ONE, GL_ONE);
+       GL_DepthMask(false);
+       GL_DepthTest(!r_showdisabledepthtest.integer);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
+       GL_Color(0.1, 0.0125, 0, 1);
+       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
+       if (transparent)
+               qglDepthFunc(GL_LEQUAL);
+       else
+               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_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
+}
+
+void R_Shadow_RenderMode_End(void)
+{
+       R_Shadow_RenderMode_Reset();
+       R_Shadow_RenderMode_ActiveLight(NULL);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(true);
        GL_DepthTest(true);
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        //qglDisable(GL_POLYGON_OFFSET_FILL);
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
        GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
        qglDepthFunc(GL_LEQUAL);
        qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);
        qglDisable(GL_STENCIL_TEST);
        qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        if (gl_support_stenciltwoside)
                qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
        qglStencilMask(~0);
        qglStencilFunc(GL_ALWAYS, 128, ~0);
-       r_shadowstage = SHADOWSTAGE_NONE;
+       r_shadow_rendermode = R_SHADOW_RENDERMODE_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++;
+       renderstats.lights_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_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_t *surface, const float *diffusecolor, const float *ambientcolor)
 {
-       float *color4f = varray_color4f;
-       float dist, dot, intensity, v[3], n[3];
-       for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
+       int numverts = surface->num_vertices;
+       float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
+       float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
+       float *color4f = rsurface_array_color4f + 4 * surface->num_firstvertex;
+       float dist, dot, distintensity, shadeintensity, v[3], n[3];
+       if (r_textureunits.integer >= 3)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
-               if ((dist = DotProduct(v, v)) < 1)
+               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                {
-                       Matrix4x4_Transform3x3(m, normal3f, n);
-                       if ((dot = DotProduct(n, v)) > 0)
+                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                       if ((dot = DotProduct(n, v)) < 0)
                        {
-                               dist = sqrt(dist);
-                               intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                               intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                               VectorScale(lightcolor, intensity, color4f);
-                               color4f[3] = 1;
+                               shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                               color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
+                               color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
+                               color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
+                               if (fogenabled)
+                               {
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       VectorScale(color4f, f, color4f);
+                               }
                        }
                        else
-                       {
                                VectorClear(color4f);
-                               color4f[3] = 1;
-                       }
-               }
-               else
-               {
-                       VectorClear(color4f);
                        color4f[3] = 1;
                }
        }
-}
-
-static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
-{
-       float *color4f = varray_color4f;
-       float dist, dot, intensity, v[3], n[3];
-       for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
+       else if (r_textureunits.integer >= 2)
        {
-               Matrix4x4_Transform(m, vertex3f, v);
-               if ((dist = fabs(v[2])) < 1)
+               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                {
-                       Matrix4x4_Transform3x3(m, normal3f, n);
-                       if ((dot = DotProduct(n, v)) > 0)
+                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                       if ((dist = fabs(v[2])) < 1)
                        {
-                               intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                               intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                               VectorScale(lightcolor, intensity, color4f);
-                               color4f[3] = 1;
+                               distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
+                               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                               if ((dot = DotProduct(n, v)) < 0)
+                               {
+                                       shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                                       color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
+                                       color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
+                                       color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                               }
+                               else
+                               {
+                                       color4f[0] = ambientcolor[0] * distintensity;
+                                       color4f[1] = ambientcolor[1] * distintensity;
+                                       color4f[2] = ambientcolor[2] * distintensity;
+                               }
+                               if (fogenabled)
+                               {
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       VectorScale(color4f, f, color4f);
+                               }
                        }
                        else
-                       {
                                VectorClear(color4f);
-                               color4f[3] = 1;
-                       }
-               }
-               else
-               {
-                       VectorClear(color4f);
-                       color4f[3] = 1;
-               }
-       }
-}
-
-static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
-{
-       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);
-               if ((dot = DotProduct(n, v)) > 0)
-               {
-                       intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                       VectorScale(lightcolor, intensity, color4f);
-                       color4f[3] = 1;
-               }
-               else
-               {
-                       VectorClear(color4f);
-                       color4f[3] = 1;
-               }
-       }
-}
-
-static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
-{
-       float *color4f = varray_color4f;
-       float dist, intensity, v[3];
-       for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
-       {
-               Matrix4x4_Transform(m, vertex3f, v);
-               if ((dist = DotProduct(v, v)) < 1)
-               {
-                       dist = sqrt(dist);
-                       intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                       VectorScale(lightcolor, intensity, color4f);
-                       color4f[3] = 1;
-               }
-               else
-               {
-                       VectorClear(color4f);
                        color4f[3] = 1;
                }
        }
-}
-
-static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
-{
-       float *color4f = varray_color4f;
-       float dist, intensity, v[3];
-       for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
+       else
        {
-               Matrix4x4_Transform(m, vertex3f, v);
-               if ((dist = fabs(v[2])) < 1)
-               {
-                       intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                       VectorScale(lightcolor, intensity, color4f);
-                       color4f[3] = 1;
-               }
-               else
+               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                {
-                       VectorClear(color4f);
+                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                       if ((dist = DotProduct(v, v)) < 1)
+                       {
+                               dist = sqrt(dist);
+                               distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
+                               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                               if ((dot = DotProduct(n, v)) < 0)
+                               {
+                                       shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                                       color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
+                                       color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
+                                       color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                               }
+                               else
+                               {
+                                       color4f[0] = ambientcolor[0] * distintensity;
+                                       color4f[1] = ambientcolor[1] * distintensity;
+                                       color4f[2] = ambientcolor[2] * distintensity;
+                               }
+                               if (fogenabled)
+                               {
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       VectorScale(color4f, f, color4f);
+                               }
+                       }
+                       else
+                               VectorClear(color4f);
                        color4f[3] = 1;
                }
        }
 }
 
 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
-#define USETEXMATRIX
-
-#ifndef USETEXMATRIX
-// this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
-// if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
-static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
-{
-       do
-       {
-               tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
-               tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
-               tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
-               vertex3f += 3;
-               tc3f += 3;
-       }
-       while (--numverts);
-}
-
-static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
-{
-       do
-       {
-               tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
-               tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
-               vertex3f += 3;
-               tc2f += 2;
-       }
-       while (--numverts);
-}
-#endif
 
 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
 {
@@ -1535,7 +1206,7 @@ static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numver
        float lightdir[3];
        for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
-               VectorSubtract(vertex3f, relativelightorigin, lightdir);
+               VectorSubtract(relativelightorigin, vertex3f, lightdir);
                // the cubemap normalizes this for us
                out3f[0] = DotProduct(svector3f, lightdir);
                out3f[1] = DotProduct(tvector3f, lightdir);
@@ -1549,10 +1220,10 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numve
        float lightdir[3], eyedir[3], halfdir[3];
        for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
        {
-               VectorSubtract(vertex3f, relativelightorigin, lightdir);
-               VectorNormalizeFast(lightdir);
-               VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
-               VectorNormalizeFast(eyedir);
+               VectorSubtract(relativelightorigin, vertex3f, lightdir);
+               VectorNormalize(lightdir);
+               VectorSubtract(relativeeyeorigin, vertex3f, eyedir);
+               VectorNormalize(eyedir);
                VectorAdd(lightdir, eyedir, halfdir);
                // the cubemap normalizes this for us
                out3f[0] = DotProduct(svector3f, halfdir);
@@ -1561,1067 +1232,841 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numve
        }
 }
 
-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 *relativelightorigin, const float *relativeeyeorigin, const float *lightcolorbase, const float *lightcolorpants, const float *lightcolorshirt, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int visiblelighting)
+static void R_Shadow_RenderSurfacesLighting_VisibleLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
 {
-       int renders;
-       float color[3], color2[3], colorscale;
+       // used to display how many times a surface is lit for level design purposes
+       int surfacelistindex;
+       model_t *model = ent->model;
        rmeshstate_t m;
-       if (!basetexture)
-               basetexture = r_texture_white;
-       if (!bumptexture)
-               bumptexture = r_texture_blanknormalmap;
-       if (!lightcolorbase)
-               lightcolorbase = vec3_origin;
-       if (!lightcolorpants)
-               lightcolorpants = vec3_origin;
-       if (!lightcolorshirt)
-               lightcolorshirt = vec3_origin;
-       specularscale *= r_shadow_glossintensity.value;
-       if (!glosstexture)
-       {
-               if (r_shadow_gloss.integer >= 2)
-               {
-                       glosstexture = r_texture_white;
-                       specularscale *= r_shadow_gloss2intensity.value;
-               }
-               else
-               {
-                       glosstexture = r_texture_black;
-                       specularscale = 0;
-               }
+       GL_Color(0.1, 0.025, 0, 1);
+       memset(&m, 0, sizeof(m));
+       R_Mesh_State(&m);
+       RSurf_SetPointersForPass(false, false);
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       {
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               if (rsurface_dynamicvertex)
+                       RSurf_PrepareDynamicSurfaceVertices(surface);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
+               GL_LockArrays(0, 0);
        }
-       if (r_shadow_gloss.integer < 1)
-               specularscale = 0;
-       if (!lightcubemap)
-               lightcubemap = r_shadow_blankwhitecubetexture;
-       if ((ambientscale + diffusescale) * (VectorLength2(lightcolorbase) + VectorLength2(lightcolorpants) + VectorLength2(lightcolorshirt)) + specularscale * VectorLength2(lightcolorbase) <= 0.001)
-               return;
-       if (visiblelighting)
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_GLSL(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+{
+       // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
+       int surfacelistindex;
+       model_t *model = ent->model;
+       R_SetupSurfaceShader(ent, texture, r_shadow_entityeyeorigin, lightcolorbase, false);
+       RSurf_SetPointersForPass(false, true);
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
-               int passes = 0;
-               if (r_shadow_glsl.integer && r_shadow_program_light[0])
-                       passes++; // GLSL shader path (GFFX5200, Radeon 9500)
-               else if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
-               {
-                       // TODO: add direct pants/shirt rendering
-                       if (pantstexture && (ambientscale + diffusescale) * VectorLength2(lightcolorpants) > 0.001)
-                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorpants, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, pantstexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-                       if (shirttexture && (ambientscale + diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
-                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorshirt, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, shirttexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-                       if (!bumptexture)
-                               bumptexture = r_texture_blanknormalmap;
-                       if (!glosstexture)
-                               glosstexture = r_texture_white;
-                       if (ambientscale)
-                       {
-                               colorscale = ambientscale;
-                               if (r_shadow_texture3d.integer && lightcubemap && r_textureunits.integer >= 4)
-                               {
-                               }
-                               else if (r_shadow_texture3d.integer && !lightcubemap && r_textureunits.integer >= 2)
-                               {
-                               }
-                               else if (r_textureunits.integer >= 4 && lightcubemap)
-                               {
-                               }
-                               else if (r_textureunits.integer >= 3 && !lightcubemap)
-                               {
-                               }
-                               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 (diffusescale)
-                       {
-                               colorscale = 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 && lightcubemap)
-                               {
-                                       // 1/2/2 3D combine path (original Radeon)
-                                       passes += 2;
-                               }
-                               else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
-                               {
-                                       // 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 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
-                                               passes += 4;
-                                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& 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++;
-                               }
-                       }
-               }
-               else
-               {
-                       // TODO: add direct pants/shirt rendering
-                       if (pantstexture && (ambientscale + diffusescale) * VectorLength2(lightcolorpants) > 0.001)
-                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorpants, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, pantstexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-                       if (shirttexture && (ambientscale + diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
-                               R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorshirt, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, shirttexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-                       if (ambientscale)
-                       {
-                               VectorScale(lightcolorbase, 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 (diffusescale)
-                       {
-                               VectorScale(lightcolorbase, diffusescale, color2);
-                               for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
-                                       passes++;
-                       }
-               }
-               if (passes)
-               {
-                       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);
-               }
-               return;
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               const int *elements = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+               if (rsurface_dynamicvertex)
+                       RSurf_PrepareDynamicSurfaceVertices(surface);
+               R_Mesh_TexCoordPointer(0, 2, model->surfmesh.data_texcoordtexture2f);
+               R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
+               R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
+               R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
        }
-       // FIXME: support EF_NODEPTHTEST
-       GL_DepthMask(false);
-       GL_DepthTest(true);
-       if (r_shadow_glsl.integer && r_shadow_program_light[0])
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
+{
+       int renders;
+       model_t *model = ent->model;
+       float color2[3];
+       rmeshstate_t m;
+       const int *elements = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+       GL_Color(1,1,1,1);
+       // colorscale accounts for how much we multiply the brightness
+       // during combine.
+       //
+       // mult is how many times the final pass of the lighting will be
+       // performed to get more brightness than otherwise possible.
+       //
+       // Limit mult to 64 for sanity sake.
+       if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
+       {
+               // 3 3D combine path (Geforce3, Radeon 8500)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(basetexture);
+               m.pointer_texcoord[1] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[1] = texture->currenttexmatrix;
+               m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+               m.pointer_texcoord3f[2] = rsurface_vertex3f;
+               m.texmatrix[2] = r_shadow_entitytolight;
+               GL_BlendFunc(GL_ONE, GL_ONE);
+       }
+       else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
        {
-               unsigned int perm, prog;
-               // GLSL shader path (GFFX5200, Radeon 9500)
+               // 2 3D combine path (Geforce3, original Radeon)
                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.pointer_vertex = rsurface_vertex3f;
+               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
                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_texture_white);
-               m.texmatrix[3] = *matrix_modeltolight;
-               R_Mesh_State(&m);
+               m.pointer_texcoord[1] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[1] = texture->currenttexmatrix;
                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)
-               {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "FogRangeRecip"), 0);CHECKGLERROR
-               }
-               qglUniform1fARB(qglGetUniformLocationARB(prog, "AmbientScale"), ambientscale);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(prog, "DiffuseScale"), diffusescale);CHECKGLERROR
-               if (perm & SHADERPERMUTATION_SPECULAR)
-               {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "SpecularPower"), 8);CHECKGLERROR
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "SpecularScale"), specularscale);CHECKGLERROR
+       }
+       else if (r_textureunits.integer >= 4 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+       {
+               // 4 2D combine path (Geforce3, Radeon 8500)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationz;
+               m.tex[2] = R_GetTexture(basetexture);
+               m.pointer_texcoord[2] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[2] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+               {
+                       m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[3] = rsurface_vertex3f;
+                       m.texmatrix[3] = r_shadow_entitytolight;
                }
-               qglUniform3fARB(qglGetUniformLocationARB(prog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
-               qglUniform3fARB(qglGetUniformLocationARB(prog, "LightPosition"), relativelightorigin[0], relativelightorigin[1], relativelightorigin[2]);CHECKGLERROR
-               if (perm & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
+               GL_BlendFunc(GL_ONE, GL_ONE);
+       }
+       else if (r_textureunits.integer >= 3 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
+       {
+               // 3 2D combine path (Geforce3, original Radeon)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationz;
+               m.tex[2] = R_GetTexture(basetexture);
+               m.pointer_texcoord[2] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[2] = texture->currenttexmatrix;
+               GL_BlendFunc(GL_ONE, GL_ONE);
+       }
+       else
+       {
+               // 2/2/2 2D combine path (any dot3 card)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationz;
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       qglUniform3fARB(qglGetUniformLocationARB(prog, "EyePosition"), relativeeyeorigin[0], relativeeyeorigin[1], relativeeyeorigin[2]);CHECKGLERROR
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               if (perm & SHADERPERMUTATION_OFFSETMAPPING)
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       // 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(lightcolorbase, colorscale, color2);
+       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+       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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+       }
+       GL_LockArrays(0, 0);
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
+{
+       int renders;
+       model_t *model = ent->model;
+       float color2[3];
+       rmeshstate_t m;
+       const int *elements = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+       GL_Color(1,1,1,1);
+       // colorscale accounts for how much we multiply the brightness
+       // during combine.
+       //
+       // mult is how many times the final pass of the lighting will be
+       // performed to get more brightness than otherwise possible.
+       //
+       // Limit mult to 64 for sanity sake.
+       if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
+       {
+               // 3/2 3D combine path (Geforce3, Radeon 8500)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
+               m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[2] = rsurface_vertex3f;
+               m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
-                       qglUniform1fARB(qglGetUniformLocationARB(prog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               CHECKGLERROR
-               GL_LockArrays(firstvertex, numvertices);
-               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-               c_rt_lightmeshes++;
-               c_rt_lighttris += numtriangles;
-               // TODO: add direct pants/shirt rendering
-               if (pantstexture && (ambientscale + diffusescale) * VectorLength2(lightcolorpants) > 0.001)
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+       {
+               // 1/2/2 3D combine path (original Radeon)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
+               R_Mesh_State(&m);
+               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       R_Mesh_TexBind(1, R_GetTexture(pantstexture));
-                       qglUniform3fARB(qglGetUniformLocationARB(prog, "LightColor"), lightcolorpants[0], lightcolorpants[1], lightcolorpants[2]);CHECKGLERROR
-                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               if (shirttexture && (ambientscale + diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
+       {
+               // 2/2 3D combine path (original Radeon)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       else if (r_textureunits.integer >= 4)
+       {
+               // 4/2 2D combine path (Geforce3, Radeon 8500)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
+               m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[2] = rsurface_vertex3f;
+               m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
+               m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[3] = rsurface_vertex3f;
+               m.texmatrix[3] = r_shadow_entitytoattenuationz;
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       R_Mesh_TexBind(1, R_GetTexture(shirttexture));
-                       qglUniform3fARB(qglGetUniformLocationARB(prog, "LightColor"), lightcolorshirt[0], lightcolorshirt[1], lightcolorshirt[2]);CHECKGLERROR
-                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                       c_rt_lightmeshes++;
-                       c_rt_lighttris += numtriangles;
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               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
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
-       else if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
-       {
-               // TODO: add direct pants/shirt rendering
-               if (pantstexture && (ambientscale + diffusescale) * VectorLength2(lightcolorpants) > 0.001)
-                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorpants, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, pantstexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-               if (shirttexture && (ambientscale + diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
-                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorshirt, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, shirttexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-               if (!bumptexture)
-                       bumptexture = r_texture_blanknormalmap;
-               if (!glosstexture)
-                       glosstexture = r_texture_white;
-               if (ambientscale)
+       else
+       {
+               // 2/2/2 2D combine path (any dot3 card)
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationz;
+               R_Mesh_State(&m);
+               GL_ColorMask(0,0,0,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.texcombinergb[0] = GL_REPLACE;
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
+               R_Mesh_State(&m);
+               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(basetexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       GL_Color(1,1,1,1);
-                       colorscale = ambientscale;
-                       // colorscale accounts for how much we multiply the brightness
-                       // during combine.
-                       //
-                       // mult is how many times the final pass of the lighting will be
-                       // 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)
-                       {
-                               // 3 3D combine path (Geforce3, Radeon 8500)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[1] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[1] = texcoord2f;
-                               m.texcubemap[2] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltolight;
-#else
-                               m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               GL_BlendFunc(GL_ONE, GL_ONE);
-                       }
-                       else if (r_shadow_texture3d.integer && !lightcubemap && r_textureunits.integer >= 2)
-                       {
-                               // 2 3D combine path (Geforce3, original Radeon)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#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)
-                       {
-                               // 4 2D combine path (Geforce3, Radeon 8500)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               m.tex[2] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[2] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[3] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[3] = vertex3f;
-                                       m.texmatrix[3] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[3] = varray_texcoord3f[3];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_ONE, GL_ONE);
-                       }
-                       else if (r_textureunits.integer >= 3 && !lightcubemap)
-                       {
-                               // 3 2D combine path (Geforce3, original Radeon)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               m.tex[2] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[2] = texcoord2f;
-                               GL_BlendFunc(GL_ONE, GL_ONE);
-                       }
-                       else
-                       {
-                               // 2/2/2 2D combine path (any dot3 card)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       // 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(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(firstvertex, numvertices, numtriangles, elements);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-                       }
-                       GL_LockArrays(0, 0);
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               if (diffusescale)
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       // 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(lightcolorbase, colorscale, color2);
+       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+       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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+       }
+       GL_LockArrays(0, 0);
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
+{
+       int renders;
+       model_t *model = ent->model;
+       float color2[3];
+       rmeshstate_t m;
+       const int *elements = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+       // FIXME: detect blendsquare!
+       //if (!gl_support_blendsquare)
+       //      return;
+       GL_Color(1,1,1,1);
+       if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != 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 = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Specular_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_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(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               R_Mesh_State(&m);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               // 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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               R_Mesh_State(&m);
+               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(glosstexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       GL_Color(1,1,1,1);
-                       colorscale = diffusescale;
-                       // colorscale accounts for how much we multiply the brightness
-                       // during combine.
-                       //
-                       // mult is how many times the final pass of the lighting will be
-                       // performed to get more brightness than otherwise possible.
-                       //
-                       // Limit mult to 64 for sanity sake.
-                       if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
-                       {
-                               // 3/2 3D combine path (Geforce3, Radeon 8500)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin);
-                               m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
-                       {
-                               // 1/2/2 3D combine path (original Radeon)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin);
-                               R_Mesh_State(&m);
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
-                       {
-                               // 2/2 3D combine path (original Radeon)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin);
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       else if (r_textureunits.integer >= 4)
-                       {
-                               // 4/2 2D combine path (Geforce3, Radeon 8500)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin);
-                               m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[2] = vertex3f;
-                               m.texmatrix[2] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[2] = varray_texcoord2f[2];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[3] = vertex3f;
-                               m.texmatrix[3] = *matrix_modeltoattenuationz;
-#else
-                               m.pointer_texcoord[3] = varray_texcoord2f[3];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       else
-                       {
-                               // 2/2/2 2D combine path (any dot3 card)
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[0] = vertex3f;
-                               m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[0] = varray_texcoord2f[0];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               R_Mesh_State(&m);
-                               GL_ColorMask(0,0,0,1);
-                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin);
-                               R_Mesh_State(&m);
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-
-                               memset(&m, 0, sizeof(m));
-                               m.pointer_vertex = vertex3f;
-                               m.tex[0] = R_GetTexture(basetexture);
-                               m.pointer_texcoord[0] = texcoord2f;
-                               if (lightcubemap)
-                               {
-                                       m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#endif
-                               }
-                               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                       }
-                       // 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(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(firstvertex, numvertices, numtriangles, elements);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
-                       }
-                       GL_LockArrays(0, 0);
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
-               if (specularscale && glosstexture != r_texture_black)
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == 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 = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Specular_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_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(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               R_Mesh_State(&m);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               // 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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(glosstexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+       }
+       else
+       {
+               // 2/0/0/2/2 2D combine blendsquare path
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(normalmaptexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+               m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Shadow_GenTexCoords_Specular_NormalCubeMap(rsurface_array_texcoord3f + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_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(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               R_Mesh_State(&m);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               // 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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[0] = rsurface_vertex3f;
+               m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
+               m.texmatrix[1] = r_shadow_entitytoattenuationz;
+               R_Mesh_State(&m);
+               GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
+
+               memset(&m, 0, sizeof(m));
+               m.pointer_vertex = rsurface_vertex3f;
+               m.tex[0] = R_GetTexture(glosstexture);
+               m.pointer_texcoord[0] = model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = texture->currenttexmatrix;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
-                       // 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!
-                               {
-                                       // 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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin, relativeeyeorigin);
-                                       R_Mesh_State(&m);
-                                       GL_ColorMask(0,0,0,1);
-                                       // this squares the result
-                                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(firstvertex, numvertices);
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       GL_LockArrays(0, 0);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       R_Mesh_State(&m);
-                                       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(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       GL_LockArrays(0, 0);
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[0] = vertex3f;
-                                       m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                                       m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                                       R_Mesh_State(&m);
-                                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                                       GL_LockArrays(firstvertex, numvertices);
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       GL_LockArrays(0, 0);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       m.tex[0] = R_GetTexture(glosstexture);
-                                       m.pointer_texcoord[0] = texcoord2f;
-                                       if (lightcubemap)
-                                       {
-                                               m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                               m.pointer_texcoord3f[1] = vertex3f;
-                                               m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#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!
-                               {
-                                       // 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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin, relativeeyeorigin);
-                                       R_Mesh_State(&m);
-                                       GL_ColorMask(0,0,0,1);
-                                       // this squares the result
-                                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(firstvertex, numvertices);
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       GL_LockArrays(0, 0);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       R_Mesh_State(&m);
-                                       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(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       GL_LockArrays(0, 0);
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       m.tex[0] = R_GetTexture(glosstexture);
-                                       m.pointer_texcoord[0] = texcoord2f;
-                                       m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltoattenuationxyz;
-#else
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                                       GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
-                               }
-                               else
-                               {
-                                       // 2/0/0/2/2 2D 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.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-                                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                       R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, relativelightorigin, relativeeyeorigin);
-                                       R_Mesh_State(&m);
-                                       GL_ColorMask(0,0,0,1);
-                                       // this squares the result
-                                       GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
-                                       GL_LockArrays(firstvertex, numvertices);
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       GL_LockArrays(0, 0);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       R_Mesh_State(&m);
-                                       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(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                                       GL_LockArrays(0, 0);
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[0] = vertex3f;
-                                       m.texmatrix[0] = *matrix_modeltoattenuationxyz;
-#else
-                                       m.pointer_texcoord[0] = varray_texcoord2f[0];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                                       m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[1] = vertex3f;
-                                       m.texmatrix[1] = *matrix_modeltoattenuationz;
-#else
-                                       m.pointer_texcoord[1] = varray_texcoord2f[1];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                                       R_Mesh_State(&m);
-                                       GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
-                                       GL_LockArrays(firstvertex, numvertices);
-                                       R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                                       GL_LockArrays(0, 0);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-
-                                       memset(&m, 0, sizeof(m));
-                                       m.pointer_vertex = vertex3f;
-                                       m.tex[0] = R_GetTexture(glosstexture);
-                                       m.pointer_texcoord[0] = texcoord2f;
-                                       if (lightcubemap)
-                                       {
-                                               m.texcubemap[1] = R_GetTexture(lightcubemap);
-#ifdef USETEXMATRIX
-                                               m.pointer_texcoord3f[1] = vertex3f;
-                                               m.texmatrix[1] = *matrix_modeltolight;
-#else
-                                               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                                               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltolight);
-#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(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(firstvertex, numvertices, numtriangles, elements);
-                                       c_rt_lightmeshes++;
-                                       c_rt_lighttris += numtriangles;
-                               }
-                               GL_LockArrays(0, 0);
-                       }
+                       m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+                       m.pointer_texcoord3f[1] = rsurface_vertex3f;
+                       m.texmatrix[1] = r_shadow_entitytolight;
                }
+               GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
-       else
+       R_Mesh_State(&m);
+       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
+       VectorScale(lightcolorbase, colorscale, color2);
+       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+       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(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+       }
+       GL_LockArrays(0, 0);
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+{
+       // ARB path (any Geforce, any Radeon)
+       int surfacelistindex;
+       qboolean doambient = r_shadow_rtlight->ambientscale > 0;
+       qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
+       qboolean dospecular = specularscale > 0;
+       if (!doambient && !dodiffuse && !dospecular)
+               return;
+       RSurf_SetPointersForPass(false, true);
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
-               // TODO: add direct pants/shirt rendering
-               if (pantstexture && (ambientscale + diffusescale) * VectorLength2(lightcolorpants) > 0.001)
-                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorpants, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, pantstexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-               if (shirttexture && (ambientscale + diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
-                       R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, relativelightorigin, relativeeyeorigin, lightcolorshirt, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, shirttexture, NULL, NULL, bumptexture, NULL, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
-               if (ambientscale)
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               if (rsurface_dynamicvertex)
+                       RSurf_PrepareDynamicSurfaceVertices(surface);
+               if (doambient)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
+               if (dodiffuse)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+               if (dopants)
+               {
+                       if (doambient)
+                               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
+                       if (dodiffuse)
+                               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+               }
+               if (doshirt)
                {
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-                       VectorScale(lightcolorbase, ambientscale, color2);
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (r_textureunits.integer >= 2)
+                       if (doambient)
+                               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
+                       if (dodiffuse)
+                               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+               }
+               if (dospecular)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(ent, texture, surface, lightcolorbase, glosstexture, normalmaptexture, specularscale);
+       }
+}
+
+void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, const msurface_t *surface, vec3_t diffusecolor2, vec3_t ambientcolor2)
+{
+       int renders;
+       const int *elements = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+       R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(surface, diffusecolor2, ambientcolor2);
+       for (renders = 0;renders < 64 && (ambientcolor2[0] > renders || ambientcolor2[1] > renders || ambientcolor2[2] > renders || diffusecolor2[0] > renders || diffusecolor2[1] > renders || diffusecolor2[2] > renders);renders++)
+       {
+               int i;
+               float *c;
+#if 1
+               // due to low fillrate on the cards this vertex lighting path is
+               // designed for, we manually cull all triangles that do not
+               // contain a lit vertex
+               int draw;
+               const int *e;
+               int newnumtriangles;
+               int *newe;
+               int newelements[3072];
+               draw = false;
+               newnumtriangles = 0;
+               newe = newelements;
+               for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
+               {
+                       if (newnumtriangles >= 1024)
                        {
-                               // voodoo2
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               if (r_textureunits.integer >= 3)
-                               {
-                                       // Geforce3/Radeon class but not using dot3
-                                       m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[2] = vertex3f;
-                                       m.texmatrix[2] = *matrix_modeltoattenuationz;
-#else
-                                       m.pointer_texcoord[2] = varray_texcoord2f[2];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
-#endif
-                               }
+                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
+                               GL_LockArrays(0, 0);
+                               newnumtriangles = 0;
+                               newe = newelements;
                        }
-                       if (r_textureunits.integer >= 3)
-                               m.pointer_color = NULL;
-                       else
-                               m.pointer_color = varray_color4f;
-                       R_Mesh_State(&m);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+                       if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               if (r_textureunits.integer >= 3)
-                                       GL_Color(color[0], color[1], color[2], 1);
-                               else if (r_textureunits.integer >= 2)
-                                       R_Shadow_VertexNoShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, color, matrix_modeltolight);
-                               else
-                                       R_Shadow_VertexNoShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, color, matrix_modeltolight);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
+                               newe[0] = e[0];
+                               newe[1] = e[1];
+                               newe[2] = e[2];
+                               newnumtriangles++;
+                               newe += 3;
+                               draw = true;
                        }
                }
-               if (diffusescale)
+               if (newnumtriangles >= 1)
                {
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-                       VectorScale(lightcolorbase, diffusescale, color2);
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = vertex3f;
-                       m.pointer_color = varray_color4f;
-                       m.tex[0] = R_GetTexture(basetexture);
-                       m.pointer_texcoord[0] = texcoord2f;
-                       if (r_textureunits.integer >= 2)
-                       {
-                               // voodoo2
-                               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                               m.pointer_texcoord3f[1] = vertex3f;
-                               m.texmatrix[1] = *matrix_modeltoattenuationxyz;
-#else
-                               m.pointer_texcoord[1] = varray_texcoord2f[1];
-                               R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationxyz);
-#endif
-                               if (r_textureunits.integer >= 3)
-                               {
-                                       // Geforce3/Radeon class but not using dot3
-                                       m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
-                                       m.pointer_texcoord3f[2] = vertex3f;
-                                       m.texmatrix[2] = *matrix_modeltoattenuationz;
+                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
+                       GL_LockArrays(0, 0);
+                       draw = true;
+               }
+               if (!draw)
+                       break;
 #else
-                                       m.pointer_texcoord[2] = varray_texcoord2f[2];
-                                       R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, matrix_modeltoattenuationz);
+               for (i = 0, c = rsurface_array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+                       if (VectorLength2(c))
+                               goto goodpass;
+               break;
+goodpass:
+               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
+               GL_LockArrays(0, 0);
 #endif
-                               }
-                       }
-                       R_Mesh_State(&m);
-                       for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
+               // now reduce the intensity for the next overbright pass
+               for (i = 0, c = rsurface_array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+               {
+                       c[0] = max(0, c[0] - 1);
+                       c[1] = max(0, c[1] - 1);
+                       c[2] = max(0, c[2] - 1);
+               }
+       }
+}
+
+static void R_Shadow_RenderSurfacesLighting_Light_Vertex(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+{
+       int surfacelistindex;
+       model_t *model = ent->model;
+       float ambientcolorbase[3], diffusecolorbase[3];
+       float ambientcolorpants[3], diffusecolorpants[3];
+       float ambientcolorshirt[3], diffusecolorshirt[3];
+       rmeshstate_t m;
+       VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2, ambientcolorbase);
+       VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2, diffusecolorbase);
+       VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2, ambientcolorpants);
+       VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2, diffusecolorpants);
+       VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
+       VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, diffusecolorshirt);
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+       memset(&m, 0, sizeof(m));
+       m.tex[0] = R_GetTexture(basetexture);
+       if (r_textureunits.integer >= 2)
+       {
+               // voodoo2
+               m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+               m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
+               if (r_textureunits.integer >= 3)
+               {
+                       // Geforce3/Radeon class but not using dot3
+                       m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
+                       m.texmatrix[2] = r_shadow_entitytoattenuationz;
+               }
+       }
+       m.pointer_color = rsurface_array_color4f;
+       R_Mesh_State(&m);
+       RSurf_SetPointersForPass(true, false);
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       {
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               if (rsurface_dynamicvertex)
+                       RSurf_PrepareDynamicSurfaceVertices(surface);
+               // OpenGL 1.1 path (anything)
+               R_Mesh_TexCoordPointer(0, 2, model->surfmesh.data_texcoordtexture2f);
+               R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
+               if (r_textureunits.integer >= 2)
+               {
+                       // voodoo2 or TNT
+                       R_Mesh_TexCoordPointer(1, 3, rsurface_vertex3f);
+                       if (r_textureunits.integer >= 3)
                        {
-                               color[0] = bound(0, color2[0], 1);
-                               color[1] = bound(0, color2[1], 1);
-                               color[2] = bound(0, color2[2], 1);
-                               if (r_textureunits.integer >= 3)
-                                       R_Shadow_VertexShading(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color, matrix_modeltolight);
-                               else if (r_textureunits.integer >= 2)
-                                       R_Shadow_VertexShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color, matrix_modeltolight);
-                               else
-                                       R_Shadow_VertexShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color, matrix_modeltolight);
-                               GL_LockArrays(firstvertex, numvertices);
-                               R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
-                               GL_LockArrays(0, 0);
-                               c_rt_lightmeshes++;
-                               c_rt_lighttris += numtriangles;
+                               // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
+                               R_Mesh_TexCoordPointer(2, 3, rsurface_vertex3f);
                        }
                }
+               R_Mesh_TexBind(0, R_GetTexture(basetexture));
+               R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, surface, diffusecolorbase, ambientcolorbase);
+               if (dopants)
+               {
+                       R_Mesh_TexBind(0, R_GetTexture(pantstexture));
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, surface, diffusecolorpants, ambientcolorpants);
+               }
+               if (doshirt)
+               {
+                       R_Mesh_TexBind(0, R_GetTexture(shirttexture));
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, surface, diffusecolorshirt, ambientcolorshirt);
+               }
+       }
+}
+
+void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist)
+{
+       // FIXME: support MATERIALFLAG_NODEPTHTEST
+       vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
+       // calculate colors to render this texture with
+       lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * ent->colormod[0] * texture->currentalpha;
+       lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * ent->colormod[1] * texture->currentalpha;
+       lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * ent->colormod[2] * texture->currentalpha;
+       if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
+               return;
+       if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
+               qglDisable(GL_CULL_FACE);
+       else
+               qglEnable(GL_CULL_FACE);
+       RSurf_PrepareForBatch(ent, texture, r_shadow_entityeyeorigin);
+       if (texture->colormapping)
+       {
+               qboolean dopants = texture->skin.pants != NULL && VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f);
+               qboolean doshirt = texture->skin.shirt != NULL && VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
+               if (dopants)
+               {
+                       lightcolorpants[0] = lightcolorbase[0] * ent->colormap_pantscolor[0];
+                       lightcolorpants[1] = lightcolorbase[1] * ent->colormap_pantscolor[1];
+                       lightcolorpants[2] = lightcolorbase[2] * ent->colormap_pantscolor[2];
+               }
+               else
+                       VectorClear(lightcolorpants);
+               if (doshirt)
+               {
+                       lightcolorshirt[0] = lightcolorbase[0] * ent->colormap_shirtcolor[0];
+                       lightcolorshirt[1] = lightcolorbase[1] * ent->colormap_shirtcolor[1];
+                       lightcolorshirt[2] = lightcolorbase[2] * ent->colormap_shirtcolor[2];
+               }
+               else
+                       VectorClear(lightcolorshirt);
+               switch (r_shadow_rendermode)
+               {
+               case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_GLSL:
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_DOT3:
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
+                       break;
+               default:
+                       Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
+                       break;
+               }
+       }
+       else
+       {
+               switch (r_shadow_rendermode)
+               {
+               case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_GLSL:
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_DOT3:
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
+                       break;
+               case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
+                       break;
+               default:
+                       Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
+                       break;
+               }
        }
 }
 
-void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
+void R_RTLight_Update(dlight_t *light, int isstatic)
 {
        int j, k;
        float scale;
+       rtlight_t *rtlight = &light->rtlight;
        R_RTLight_Uncompile(rtlight);
        memset(rtlight, 0, sizeof(*rtlight));
 
@@ -2657,23 +2102,16 @@ 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;
-       VectorScale(rtlight->color, rtlight->radius * (rtlight->style >= 0 ? d_lightstylevalue[rtlight->style] : 128) * 0.125f, rtlight->lightmap_light);
-       rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
 }
 
 // compiles rtlight geometry
 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
 void R_RTLight_Compile(rtlight_t *rtlight)
 {
-       int shadowmeshes, shadowtris, lightmeshes, lighttris, numleafs, numleafpvsbytes, numsurfaces;
+       int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
        entity_render_t *ent = r_refdef.worldentity;
        model_t *model = r_refdef.worldmodel;
-       qbyte *data;
+       unsigned char *data;
 
        // compile the light
        rtlight->compiled = true;
@@ -2692,37 +2130,27 @@ void R_RTLight_Compile(rtlight_t *rtlight)
 
        if (model && model->GetLightInfo)
        {
-               // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
+               // this variable must be set for the CompileShadowVolume code
                r_shadow_compilingrtlight = rtlight;
-               R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->brush.num_surfaces);
+               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);
+               data = (unsigned char *)Mem_Alloc(r_main_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_leaflist = (int *)data;data += sizeof(int) * numleafs;
+               rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
                rtlight->static_numsurfaces = numsurfaces;
-               rtlight->static_surfacelist = (void *)data;data += sizeof(int) * numsurfaces;
+               rtlight->static_surfacelist = (int *)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, 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, 0);
-                       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
+               if (model->CompileShadowVolume && rtlight->shadow)
+                       model->CompileShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
+               // now we're done compiling the rtlight
                r_shadow_compilingrtlight = NULL;
        }
 
@@ -2743,19 +2171,8 @@ 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);
+       if (developer.integer >= 10)
+               Con_Printf("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)
@@ -2765,9 +2182,6 @@ 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;
                // these allocations are grouped
                if (rtlight->static_leaflist)
                        Mem_Free(rtlight->static_leaflist);
@@ -2788,60 +2202,125 @@ void R_Shadow_UncompileWorldLights(void)
                R_RTLight_Uncompile(&light->rtlight);
 }
 
-void R_DrawRTLight(rtlight_t *rtlight, int visiblelighting, int visiblevolumes)
+void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfacelist)
+{
+       model_t *model = ent->model;
+       vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
+       vec_t relativeshadowradius;
+       if (ent == r_refdef.worldentity)
+       {
+               if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
+               {
+                       shadowmesh_t *mesh;
+                       R_Mesh_Matrix(&ent->matrix);
+                       for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+                       {
+                               renderstats.lights_shadowtriangles += mesh->numtriangles;
+                               R_Mesh_VertexPointer(mesh->vertex3f);
+                               GL_LockArrays(0, mesh->numverts);
+                               if (r_shadow_rendermode == R_SHADOW_RENDERMODE_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);
+                                       // 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);
+                               GL_LockArrays(0, 0);
+                       }
+               }
+               else if (numsurfaces)
+               {
+                       R_Mesh_Matrix(&ent->matrix);
+                       model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
+               }
+       }
+       else
+       {
+               Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
+               relativeshadowradius = r_shadow_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);
+               model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, model->nummodelsurfaces, model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+       }
+}
+
+void R_Shadow_SetupEntityLight(const entity_render_t *ent)
+{
+       // set up properties for rendering light onto this entity
+       Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_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, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
+       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
+       R_Mesh_Matrix(&ent->matrix);
+}
+
+void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
+{
+       model_t *model = ent->model;
+       if (!model->DrawLight)
+               return;
+       R_Shadow_SetupEntityLight(ent);
+       if (ent == r_refdef.worldentity)
+               model->DrawLight(ent, numsurfaces, surfacelist);
+       else
+               model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist);
+}
+
+void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
 {
-       int i, shadow, usestencil;
-       entity_render_t *ent;
+       int i, usestencil;
        float f;
-       vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2;
-       rtexture_t *cubemaptexture;
-       matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
        int numleafs, numsurfaces;
        int *leaflist, *surfacelist;
-       qbyte *leafpvs;
-       vec3_t cullmins, cullmaxs, relativelightmins, relativelightmaxs;
-       shadowmesh_t *mesh;
-       rmeshstate_t m;
-
-       // skip lights that don't light (corona only lights)
-       if (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale < 0.01)
+       unsigned char *leafpvs;
+       int numlightentities;
+       int numshadowentities;
+       entity_render_t *lightentities[MAX_EDICTS];
+       entity_render_t *shadowentities[MAX_EDICTS];
+
+       // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
+       // skip lights that are basically invisible (color 0 0 0)
+       if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.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)
-               return;
+       // 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)
+       // compile light
+       if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
+               R_RTLight_Compile(rtlight);
+       // load cubemap
+       rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
+
+       // look up the light style value at this time
+       f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
+       VectorScale(rtlight->color, f, rtlight->currentcolor);
        /*
        if (rtlight->selected)
        {
                f = 2 + sin(realtime * M_PI * 4.0);
-               VectorScale(lightcolor, f, lightcolor);
+               VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
        }
        */
 
-       // 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_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)
+       // if lightstyle is currently off, don't draw the light
+       if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
                return;
-       numleafs = 0;
-       leaflist = NULL;
-       leafpvs = NULL;
-       numsurfaces = 0;
-       surfacelist = NULL;
+
+       // if the light box is offscreen, skip it
+       if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
+               return;
+
        if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
        {
                // compiled light, world available and can receive realtime lighting
@@ -2851,25 +2330,29 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblelighting, int visiblevolumes)
                leafpvs = rtlight->static_leafpvs;
                numsurfaces = rtlight->static_numsurfaces;
                surfacelist = rtlight->static_surfacelist;
-               VectorCopy(rtlight->cullmins, cullmins);
-               VectorCopy(rtlight->cullmaxs, cullmaxs);
        }
        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 leafs
-               R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->brush.num_surfaces);
-               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               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;
+       }
+       else
+       {
+               // no world
+               numleafs = 0;
+               leaflist = NULL;
+               leafpvs = NULL;
+               numsurfaces = 0;
+               surfacelist = NULL;
        }
-       // if the reduced leaf bounds are offscreen, skip it
-       if (R_CullBox(cullmins, cullmaxs))
-               return;
        // check if light is illuminating any visible leafs
        if (numleafs)
        {
@@ -2880,185 +2363,119 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblelighting, int visiblevolumes)
                        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 && !visiblelighting) || visiblevolumes))
+       // make a list of lit entities and shadow casting entities
+       numlightentities = 0;
+       numshadowentities = 0;
+       // don't count the world unless some surfaces are actually lit
+       if (numsurfaces)
        {
-               if (visiblevolumes)
-               {
-                       qglDisable(GL_CULL_FACE);
-                       GL_DepthTest(visiblevolumes < 2);
-                       GL_Color(0.0, 0.0125, 0.1, 1);
-               }
-               else
-               {
-                       R_Shadow_Stage_ShadowVolumes();
-                       usestencil = true;
-               }
-               ent = r_refdef.worldentity;
-               if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
-               {
-                       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(0, 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(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
-                               c_rtcached_shadowmeshes++;
-                               c_rtcached_shadowtris += mesh->numtriangles;
-                               GL_LockArrays(0, 0);
-                       }
-               }
-               else if (numsurfaces)
-               {
-                       Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
-                       ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist, rtlight->cullmins, rtlight->cullmaxs);
-               }
-               if (r_drawentities.integer)
+               lightentities[numlightentities++] = r_refdef.worldentity;
+               shadowentities[numshadowentities++] = r_refdef.worldentity;
+       }
+       // add dynamic entities that are lit by the light
+       if (r_drawentities.integer)
+       {
+               for (i = 0;i < r_refdef.numentities;i++)
                {
-                       for (i = 0;i < r_refdef.numentities;i++)
+                       model_t *model;
+                       entity_render_t *ent = r_refdef.entities[i];
+                       if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
+                        && (model = 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.BoxTouchingLeafPVS != NULL && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, 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;
-                               relativelightmins[0] = relativelightorigin[0] - rtlight->radius;
-                               relativelightmins[1] = relativelightorigin[1] - rtlight->radius;
-                               relativelightmins[2] = relativelightorigin[2] - rtlight->radius;
-                               relativelightmaxs[0] = relativelightorigin[0] + rtlight->radius;
-                               relativelightmaxs[1] = relativelightorigin[1] + rtlight->radius;
-                               relativelightmaxs[2] = relativelightorigin[2] + rtlight->radius;
-                               ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativelightmins, relativelightmaxs);
+                               // about the VectorDistance2 - light emitting entities should not cast their own shadow
+                               if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
+                                       shadowentities[numshadowentities++] = ent;
+                               if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && model->DrawLight)
+                                       lightentities[numlightentities++] = ent;
                        }
                }
        }
 
-       if (visiblelighting || !visiblevolumes)
+       // return if there's nothing at all to light
+       if (!numlightentities)
+               return;
+
+       // don't let sound skip if going slow
+       if (r_refdef.extraupdate)
+               S_ExtraUpdate ();
+
+       // make this the active rtlight for rendering purposes
+       R_Shadow_RenderMode_ActiveLight(rtlight);
+       // count this light in the r_speeds
+       renderstats.lights++;
+
+       usestencil = false;
+       if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
        {
-               if (visiblelighting)
+               // draw stencil shadow volumes to mask off pixels that are in shadow
+               // so that they won't receive lighting
+               if (gl_stencil)
                {
-                       qglEnable(GL_CULL_FACE);
-                       GL_DepthTest(visiblelighting < 2);
-                       GL_Color(0.1, 0.0125, 0, 1);
+                       usestencil = true;
+                       R_Shadow_RenderMode_StencilShadowVolumes();
+                       for (i = 0;i < numshadowentities;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
                }
-               else
-                       R_Shadow_Stage_Light(usestencil);
 
-               ent = r_refdef.worldentity;
-               if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
+               // optionally draw visible shape of the shadow volumes
+               // for performance analysis by level designers
+               if (r_showshadowvolumes.integer)
                {
-                       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 (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compilelight.integer)
-                       {
-                               R_Mesh_Matrix(&ent->matrix);
-                               for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
-                                       R_Shadow_RenderLighting(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor2, NULL, NULL, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, NULL, NULL, mesh->map_normal, mesh->map_specular, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, visiblelighting);
-                       }
-                       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, visiblelighting);
+                       R_Shadow_RenderMode_VisibleShadowVolumes();
+                       for (i = 0;i < numshadowentities;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
                }
-               if (r_drawentities.integer)
+       }
+
+       if (numlightentities)
+       {
+               // draw lighting in the unmasked areas
+               R_Shadow_RenderMode_Lighting(usestencil, false);
+               for (i = 0;i < numlightentities;i++)
+                       R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+
+               // optionally draw the illuminated areas
+               // for performance analysis by level designers
+               if (r_showlighting.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, visiblelighting);
-                               }
-                       }
+                       R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
+                       for (i = 0;i < numlightentities;i++)
+                               R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
                }
        }
 }
 
-void R_ShadowVolumeLighting(int visiblelighting, 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 (visiblelighting || visiblevolumes)
-       {
-               memset(&m, 0, sizeof(m));
-               R_Mesh_State(&m);
+       R_Shadow_RenderMode_Begin();
 
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               GL_DepthMask(false);
-               qglCullFace(GL_FRONT); // this culls back
-       }
-       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, visiblelighting, 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, visiblelighting, visiblevolumes);
+                               R_DrawRTLight(&light->rtlight, visible);
        if (r_rtdlight)
-               for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
-                       R_DrawRTLight(&light->rtlight, visiblelighting, visiblevolumes);
+               for (lnum = 0;lnum < r_refdef.numlights;lnum++)
+                       R_DrawRTLight(&r_refdef.lights[lnum]->rtlight, visible);
 
-       if (visiblelighting || 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_RenderMode_End();
 }
 
 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
@@ -3101,7 +2518,7 @@ static int componentorder[4] = {0, 1, 2, 3};
 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
 {
        int i, j, cubemapsize;
-       qbyte *cubemappixels, *image_rgba;
+       unsigned char *cubemappixels, *image_rgba;
        rtexture_t *cubemaptexture;
        char name[256];
        // must start 0 so the first loadimagepixels has no requested width/height
@@ -3127,7 +2544,7 @@ rtexture_t *R_Shadow_LoadCubemap(const char *basename)
                                        {
                                                cubemapsize = image_width;
                                                // note this clears to black, so unavailable sides are black
-                                               cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
+                                               cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
                                        }
                                        // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
                                        if (cubemappixels)
@@ -3166,10 +2583,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;
 }
 
@@ -3182,7 +2601,7 @@ void R_Shadow_FreeCubemaps(void)
 dlight_t *R_Shadow_NewWorldLight(void)
 {
        dlight_t *light;
-       light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
+       light = (dlight_t *)Mem_Alloc(r_main_mempool, sizeof(dlight_t));
        light->next = r_shadow_worldlightchain;
        r_shadow_worldlightchain = light;
        return light;
@@ -3216,7 +2635,7 @@ void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, ve
        light->flags = flags;
        Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
 
-       R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
+       R_RTLight_Update(light, true);
 }
 
 void R_Shadow_FreeWorldLight(dlight_t *light)
@@ -3225,7 +2644,7 @@ void R_Shadow_FreeWorldLight(dlight_t *light)
        R_RTLight_Uncompile(&light->rtlight);
        for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
        if (*lightpointer != light)
-               Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
+               Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
        *lightpointer = light->next;
        Mem_Free(light);
 }
@@ -3247,41 +2666,32 @@ void R_Shadow_SelectLight(dlight_t *light)
                r_shadow_selectedlight->selected = true;
 }
 
-void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
+void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
 {
        float scale = r_editlights_cursorgrid.value * 0.5f;
-       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
+       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[1]->tex, NULL, false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
 }
 
-void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
+void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
 {
        float intensity;
-       const dlight_t *light;
-       light = calldata1;
+       const dlight_t *light = (dlight_t *)ent;
        intensity = 0.5;
        if (light->selected)
                intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
        if (!light->shadow)
                intensity *= 0.5f;
-       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
+       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[surfacenumber]->tex, NULL, false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
 }
 
 void R_Shadow_DrawLightSprites(void)
 {
        int i;
-       cachepic_t *pic;
        dlight_t *light;
 
-       for (i = 0;i < 5;i++)
-       {
-               lighttextures[i] = NULL;
-               if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1), true)))
-                       lighttextures[i] = pic->tex;
-       }
-
        for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
-               R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5);
-       R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
+               R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
+       R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
 }
 
 void R_Shadow_SelectLightInView(void)
@@ -3319,7 +2729,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, NULL);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3385,8 +2795,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++;
@@ -3403,10 +2811,10 @@ 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];
+       char line[MAX_INPUTLINE];
        if (!r_shadow_worldlightchain)
                return;
        if (r_refdef.worldmodel == NULL)
@@ -3421,16 +2829,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(tempmempool, bufmaxchars);
+                       buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
                        if (oldbuf)
                        {
                                if (bufchars)
@@ -3445,7 +2853,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);
 }
@@ -3462,7 +2870,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, NULL);
        if (lightsstring)
        {
                s = lightsstring;
@@ -3508,7 +2916,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
        char *entfiledata;
        const char *data;
        float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
-       char key[256], value[1024];
+       char key[256], value[MAX_INPUTLINE];
 
        if (r_refdef.worldmodel == NULL)
        {
@@ -3518,7 +2926,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, NULL);
        // and if that is not found, fall back to the bsp file entity string
        if (!data)
                data = r_refdef.worldmodel->brush.entities;
@@ -3625,7 +3033,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;
@@ -3634,7 +3042,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;
@@ -3643,7 +3051,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;
@@ -3652,7 +3060,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;
@@ -3661,6 +3069,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))
@@ -3668,12 +3082,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;
@@ -3737,6 +3145,10 @@ void R_Shadow_SetCursorLocationForView(void)
                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;
        r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
@@ -3815,7 +3227,7 @@ void R_Shadow_EditLights_Edit_f(void)
        vec3_t origin, angles, color;
        vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
        int style, shadows, flags, normalmode, realtimemode;
-       char cubemapname[1024];
+       char cubemapname[MAX_INPUTLINE];
        if (!r_editlights.integer)
        {
                Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
@@ -3832,7 +3244,7 @@ void R_Shadow_EditLights_Edit_f(void)
        radius = r_shadow_selectedlight->radius;
        style = r_shadow_selectedlight->style;
        if (r_shadow_selectedlight->cubemapname)
-               strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
+               strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
        else
                cubemapname[0] = 0;
        shadows = r_shadow_selectedlight->shadow;
@@ -3978,6 +3390,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)
@@ -4204,8 +3646,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"
@@ -4232,6 +3672,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"
@@ -4298,21 +3742,19 @@ 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);
-       Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
-       Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
-       Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
-       Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
-       Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
-       Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
-       Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
-       Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
-       Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
-       Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f);
-       Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f);
+       Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
+       Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
+       Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f, "reloads rtlights file (or imports from .lights file or .ent file or the map itself)");
+       Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
+       Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
+       Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
+       Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f, "changes a property on ALL lights at once (tip: use radiusscale and colorscale to alter these properties)");
+       Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
+       Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
+       Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
+       Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
+       Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
+       Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
+       Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f, "apply the stored properties onto the selected light (making it exactly identical except for origin)");
 }