]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - r_shadow.c
redesigned skeletal model loading and rendering to use matrix-palette animation ...
[xonotic/darkplaces.git] / r_shadow.c
index 52351379f7a15c0f01c5048a771d28cb0be78c64..e1a736e4e317e3af85658085aa4be9b76daedbac 100644 (file)
@@ -159,11 +159,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;
@@ -267,8 +268,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;
@@ -294,10 +297,13 @@ void r_shadow_shutdown(void)
        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);
@@ -405,10 +411,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;
@@ -447,17 +454,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)
@@ -471,8 +485,8 @@ static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
                if (r_shadow_buffer_leaflist)
                        Mem_Free(r_shadow_buffer_leaflist);
                r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
-               r_shadow_buffer_leafpvs = (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)
        {
@@ -481,8 +495,8 @@ static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
                if (r_shadow_buffer_surfacelist)
                        Mem_Free(r_shadow_buffer_surfacelist);
                r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
-               r_shadow_buffer_surfacepvs = (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));
        }
 }
 
@@ -496,8 +510,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++;
@@ -524,8 +538,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++;
@@ -642,11 +656,11 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f,
        if (!numverts || !nummarktris)
                return;
        // make sure shadowelements is big enough for this volume
-       if (maxshadowelements < nummarktris * 24)
-               R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
-       tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
+       if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
+               R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
+       tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdistance, nummarktris, marktris);
        renderstats.lights_dynamicshadowtriangles += tris;
-       R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
+       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)
@@ -688,30 +702,29 @@ void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *inv
 
 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);
+       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);
+               qglCullFace(GL_BACK);CHECKGLERROR // 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);
+               qglCullFace(GL_FRONT);CHECKGLERROR // 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)
@@ -736,7 +749,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;
@@ -758,7 +771,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;
@@ -782,8 +795,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
@@ -797,8 +808,6 @@ matrix4x4_t r_shadow_entitytoattenuationz;
 
 void R_Shadow_RenderMode_Begin(void)
 {
-       rmeshstate_t m;
-
        R_Shadow_ValidateCvars();
 
        if (!r_shadow_attenuation2dtexture
@@ -807,14 +816,15 @@ 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_Color(0, 0, 0, 1);
-       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-       qglEnable(GL_CULL_FACE);
+       qglCullFace(GL_FRONT);CHECKGLERROR // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);CHECKGLERROR
        GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
 
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
@@ -839,60 +849,51 @@ 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();
 }
 
 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);
-       if (!r_showtrispass)
-               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_shadowpolygonfactor, r_shadowpolygonoffset);CHECKGLERROR
+       qglDepthFunc(GL_LESS);CHECKGLERROR
+       qglCullFace(GL_FRONT);CHECKGLERROR // quake is backwards, this culls back faces
+       qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+       qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
        r_shadow_rendermode = r_shadow_shadowingrendermode;
        if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
        {
-               qglDisable(GL_CULL_FACE);
-               qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
-               qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
-               qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
-               qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
-               qglStencilMask(~0);
-               qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+               qglDisable(GL_CULL_FACE);CHECKGLERROR
+               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
        }
        else
        {
-               qglEnable(GL_CULL_FACE);
-               qglStencilMask(~0);
+               qglEnable(GL_CULL_FACE);CHECKGLERROR
+               qglStencilMask(~0);CHECKGLERROR
                // this is changed by every shadow render so its value here is unimportant
-               qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+               qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
        }
        GL_Clear(GL_STENCIL_BUFFER_BIT);
        renderstats.lights_clears++;
@@ -900,39 +901,42 @@ void R_Shadow_RenderMode_StencilShadowVolumes(void)
 
 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
-       GL_BlendFunc(GL_ONE, GL_ONE);
+       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        GL_DepthMask(false);
        GL_DepthTest(true);
-       if (!r_showtrispass)
-               qglPolygonOffset(0, 0);
-       //qglDisable(GL_POLYGON_OFFSET_FILL);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);CHECKGLERROR
+       //qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
        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);
+       {
+               qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+       }
        else
-               qglDepthFunc(GL_EQUAL);
-       qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
-       qglEnable(GL_CULL_FACE);
+       {
+               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+       }
+       qglCullFace(GL_FRONT);CHECKGLERROR // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);CHECKGLERROR
        if (stenciltest)
-               qglEnable(GL_STENCIL_TEST);
+       {
+               qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+       }
        else
-               qglDisable(GL_STENCIL_TEST);
-       qglStencilMask(~0);
-       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+       {
+               qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+       }
+       qglStencilMask(~0);CHECKGLERROR
+       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);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);
+       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
@@ -940,8 +944,11 @@ 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_BlendFunc(GL_SRC_ALPHA, GL_ONE);
                GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
                CHECKGLERROR
        }
