]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
implemented Shadow Volume BSP based culling of lit surfaces, this is slightly better...
[xonotic/darkplaces.git] / r_shadow.c
index 88bc073587445478ce9656fe991536ed5a4075d2..76ed116fdea7eec7811dac0f75140beab139eea8 100644 (file)
@@ -146,6 +146,7 @@ typedef enum r_shadow_rendermode_e
 {
        R_SHADOW_RENDERMODE_NONE,
        R_SHADOW_RENDERMODE_STENCIL,
+       R_SHADOW_RENDERMODE_SEPARATESTENCIL,
        R_SHADOW_RENDERMODE_STENCILTWOSIDE,
        R_SHADOW_RENDERMODE_LIGHT_VERTEX,
        R_SHADOW_RENDERMODE_LIGHT_DOT3,
@@ -159,11 +160,12 @@ 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;
 
-mempool_t *r_shadow_mempool;
-
-int maxshadowelements;
+int maxshadowtriangles;
 int *shadowelements;
 
+int maxshadowvertices;
+float *shadowvertex3f;
+
 int maxshadowmark;
 int numshadowmark;
 int *shadowmark;
@@ -183,6 +185,10 @@ int r_shadow_buffer_numsurfacepvsbytes;
 unsigned char *r_shadow_buffer_surfacepvs;
 int *r_shadow_buffer_surfacelist;
 
+// current light's cull box (copied out of an rtlight or calculated by GetLightInfo)
+vec3_t r_shadow_rtlight_cullmins;
+vec3_t r_shadow_rtlight_cullmaxs;
+
 rtexturepool_t *r_shadow_texturepool;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
@@ -197,34 +203,29 @@ cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0
 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_shadow_glsl lighting)"};
-cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_shadow_glsl lighting)"};
+cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.5", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
+cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "2", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
+cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
+cvar_t r_shadow_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_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
 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_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
 cvar_t r_shadow_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_shadow_glsl lighting)"};
-cvar_t r_shadow_visiblelighting = {0, "r_shadow_visiblelighting", "0", "shows areas lit by lights, useful for finding out why some areas of a map render slowly (bright orange = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
-cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0", "shows areas shadowed by lights, useful for finding out why some areas of a map render slowly (bright blue = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
-cvar_t r_shadow_glsl = {0, "r_shadow_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
-cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "0", "enables offset mapping effect (also known as parallax mapping or sometimes as virtual displacement mapping, not as good as relief mapping or silohuette mapping but much faster), can cause strange artifacts on many textures, requires bumpmaps for depth information (normalmaps can have depth information as alpha channel, but most do not)"};
-cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "-0.04", "how deep the offset mapping effect is, and whether it is inward or outward"};
-cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
-cvar_t r_shadow_glsl_usehalffloat = {0, "r_shadow_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
-cvar_t r_shadow_glsl_surfacenormalize = {0, "r_shadow_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+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_separatestencil = {0, "gl_ext_separatetencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
 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"};
@@ -241,8 +242,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
@@ -256,17 +255,6 @@ cubemapinfo_t;
 static int numcubemaps;
 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
 
-#define SHADERPERMUTATION_COLORMAPPING (1<<0)
-#define SHADERPERMUTATION_SPECULAR (1<<1)
-#define SHADERPERMUTATION_FOG (1<<2)
-#define SHADERPERMUTATION_CUBEFILTER (1<<3)
-#define SHADERPERMUTATION_OFFSETMAPPING (1<<4)
-#define SHADERPERMUTATION_SURFACENORMALIZE (1<<5)
-#define SHADERPERMUTATION_GEFORCEFX (1<<6)
-#define SHADERPERMUTATION_COUNT (1<<7)
-
-GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
-
 void R_Shadow_UncompileWorldLights(void);
 void R_Shadow_ClearWorldLights(void);
 void R_Shadow_SaveWorldLights(void);
@@ -278,188 +266,8 @@ 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"
-"// use half floats if available for math performance\n"
-"#ifdef GEFORCEFX\n"
-"#define myhalf half\n"
-"#define myhvec2 hvec2\n"
-"#define myhvec3 hvec3\n"
-"#define myhvec4 hvec4\n"
-"#else\n"
-"#define myhalf float\n"
-"#define myhvec2 vec2\n"
-"#define myhvec3 vec3\n"
-"#define myhvec4 vec4\n"
-"#endif\n"
-"\n"
-"uniform vec3 LightPosition;\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"varying myhvec3 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 = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\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"
-"// use half floats if available for math performance\n"
-"#ifdef GEFORCEFX\n"
-"#define myhalf half\n"
-"#define myhvec2 hvec2\n"
-"#define myhvec3 hvec3\n"
-"#define myhvec4 hvec4\n"
-"#else\n"
-"#define myhalf float\n"
-"#define myhvec2 vec2\n"
-"#define myhvec3 vec3\n"
-"#define myhvec4 vec4\n"
-"#endif\n"
-"\n"
-"uniform myhvec3 LightColor;\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform myhalf OffsetMapping_Scale;\n"
-"uniform myhalf OffsetMapping_Bias;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf SpecularPower;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform myhalf FogRangeRecip;\n"
-"#endif\n"
-"uniform myhalf AmbientScale;\n"
-"uniform myhalf DiffuseScale;\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf SpecularScale;\n"
-"#endif\n"
-"\n"
-"#ifdef USECOLORMAPPING\n"
-"uniform myhvec3 Color_Pants;\n"
-"uniform myhvec3 Color_Shirt;\n"
-"#endif\n"
-"\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Color;\n"
-"uniform sampler2D Texture_Pants;\n"
-"uniform sampler2D Texture_Shirt;\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 myhvec3 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"
-"      myhalf colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
-"\n"
-"#ifdef USEFOG\n"
-"      // apply fog\n"
-"      colorscale *= texture2D(Texture_FogMask, myhvec2(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"
-"      myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      myhvec2 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 surface normal\n"
-"#ifdef SURFACENORMALIZE\n"
-"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"#else\n"
-"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
-"#endif\n"
-"\n"
-"      // calculate shading\n"
-"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
-"      myhvec3 color = myhvec3(texture2D(Texture_Color, TexCoord));\n"
-"#ifdef USECOLORMAPPING\n"
-"      color += myhvec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhvec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
-"#endif\n"
-"      color *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
-"#ifdef USESPECULAR\n"
-"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
-"      color += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
-"#endif\n"
-"\n"
-"#ifdef USECUBEFILTER\n"
-"      // apply light cubemap filter\n"
-"      color *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
-"#endif\n"
-"\n"
-"      // calculate fragment color (apply light color and attenuation/fog scaling)\n"
-"      gl_FragColor = myhvec4(color * LightColor * colorscale, 1);\n"
-"}\n"
-;
-
 void r_shadow_start(void)
 {
-       int i;
-       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
-       if (gl_support_half_float)
-               Cvar_SetValue("r_shadow_glsl_usehalffloat", 1);
        // allocate vertex processing arrays
        numcubemaps = 0;
        r_shadow_attenuation2dtexture = NULL;
@@ -468,8 +276,10 @@ void r_shadow_start(void)
        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;
@@ -485,123 +295,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+1];
-               const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
-               vertstring = (char *)FS_LoadFile("glsl/light.vert", tempmempool, false, NULL);
-               fragstring = (char *)FS_LoadFile("glsl/light.frag", tempmempool, false, NULL);
-               for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               {
-                       char permutationname[256];
-                       vertstrings_count = 0;
-                       fragstrings_count = 0;
-                       permutationname[0] = 0;
-                       if (i & SHADERPERMUTATION_COLORMAPPING)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USECOLORMAPPING\n";
-                               fragstrings_list[fragstrings_count++] = "#define USECOLORMAPPING\n";
-                               strlcat(permutationname, " colormapping", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_SPECULAR)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
-                               fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
-                               strlcat(permutationname, " specular", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_FOG)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
-                               fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
-                               strlcat(permutationname, " fog", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_CUBEFILTER)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
-                               fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
-                               strlcat(permutationname, " cubefilter", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_OFFSETMAPPING)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
-                               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
-                               strlcat(permutationname, " offsetmapping", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_SURFACENORMALIZE)
-                       {
-                               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
-                               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
-                               strlcat(permutationname, " surfacenormalize", sizeof(permutationname));
-                       }
-                       if (i & SHADERPERMUTATION_GEFORCEFX)
-                       {
-                               // if the extension does not exist, don't try to compile it
-                               if (!gl_support_half_float)
-                                       continue;
-                               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
-                               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
-                               strlcat(permutationname, " halffloat", sizeof(permutationname));
-                       }
-                       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 failed for shader %s, some features may not work properly!\n", permutationname, "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
-                       }
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Pants"), 5);CHECKGLERROR
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Shirt"), 6);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_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = 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);
@@ -657,25 +367,18 @@ void R_Shadow_Help_f(void)
 "r_shadow_projectdistance : shadow volume projection distance\n"
 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
-"r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
 "r_shadow_realtime_world : use high quality world lighting mode\n"
 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
-"r_shadow_glsl : use OpenGL Shading Language for lighting\n"
-"r_shadow_glsl_offsetmapping : enables Offset Mapping bumpmap enhancement\n"
-"r_shadow_glsl_offsetmapping_scale : controls depth of Offset Mapping\n"
-"r_shadow_glsl_offsetmapping_bias : should be negative half of scale\n"
-"r_shadow_glsl_usehalffloat : use lower quality lighting\n"
-"r_shadow_glsl_surfacenormalize : makes bumpmapping slightly higher quality\n"
 "r_shadow_scissor : use scissor optimization\n"
 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
 "r_shadow_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"
        );
@@ -689,6 +392,7 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
        Cvar_RegisterVariable(&r_shadow_glossintensity);
+       Cvar_RegisterVariable(&r_shadow_glossexponent);
        Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
        Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
        Cvar_RegisterVariable(&r_shadow_lightintensityscale);
@@ -696,25 +400,19 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_projectdistance);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight);
        Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