@@ -949,66 +956,76 @@ void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
 
 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
 {
+       CHECKGLERROR
        R_Shadow_RenderMode_Reset();
        GL_BlendFunc(GL_ONE, GL_ONE);
        GL_DepthMask(false);
        GL_DepthTest(!r_showdisabledepthtest.integer);
-       if (!r_showtrispass)
-               qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);CHECKGLERROR
        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);
+       qglDepthFunc(GL_GEQUAL);CHECKGLERROR
+       qglCullFace(GL_FRONT);CHECKGLERROR // this culls back
+       qglDisable(GL_CULL_FACE);CHECKGLERROR
+       qglDisable(GL_STENCIL_TEST);CHECKGLERROR
        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_showdisabledepthtest.integer);
-       if (!r_showtrispass)
-               qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);CHECKGLERROR
        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);
+       {
+               qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+       }
        else
-               qglDepthFunc(GL_EQUAL);
-       qglCullFace(GL_FRONT); // this culls back
-       qglEnable(GL_CULL_FACE);
+       {
+               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+       }
+       qglCullFace(GL_FRONT);CHECKGLERROR // this culls back
+       qglEnable(GL_CULL_FACE);CHECKGLERROR
        if (stenciltest)
-               qglEnable(GL_STENCIL_TEST);
+       {
+               qglEnable(GL_STENCIL_TEST);CHECKGLERROR
+       }
        else
-               qglDisable(GL_STENCIL_TEST);
+       {
+               qglDisable(GL_STENCIL_TEST);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);
-       if (!r_showtrispass)
-               qglPolygonOffset(0, 0);
-       //qglDisable(GL_POLYGON_OFFSET_FILL);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);CHECKGLERROR
+       //qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
        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);
+       qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+       qglCullFace(GL_FRONT);CHECKGLERROR // quake is backwards, this culls back faces
+       qglEnable(GL_CULL_FACE);CHECKGLERROR
+       qglDisable(GL_STENCIL_TEST);CHECKGLERROR
+       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
        if (gl_support_stenciltwoside)
-               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
-       qglStencilMask(~0);
-       qglStencilFunc(GL_ALWAYS, 128, ~0);
+       {
+               qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
+       }
+       qglStencilMask(~0);CHECKGLERROR
+       qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
        r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
 }
 
@@ -1078,10 +1095,10 @@ 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
@@ -1096,24 +1113,18 @@ qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
 
        // 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);
+       //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
+       //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
        renderstats.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, qboolean generatenormals, qboolean generatetangents);
-
 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)
        {
@@ -1129,7 +1140,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
                                if (fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1162,7 +1173,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                }
                                if (fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1196,7 +1207,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_
                                }
                                if (fogenabled)
                                {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
+                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                        }
@@ -1208,114 +1219,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, 0.025, 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)
+       // 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)
        {
-               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);
+               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, qboolean dopants, qboolean doshirt)
-{
-       // used to display how many times a surface is lit for level design purposes
-       int surfacelistindex;
-       rmeshstate_t m;
-       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, false, false);
-               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, qboolean dopants, qboolean doshirt)
+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;
-       R_SetupSurfaceShader(ent, texture, lightcolorbase, lightcolorpants, lightcolorshirt, basetexture, pantstexture, shirttexture, normalmaptexture, glosstexture, specularscale, dopants, doshirt);
-       for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+       // shared final code for all the dot3 layers
+       int renders;
+       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.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, false, true);
-               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.
        //
@@ -1323,84 +1327,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);
        }
@@ -1408,94 +1380,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.
        //
@@ -1503,49 +1435,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);
        }
@@ -1553,53 +1475,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);
        }
@@ -1607,88 +1515,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);
        }
@@ -1696,152 +1578,108 @@ 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];
        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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
+               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);
        }
@@ -1849,248 +1687,243 @@ 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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
+               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);
+               RSurf_DrawBatch_Simple(numsurfaces, surfacelist);
+               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, qboolean dopants, qboolean doshirt)
+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 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);
+       if (dodiffuse)
+               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+       if (dopants)
        {
-               const msurface_t *surface = surfacelist[surfacelistindex];
-               RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, true);
                if (doambient)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
                if (dodiffuse)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
-               if (dopants)
-               {
-                       if (doambient)
-                               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
-                       if (dodiffuse)
-                               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
-               }
-               if (doshirt)
-               {
-                       if (doambient)
-                               R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
-                       if (dodiffuse)
-                               R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
-               }
-               if (dospecular)
-                       R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(ent, texture, surface, lightcolorbase, glosstexture, normalmaptexture, specularscale);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
        }
+       if (doshirt)
+       {
+               if (doambient)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
+               if (dodiffuse)
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(numsurfaces, surfacelist, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
+       }
+       if (dospecular)
+               R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(numsurfaces, surfacelist, lightcolorbase, glosstexture, normalmaptexture, specularscale);
 }
 
-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)
+               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)
                        {
-                               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)
-                       {
-                               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, qboolean dopants, qboolean doshirt)
+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];
@@ -2102,153 +1935,92 @@ static void R_Shadow_RenderSurfacesLighting_Light_Vertex(const entity_render_t *
        VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
        VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, 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, true, false);
-               // 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
-                       }
-               }
-               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 *pantstexture;
-       rtexture_t *shirttexture;
-       rtexture_t *glosstexture;
-       float specularscale;
-       qboolean dopants, doshirt;
-       glosstexture = r_texture_black;
-       specularscale = 0;
-       if (r_shadow_gloss.integer > 0)
-       {
-               if (texture->skin.gloss)
-               {
-                       if (r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
-                       {
-                               glosstexture = texture->skin.gloss;
-                               specularscale = r_shadow_rtlight->specularscale * r_shadow_glossintensity.value;
-                       }
-               }
-               else
-               {
-                       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;
-                       }
-               }
-       }
        // 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))
+       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;
-       if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
-               qglDisable(GL_CULL_FACE);
+       if ((rsurface_texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (rsurface_entity->flags & RENDER_NOCULLFACE))
+       {
+               qglDisable(GL_CULL_FACE);CHECKGLERROR
+       }
        else
-               qglEnable(GL_CULL_FACE);
-       dopants = texture->skin.pants != NULL && VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f);
-       doshirt = texture->skin.shirt != NULL && VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
-       if (dopants + doshirt)
        {
+               qglEnable(GL_CULL_FACE);CHECKGLERROR
+       }
+       if (rsurface_texture->colormapping)
+       {
+               qboolean dopants = rsurface_texture->skin.pants != NULL && VectorLength2(rsurface_entity->colormap_pantscolor) >= (1.0f / 1048576.0f);
+               qboolean doshirt = rsurface_texture->skin.shirt != NULL && VectorLength2(rsurface_entity->colormap_shirtcolor) >= (1.0f / 1048576.0f);
                if (dopants)
                {
-                       lightcolorpants[0] = lightcolorbase[0] * ent->colormap_pantscolor[0];
-                       lightcolorpants[1] = lightcolorbase[1] * ent->colormap_pantscolor[1];
-                       lightcolorpants[2] = lightcolorbase[2] * ent->colormap_pantscolor[2];
+                       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
-               {
-                       pantstexture = r_texture_black;
                        VectorClear(lightcolorpants);
-               }
                if (doshirt)
                {
-                       shirttexture = texture->skin.shirt;
-                       lightcolorshirt[0] = lightcolorbase[0] * ent->colormap_shirtcolor[0];
-                       lightcolorshirt[1] = lightcolorbase[1] * ent->colormap_shirtcolor[1];
-                       lightcolorshirt[2] = lightcolorbase[2] * ent->colormap_shirtcolor[2];
+                       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];
                }
                else
-               {
-                       shirttexture = r_texture_black;
                        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, dopants, doshirt);
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->skin.pants, rsurface_texture->skin.shirt, rsurface_texture->skin.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, dopants, doshirt);
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->skin.pants, rsurface_texture->skin.shirt, rsurface_texture->skin.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, dopants, doshirt);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->skin.pants, rsurface_texture->skin.shirt, rsurface_texture->skin.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, dopants, doshirt);
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->skin.pants, rsurface_texture->skin.shirt, rsurface_texture->skin.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);
@@ -2257,20 +2029,19 @@ 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, false, false);
+                       R_Shadow_RenderSurfacesLighting_VisibleLighting(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->skin.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, false, false);
+                       R_Shadow_RenderSurfacesLighting_Light_GLSL(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->skin.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, false, false);
+                       R_Shadow_RenderSurfacesLighting_Light_Dot3(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->skin.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, false, false);
+                       R_Shadow_RenderSurfacesLighting_Light_Vertex(numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->skin.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);
@@ -2319,11 +2090,6 @@ void R_RTLight_Update(dlight_t *light, int isstatic)
        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;
 }
 
 // compiles rtlight geometry
@@ -2357,7 +2123,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;
@@ -2393,7 +2159,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)
@@ -2425,6 +2192,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)
@@ -2433,6 +2201,7 @@ 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;
@@ -2441,21 +2210,22 @@ void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfa
                                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);
+                                       qglCullFace(GL_BACK);CHECKGLERROR // 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);
+                                       qglCullFace(GL_FRONT);CHECKGLERROR // 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, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
                }
        }
        else
@@ -2469,28 +2239,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, 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)
@@ -2597,16 +2371,17 @@ 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
+                        && (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)
+                               if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
                                        shadowentities[numshadowentities++] = ent;
-                               if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
+                               if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && model->DrawLight)
                                        lightentities[numlightentities++] = ent;
                        }
                }
@@ -2817,7 +2592,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;
@@ -2882,14 +2657,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, r_crosshairs[0]->tex, 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_viewright, r_viewup, 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;
@@ -2897,7 +2675,7 @@ 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, r_crosshairs[surfacenumber]->tex, 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_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
 }
 
 void R_Shadow_DrawLightSprites(void)