-       Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
+       Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
        Cvar_RegisterVariable(&r_shadow_realtime_world);
        Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
        Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
        Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
        Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
        Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
+       Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
        Cvar_RegisterVariable(&r_shadow_scissor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
-       Cvar_RegisterVariable(&r_shadow_visiblelighting);
-       Cvar_RegisterVariable(&r_shadow_visiblevolumes);
-       Cvar_RegisterVariable(&r_shadow_glsl);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_scale);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_bias);
-       Cvar_RegisterVariable(&r_shadow_glsl_usehalffloat);
-       Cvar_RegisterVariable(&r_shadow_glsl_surfacenormalize);
+       Cvar_RegisterVariable(&gl_ext_separatestencil);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
        {
@@ -723,10 +421,11 @@ void R_Shadow_Init(void)
        }
        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;
@@ -765,17 +464,24 @@ 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 = (int *)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)
@@ -789,8 +495,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 = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
-               r_shadow_buffer_leaflist = (int *)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)
        {
@@ -799,8 +505,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 = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
-               r_shadow_buffer_surfacelist = (int *)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));
        }
 }
 
@@ -814,8 +520,8 @@ void R_Shadow_PrepareShadowMark(int numtris)
                        Mem_Free(shadowmark);
                if (shadowmarklist)
                        Mem_Free(shadowmarklist);
-               shadowmark = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
-               shadowmarklist = (int *)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++;
@@ -828,12 +534,18 @@ void R_Shadow_PrepareShadowMark(int numtris)
        numshadowmark = 0;
 }
 
-int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
+int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, const float *projectdirection, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
 {
        int i, j;
        int outtriangles = 0, outvertices = 0;
        const int *element;
        const float *vertex;
+       float ratio, direction[3], projectvector[3];
+
+       if (projectdirection)
+               VectorScale(projectdirection, projectdistance, projectvector);
+       else
+               VectorClear(projectvector);
 
        if (maxvertexupdate < innumvertices)
        {
@@ -842,8 +554,8 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
                        Mem_Free(vertexupdate);
                if (vertexremap)
                        Mem_Free(vertexremap);
-               vertexupdate = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
-               vertexremap = (int *)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++;
@@ -857,26 +569,49 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
        for (i = 0;i < numshadowmarktris;i++)
                shadowmark[shadowmarktris[i]] = shadowmarkcount;
 
-       for (i = 0;i < numshadowmarktris;i++)
+       // create the vertices
+       if (projectdirection)
        {
-               element = inelement3i + shadowmarktris[i] * 3;
-               // make sure the vertices are created
-               for (j = 0;j < 3;j++)
+               for (i = 0;i < numshadowmarktris;i++)
                {
-                       if (vertexupdate[element[j]] != vertexupdatenum)
+                       element = inelement3i + shadowmarktris[i] * 3;
+                       for (j = 0;j < 3;j++)
                        {
-                               float ratio, direction[3];
-                               vertexupdate[element[j]] = vertexupdatenum;
-                               vertexremap[element[j]] = outvertices;
-                               vertex = invertex3f + element[j] * 3;
-                               // project one copy of the vertex to the sphere radius of the light
-                               // (FIXME: would projecting it to the light box be better?)
-                               VectorSubtract(vertex, projectorigin, direction);
-                               ratio = projectdistance / VectorLength(direction);
-                               VectorCopy(vertex, outvertex3f);
-                               VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
-                               outvertex3f += 6;
-                               outvertices += 2;
+                               if (vertexupdate[element[j]] != vertexupdatenum)
+                               {
+                                       vertexupdate[element[j]] = vertexupdatenum;
+                                       vertexremap[element[j]] = outvertices;
+                                       vertex = invertex3f + element[j] * 3;
+                                       // project one copy of the vertex according to projectvector
+                                       VectorCopy(vertex, outvertex3f);
+                                       VectorAdd(vertex, projectvector, (outvertex3f + 3));
+                                       outvertex3f += 6;
+                                       outvertices += 2;
+                               }
+                       }
+               }
+       }
+       else
+       {
+               for (i = 0;i < numshadowmarktris;i++)
+               {
+                       element = inelement3i + shadowmarktris[i] * 3;
+                       for (j = 0;j < 3;j++)
+                       {
+                               if (vertexupdate[element[j]] != vertexupdatenum)
+                               {
+                                       vertexupdate[element[j]] = vertexupdatenum;
+                                       vertexremap[element[j]] = outvertices;
+                                       vertex = invertex3f + element[j] * 3;
+                                       // project one copy of the vertex to the sphere radius of the light
+                                       // (FIXME: would projecting it to the light box be better?)
+                                       VectorSubtract(vertex, projectorigin, direction);
+                                       ratio = projectdistance / VectorLength(direction);
+                                       VectorCopy(vertex, outvertex3f);
+                                       VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
+                                       outvertex3f += 6;
+                                       outvertices += 2;
+                               }
                        }
                }
        }
@@ -949,29 +684,30 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *
        return outtriangles;
 }
 
-void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
+void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris)
 {
        int tris, outverts;
        if (projectdistance < 0.1)
        {
-               Con_Printf("R_Shadow_Volume: projectdistance %f\n");
+               Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
                return;
        }
        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);
-       renderstats.lights_dynamicshadowtriangles += tris;
-       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, projectdirection, projectdistance, nummarktris, marktris);
+       r_refdef.stats.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)
+void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
 {
        int t, tend;
        const int *e;
        const float *v[3];
+       float normal[3];
        if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
                return;
        tend = firsttriangle + numtris;
@@ -980,56 +716,88 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv
         && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
        {
                // surface box entirely inside light box, no box cull
-               for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
-                       if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
-                               shadowmarklist[numshadowmark++] = t;
+               if (projectdirection)
+               {
+                       for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+                       {
+                               TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
+                               if (DotProduct(normal, projectdirection) < 0)
+                                       shadowmarklist[numshadowmark++] = t;
+                       }
+               }
+               else
+               {
+                       for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+                               if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
+                                       shadowmarklist[numshadowmark++] = t;
+               }
        }
        else
        {
                // surface box not entirely inside light box, cull each triangle
-               for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+               if (projectdirection)
                {
-                       v[0] = invertex3f + e[0] * 3;
-                       v[1] = invertex3f + e[1] * 3;
-                       v[2] = invertex3f + e[2] * 3;
-                       if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
-                        && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
-                        && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
-                        && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
-                        && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
-                        && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
-                        && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                               shadowmarklist[numshadowmark++] = t;
+                       for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+                       {
+                               v[0] = invertex3f + e[0] * 3;
+                               v[1] = invertex3f + e[1] * 3;
+                               v[2] = invertex3f + e[2] * 3;
+                               TriangleNormal(v[0], v[1], v[2], normal);
+                               if (DotProduct(normal, projectdirection) < 0
+                                && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
+                                && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
+                                && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
+                                && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
+                                && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
+                                && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
+                                       shadowmarklist[numshadowmark++] = t;
+                       }
+               }
+               else
+               {
+                       for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
+                       {
+                               v[0] = invertex3f + e[0] * 3;
+                               v[1] = invertex3f + e[1] * 3;
+                               v[2] = invertex3f + e[2] * 3;
+                               if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
+                                && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
+                                && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
+                                && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
+                                && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
+                                && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
+                                && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
+                                       shadowmarklist[numshadowmark++] = t;
+                       }
                }
        }
 }
 
 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
 {
-       rmeshstate_t m;
        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);
+       r_refdef.stats.lights_shadowtriangles += numtriangles;
+       CHECKGLERROR
+       R_Mesh_VertexPointer(vertex3f);
        GL_LockArrays(0, numvertices);
        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);
+               GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
                R_Mesh_Draw(0, numvertices, numtriangles, 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);
+               GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
        }
        R_Mesh_Draw(0, numvertices, numtriangles, element3i);
        GL_LockArrays(0, 0);
+       CHECKGLERROR
 }
 
 static void R_Shadow_MakeTextures(void)
@@ -1054,7 +822,7 @@ static void R_Shadow_MakeTextures(void)
                        intensity = 1.0f - sqrt(DotProduct(v, v));
                        if (intensity > 0)
                                intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
-                       d = bound(0, intensity, 255);
+                       d = (int)bound(0, intensity, 255);
                        data[(y*ATTEN2DSIZE+x)*4+0] = d;
                        data[(y*ATTEN2DSIZE+x)*4+1] = d;
                        data[(y*ATTEN2DSIZE+x)*4+2] = d;
@@ -1062,7 +830,7 @@ static void R_Shadow_MakeTextures(void)
                }
        }
        r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
-       if (r_shadow_texture3d.integer)
+       if (r_shadow_texture3d.integer && gl_texture3d)
        {
                for (z = 0;z < ATTEN3DSIZE;z++)
                {
@@ -1076,7 +844,7 @@ static void R_Shadow_MakeTextures(void)
                                        intensity = 1.0f - sqrt(DotProduct(v, v));
                                        if (intensity > 0)
                                                intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
-                                       d = bound(0, intensity, 255);
+                                       d = (int)bound(0, intensity, 255);
                                        data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
                                        data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
                                        data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
@@ -1093,6 +861,8 @@ void R_Shadow_ValidateCvars(void)
 {
        if (r_shadow_texture3d.integer && !gl_texture3d)
                Cvar_SetValueQuick(&r_shadow_texture3d, 0);
+       if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
+               Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
        if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
                Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
 }
@@ -1100,8 +870,6 @@ void R_Shadow_ValidateCvars(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
@@ -1113,13 +881,8 @@ matrix4x4_t r_shadow_entitytoattenuationxyz;
 // this transforms only the Z to S, and T is always 0.5
 matrix4x4_t r_shadow_entitytoattenuationz;
 
-static int r_shadow_lightpermutation;
-static int r_shadow_lightprog;
-
 void R_Shadow_RenderMode_Begin(void)
 {
-       rmeshstate_t m;
-
        R_Shadow_ValidateCvars();
 
        if (!r_shadow_attenuation2dtexture
@@ -1128,24 +891,25 @@ void R_Shadow_RenderMode_Begin(void)
         || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
                R_Shadow_MakeTextures();
 
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
+       CHECKGLERROR
+       R_Mesh_ColorPointer(NULL);
+       R_Mesh_ResetTextureState();
        GL_BlendFunc(GL_ONE, GL_ZERO);
-       GL_DepthMask(false);
        GL_DepthTest(true);
+       GL_DepthMask(false);
        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);
+       GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
 
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 
-       if (gl_ext_stenciltwoside.integer)
+       if (gl_ext_separatestencil.integer)
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
+       else if (gl_ext_stenciltwoside.integer)
                r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
        else
                r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
 
-       if (r_shadow_glsl.integer && r_shadow_program_light[0])
+       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;
@@ -1160,98 +924,81 @@ void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
 
 void R_Shadow_RenderMode_Reset(void)
 {
-       rmeshstate_t m;
+       CHECKGLERROR
        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
+               qglUseProgramObjectARB(0);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);
+       {
+               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
+       }
+       R_Mesh_ColorPointer(NULL);
+       R_Mesh_ResetTextureState();
+       GL_DepthTest(true);
+       GL_DepthMask(false);
+       qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+       qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
+       qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+       qglStencilMask(~0);CHECKGLERROR
+       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
+       qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
+       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+       GL_Color(1, 1, 1, 1);
+       GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+       GL_BlendFunc(GL_ONE, GL_ZERO);
 }
 
 void R_Shadow_RenderMode_StencilShadowVolumes(void)
 {
+       CHECKGLERROR
        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);
-       qglDepthFunc(GL_LESS);
-       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-       qglEnable(GL_STENCIL_TEST);
-       qglStencilFunc(GL_ALWAYS, 128, ~0);
+       qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
+       qglDepthFunc(GL_LESS);CHECKGLERROR
+       qglEnable(GL_STENCIL_TEST);CHECKGLERROR
        r_shadow_rendermode = r_shadow_shadowingrendermode;
-       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
+       if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
        {
-               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_INCR, GL_KEEP);
-               qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
-               qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               GL_CullFace(GL_NONE);
+               qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR // quake is backwards, this is front faces
+               qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR // quake is backwards, this is back faces
        }
-       else
+       else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
        {
-               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_CullFace(GL_NONE);
+               qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
+               qglActiveStencilFaceEXT(GL_BACK);CHECKGLERROR // quake is backwards, this is front faces
+               qglStencilMask(~0);CHECKGLERROR
+               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
+               qglActiveStencilFaceEXT(GL_FRONT);CHECKGLERROR // quake is backwards, this is back faces
+               qglStencilMask(~0);CHECKGLERROR
+               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
        }
        GL_Clear(GL_STENCIL_BUFFER_BIT);
-       renderstats.lights_clears++;
+       r_refdef.stats.lights_clears++;
 }
 
 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
-       GL_BlendFunc(GL_ONE, GL_ONE);
-       GL_DepthMask(false);
-       GL_DepthTest(true);
-       qglPolygonOffset(0, 0);
-       //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);
-       if (transparent)
-               qglDepthFunc(GL_LEQUAL);
-       else
-               qglDepthFunc(GL_EQUAL);
-       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-       qglEnable(GL_CULL_FACE);
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+       if (!transparent)
+       {
+               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+       }
        if (stenciltest)
-               qglEnable(GL_STENCIL_TEST);
-       else
-               qglDisable(GL_STENCIL_TEST);
-       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);
+       {
+               qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+               // only draw light where this geometry was already rendered AND the
+               // stencil is 128 (values other than this mean shadow)
+               qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
+       }
        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_VertexPointer(varray_vertex3f);
-               R_Mesh_TexCoordPointer(0, 2, varray_texcoord2f[0]);
-               R_Mesh_TexCoordPointer(1, 3, varray_svector3f);
-               R_Mesh_TexCoordPointer(2, 3, varray_tvector3f);
-               R_Mesh_TexCoordPointer(3, 3, varray_normal3f);
                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
@@ -1259,72 +1006,54 @@ void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
                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_TexBind(7, R_GetTexture(r_texture_white)); // lightmap
+               R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap)); // deluxemap
+               R_Mesh_TexBind(9, R_GetTexture(r_texture_black)); // glow
                //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
                CHECKGLERROR
        }
 }
 
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
        GL_BlendFunc(GL_ONE, GL_ONE);
-       GL_DepthMask(false);
-       GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
-       qglPolygonOffset(0, 0);
-       GL_Color(0.0, 0.0125, 0.1, 1);
-       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
-       qglDepthFunc(GL_GEQUAL);
-       qglCullFace(GL_FRONT); // this culls back
-       qglDisable(GL_CULL_FACE);
-       qglDisable(GL_STENCIL_TEST);
+       GL_DepthTest(r_showshadowvolumes.integer < 2);
+       GL_Color(0.0, 0.0125 * r_view.colorscale, 0.1 * r_view.colorscale, 1);
+       qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
+       GL_CullFace(GL_NONE);
        r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
 }
 
 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
        GL_BlendFunc(GL_ONE, GL_ONE);
-       GL_DepthMask(false);
-       GL_DepthTest(r_shadow_visiblelighting.integer < 2);
-       qglPolygonOffset(0, 0);
-       GL_Color(0.1, 0.0125, 0, 1);
-       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
-       if (transparent)
-               qglDepthFunc(GL_LEQUAL);
-       else
-               qglDepthFunc(GL_EQUAL);
-       qglCullFace(GL_FRONT); // this culls back
-       qglEnable(GL_CULL_FACE);
+       GL_DepthTest(r_showlighting.integer < 2);
+       GL_Color(0.1 * r_view.colorscale, 0.0125 * r_view.colorscale, 0, 1);
+       if (!transparent)
+       {
+               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+       }
        if (stenciltest)
-               qglEnable(GL_STENCIL_TEST);
-       else
-               qglDisable(GL_STENCIL_TEST);
+       {
+               qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+               qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
+       }
        r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
 }
 
 void R_Shadow_RenderMode_End(void)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
        R_Shadow_RenderMode_ActiveLight(NULL);
-       GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_DepthMask(true);
-       GL_DepthTest(true);
-       qglPolygonOffset(0, 0);
-       //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);
+       GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 }
 
@@ -1338,18 +1067,18 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        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))
+       if (BoxesOverlap(r_view.origin, r_view.origin, mins, maxs))
        {
-               GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
+               GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
                return false;
        }
 
        // 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;
+       VectorNegate(r_view.frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -r_view.frustum[0].dist;
+       VectorNegate(r_view.frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -r_view.frustum[1].dist;
+       VectorNegate(r_view.frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -r_view.frustum[2].dist;
+       VectorNegate(r_view.frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -r_view.frustum[3].dist;
+       VectorNegate(r_view.frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -r_view.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];
@@ -1374,6 +1103,7 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        // if that mesh is not empty, check what area of the screen it covers
        x1 = y1 = x2 = y2 = 0;
        v[3] = 1.0f;
+       //Con_Printf("%i vertices to transform...\n", mesh.numvertices);
        for (i = 0;i < mesh.numvertices;i++)
        {
                VectorCopy(mesh.vertex3f + i * 3, v);
@@ -1394,42 +1124,36 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
        }
 
        // 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;
+       ix1 = (int)(x1 - 1.0f);
+       iy1 = (int)(y1 - 1.0f);
+       ix2 = (int)(x2 + 1.0f);
+       iy2 = (int)(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 (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;
 
        // 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);
-       renderstats.lights_scissored++;
+       GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
+       //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
+       //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
+       r_refdef.stats.lights_scissored++;
        return false;
 }
 
-extern float *rsurface_vertex3f;
-extern float *rsurface_svector3f;
-extern float *rsurface_tvector3f;
-extern float *rsurface_normal3f;
-extern void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg);
-
 static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_t *surface, const float *diffusecolor, const float *ambientcolor)
 {
        int numverts = surface->num_vertices;
        float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
        float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
-       float *color4f = varray_color4f + 4 * 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)
        {
@@ -1443,9 +1167,9 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
                                color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
                                color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
-                               if (fogenabled)
+                               if (r_refdef.fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1476,9 +1200,9 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
                                }
-                               if (fogenabled)
+                               if (r_refdef.fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1510,9 +1234,9 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
                                }
-                               if (fogenabled)
+                               if (r_refdef.fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1524,193 +1248,107 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
 }
 
 // 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)
+static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int numsurfaces, msurface_t **surfacelist)
 {
-       do
+       int surfacelistindex;
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
-               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;
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               int i;
+               float *out3f = rsurface_array_texcoord3f + 3 * surface->num_firstvertex;
+               const float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
+               const float *svector3f = rsurface_svector3f + 3 * surface->num_firstvertex;
+               const float *tvector3f = rsurface_tvector3f + 3 * surface->num_firstvertex;
+               const float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
+               float lightdir[3];
+               for (i = 0;i < surface->num_vertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
+               {
+                       VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
+                       // the cubemap normalizes this for us
+                       out3f[0] = DotProduct(svector3f, lightdir);
+                       out3f[1] = DotProduct(tvector3f, lightdir);
+                       out3f[2] = DotProduct(normal3f, lightdir);
+               }
        }
-       while (--numverts);
 }
 
-static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
+static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int numsurfaces, msurface_t **surfacelist)
 {
-       do
+       int surfacelistindex;
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
-               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;
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               int i;
+               float *out3f = rsurface_array_texcoord3f + 3 * surface->num_firstvertex;
+               const float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
+               const float *svector3f = rsurface_svector3f + 3 * surface->num_firstvertex;
+               const float *tvector3f = rsurface_tvector3f + 3 * surface->num_firstvertex;
+               const float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
+               float lightdir[3], eyedir[3], halfdir[3];
+               for (i = 0;i < surface->num_vertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
+               {
+                       VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
+                       VectorNormalize(lightdir);
+                       VectorSubtract(rsurface_modelorg, vertex3f, eyedir);
+                       VectorNormalize(eyedir);
+                       VectorAdd(lightdir, eyedir, halfdir);
+                       // the cubemap normalizes this for us
+                       out3f[0] = DotProduct(svector3f, halfdir);
+                       out3f[1] = DotProduct(tvector3f, halfdir);
+                       out3f[2] = DotProduct(normal3f, halfdir);
+               }
        }
-       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)
+static void R_Shadow_RenderSurfacesLighting_VisibleLighting(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 i;
-       float lightdir[3];
-       for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
-       {
-               VectorSubtract(relativelightorigin, vertex3f, lightdir);
-               // the cubemap normalizes this for us
-               out3f[0] = DotProduct(svector3f, lightdir);
-               out3f[1] = DotProduct(tvector3f, lightdir);
-               out3f[2] = DotProduct(normal3f, lightdir);
-       }
+       // used to display how many times a surface is lit for level design purposes
+       GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
+       R_Mesh_ColorPointer(NULL);
+       R_Mesh_ResetTextureState();
+       RSurf_PrepareVerticesForBatch(false, false, numsurfaces, surfacelist);
+       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
+       GL_LockArrays(0, 0);
 }
 
-static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
+static void R_Shadow_RenderSurfacesLighting_Light_GLSL(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 i;
-       float lightdir[3], eyedir[3], halfdir[3];
-       for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
-       {
-               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);
-               out3f[1] = DotProduct(tvector3f, halfdir);
-               out3f[2] = DotProduct(normal3f, halfdir);
+       // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
+       RSurf_PrepareVerticesForBatch(true, true, numsurfaces, surfacelist);
+       R_SetupSurfaceShader(lightcolorbase, false);
+       R_Mesh_TexCoordPointer(0, 2, rsurface_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);
+       if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
+       {
+               qglDepthFunc(GL_EQUAL);CHECKGLERROR
        }
-}
-
-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)
-{
-       // used to display how many times a surface is lit for level design purposes
-       int surfacelistindex;
-       rmeshstate_t m;
-       qboolean doambientbase = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorbase) > 0.00001 && basetexture != r_texture_black;
-       qboolean dodiffusebase = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorbase) > 0.00001 && basetexture != r_texture_black;
-       qboolean doambientpants = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorpants) > 0.00001 && pantstexture != r_texture_black;
-       qboolean dodiffusepants = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorpants) > 0.00001 && pantstexture != r_texture_black;
-       qboolean doambientshirt = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorshirt) > 0.00001 && shirttexture != r_texture_black;
-       qboolean dodiffuseshirt = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorshirt) > 0.00001 && shirttexture != r_texture_black;
-       qboolean dospecular = specularscale * VectorLength2(lightcolorbase) > 0.00001 && glosstexture != r_texture_black;
-       if (!doambientbase && !dodiffusebase && !doambientpants && !dodiffusepants && !doambientshirt && !dodiffuseshirt && !dospecular)
-               return;
-       GL_Color(0.1, 0.025, 0, 1);
-       memset(&m, 0, sizeof(m));
-       R_Mesh_State(&m);
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
+       GL_LockArrays(0, 0);
+       if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin);
-               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle);
-               GL_LockArrays(0, 0);
+               qglDepthFunc(GL_LEQUAL);CHECKGLERROR
        }
 }
 
-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)
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(int numsurfaces, msurface_t **surfacelist, float r, float g, float b)
 {
-       // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       int surfacelistindex;
-       qboolean dobase = (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) > 0.00001 && basetexture != r_texture_black;
-       qboolean dopants = (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.00001 && pantstexture != r_texture_black;
-       qboolean doshirt = (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.00001 && shirttexture != r_texture_black;
-       qboolean dospecular = specularscale * VectorLength2(lightcolorbase) > 0.00001 && glosstexture != r_texture_black;
-       if (!dobase && !dopants && !doshirt && !dospecular)
-               return;
-       // select a permutation of the lighting shader appropriate to this
-       // combination of texture, entity, light source, and fogging, only use the
-       // minimum features necessary to avoid wasting rendering time in the
-       // fragment shader on features that are not being used
-       r_shadow_lightpermutation = 0;
-       // only add a feature to the permutation if that permutation exists
-       // (otherwise it might end up not using a shader at all, which looks
-       // worse than using less features)
-       if (fogenabled && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_FOG])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_FOG;
-       if ((dopants || doshirt) && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_COLORMAPPING])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_COLORMAPPING;
-       if (specularscale > 0 && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SPECULAR])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_SPECULAR;
-       if (r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_CUBEFILTER])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_CUBEFILTER;
-       if (r_shadow_glsl_offsetmapping.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_OFFSETMAPPING])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_OFFSETMAPPING;
-       if (r_shadow_glsl_surfacenormalize.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SURFACENORMALIZE])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_SURFACENORMALIZE;
-       if (r_shadow_glsl_usehalffloat.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_GEFORCEFX])
-               r_shadow_lightpermutation |= SHADERPERMUTATION_GEFORCEFX;
-       r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
-       qglUseProgramObjectARB(r_shadow_lightprog);CHECKGLERROR
-       R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
-       R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
-       R_Mesh_TexBind(0, R_GetTexture(normalmaptexture));
-       R_Mesh_TexBind(1, R_GetTexture(basetexture));
-       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);CHECKGLERROR
-       if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
-       {
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);CHECKGLERROR
-       }
-       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_COLORMAPPING)
-       {
-               R_Mesh_TexBind(5, R_GetTexture(pantstexture));
-               R_Mesh_TexBind(6, R_GetTexture(shirttexture));
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "Color_Pants"), ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);CHECKGLERROR
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "Color_Shirt"), ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);CHECKGLERROR
-       }
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_FOG)
-       {
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "FogRangeRecip"), fograngerecip);CHECKGLERROR
-       }
-       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "AmbientScale"), r_shadow_rtlight->ambientscale);CHECKGLERROR
-       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "DiffuseScale"), r_shadow_rtlight->diffusescale);CHECKGLERROR
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
-       {
-               R_Mesh_TexBind(2, R_GetTexture(glosstexture));
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularPower"), 8);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), specularscale);CHECKGLERROR
-       }
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_OFFSETMAPPING)
-       {
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
-       }
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       // shared final code for all the dot3 layers
+       int renders;
+       GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
+       for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
-               RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin);
-               if (!rsurface_svector3f)
-               {
-                       rsurface_svector3f = varray_svector3f;
-                       rsurface_tvector3f = varray_tvector3f;
-                       rsurface_normal3f = varray_normal3f;
-                       Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
-               }
-               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->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_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 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)
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
 {
-       int renders;
-       float color2[3];
        rmeshstate_t m;
-       const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
-       GL_Color(1,1,1,1);
        // colorscale accounts for how much we multiply the brightness
        // during combine.
        //
@@ -1718,84 +1356,52 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(const entity_
        // performed to get more brightness than otherwise possible.
        //
        // Limit mult to 64 for sanity sake.
+       GL_Color(1,1,1,1);
        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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(basetexture);
-               m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[1] = texture->currenttexmatrix;
+               m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[1] = rsurface_texture->currenttexmatrix;
                m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[2] = rsurface_vertex3f;
                m.texmatrix[2] = r_shadow_entitytolight;
-#else
-               m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                GL_BlendFunc(GL_ONE, GL_ONE);
        }
        else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
        {
                // 2 3D combine path (Geforce3, original Radeon)
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(basetexture);
-               m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[1] = texture->currenttexmatrix;
+               m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[1] = rsurface_texture->currenttexmatrix;
                GL_BlendFunc(GL_ONE, GL_ONE);
        }
        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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
                m.tex[2] = R_GetTexture(basetexture);
-               m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[2] = texture->currenttexmatrix;
+               m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[2] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[3] = rsurface_vertex3f;
                        m.texmatrix[3] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[3] = varray_texcoord3f[3];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_ONE, GL_ONE);
        }
@@ -1803,94 +1409,54 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(const entity_
        {
                // 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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
                m.tex[2] = R_GetTexture(basetexture);
-               m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[2] = texture->currenttexmatrix;
+               m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[2] = rsurface_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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#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(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);
+       R_Mesh_TextureState(&m);
+       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-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)
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
 {
-       int renders;
-       float color2[3];
        rmeshstate_t m;
-       const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
-       GL_Color(1,1,1,1);
        // colorscale accounts for how much we multiply the brightness
        // during combine.
        //
@@ -1898,49 +1464,39 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_
        // performed to get more brightness than otherwise possible.
        //
        // Limit mult to 64 for sanity sake.
+       GL_Color(1,1,1,1);
+       // generate normalization cubemap texcoords
+       R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(numsurfaces, surfacelist);
        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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 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.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
                m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[2] = rsurface_vertex3f;
                m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[2] = varray_texcoord3f[2];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
@@ -1948,53 +1504,39 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_
        {
                // 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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
@@ -2002,88 +1544,62 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_
        {
                // 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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#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 = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(normalmaptexture);
                m.texcombinergb[0] = GL_REPLACE;
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 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.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
                m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[2] = rsurface_vertex3f;
                m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[2] = varray_texcoord2f[2];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[3] = rsurface_vertex3f;
                m.texmatrix[3] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[3] = varray_texcoord2f[3];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[3] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
@@ -2091,152 +1607,105 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_
        {
                // 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);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // second pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(basetexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#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(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);
+       R_Mesh_TextureState(&m);
+       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-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)
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
 {
-       int renders;
-       float color2[3];
+       float glossexponent;
        rmeshstate_t m;
-       const int *elements = surface->groupmesh->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!
+       // generate normalization cubemap texcoords
+       R_Shadow_GenTexCoords_Specular_NormalCubeMap(numsurfaces, surfacelist);
+       if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
        {
                // 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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                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);
+               // second and third pass
+               R_Mesh_ResetTextureState();
                // 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);
+               for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // fourth pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[0] = varray_texcoord3f[0];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // fifth pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(glosstexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
@@ -2244,414 +1713,327 @@ static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(const entity
        {
                // 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] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                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);
+               // second and third pass
+               R_Mesh_ResetTextureState();
                // 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);
+               for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // fourth pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(glosstexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#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 = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(normalmaptexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
                m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
-               m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-               R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 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);
+               m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                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);
+               // second and third pass
+               R_Mesh_ResetTextureState();
                // 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);
+               for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+                       RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // fourth pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[0] = rsurface_vertex3f;
                m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.pointer_texcoord3f[1] = rsurface_vertex3f;
                m.texmatrix[1] = r_shadow_entitytoattenuationz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
-               R_Mesh_State(&m);
+               R_Mesh_TextureState(&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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
                GL_LockArrays(0, 0);
 
+               // fifth pass
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = rsurface_vertex3f;
                m.tex[0] = R_GetTexture(glosstexture);
-               m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
-               m.texmatrix[0] = texture->currenttexmatrix;
+               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+               m.texmatrix[0] = rsurface_texture->currenttexmatrix;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                {
                        m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
-#ifdef USETEXMATRIX
                        m.pointer_texcoord3f[1] = rsurface_vertex3f;
                        m.texmatrix[1] = r_shadow_entitytolight;
-#else
-                       m.pointer_texcoord3f[1] = varray_texcoord3f[1];
-                       R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
-#endif
                }
                GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
        }
-       R_Mesh_State(&m);
-       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
-       VectorScale(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);
+       // this final code is shared
+       R_Mesh_TextureState(&m);
+       R_Shadow_RenderSurfacesLighting_Light_Dot3_Finalize(numsurfaces, surfacelist, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-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)
+static void R_Shadow_RenderSurfacesLighting_Light_Dot3(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 doambientbase = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorbase) > 0.00001 && basetexture != r_texture_black;
-       qboolean dodiffusebase = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorbase) > 0.00001 && basetexture != r_texture_black;
-       qboolean doambientpants = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorpants) > 0.00001 && pantstexture != r_texture_black;
-       qboolean dodiffusepants = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorpants) > 0.00001 && pantstexture != r_texture_black;
-       qboolean doambientshirt = r_shadow_rtlight->ambientscale * VectorLength2(lightcolorshirt) > 0.00001 && shirttexture != r_texture_black;
-       qboolean dodiffuseshirt = r_shadow_rtlight->diffusescale * VectorLength2(lightcolorshirt) > 0.00001 && shirttexture != r_texture_black;
-       qboolean dospecular = specularscale * VectorLength2(lightcolorbase) > 0.00001 && glosstexture != r_texture_black;
-       if (!doambientbase && !dodiffusebase && !doambientpants && !dodiffusepants && !doambientshirt && !dodiffuseshirt && !dospecular)
+       qboolean doambient = r_shadow_rtlight->ambientscale > 0;
+       qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
+       qboolean dospecular = specularscale > 0;
+       if (!doambient && !dodiffuse && !dospecular)
                return;
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       RSurf_PrepareVerticesForBatch(true, true, numsurfaces, surfacelist);
+       R_Mesh_ColorPointer(NULL);
+       if (doambient)
+               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+       if (dodiffuse)
+               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+       if (dopants)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin);
-               if (!rsurface_svector3f)
-               {
-                       rsurface_svector3f = varray_svector3f;
-                       rsurface_tvector3f = varray_tvector3f;
-                       rsurface_normal3f = varray_normal3f;
-                       Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
-               }
-               if (doambientbase)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
-               if (doambientpants)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
-               if (doambientshirt)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
-               if (dodiffusebase)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
-               if (dodiffusepants)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
-               if (dodiffuseshirt)
-                       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);
+               if (doambient)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+               if (dodiffuse)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+       }
+       if (doshirt)
+       {
+               if (doambient)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+               if (dodiffuse)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
        }
+       if (dospecular)
+               R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(numsurfaces, surfacelist, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
 }
 
-void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const msurface_t *surface, vec3_t diffusecolor2, vec3_t ambientcolor2)
+void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const model_t *model, int numsurfaces, msurface_t **surfacelist, vec3_t diffusecolor2, vec3_t ambientcolor2)
 {
+       int surfacelistindex;
        int renders;
-       const int *elements = surface->groupmesh->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++)
+       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       {
+               const msurface_t *surface = surfacelist[surfacelistindex];
+               R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(surface, diffusecolor2, ambientcolor2);
+       }
+       for (renders = 0;renders < 64;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 stop;
+               int firstvertex;
+               int lastvertex;
                int newnumtriangles;
                int *newe;
                int newelements[3072];
-               draw = false;
+               stop = true;
+               firstvertex = 0;
+               lastvertex = 0;
                newnumtriangles = 0;
                newe = newelements;
-               for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
-               {
-                       if (newnumtriangles >= 1024)
-                       {
-                               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 (VectorLength2(varray_color4f + e[0] * 4) + VectorLength2(varray_color4f + e[1] * 4) + VectorLength2(varray_color4f + e[2] * 4) >= 0.01)
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+               {
+                       const msurface_t *surface = surfacelist[surfacelistindex];
+                       const int *elements = rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3;
+                       int i;
+                       // 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
+                       // this builds batches of triangles from multiple surfaces and
+                       // renders them at once
+                       for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
                        {
-                               newe[0] = e[0];
-                               newe[1] = e[1];
-                               newe[2] = e[2];
-                               newnumtriangles++;
-                               newe += 3;
-                               draw = true;
+                               if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
+                               {
+                                       if (newnumtriangles)
+                                       {
+                                               firstvertex = min(firstvertex, e[0]);
+                                               lastvertex = max(lastvertex, e[0]);
+                                       }
+                                       else
+                                       {
+                                               firstvertex = e[0];
+                                               lastvertex = e[0];
+                                       }
+                                       firstvertex = min(firstvertex, e[1]);
+                                       lastvertex = max(lastvertex, e[1]);
+                                       firstvertex = min(firstvertex, e[2]);
+                                       lastvertex = max(lastvertex, e[2]);
+                                       newe[0] = e[0];
+                                       newe[1] = e[1];
+                                       newe[2] = e[2];
+                                       newnumtriangles++;
+                                       newe += 3;
+                                       if (newnumtriangles >= 1024)
+                                       {
+                                               GL_LockArrays(firstvertex, lastvertex - firstvertex + 1);
+                                               R_Mesh_Draw(firstvertex, lastvertex - firstvertex + 1, newnumtriangles, newelements);
+                                               newnumtriangles = 0;
+                                               newe = newelements;
+                                               stop = false;
+                                       }
+                               }
                        }
                }
                if (newnumtriangles >= 1)
                {
-                       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;
+                       GL_LockArrays(firstvertex, lastvertex - firstvertex + 1);
+                       R_Mesh_Draw(firstvertex, lastvertex - firstvertex + 1, newnumtriangles, newelements);
+                       stop = false;
                }
-               if (!draw)
-                       break;
-#else
-               for (i = 0, c = varray_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
+               // if we couldn't find any lit triangles, exit early
+               if (stop)
+                       break;
                // now reduce the intensity for the next overbright pass
-               for (i = 0, c = varray_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);
+               // we have to clamp to 0 here incase the drivers have improper
+               // handling of negative colors
+               // (some old drivers even have improper handling of >1 color)
+               stop = true;
+               for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+               {
+                       int i;
+                       float *c;
+                       const msurface_t *surface = surfacelist[surfacelistindex];
+                       for (i = 0, c = rsurface_array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+                       {
+                               if (c[0] > 1 || c[1] > 1 || c[2] > 1)
+                               {
+                                       c[0] = max(0, c[0] - 1);
+                                       c[1] = max(0, c[1] - 1);
+                                       c[2] = max(0, c[2] - 1);
+                                       stop = false;
+                               }
+                               else
+                                       VectorClear(c);
+                       }
                }
+               // another check...
+               if (stop)
+                       break;
        }
 }
 
-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)
+static void R_Shadow_RenderSurfacesLighting_Light_Vertex(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;
+       // OpenGL 1.1 path (anything)
+       model_t *model = rsurface_entity->model;
        float ambientcolorbase[3], diffusecolorbase[3];
        float ambientcolorpants[3], diffusecolorpants[3];
        float ambientcolorshirt[3], diffusecolorshirt[3];
        rmeshstate_t m;
-       qboolean dobase  = basetexture != r_texture_black;
-       qboolean dopants = pantstexture != r_texture_black;
-       qboolean doshirt = shirttexture != r_texture_black;
-       if (!dobase && !dopants && !doshirt)
-               return;
-       if (dobase)
-       {
-               VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2, ambientcolorbase);
-               VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2, diffusecolorbase);
-       }
-       if (dopants)
-       {
-               VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2, ambientcolorpants);
-               VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2, diffusecolorpants);
-       }
-       if (doshirt)
-       {
-               VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
-               VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, diffusecolorshirt);
-       }
+       VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorbase);
+       VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorbase);
+       VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorpants);
+       VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorpants);
+       VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
+       VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+       R_Mesh_ColorPointer(rsurface_array_color4f);
        memset(&m, 0, sizeof(m));
        m.tex[0] = R_GetTexture(basetexture);
+       m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+       m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
        if (r_textureunits.integer >= 2)
        {
-               // voodoo2
+               // voodoo2 or TNT
                m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
-#else
-               m.pointer_texcoord[1] = varray_texcoord2f[1];
-               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
+               m.pointer_texcoord3f[1] = rsurface_vertex3f;
                if (r_textureunits.integer >= 3)
                {
-                       // Geforce3/Radeon class but not using dot3
+                       // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
                        m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
-#ifdef USETEXMATRIX
                        m.texmatrix[2] = r_shadow_entitytoattenuationz;
-#else
-                       m.pointer_texcoord[2] = varray_texcoord2f[2];
-                       R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
+                       m.pointer_texcoord3f[2] = rsurface_vertex3f;
                }
        }
-       m.pointer_color = varray_color4f;
-       R_Mesh_State(&m);
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       R_Mesh_TextureState(&m);
+       RSurf_PrepareVerticesForBatch(true, false, numsurfaces, surfacelist);
+       R_Mesh_TexBind(0, R_GetTexture(basetexture));
+       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorbase, ambientcolorbase);
+       if (dopants)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin);
-               if (!rsurface_svector3f)
-               {
-                       rsurface_svector3f = varray_svector3f;
-                       rsurface_tvector3f = varray_tvector3f;
-                       rsurface_normal3f = varray_normal3f;
-                       Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
-               }
-               // OpenGL 1.1 path (anything)
-               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-               R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
-               if (r_textureunits.integer >= 2)
-               {
-                       // voodoo2 or TNT
-#ifdef USETEXMATRIX
-                       R_Mesh_TexCoordPointer(1, 3, rsurface_vertex3f);
-#else
-                       R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
-#endif
-                       if (r_textureunits.integer >= 3)
-                       {
-                               // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
-#ifdef USETEXMATRIX
-                               R_Mesh_TexCoordPointer(2, 3, rsurface_vertex3f);
-#else
-                               R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
-#endif
-                       }
-               }
-               if (dobase)
-               {
-                       R_Mesh_TexBind(0, R_GetTexture(basetexture));
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorbase, ambientcolorbase);
-               }
-               if (dopants)
-               {
-                       R_Mesh_TexBind(0, R_GetTexture(pantstexture));
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorpants, ambientcolorpants);
-               }
-               if (doshirt)
-               {
-                       R_Mesh_TexBind(0, R_GetTexture(shirttexture));
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorshirt, ambientcolorshirt);
-               }
+               R_Mesh_TexBind(0, R_GetTexture(pantstexture));
+               R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorpants, ambientcolorpants);
+       }
+       if (doshirt)
+       {
+               R_Mesh_TexBind(0, R_GetTexture(shirttexture));
+               R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(model, numsurfaces, surfacelist, diffusecolorshirt, ambientcolorshirt);
        }
 }
 
-void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist)
+void R_Shadow_RenderSurfacesLighting(int numsurfaces, msurface_t **surfacelist)
 {
        // FIXME: support MATERIALFLAG_NODEPTHTEST
        vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
-       rtexture_t *basetexture;
-       rtexture_t *glosstexture;
-       float specularscale;
-       glosstexture = r_texture_black;
-       specularscale = 0;
-       if (r_shadow_gloss.integer > 0)
-       {
-               if (texture->skin.gloss)
+       // calculate colors to render this texture with
+       lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * rsurface_entity->colormod[0] * rsurface_texture->currentalpha;
+       lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * rsurface_entity->colormod[1] * rsurface_texture->currentalpha;
+       lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * rsurface_entity->colormod[2] * rsurface_texture->currentalpha;
+       if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * rsurface_texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
+               return;
+       GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
+       GL_CullFace(((rsurface_texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (rsurface_entity->flags & RENDER_NOCULLFACE)) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
+       if (rsurface_texture->colormapping)
+       {
+               qboolean dopants = rsurface_texture->currentskinframe->pants != NULL && VectorLength2(rsurface_entity->colormap_pantscolor) >= (1.0f / 1048576.0f);
+               qboolean doshirt = rsurface_texture->currentskinframe->shirt != NULL && VectorLength2(rsurface_entity->colormap_shirtcolor) >= (1.0f / 1048576.0f);
+               if (dopants)
                {
-                       if (r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
-                       {
-                               glosstexture = texture->skin.gloss;
-                               specularscale = r_shadow_rtlight->specularscale * r_shadow_glossintensity.value;
-                       }
+                       lightcolorpants[0] = lightcolorbase[0] * rsurface_entity->colormap_pantscolor[0];
+                       lightcolorpants[1] = lightcolorbase[1] * rsurface_entity->colormap_pantscolor[1];
+                       lightcolorpants[2] = lightcolorbase[2] * rsurface_entity->colormap_pantscolor[2];
                }
                else
+                       VectorClear(lightcolorpants);
+               if (doshirt)
                {
-                       if (r_shadow_gloss.integer >= 2 && r_shadow_gloss2intensity.value > 0 && r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
-                       {
-                               glosstexture = r_texture_white;
-                               specularscale = r_shadow_rtlight->specularscale * r_shadow_gloss2intensity.value;
-                       }
+                       lightcolorshirt[0] = lightcolorbase[0] * rsurface_entity->colormap_shirtcolor[0];
+                       lightcolorshirt[1] = lightcolorbase[1] * rsurface_entity->colormap_shirtcolor[1];
+                       lightcolorshirt[2] = lightcolorbase[2] * rsurface_entity->colormap_shirtcolor[2];
                }
-       }
-       // 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) + 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);
-       if ((VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor)) >= (1.0f / 1048576.0f))
-       {
-               lightcolorpants[0] = r_shadow_rtlight->currentcolor[0] * ent->colormap_pantscolor[0] * texture->currentalpha;
-               lightcolorpants[1] = r_shadow_rtlight->currentcolor[1] * ent->colormap_pantscolor[1] * texture->currentalpha;
-               lightcolorpants[2] = r_shadow_rtlight->currentcolor[2] * ent->colormap_pantscolor[2] * texture->currentalpha;
-               lightcolorshirt[0] = r_shadow_rtlight->currentcolor[0] * ent->colormap_shirtcolor[0] * texture->currentalpha;
-               lightcolorshirt[1] = r_shadow_rtlight->currentcolor[1] * ent->colormap_shirtcolor[1] * texture->currentalpha;
-               lightcolorshirt[2] = r_shadow_rtlight->currentcolor[2] * ent->colormap_shirtcolor[2] * texture->currentalpha;
-               basetexture = texture->skin.base;
+               else
+                       VectorClear(lightcolorshirt);
                switch (r_shadow_rendermode)
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
-                       R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale);
+                       GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
@@ -2660,20 +2042,20 @@ void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t
        }
        else
        {
-               basetexture = texture->skin.merged ? texture->skin.merged : texture->skin.base;
                switch (r_shadow_rendermode)
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
-                       R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale);
+                       GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_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, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_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, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_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, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
@@ -2684,8 +2066,7 @@ void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t
 
 void R_RTLight_Update(dlight_t *light, int isstatic)
 {
-       int j, k;
-       float scale;
+       double scale;
        rtlight_t *rtlight = &light->rtlight;
        R_RTLight_Uncompile(rtlight);
        memset(rtlight, 0, sizeof(*rtlight));
@@ -2703,7 +2084,7 @@ void R_RTLight_Update(dlight_t *light, int isstatic)
        rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
        rtlight->cubemapname[0] = 0;
        if (light->cubemapname[0])
-               strcpy(rtlight->cubemapname, light->cubemapname);
+               strlcpy(rtlight->cubemapname, light->cubemapname, sizeof(rtlight->cubemapname));
        else if (light->cubemapnum > 0)
                sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
        rtlight->shadow = light->shadow;
@@ -2716,17 +2097,11 @@ void R_RTLight_Update(dlight_t *light, int isstatic)
        rtlight->specularscale = light->specularscale;
        rtlight->flags = light->flags;
        Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
-       // ConcatScale won't work here because this needs to scale rotate and
-       // translate, not just rotate
-       scale = 1.0f / rtlight->radius;
-       for (k = 0;k < 3;k++)
-               for (j = 0;j < 4;j++)
-                       rtlight->matrix_worldtolight.m[k][j] *= scale;
-
-       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 ? r_refdef.lightstylevalue[rtlight->style] : 128) * 0.125f, rtlight->lightmap_light);
-       rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
+       // this has to scale both rotate and translate because this is an already
+       // inverted matrix (it transforms from world to light space, not the other
+       // way around)
+       scale = 1.0 / rtlight->radius;
+       Matrix4x4_Scale(&rtlight->matrix_worldtolight, scale, scale);
 }
 
 // compiles rtlight geometry
@@ -2760,7 +2135,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                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 = (unsigned char *)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 = (int *)data;data += sizeof(int) * numleafs;
@@ -2774,7 +2149,7 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                if (numsurfaces)
                        memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
                if (model->CompileShadowVolume && rtlight->shadow)
-                       model->CompileShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
+                       model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
                // now we're done compiling the rtlight
                r_shadow_compilingrtlight = NULL;
        }
@@ -2796,7 +2171,8 @@ void R_RTLight_Compile(rtlight_t *rtlight)
                }
        }
 
-       Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes);
+       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)
@@ -2828,6 +2204,7 @@ void R_Shadow_UncompileWorldLights(void)
 
 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)
@@ -2836,29 +2213,31 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfa
                {
                        shadowmesh_t *mesh;
                        R_Mesh_Matrix(&ent->matrix);
+                       CHECKGLERROR
                        for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
                        {
-                               renderstats.lights_shadowtriangles += mesh->numtriangles;
+                               r_refdef.stats.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);
+                                       GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
+                                       qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
                                        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);
+                                       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+                                       qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
                                }
                                R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
                                GL_LockArrays(0, 0);
                        }
+                       CHECKGLERROR
                }
                else if (numsurfaces)
                {
                        R_Mesh_Matrix(&ent->matrix);
-                       ent->model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
+                       model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs);
                }
        }
        else
@@ -2872,28 +2251,32 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfa
                relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
                relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
                R_Mesh_Matrix(&ent->matrix);
-               ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+               model->DrawShadowVolume(ent, relativeshadoworigin, NULL, 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
+       RSurf_ActiveEntity(ent, true, true);
        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);
+       if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
+               R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
 }
 
 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)
-               ent->model->DrawLight(ent, numsurfaces, surfacelist);
+               model->DrawLight(ent, numsurfaces, surfacelist);
        else
-               ent->model->DrawLight(ent, ent->model->nummodelsurfaces, ent->model->surfacelist);
+               model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist);
 }
 
 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
@@ -2941,6 +2324,9 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
                return;
 
+       VectorCopy(rtlight->cullmins, r_shadow_rtlight_cullmins);
+       VectorCopy(rtlight->cullmaxs, r_shadow_rtlight_cullmaxs);
+
        if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
        {
                // compiled light, world available and can receive realtime lighting
@@ -2956,12 +2342,12 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
                // dynamic light, world available and can receive realtime lighting
                // calculate lit surfaces and leafs
                R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
-               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+               r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, r_shadow_rtlight_cullmins, r_shadow_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))
+               if (R_CullBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
                        return;
        }
        else
@@ -2977,13 +2363,13 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        if (numleafs)
        {
                for (i = 0;i < numleafs;i++)
-                       if (r_worldleafvisible[leaflist[i]])
+                       if (r_viewcache.world_leafvisible[leaflist[i]])
                                break;
                if (i == numleafs)
                        return;
        }
        // set up a scissor rectangle for this light
-       if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
+       if (R_Shadow_ScissorForBBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
                return;
 
        // make a list of lit entities and shadow casting entities
@@ -3000,16 +2386,19 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        {
                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)
-                        && ent->model
+                       if (BoxesOverlap(ent->mins, ent->maxs, r_shadow_rtlight_cullmins, r_shadow_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)))
                        {
                                // about the VectorDistance2 - light emitting entities should not cast their own shadow
-                               if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
+                               vec3_t org;
+                               Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+                               if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
                                        shadowentities[numshadowentities++] = ent;
-                               if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
+                               if (r_viewcache.entityvisible[i] && (ent->flags & RENDER_LIGHT) && model->DrawLight)
                                        lightentities[numlightentities++] = ent;
                        }
                }
@@ -3019,46 +2408,53 @@ void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
        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++;
+       r_refdef.stats.lights++;
 
-       // draw stencil shadow volumes to mask off pixels that are in shadow
-       // so that they won't receive lighting
        usestencil = false;
-       if (numshadowentities && (!visible || r_shadow_visiblelighting.integer == 1) && gl_stencil && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
+       if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
        {
-               usestencil = true;
-               R_Shadow_RenderMode_StencilShadowVolumes();
-               for (i = 0;i < numshadowentities;i++)
-                       R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
+               // draw stencil shadow volumes to mask off pixels that are in shadow
+               // so that they won't receive lighting
+               if (gl_stencil)
+               {
+                       usestencil = true;
+                       R_Shadow_RenderMode_StencilShadowVolumes();
+                       for (i = 0;i < numshadowentities;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
+               }
+
+               // optionally draw visible shape of the shadow volumes
+               // for performance analysis by level designers
+               if (r_showshadowvolumes.integer)
+               {
+                       R_Shadow_RenderMode_VisibleShadowVolumes();
+                       for (i = 0;i < numshadowentities;i++)
+                               R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
+               }
        }
 
-       // draw lighting in the unmasked areas
-       if (numlightentities && !visible)
+       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 visible shape of the shadow volumes
-       // for performance analysis by level designers
-       if (numshadowentities && visible && r_shadow_visiblevolumes.integer > 0 && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
-       {
-               R_Shadow_RenderMode_VisibleShadowVolumes();
-               for (i = 0;i < numshadowentities;i++)
-                       R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
-       }
-
-       // optionally draw the illuminated areas
-       // for performance analysis by level designers
-       if (numlightentities && visible && r_shadow_visiblelighting.integer > 0)
-       {
-               R_Shadow_RenderMode_VisibleLighting(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)
+               {
+                       R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
+                       for (i = 0;i < numlightentities;i++)
+                               R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
+               }
        }
 }
 
@@ -3072,7 +2468,7 @@ void R_ShadowVolumeLighting(qboolean visible)
 
        R_Shadow_RenderMode_Begin();
 
-       flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
+       flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
        if (r_shadow_debuglight.integer >= 0)
        {
                for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
@@ -3083,13 +2479,101 @@ void R_ShadowVolumeLighting(qboolean visible)
                for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
                        if (light->flags & flag)
                                R_DrawRTLight(&light->rtlight, visible);
-       if (r_rtdlight)
+       if (r_refdef.rtdlight)
                for (lnum = 0;lnum < r_refdef.numlights;lnum++)
                        R_DrawRTLight(&r_refdef.lights[lnum]->rtlight, visible);
 
        R_Shadow_RenderMode_End();
 }
 
+extern void R_SetupView(const matrix4x4_t *matrix);
+extern cvar_t r_shadows_throwdistance;
+void R_DrawModelShadows(void)
+{
+       int i;
+       float relativethrowdistance;
+       entity_render_t *ent;
+       vec3_t relativelightorigin;
+       vec3_t relativelightdirection;
+       vec3_t relativeshadowmins, relativeshadowmaxs;
+       float vertex3f[12];
+
+       if (!r_drawentities.integer || !gl_stencil)
+               return;
+
+       CHECKGLERROR
+       GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
+
+       r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
+
+       if (gl_ext_separatestencil.integer)
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
+       else if (gl_ext_stenciltwoside.integer)
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
+       else
+               r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
+
+       R_Shadow_RenderMode_StencilShadowVolumes();
+
+       for (i = 0;i < r_refdef.numentities;i++)
+       {
+               ent = r_refdef.entities[i];
+               // cast shadows from anything that is not a submodel of the map
+               if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
+               {
+                       relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
+                       VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
+                       VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
+                       VectorNegate(ent->modellight_lightdir, relativelightdirection);
+                       VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
+                       R_Mesh_Matrix(&ent->matrix);
+                       ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+               }
+       }
+
+       // not really the right mode, but this will disable any silly stencil features
+       R_Shadow_RenderMode_VisibleLighting(true, true);
+
+       // vertex coordinates for a quad that covers the screen exactly
+       vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
+       vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
+       vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
+       vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+
+       // set up ortho view for rendering this pass
+       GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
+       GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
+       GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+       GL_ScissorTest(true);
+       R_Mesh_Matrix(&identitymatrix);
+       R_Mesh_ResetTextureState();
+       R_Mesh_VertexPointer(vertex3f);
+       R_Mesh_ColorPointer(NULL);
+
+       // set up a 50% darkening blend on shadowed areas
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       GL_DepthTest(false);
+       GL_DepthMask(false);
+       qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
+       GL_Color(0, 0, 0, 0.5);
+       GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
+       qglDepthFunc(GL_ALWAYS);CHECKGLERROR
+       qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+       qglStencilMask(~0);CHECKGLERROR
+       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
+       qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
+
+       // apply the blend to the shadowed areas
+       R_Mesh_Draw(0, 4, 2, polygonelements);
+
+       // restoring the perspective view is done by R_RenderScene
+       //R_SetupView(&r_view.matrix);
+
+       // restore other state to normal
+       R_Shadow_RenderMode_End();
+}
+
+
 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
 typedef struct suffixinfo_s
 {
@@ -3197,7 +2681,7 @@ rtexture_t *R_Shadow_Cubemap(const char *basename)
        if (i >= MAX_CUBEMAPS)
                return r_texture_whitecube;
        numcubemaps++;
-       strcpy(cubemaps[i].basename, basename);
+       strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
        cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
        if (!cubemaps[i].texture)
                cubemaps[i].texture = r_texture_whitecube;
@@ -3213,7 +2697,7 @@ void R_Shadow_FreeCubemaps(void)
 dlight_t *R_Shadow_NewWorldLight(void)
 {
        dlight_t *light;
-       light = (dlight_t *)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;
@@ -3278,14 +2762,17 @@ void R_Shadow_SelectLight(dlight_t *light)
                r_shadow_selectedlight->selected = true;
 }
 
-void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
+void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
+       // this is never batched (there can be only one)
        float scale = r_editlights_cursorgrid.value * 0.5f;
-       R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], NULL, 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_view.right, r_view.up, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
 }
 
-void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
+void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
 {
+       // this is never batched (due to the ent parameter changing every time)
+       // so numsurfaces == 1 and surfacelist[0] == lightnumber
        float intensity;
        const dlight_t *light = (dlight_t *)ent;
        intensity = 0.5;
@@ -3293,24 +2780,16 @@ void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, in
                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[surfacenumber], NULL, 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[surfacelist[0]]->tex, NULL, false, light->origin, r_view.right, r_view.up, 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", 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_DrawLightSprite_TransparentCallback, (entity_render_t *)light, i % 5, &light->rtlight);
+               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);
 }
 
@@ -3322,12 +2801,12 @@ void R_Shadow_SelectLightInView(void)
        bestrating = 0;
        for (light = r_shadow_worldlightchain;light;light = light->next)
        {
-               VectorSubtract(light->origin, r_vieworigin, temp);
-               rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
+               VectorSubtract(light->origin, r_view.origin, temp);
+               rating = (DotProduct(temp, r_view.forward) / sqrt(DotProduct(temp, temp)));
                if (rating >= 0.95)
                {
                        rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
-                       if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_vieworigin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
+                       if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_view.origin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
                        {
                                bestrating = rating;
                                best = light;
@@ -3407,8 +2886,10 @@ void R_Shadow_LoadWorldLights(void)
                        // remove quotes on cubemapname
                        if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
                        {
-                               cubemapname[strlen(cubemapname)-1] = 0;
-                               strcpy(cubemapname, cubemapname + 1);
+                               size_t namelen;
+                               namelen = strlen(cubemapname) - 2;
+                               memmove(cubemapname, cubemapname + 1, namelen);
+                               cubemapname[namelen] = '\0';
                        }
                        if (a < 8)
                        {
@@ -3552,7 +3033,7 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                data = r_refdef.worldmodel->brush.entities;
        if (!data)
                return;
-       for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
+       for (entnum = 0;COM_ParseTokenConsole(&data) && com_token[0] == '{';entnum++)
        {
                type = LIGHTTYPE_MINUSX;
                origin[0] = origin[1] = origin[2] = 0;
@@ -3570,19 +3051,19 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
                islight = false;
                while (1)
                {
-                       if (!COM_ParseToken(&data, false))
+                       if (!COM_ParseTokenConsole(&data))
                                break; // error
                        if (com_token[0] == '}')
                                break; // end of entity
                        if (com_token[0] == '_')
-                               strcpy(key, com_token + 1);
+                               strlcpy(key, com_token + 1, sizeof(key));
                        else
-                               strcpy(key, com_token);
+                               strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
-                       if (!COM_ParseToken(&data, false))
+                       if (!COM_ParseTokenConsole(&data))
                                break; // error
-                       strcpy(value, com_token);
+                       strlcpy(value, com_token, sizeof(value));
 
                        // now that we have the key pair worked out...
                        if (!strcmp("light", key))
@@ -3753,8 +3234,8 @@ void R_Shadow_SetCursorLocationForView(void)
        vec_t dist, push;
        vec3_t dest, endpos;
        trace_t trace;
-       VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
-       trace = CL_TraceBox(r_vieworigin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
+       VectorMA(r_view.origin, r_editlights_cursordistance.value, r_view.forward, dest);
+       trace = CL_TraceBox(r_view.origin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
        if (trace.fraction < 1)
        {
                dist = trace.fraction * r_editlights_cursordistance.value;
@@ -3762,7 +3243,7 @@ void R_Shadow_SetCursorLocationForView(void)
                if (push > dist)
                        push = dist;
                push = -push;
-               VectorMA(trace.endpos, push, r_viewforward, endpos);
+               VectorMA(trace.endpos, push, r_view.forward, endpos);
                VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
        }
        else
@@ -4057,7 +3538,7 @@ void R_Shadow_EditLights_Edit_f(void)
                        return;
                }
                if (Cmd_Argc() == 3)
-                       strcpy(cubemapname, Cmd_Argv(2));
+                       strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
                else
                        cubemapname[0] = 0;
        }
@@ -4327,7 +3808,7 @@ void R_Shadow_EditLights_CopyInfo_f(void)
        r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
        r_shadow_bufferlight.style = r_shadow_selectedlight->style;
        if (r_shadow_selectedlight->cubemapname)
-               strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
+               strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
        else
                r_shadow_bufferlight.cubemapname[0] = 0;
        r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;