overhauled vertex formats, now supports 100% interleaved arrays
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 1 May 2010 16:48:33 +0000 (16:48 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 1 May 2010 16:48:33 +0000 (16:48 +0000)
rendering mode, reworked vertexbuffer management to be a more robust
system, added fully vertex buffered rendering options
(gl_vbo_dynamicvertex, gl_vbo_dynamicindex) which are off by default
(they seem to hurt fps in tests here)
centralized handling of "generic" mesh rendering (fixed function stuff)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10109 d7cf8633-e32d-0410-b094-e92efae38249

19 files changed:
cl_particles.c
cl_screen.c
client.h
clvm_cmds.c
gl_backend.c
gl_backend.h
gl_draw.c
gl_rmain.c
gl_rsurf.c
glquake.h
mathlib.h
model_shared.c
model_shared.h
r_explosion.c
r_shadow.c
r_shadow.h
render.h
vid.h
vid_shared.c

index 04bcbfb..a5b987e 100644 (file)
@@ -2270,15 +2270,11 @@ void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t
        particletexture_t *tex;
        float right[3], up[3], size, ca;
        float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value;
-       float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
 
        RSurf_ActiveWorldEntity();
 
        r_refdef.stats.drawndecals += numsurfaces;
        R_Mesh_ResetTextureState();
-       R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
-       R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
-       R_Mesh_ColorPointer(particle_color4f, 0, 0);
        GL_DepthMask(false);
        GL_DepthRange(0, 1);
        GL_PolygonOffset(0, 0);
@@ -2335,7 +2331,8 @@ void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t
        // (this assumes they all use one particle font texture!)
        GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
        R_SetupShader_Generic(particletexture[63].texture, NULL, GL_MODULATE, 1);
-       R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, particle_elements, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(numsurfaces * 4, particle_vertex3f, particle_color4f, particle_texcoord2f);
+       R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, NULL, 0, particle_elements, NULL, 0);
 }
 
 void R_DrawDecals (void)
@@ -2436,9 +2433,6 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
 
        r_refdef.stats.particles += numsurfaces;
        R_Mesh_ResetTextureState();
-       R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
-       R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
-       R_Mesh_ColorPointer(particle_color4f, 0, 0);
        GL_DepthMask(false);
        GL_DepthRange(0, 1);
        GL_PolygonOffset(0, 0);
@@ -2637,6 +2631,7 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
        texture = NULL;
        batchstart = 0;
        batchcount = 0;
+       R_Mesh_PrepareVertices_Generic_Arrays(numsurfaces * 4, particle_vertex3f, particle_color4f, particle_texcoord2f);
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
        {
                p = cl.particles + surfacelist[surfacelistindex];
@@ -2674,7 +2669,7 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
                }
 
                batchcount = surfacelistindex - batchstart;
-               R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, particle_elements, 0, 0);
+               R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, NULL, 0, particle_elements, NULL, 0);
        }
 }
 
index b26ac8f..9050ab0 100644 (file)
@@ -765,6 +765,7 @@ void R_TimeReport_EndFrame(void)
 "%7i lightmap updates (%7i pixels)\n"
 "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n"
 "rendered%6i meshes%8i triangles bloompixels%8i copied%8i drawn\n"
+"updated%5i indexbuffers%8i bytes%5i vertexbuffers%8i bytes\n"
 "%s"
 , loc ? "Location: " : "", loc ? loc->name : ""
 , r_refdef.stats.renders, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]
@@ -774,6 +775,7 @@ void R_TimeReport_EndFrame(void)
 , r_refdef.stats.lightmapupdates, r_refdef.stats.lightmapupdatepixels
 , r_refdef.stats.lights, r_refdef.stats.lights_clears, r_refdef.stats.lights_scissored, r_refdef.stats.lights_lighttriangles, r_refdef.stats.lights_shadowtriangles, r_refdef.stats.lights_dynamicshadowtriangles
 , r_refdef.stats.meshes, r_refdef.stats.meshes_elements / 3, r_refdef.stats.bloom_copypixels, r_refdef.stats.bloom_drawpixels
+, r_refdef.stats.indexbufferuploadcount, r_refdef.stats.indexbufferuploadsize, r_refdef.stats.vertexbufferuploadcount, r_refdef.stats.vertexbufferuploadsize
 , r_speeds_timestring);
 
                memset(&r_refdef.stats, 0, sizeof(r_refdef.stats));
@@ -1885,10 +1887,7 @@ static void SCR_DrawLoadingStack(void)
                GL_DepthRange(0, 1);
                GL_PolygonOffset(0, 0);
                GL_DepthTest(false);
-               R_Mesh_VertexPointer(verts, 0, 0);
-               R_Mesh_ColorPointer(colors, 0, 0);
                R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                verts[2] = verts[5] = verts[8] = verts[11] = 0;
                verts[0] = verts[9] = 0;
                verts[1] = verts[4] = vid_conheight.integer - scr_loadingscreen_barheight.value;
@@ -1906,7 +1905,9 @@ static void SCR_DrawLoadingStack(void)
                sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[8], &colors[9], &colors[10]); colors[11] = 1;
                sscanf(scr_loadingscreen_barcolor.string, "%f %f %f", &colors[12], &colors[13], &colors[14]);  colors[15] = 1;
 
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, verts, colors, NULL);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 
                // make sure everything is cleared, including the progress indicator
                if(loadingscreenheight < 8)
@@ -1956,25 +1957,20 @@ static void SCR_DrawLoadingScreen_SharedSetup (qboolean clear)
 static void SCR_DrawLoadingScreen (qboolean clear)
 {
        // we only need to draw the image if it isn't already there
-       GL_Color(1,1,1,1);
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_DepthRange(0, 1);
        GL_PolygonOffset(0, 0);
        GL_DepthTest(false);
-       R_Mesh_ColorPointer(NULL, 0, 0);
+       R_Mesh_ResetTextureState();
+       GL_Color(1,1,1,1);
+       R_Mesh_PrepareVertices_Generic_Arrays(4, loadingscreentexture_vertex3f, NULL, loadingscreentexture_texcoord2f);
        if(loadingscreentexture)
        {
-               R_Mesh_VertexPointer(loadingscreentexture_vertex3f, 0, 0);
-               R_Mesh_ResetTextureState();
                R_SetupShader_Generic(loadingscreentexture, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, loadingscreentexture_texcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        }
-       R_Mesh_VertexPointer(loadingscreenpic_vertex3f, 0, 0);
-       R_Mesh_ResetTextureState();
        R_SetupShader_Generic(loadingscreenpic->tex, NULL, GL_MODULATE, 1);
-       R_Mesh_TexCoordPointer(0, 2, loadingscreenpic_texcoord2f, 0, 0);
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        SCR_DrawLoadingStack();
 }
 
index f9485cc..e177b76 100644 (file)
--- a/client.h
+++ b/client.h
@@ -368,6 +368,11 @@ typedef struct entity_render_s
        float *animcache_normal3f;
        float *animcache_svector3f;
        float *animcache_tvector3f;
+       // interleaved arrays for rendering and dynamic vertex buffers for them
+       r_vertexposition_t *animcache_vertexposition;
+       r_meshbuffer_t *animcache_vertexpositionbuffer;
+       r_vertexmesh_t *animcache_vertexmesh;
+       r_meshbuffer_t *animcache_vertexmeshbuffer;
 
        // current lighting from map (updated ONLY by client code, not renderer)
        vec3_t modellight_ambient;
@@ -1522,6 +1527,10 @@ typedef struct r_refdef_stats_s
        int bloom;
        int bloom_copypixels;
        int bloom_drawpixels;
+       int indexbufferuploadcount;
+       int indexbufferuploadsize;
+       int vertexbufferuploadcount;
+       int vertexbufferuploadsize;
 }
 r_refdef_stats_t;
 
index 2c6fb4f..06d1d64 100644 (file)
@@ -2929,9 +2929,7 @@ static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t
        R_Mesh_ResetTextureState();
        R_EntityMatrix(&identitymatrix);
        GL_CullFace(GL_NONE);
-       R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0);
-       R_Mesh_ColorPointer(polys->data_color4f, 0, 0);
-       R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(polys->num_vertices, polys->data_vertex3f, polys->data_color4f, polys->data_texcoord2f);
 
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
        {
@@ -2959,7 +2957,7 @@ static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t
                        VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
                        numtriangles++;
                }
-               R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0);
+               R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, NULL, 0, polys->data_sortedelement3s, NULL, 0);
        }
 }
 
index fd0cff6..8122aa0 100644 (file)
@@ -3,9 +3,9 @@
 #include "cl_collision.h"
 
 cvar_t gl_mesh_drawrangeelements = {0, "gl_mesh_drawrangeelements", "1", "use glDrawRangeElements function if available instead of glDrawElements (for performance comparisons or bug testing)"};
-cvar_t gl_mesh_testarrayelement = {0, "gl_mesh_testarrayelement", "0", "use glBegin(GL_TRIANGLES);glArrayElement();glEnd(); primitives instead of glDrawElements (useful to test for driver bugs with glDrawElements)"};
 cvar_t gl_mesh_testmanualfeeding = {0, "gl_mesh_testmanualfeeding", "0", "use glBegin(GL_TRIANGLES);glTexCoord2f();glVertex3f();glEnd(); primitives instead of glDrawElements (useful to test for driver bugs with glDrawElements)"};
-cvar_t gl_mesh_prefer_short_elements = {0, "gl_mesh_prefer_short_elements", "1", "use GL_UNSIGNED_SHORT element arrays instead of GL_UNSIGNED_INT"};
+cvar_t gl_mesh_prefer_short_elements = {CVAR_SAVE, "gl_mesh_prefer_short_elements", "1", "use GL_UNSIGNED_SHORT element arrays instead of GL_UNSIGNED_INT"};
+cvar_t gl_mesh_separatearrays = {0, "gl_mesh_separatearrays", "1", "use several separate vertex arrays rather than one combined stream"};
 cvar_t gl_paranoid = {0, "gl_paranoid", "0", "enables OpenGL error checking and other tests"};
 cvar_t gl_printcheckerror = {0, "gl_printcheckerror", "0", "prints all OpenGL error checks, useful to identify location of driver crashes"};
 
@@ -15,6 +15,8 @@ cvar_t r_waterwarp = {CVAR_SAVE, "r_waterwarp", "1", "warp view while underwater
 cvar_t gl_polyblend = {CVAR_SAVE, "gl_polyblend", "1", "tints view while underwater, hurt, etc"};
 cvar_t gl_dither = {CVAR_SAVE, "gl_dither", "1", "enables OpenGL dithering (16bit looks bad with this off)"};
 cvar_t gl_vbo = {CVAR_SAVE, "gl_vbo", "3", "make use of GL_ARB_vertex_buffer_object extension to store static geometry in video memory for faster rendering, 0 disables VBO allocation or use, 1 enables VBOs for vertex and triangle data, 2 only for vertex data, 3 for vertex data and triangle data of simple meshes (ones with only one surface)"};
+cvar_t gl_vbo_dynamicvertex = {CVAR_SAVE, "gl_vbo_dynamicvertex", "0", "make use of GL_ARB_vertex_buffer_object extension when rendering dynamic (animated/procedural) geometry such as text and particles"};
+cvar_t gl_vbo_dynamicindex = {CVAR_SAVE, "gl_vbo_dynamicindex", "0", "make use of GL_ARB_vertex_buffer_object extension when rendering dynamic (animated/procedural) geometry such as text and particles"};
 cvar_t gl_fbo = {CVAR_SAVE, "gl_fbo", "1", "make use of GL_ARB_framebuffer_object extension to enable shadowmaps and other features using pixel formats different from the framebuffer"};
 
 cvar_t v_flipped = {0, "v_flipped", "0", "mirror the screen (poor man's left handed mode)"};
@@ -91,23 +93,17 @@ void GL_PrintError(int errornumber, char *filename, int linenumber)
 
 void SCR_ScreenShot_f (void);
 
-typedef struct gl_bufferobjectinfo_s
-{
-       int target;
-       int object;
-       size_t size;
-       char name[MAX_QPATH];
-}
-gl_bufferobjectinfo_t;
-
 typedef struct gltextureunit_s
 {
-       const void *pointer_texcoord;
+       int pointer_texcoord_components;
+       int pointer_texcoord_gltype;
+       size_t pointer_texcoord_stride;
+       const void *pointer_texcoord_pointer;
+       const r_meshbuffer_t *pointer_texcoord_vertexbuffer;
        size_t pointer_texcoord_offset;
-       int pointer_texcoord_buffer;
+
        int t2d, t3d, tcubemap, trectangle;
        int arrayenabled;
-       unsigned int arraycomponents;
        int rgbscale, alphascale;
        int combine;
        int combinergb, combinealpha;
@@ -140,14 +136,37 @@ typedef struct gl_state_s
        int vertexbufferobject;
        int elementbufferobject;
        qboolean pointer_color_enabled;
-       const void *pointer_vertex;
-       const void *pointer_color;
+
+       int pointer_vertex_components;
+       int pointer_vertex_gltype;
+       size_t pointer_vertex_stride;
+       const void *pointer_vertex_pointer;
+       const r_meshbuffer_t *pointer_vertex_vertexbuffer;
        size_t pointer_vertex_offset;
+
+       int pointer_color_components;
+       int pointer_color_gltype;
+       size_t pointer_color_stride;
+       const void *pointer_color_pointer;
+       const r_meshbuffer_t *pointer_color_vertexbuffer;
        size_t pointer_color_offset;
-       int pointer_vertex_buffer;
-       int pointer_color_buffer;
 
-       memexpandablearray_t bufferobjectinfoarray;
+       void *preparevertices_tempdata;
+       size_t preparevertices_tempdatamaxsize;
+       r_meshbuffer_t *preparevertices_dynamicvertexbuffer;
+       r_vertexposition_t *preparevertices_vertexposition;
+       r_vertexgeneric_t *preparevertices_vertexgeneric;
+       r_vertexmesh_t *preparevertices_vertexmesh;
+       int preparevertices_numvertices;
+
+       r_meshbuffer_t *draw_dynamicindexbuffer;
+
+       qboolean usevbo_staticvertex;
+       qboolean usevbo_staticindex;
+       qboolean usevbo_dynamicvertex;
+       qboolean usevbo_dynamicindex;
+
+       memexpandablearray_t meshbufferarray;
 
        qboolean active;
 }
@@ -226,7 +245,11 @@ static void gl_backend_start(void)
 {
        memset(&gl_state, 0, sizeof(gl_state));
 
-       Mem_ExpandableArray_NewArray(&gl_state.bufferobjectinfoarray, r_main_mempool, sizeof(gl_bufferobjectinfo_t), 128);
+       gl_state.usevbo_staticvertex = (vid.support.arb_vertex_buffer_object && gl_vbo.integer) || vid.forcevbo;
+       gl_state.usevbo_staticindex = (vid.support.arb_vertex_buffer_object && (gl_vbo.integer == 1 || gl_vbo.integer == 3)) || vid.forcevbo;
+       gl_state.usevbo_dynamicvertex = (vid.support.arb_vertex_buffer_object && gl_vbo_dynamicvertex.integer) || vid.forcevbo;
+       gl_state.usevbo_dynamicindex = (vid.support.arb_vertex_buffer_object && gl_vbo_dynamicindex.integer) || vid.forcevbo;
+       Mem_ExpandableArray_NewArray(&gl_state.meshbufferarray, r_main_mempool, sizeof(r_meshbuffer_t), 128);
 
        Con_DPrintf("OpenGL backend started.\n");
 
@@ -239,7 +262,12 @@ static void gl_backend_shutdown(void)
 {
        Con_DPrint("OpenGL Backend shutting down\n");
 
-       Mem_ExpandableArray_FreeArray(&gl_state.bufferobjectinfoarray);
+       if (gl_state.preparevertices_tempdata)
+               Mem_Free(gl_state.preparevertices_tempdata);
+       if (gl_state.preparevertices_dynamicvertexbuffer)
+               R_Mesh_DestroyMeshBuffer(gl_state.preparevertices_dynamicvertexbuffer);
+
+       Mem_ExpandableArray_FreeArray(&gl_state.meshbufferarray);
 
        memset(&gl_state, 0, sizeof(gl_state));
 }
@@ -281,13 +309,15 @@ void gl_backend_init(void)
        Cvar_RegisterVariable(&v_flipped);
        Cvar_RegisterVariable(&gl_dither);
        Cvar_RegisterVariable(&gl_vbo);
+       Cvar_RegisterVariable(&gl_vbo_dynamicvertex);
+       Cvar_RegisterVariable(&gl_vbo_dynamicindex);
        Cvar_RegisterVariable(&gl_paranoid);
        Cvar_RegisterVariable(&gl_printcheckerror);
 
        Cvar_RegisterVariable(&gl_mesh_drawrangeelements);
-       Cvar_RegisterVariable(&gl_mesh_testarrayelement);
        Cvar_RegisterVariable(&gl_mesh_testmanualfeeding);
        Cvar_RegisterVariable(&gl_mesh_prefer_short_elements);
+       Cvar_RegisterVariable(&gl_mesh_separatearrays);
 
        Cmd_AddCommand("gl_vbostats", GL_VBOStats_f, "prints a list of all buffer objects (vertex data and triangle elements) and total video memory used by them");
 
@@ -1024,6 +1054,10 @@ void R_Mesh_Start(void)
 {
        BACKENDACTIVECHECK
        CHECKGLERROR
+       gl_state.usevbo_staticvertex = (vid.support.arb_vertex_buffer_object && gl_vbo.integer) || vid.forcevbo;
+       gl_state.usevbo_staticindex = (vid.support.arb_vertex_buffer_object && (gl_vbo.integer == 1 || gl_vbo.integer == 3)) || vid.forcevbo;
+       gl_state.usevbo_dynamicvertex = (vid.support.arb_vertex_buffer_object && gl_vbo_dynamicvertex.integer) || vid.forcevbo;
+       gl_state.usevbo_dynamicindex = (vid.support.arb_vertex_buffer_object && gl_vbo_dynamicindex.integer) || vid.forcevbo;
        if (gl_printcheckerror.integer && !gl_paranoid.integer)
        {
                Con_Printf("WARNING: gl_printcheckerror is on but gl_paranoid is off, turning it on...\n");
@@ -1129,46 +1163,84 @@ void GL_Backend_RenumberElements(int *out, int count, const int *in, int offset)
 
 // renders triangles using vertices from the active arrays
 int paranoidblah = 0;
-void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int bufferobject3i, int bufferobject3s)
+void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const r_meshbuffer_t *element3i_indexbuffer, size_t element3i_bufferoffset, const unsigned short *element3s, const r_meshbuffer_t *element3s_indexbuffer, size_t element3s_bufferoffset)
 {
        unsigned int numelements = numtriangles * 3;
+       int bufferobject3i;
+       size_t bufferoffset3i;
+       int bufferobject3s;
+       size_t bufferoffset3s;
        if (numvertices < 3 || numtriangles < 1)
        {
                if (numvertices < 0 || numtriangles < 0 || developer_extra.integer)
-                       Con_DPrintf("R_Mesh_Draw(%d, %d, %d, %d, %8p, %8p, %i, %i);\n", firstvertex, numvertices, firsttriangle, numtriangles, (void *)element3i, (void *)element3s, bufferobject3i, bufferobject3s);
+                       Con_DPrintf("R_Mesh_Draw(%d, %d, %d, %d, %8p, %8p, %8x, %8p, %8p, %8x);\n", firstvertex, numvertices, firsttriangle, numtriangles, (void *)element3i, (void *)element3i_indexbuffer, (int)element3i_bufferoffset, (void *)element3s, (void *)element3s_indexbuffer, (int)element3s_bufferoffset);
+               return;
+       }
+       if (gl_state.pointer_vertex_pointer == NULL)
+       {
+               Con_DPrintf("R_Mesh_Draw with no vertex pointer!\n");
                return;
        }
        if (!gl_mesh_prefer_short_elements.integer)
        {
                if (element3i)
                        element3s = NULL;
-               if (bufferobject3i)
-                       bufferobject3s = 0;
+               if (element3i_indexbuffer)
+                       element3i_indexbuffer = NULL;
        }
+       // adjust the pointers for firsttriangle
        if (element3i)
                element3i += firsttriangle * 3;
+       if (element3i_indexbuffer)
+               element3i_bufferoffset += firsttriangle * 3 * sizeof(*element3i);
        if (element3s)
                element3s += firsttriangle * 3;
-       switch (gl_vbo.integer)
+       if (element3s_indexbuffer)
+               element3s_bufferoffset += firsttriangle * 3 * sizeof(*element3s);
+       // check if the user specified to ignore static index buffers
+       if (!gl_state.usevbo_staticindex || (gl_vbo.integer == 3 && !vid.forcevbo && (element3i_bufferoffset || element3s_bufferoffset)))
        {
-       default:
-       case 0:
-       case 2:
-               bufferobject3i = bufferobject3s = 0;
-               break;
-       case 1:
-               break;
-       case 3:
-               if (firsttriangle)
-                       bufferobject3i = bufferobject3s = 0;
-               break;
+               element3i_indexbuffer = NULL;
+               element3s_indexbuffer = NULL;
        }
+       // upload a dynamic index buffer if needed
+       if (element3s)
+       {
+               if (!element3s_indexbuffer && gl_state.usevbo_dynamicindex)
+               {
+                       if (gl_state.draw_dynamicindexbuffer)
+                               R_Mesh_UpdateMeshBuffer(gl_state.draw_dynamicindexbuffer, (void *)element3s, numelements * sizeof(*element3s));
+                       else
+                               gl_state.draw_dynamicindexbuffer = R_Mesh_CreateMeshBuffer((void *)element3s, numelements * sizeof(*element3s), "temporary", true, true);
+                       element3s_indexbuffer = gl_state.draw_dynamicindexbuffer;
+                       element3s_bufferoffset = 0;
+               }
+       }
+       else if (element3i)
+       {
+               if (!element3i_indexbuffer && gl_state.usevbo_dynamicindex)
+               {
+                       if (gl_state.draw_dynamicindexbuffer)
+                               R_Mesh_UpdateMeshBuffer(gl_state.draw_dynamicindexbuffer, (void *)element3i, numelements * sizeof(*element3i));
+                       else
+                               gl_state.draw_dynamicindexbuffer = R_Mesh_CreateMeshBuffer((void *)element3i, numelements * sizeof(*element3i), "temporary", true, true);
+                       element3i_indexbuffer = gl_state.draw_dynamicindexbuffer;
+                       element3i_bufferoffset = 0;
+               }
+       }
+       bufferobject3i = element3i_indexbuffer ? element3i_indexbuffer->bufferobject : 0;
+       bufferoffset3i = element3i_bufferoffset;
+       bufferobject3s = element3s_indexbuffer ? element3s_indexbuffer->bufferobject : 0;
+       bufferoffset3s = element3s_bufferoffset;
        CHECKGLERROR
        r_refdef.stats.meshes++;
        r_refdef.stats.meshes_elements += numelements;
        if (gl_paranoid.integer)
        {
-               unsigned int i, j, size;
+               unsigned int i;
+               // LordHavoc: disabled this - it needs to be updated to handle components and gltype and stride in each array
+#if 0
+               unsigned int j, size;
                const int *p;
                // note: there's no validation done here on buffer objects because it
                // is somewhat difficult to get at the data, and gl_paranoid can be
@@ -1178,7 +1250,7 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                if (!qglIsEnabled(GL_VERTEX_ARRAY))
                        Con_Print("R_Mesh_Draw: vertex array not enabled\n");
                CHECKGLERROR
-               if (gl_state.pointer_vertex)
+               if (gl_state.pointer_vertex_pointer)
                        for (j = 0, size = numvertices * 3, p = (int *)((float *)gl_state.pointer_vertex + firstvertex * 3);j < size;j++, p++)
                                paranoidblah += *p;
                if (gl_state.pointer_color_enabled)
@@ -1203,6 +1275,7 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                                                paranoidblah += *p;
                        }
                }
+#endif
                if (element3i)
                {
                        for (i = 0;i < (unsigned int) numtriangles * 3;i++)
@@ -1237,83 +1310,119 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                        qglBegin(GL_TRIANGLES);
                        for (i = 0;i < (unsigned int) numtriangles * 3;i++)
                        {
-                               element = element3i ? element3i[i] : element3s[i];
+                               if (element3i)
+                                       element = element3i[i];
+                               else if (element3s)
+                                       element = element3s[i];
+                               else
+                                       element = firstvertex + i;
                                for (j = 0;j < vid.texarrayunits;j++)
                                {
-                                       if (gl_state.units[j].pointer_texcoord && gl_state.units[j].arrayenabled)
+                                       if (gl_state.units[j].pointer_texcoord_pointer && gl_state.units[j].arrayenabled)
                                        {
-                                               if (vid.texarrayunits > 1)
+                                               if (gl_state.units[j].pointer_texcoord_gltype == GL_FLOAT)
                                                {
-                                                       if (gl_state.units[j].arraycomponents == 4)
+                                                       p = (const GLfloat *)((const unsigned char *)gl_state.units[j].pointer_texcoord_pointer + element * gl_state.units[j].pointer_texcoord_stride);
+                                                       if (vid.texarrayunits > 1)
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 4;
-                                                               qglMultiTexCoord4f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2], p[3]);
-                                                       }
-                                                       else if (gl_state.units[j].arraycomponents == 3)
-                                                       {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 3;
-                                                               qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2]);
-                                                       }
-                                                       else if (gl_state.units[j].arraycomponents == 2)
-                                                       {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 2;
-                                                               qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, p[0], p[1]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglMultiTexCoord4f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2], p[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, p[0], p[1]);
+                                                               else
+                                                                       qglMultiTexCoord1f(GL_TEXTURE0_ARB + j, p[0]);
                                                        }
                                                        else
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 1;
-                                                               qglMultiTexCoord1f(GL_TEXTURE0_ARB + j, p[0]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglTexCoord4f(p[0], p[1], p[2], p[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglTexCoord3f(p[0], p[1], p[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglTexCoord2f(p[0], p[1]);
+                                                               else
+                                                                       qglTexCoord1f(p[0]);
                                                        }
                                                }
-                                               else
+                                               else if (gl_state.units[j].pointer_texcoord_gltype == GL_SHORT)
                                                {
-                                                       if (gl_state.units[j].arraycomponents == 4)
+                                                       const GLshort *s = (const GLshort *)((const unsigned char *)gl_state.units[j].pointer_texcoord_pointer + element * gl_state.units[j].pointer_texcoord_stride);
+                                                       if (vid.texarrayunits > 1)
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 4;
-                                                               qglTexCoord4f(p[0], p[1], p[2], p[3]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglMultiTexCoord4f(GL_TEXTURE0_ARB + j, s[0], s[1], s[2], s[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, s[0], s[1], s[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, s[0], s[1]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 1)
+                                                                       qglMultiTexCoord1f(GL_TEXTURE0_ARB + j, s[0]);
                                                        }
-                                                       else if (gl_state.units[j].arraycomponents == 3)
+                                                       else
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 3;
-                                                               qglTexCoord3f(p[0], p[1], p[2]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglTexCoord4f(s[0], s[1], s[2], s[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglTexCoord3f(s[0], s[1], s[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglTexCoord2f(s[0], s[1]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 1)
+                                                                       qglTexCoord1f(s[0]);
                                                        }
-                                                       else if (gl_state.units[j].arraycomponents == 2)
+                                               }
+                                               else if (gl_state.units[j].pointer_texcoord_gltype == GL_BYTE)
+                                               {
+                                                       const GLbyte *sb = (const GLbyte *)((const unsigned char *)gl_state.units[j].pointer_texcoord_pointer + element * gl_state.units[j].pointer_texcoord_stride);
+                                                       if (vid.texarrayunits > 1)
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 2;
-                                                               qglTexCoord2f(p[0], p[1]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglMultiTexCoord4f(GL_TEXTURE0_ARB + j, sb[0], sb[1], sb[2], sb[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, sb[0], sb[1], sb[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, sb[0], sb[1]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 1)
+                                                                       qglMultiTexCoord1f(GL_TEXTURE0_ARB + j, sb[0]);
                                                        }
                                                        else
                                                        {
-                                                               p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 1;
-                                                               qglTexCoord1f(p[0]);
+                                                               if (gl_state.units[j].pointer_texcoord_components == 4)
+                                                                       qglTexCoord4f(sb[0], sb[1], sb[2], sb[3]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 3)
+                                                                       qglTexCoord3f(sb[0], sb[1], sb[2]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 2)
+                                                                       qglTexCoord2f(sb[0], sb[1]);
+                                                               else if (gl_state.units[j].pointer_texcoord_components == 1)
+                                                                       qglTexCoord1f(sb[0]);
                                                        }
                                                }
                                        }
                                }
-                               if (gl_state.pointer_color && gl_state.pointer_color_enabled)
+                               if (gl_state.pointer_color_pointer && gl_state.pointer_color_enabled && gl_state.pointer_color_components == 4)
                                {
-                                       p = ((const GLfloat *)(gl_state.pointer_color)) + element * 4;
-                                       qglColor4f(p[0], p[1], p[2], p[3]);
+                                       if (gl_state.pointer_color_gltype == GL_FLOAT)
+                                       {
+                                               p = (const GLfloat *)((const unsigned char *)gl_state.pointer_color_pointer + element * gl_state.pointer_color_stride);
+                                               qglColor4f(p[0], p[1], p[2], p[3]);
+                                       }
+                                       else if (gl_state.pointer_color_gltype == GL_UNSIGNED_BYTE)
+                                       {
+                                               const GLubyte *ub = (const GLubyte *)((const unsigned char *)gl_state.pointer_color_pointer + element * gl_state.pointer_color_stride);
+                                               qglColor4ub(ub[0], ub[1], ub[2], ub[3]);
+                                       }
+                               }
+                               if (gl_state.pointer_vertex_gltype == GL_FLOAT)
+                               {
+                                       p = (const GLfloat *)((const unsigned char *)gl_state.pointer_vertex_pointer + element * gl_state.pointer_vertex_stride);
+                                       if (gl_state.pointer_vertex_components == 4)
+                                               qglVertex4f(p[0], p[1], p[2], p[3]);
+                                       else if (gl_state.pointer_vertex_components == 3)
+                                               qglVertex3f(p[0], p[1], p[2]);
+                                       else
+                                               qglVertex2f(p[0], p[1]);
                                }
-                               p = ((const GLfloat *)(gl_state.pointer_vertex)) + element * 3;
-                               qglVertex3f(p[0], p[1], p[2]);
-                       }
-                       qglEnd();
-                       CHECKGLERROR
-               }
-               else if (gl_mesh_testarrayelement.integer)
-               {
-                       int i;
-                       qglBegin(GL_TRIANGLES);
-                       if (element3i)
-                       {
-                               for (i = 0;i < numtriangles * 3;i++)
-                                       qglArrayElement(element3i[i]);
-                       }
-                       else if (element3s)
-                       {
-                               for (i = 0;i < numtriangles * 3;i++)
-                                       qglArrayElement(element3s[i]);
                        }
                        qglEnd();
                        CHECKGLERROR
@@ -1323,7 +1432,7 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                        GL_BindEBO(bufferobject3s);
                        if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
                        {
-                               qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_SHORT, (void *)(firsttriangle * sizeof(unsigned short[3])));
+                               qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_SHORT, (void *)bufferoffset3s);
                                CHECKGLERROR
                        }
                        else
@@ -1337,7 +1446,7 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                        GL_BindEBO(bufferobject3i);
                        if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
                        {
-                               qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_INT, (void *)(firsttriangle * sizeof(unsigned int[3])));
+                               qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_INT, (void *)bufferoffset3i);
                                CHECKGLERROR
                        }
                        else
@@ -1374,6 +1483,11 @@ void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtri
                                CHECKGLERROR
                        }
                }
+               else
+               {
+                       qglDrawArrays(GL_TRIANGLES, firstvertex, numvertices);
+                       CHECKGLERROR
+               }
        }
 }
 
@@ -1382,52 +1496,91 @@ void R_Mesh_Finish(void)
 {
 }
 
-int R_Mesh_CreateStaticBufferObject(unsigned int target, void *data, size_t size, const char *name)
+r_meshbuffer_t *R_Mesh_CreateMeshBuffer(const void *data, size_t size, const char *name, qboolean isindexbuffer, qboolean isdynamic)
 {
-       gl_bufferobjectinfo_t *info;
-       GLuint bufferobject;
-
-       if (!gl_vbo.integer)
-               return 0;
-
-       qglGenBuffersARB(1, &bufferobject);
-       switch(target)
+       int bufferobject = 0;
+       void *devicebuffer = NULL;
+       r_meshbuffer_t *buffer;
+       if (!(isdynamic ? (isindexbuffer ? gl_state.usevbo_dynamicindex : gl_state.usevbo_dynamicvertex) : (isindexbuffer ? gl_state.usevbo_staticindex : gl_state.usevbo_staticvertex)))
+               return NULL;
+       if (isindexbuffer)
        {
-       case GL_ELEMENT_ARRAY_BUFFER_ARB: GL_BindEBO(bufferobject);break;
-       case GL_ARRAY_BUFFER_ARB: GL_BindVBO(bufferobject);break;
-       default: Sys_Error("R_Mesh_CreateStaticBufferObject: unknown target type %i\n", target);return 0;
+               r_refdef.stats.indexbufferuploadcount++;
+               r_refdef.stats.indexbufferuploadsize += size;
        }
-       qglBufferDataARB(target, size, data, GL_STATIC_DRAW_ARB);
-
-       info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_AllocRecord(&gl_state.bufferobjectinfoarray);
-       memset(info, 0, sizeof(*info));
-       info->target = target;
-       info->object = bufferobject;
-       info->size = size;
-       strlcpy(info->name, name, sizeof(info->name));
-
-       return (int)bufferobject;
+       else
+       {
+               r_refdef.stats.vertexbufferuploadcount++;
+               r_refdef.stats.vertexbufferuploadsize += size;
+       }
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               qglGenBuffersARB(1, (GLuint *)&bufferobject);
+               if (isindexbuffer)
+                       GL_BindEBO(bufferobject);
+               else
+                       GL_BindVBO(bufferobject);
+               qglBufferDataARB(isindexbuffer ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB, size, data, isdynamic ? GL_STREAM_DRAW_ARB : GL_STATIC_DRAW_ARB);
+               break;
+       }
+       buffer = (r_meshbuffer_t *)Mem_ExpandableArray_AllocRecord(&gl_state.meshbufferarray);
+       memset(buffer, 0, sizeof(*buffer));
+       buffer->bufferobject = bufferobject;
+       buffer->devicebuffer = devicebuffer;
+       buffer->size = size;
+       buffer->isindexbuffer = isindexbuffer;
+       buffer->isdynamic = isdynamic;
+       strlcpy(buffer->name, name, sizeof(buffer->name));
+       return buffer;
 }
 
-void R_Mesh_DestroyBufferObject(int bufferobject)
+void R_Mesh_UpdateMeshBuffer(r_meshbuffer_t *buffer, const void *data, size_t size)
 {
-       int i, endindex;
-       gl_bufferobjectinfo_t *info;
-
-       qglDeleteBuffersARB(1, (GLuint *)&bufferobject);
+       if (!buffer || (!buffer->bufferobject && !buffer->devicebuffer))
+               return;
+       if (buffer->isindexbuffer)
+       {
+               r_refdef.stats.indexbufferuploadcount++;
+               r_refdef.stats.indexbufferuploadsize += size;
+       }
+       else
+       {
+               r_refdef.stats.vertexbufferuploadcount++;
+               r_refdef.stats.vertexbufferuploadsize += size;
+       }
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               if (buffer->isindexbuffer)
+                       GL_BindEBO(buffer->bufferobject);
+               else
+                       GL_BindVBO(buffer->bufferobject);
+               qglBufferDataARB(buffer->isindexbuffer ? GL_ELEMENT_ARRAY_BUFFER_ARB : GL_ARRAY_BUFFER_ARB, size, data, buffer->isdynamic ? GL_STREAM_DRAW_ARB : GL_STATIC_DRAW_ARB);
+               break;
+       }
+}
 
-       endindex = Mem_ExpandableArray_IndexRange(&gl_state.bufferobjectinfoarray);
-       for (i = 0;i < endindex;i++)
+void R_Mesh_DestroyMeshBuffer(r_meshbuffer_t *buffer)
+{
+       if (!buffer)
+               return;
+       switch(vid.renderpath)
        {
-               info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_RecordAtIndex(&gl_state.bufferobjectinfoarray, i);
-               if (!info)
-                       continue;
-               if (info->object == bufferobject)
-               {
-                       Mem_ExpandableArray_FreeRecord(&gl_state.bufferobjectinfoarray, (void *)info);
-                       break;
-               }
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               qglDeleteBuffersARB(1, (GLuint *)&buffer->bufferobject);
+               break;
        }
+       Mem_ExpandableArray_FreeRecord(&gl_state.meshbufferarray, (void *)buffer);
 }
 
 void GL_Mesh_ListVBOs(qboolean printeach)
@@ -1435,47 +1588,45 @@ void GL_Mesh_ListVBOs(qboolean printeach)
        int i, endindex;
        size_t ebocount = 0, ebomemory = 0;
        size_t vbocount = 0, vbomemory = 0;
-       gl_bufferobjectinfo_t *info;
-       endindex = Mem_ExpandableArray_IndexRange(&gl_state.bufferobjectinfoarray);
+       r_meshbuffer_t *buffer;
+       endindex = Mem_ExpandableArray_IndexRange(&gl_state.meshbufferarray);
        for (i = 0;i < endindex;i++)
        {
-               info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_RecordAtIndex(&gl_state.bufferobjectinfoarray, i);
-               if (!info)
+               buffer = (r_meshbuffer_t *) Mem_ExpandableArray_RecordAtIndex(&gl_state.meshbufferarray, i);
+               if (!buffer)
                        continue;
-               switch(info->target)
-               {
-               case GL_ELEMENT_ARRAY_BUFFER_ARB: ebocount++;ebomemory += info->size;if (printeach) Con_Printf("EBO #%i %s = %i bytes\n", info->object, info->name, (int)info->size);break;
-               case GL_ARRAY_BUFFER_ARB: vbocount++;vbomemory += info->size;if (printeach) Con_Printf("VBO #%i %s = %i bytes\n", info->object, info->name, (int)info->size);break;
-               default: Con_Printf("gl_vbostats: unknown target type %i\n", info->target);break;
-               }
+               if (buffer->isindexbuffer) {ebocount++;ebomemory += buffer->size;if (printeach) Con_Printf("indexbuffer #%i %s = %i bytes%s\n", i, buffer->name, (int)buffer->size, buffer->isdynamic ? " (dynamic)" : " (static)");}
+               else                       {vbocount++;vbomemory += buffer->size;if (printeach) Con_Printf("vertexbuffer #%i %s = %i bytes%s\n", i, buffer->name, (int)buffer->size, buffer->isdynamic ? " (dynamic)" : " (static)");}
        }
-       Con_Printf("vertex buffers: %i element buffers totalling %i bytes (%.3f MB), %i vertex buffers totalling %i bytes (%.3f MB), combined %i bytes (%.3fMB)\n", (int)ebocount, (int)ebomemory, ebomemory / 1048576.0, (int)vbocount, (int)vbomemory, vbomemory / 1048576.0, (int)(ebomemory + vbomemory), (ebomemory + vbomemory) / 1048576.0);
+       Con_Printf("vertex buffers: %i indexbuffers totalling %i bytes (%.3f MB), %i vertexbuffers totalling %i bytes (%.3f MB), combined %i bytes (%.3fMB)\n", (int)ebocount, (int)ebomemory, ebomemory / 1048576.0, (int)vbocount, (int)vbomemory, vbomemory / 1048576.0, (int)(ebomemory + vbomemory), (ebomemory + vbomemory) / 1048576.0);
 }
 
-void R_Mesh_VertexPointer(const float *vertex3f, int bufferobject, size_t bufferoffset)
+
+
+void R_Mesh_VertexPointer(int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset)
 {
-       if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
-               bufferobject = 0;
-       if (gl_state.pointer_vertex != vertex3f || gl_state.pointer_vertex_buffer != bufferobject || gl_state.pointer_vertex_offset != bufferoffset)
+       int bufferobject = vertexbuffer ? vertexbuffer->bufferobject : 0;
+       if (gl_state.pointer_vertex_components != components || gl_state.pointer_vertex_gltype != gltype || gl_state.pointer_vertex_stride != stride || gl_state.pointer_vertex_pointer != pointer || gl_state.pointer_vertex_vertexbuffer != vertexbuffer || gl_state.pointer_vertex_offset != bufferoffset)
        {
-               gl_state.pointer_vertex = vertex3f;
-               gl_state.pointer_vertex_buffer = bufferobject;
+               gl_state.pointer_vertex_components = components;
+               gl_state.pointer_vertex_gltype = gltype;
+               gl_state.pointer_vertex_stride = stride;
+               gl_state.pointer_vertex_pointer = pointer;
+               gl_state.pointer_vertex_vertexbuffer = vertexbuffer;
                gl_state.pointer_vertex_offset = bufferoffset;
                CHECKGLERROR
                GL_BindVBO(bufferobject);
-               qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), bufferobject ? (void *)bufferoffset : vertex3f);CHECKGLERROR
+               qglVertexPointer(components, gltype, stride, bufferobject ? (void *)bufferoffset : pointer);CHECKGLERROR
        }
 }
 
-void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferoffset)
+void R_Mesh_ColorPointer(int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset)
 {
-       // note: this can not rely on bufferobject to decide whether a color array
-       // is supplied, because surfmesh_t shares one vbo for all arrays, which
-       // means that a valid vbo may be supplied even if there is no color array.
-       if (color4f)
+       // note: vertexbuffer may be non-NULL even if pointer is NULL, so check
+       // the pointer only.
+       if (pointer)
        {
-               if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
-                       bufferobject = 0;
+               int bufferobject = vertexbuffer ? vertexbuffer->bufferobject : 0;
                // caller wants color array enabled
                if (!gl_state.pointer_color_enabled)
                {
@@ -1483,14 +1634,17 @@ void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferof
                        CHECKGLERROR
                        qglEnableClientState(GL_COLOR_ARRAY);CHECKGLERROR
                }
-               if (gl_state.pointer_color != color4f || gl_state.pointer_color_buffer != bufferobject || gl_state.pointer_color_offset != bufferoffset)
+               if (gl_state.pointer_color_components != components || gl_state.pointer_color_gltype != gltype || gl_state.pointer_color_stride != stride || gl_state.pointer_color_pointer != pointer || gl_state.pointer_color_vertexbuffer != vertexbuffer || gl_state.pointer_color_offset != bufferoffset)
                {
-                       gl_state.pointer_color = color4f;
-                       gl_state.pointer_color_buffer = bufferobject;
+                       gl_state.pointer_color_components = components;
+                       gl_state.pointer_color_gltype = gltype;
+                       gl_state.pointer_color_stride = stride;
+                       gl_state.pointer_color_pointer = pointer;
+                       gl_state.pointer_color_vertexbuffer = vertexbuffer;
                        gl_state.pointer_color_offset = bufferoffset;
                        CHECKGLERROR
                        GL_BindVBO(bufferobject);
-                       qglColorPointer(4, GL_FLOAT, sizeof(float[4]), bufferobject ? (void *)bufferoffset : color4f);CHECKGLERROR
+                       qglColorPointer(components, gltype, stride, bufferobject ? (void *)bufferoffset : pointer);CHECKGLERROR
                }
        }
        else
@@ -1507,17 +1661,16 @@ void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferof
        }
 }
 
-void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, const float *texcoord, int bufferobject, size_t bufferoffset)
+void R_Mesh_TexCoordPointer(unsigned int unitnum, int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset)
 {
        gltextureunit_t *unit = gl_state.units + unitnum;
        // update array settings
        CHECKGLERROR
        // note: there is no need to check bufferobject here because all cases
        // that involve a valid bufferobject also supply a texcoord array
-       if (texcoord)
+       if (pointer)
        {
-               if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
-                       bufferobject = 0;
+               int bufferobject = vertexbuffer ? vertexbuffer->bufferobject : 0;
                // texture array unit is enabled, enable the array
                if (!unit->arrayenabled)
                {
@@ -1526,15 +1679,17 @@ void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, co
                        qglEnableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
                }
                // texcoord array
-               if (unit->pointer_texcoord != texcoord || unit->pointer_texcoord_buffer != bufferobject || unit->pointer_texcoord_offset != bufferoffset || unit->arraycomponents != numcomponents)
+               if (unit->pointer_texcoord_components != components || unit->pointer_texcoord_gltype != gltype || unit->pointer_texcoord_stride != stride || unit->pointer_texcoord_pointer != pointer || unit->pointer_texcoord_vertexbuffer != vertexbuffer || unit->pointer_texcoord_offset != bufferoffset)
                {
-                       unit->pointer_texcoord = texcoord;
-                       unit->pointer_texcoord_buffer = bufferobject;
+                       unit->pointer_texcoord_components = components;
+                       unit->pointer_texcoord_gltype = gltype;
+                       unit->pointer_texcoord_stride = stride;
+                       unit->pointer_texcoord_pointer = pointer;
+                       unit->pointer_texcoord_vertexbuffer = vertexbuffer;
                        unit->pointer_texcoord_offset = bufferoffset;
-                       unit->arraycomponents = numcomponents;
                        GL_ClientActiveTexture(unitnum);
                        GL_BindVBO(bufferobject);
-                       qglTexCoordPointer(unit->arraycomponents, GL_FLOAT, sizeof(float) * unit->arraycomponents, bufferobject ? (void *)bufferoffset : texcoord);CHECKGLERROR
+                       qglTexCoordPointer(components, gltype, stride, bufferobject ? (void *)bufferoffset : pointer);CHECKGLERROR
                }
        }
        else
@@ -1919,3 +2074,467 @@ void R_Mesh_ResetTextureState(void)
                break;
        }
 }
+
+
+
+r_vertexposition_t *R_Mesh_PrepareVertices_Position_Lock(int numvertices)
+{
+       size_t size;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               size = sizeof(r_vertexposition_t) * numvertices;
+               if (gl_state.preparevertices_tempdatamaxsize < size)
+               {
+                       gl_state.preparevertices_tempdatamaxsize = size;
+                       gl_state.preparevertices_tempdata = Mem_Realloc(r_main_mempool, gl_state.preparevertices_tempdata, gl_state.preparevertices_tempdatamaxsize);
+               }
+               gl_state.preparevertices_vertexposition = (r_vertexposition_t *)gl_state.preparevertices_tempdata;
+               gl_state.preparevertices_numvertices = numvertices;
+               return gl_state.preparevertices_vertexposition;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Position_Lock: unrecognized vid.renderpath\n");
+               return NULL;
+       }
+}
+
+qboolean R_Mesh_PrepareVertices_Position_Unlock(void)
+{
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               R_Mesh_PrepareVertices_Position(gl_state.preparevertices_numvertices, gl_state.preparevertices_vertexposition, NULL);
+               gl_state.preparevertices_vertexposition = NULL;
+               gl_state.preparevertices_numvertices = 0;
+               return true;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Position_Lock: unrecognized vid.renderpath\n");
+               return false;
+       }
+}
+
+void R_Mesh_PrepareVertices_Position_Arrays(int numvertices, const float *vertex3f)
+{
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               if (vid.texunits >= 2)
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               if (vid.texunits >= 3)
+                       R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               break;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Position_Lock: unrecognized vid.renderpath\n");
+               return;
+       }
+}
+
+void R_Mesh_PrepareVertices_Position(int numvertices, const r_vertexposition_t *vertex, const r_meshbuffer_t *vertexbuffer)
+{
+       // upload temporary vertexbuffer for this rendering
+       if (!gl_state.usevbo_staticvertex)
+               vertexbuffer = NULL;
+       if (!vertexbuffer && gl_state.usevbo_dynamicvertex)
+       {
+               if (gl_state.preparevertices_dynamicvertexbuffer)
+                       R_Mesh_UpdateMeshBuffer(gl_state.preparevertices_dynamicvertexbuffer, vertex, numvertices * sizeof(*vertex));
+               else
+                       gl_state.preparevertices_dynamicvertexbuffer = R_Mesh_CreateMeshBuffer(vertex, numvertices * sizeof(*vertex), "temporary", false, true);
+               vertexbuffer = gl_state.preparevertices_dynamicvertexbuffer;
+       }
+       if (vertexbuffer)
+       {
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               case RENDERPATH_GL13:
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               case RENDERPATH_GL11:
+                       R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , vertexbuffer, (int)((unsigned char *)vertex->vertex3f           - (unsigned char *)vertex));
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       break;
+               }
+               return;
+       }
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+       case RENDERPATH_GL13:
+               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+       case RENDERPATH_GL11:
+               R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , NULL, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               break;
+       }
+}
+
+
+
+r_vertexgeneric_t *R_Mesh_PrepareVertices_Generic_Lock(int numvertices)
+{
+       size_t size;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               size = sizeof(r_vertexgeneric_t) * numvertices;
+               if (gl_state.preparevertices_tempdatamaxsize < size)
+               {
+                       gl_state.preparevertices_tempdatamaxsize = size;
+                       gl_state.preparevertices_tempdata = Mem_Realloc(r_main_mempool, gl_state.preparevertices_tempdata, gl_state.preparevertices_tempdatamaxsize);
+               }
+               gl_state.preparevertices_vertexgeneric = (r_vertexgeneric_t *)gl_state.preparevertices_tempdata;
+               gl_state.preparevertices_numvertices = numvertices;
+               return gl_state.preparevertices_vertexgeneric;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Generic_Lock: unrecognized vid.renderpath\n");
+               return NULL;
+       }
+}
+
+qboolean R_Mesh_PrepareVertices_Generic_Unlock(void)
+{
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               R_Mesh_PrepareVertices_Generic(gl_state.preparevertices_numvertices, gl_state.preparevertices_vertexgeneric, NULL);
+               gl_state.preparevertices_vertexgeneric = NULL;
+               gl_state.preparevertices_numvertices = 0;
+               return true;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Generic_Lock: unrecognized vid.renderpath\n");
+               return false;
+       }
+}
+
+void R_Mesh_PrepareVertices_Generic_Arrays(int numvertices, const float *vertex3f, const float *color4f, const float *texcoord2f)
+{
+       int i;
+       r_vertexgeneric_t *vertex;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               if (gl_mesh_separatearrays.integer)
+               {
+                       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), color4f, NULL, 0);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), texcoord2f, NULL, 0);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       return;
+               }
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               if (gl_mesh_separatearrays.integer)
+               {
+                       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), color4f, NULL, 0);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), texcoord2f, NULL, 0);
+                       if (vid.texunits >= 2)
+                               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       if (vid.texunits >= 3)
+                               R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       return;
+               }
+               break;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Position_Lock: unrecognized vid.renderpath\n");
+               return;
+       }
+
+       // no quick path for this case, convert to vertex structs
+       vertex = R_Mesh_PrepareVertices_Generic_Lock(numvertices);
+       for (i = 0;i < numvertices;i++)
+               VectorCopy(vertex3f + 3*i, vertex[i].vertex3f);
+       if (color4f)
+       {
+               for (i = 0;i < numvertices;i++)
+                       Vector4Scale(color4f + 4*i, 255.0f, vertex[i].color4ub);
+       }
+       else
+       {
+               float tempcolor4f[4];
+               unsigned char tempcolor4ub[4];
+               Vector4Scale(gl_state.color4f, 255.0f, tempcolor4f);
+               tempcolor4ub[0] = (unsigned char)bound(0.0f, tempcolor4f[0], 255.0f);
+               tempcolor4ub[1] = (unsigned char)bound(0.0f, tempcolor4f[1], 255.0f);
+               tempcolor4ub[2] = (unsigned char)bound(0.0f, tempcolor4f[2], 255.0f);
+               tempcolor4ub[3] = (unsigned char)bound(0.0f, tempcolor4f[3], 255.0f);
+               for (i = 0;i < numvertices;i++)
+                       Vector4Copy(tempcolor4ub, vertex[i].color4ub);
+       }
+       if (texcoord2f)
+               for (i = 0;i < numvertices;i++)
+                       Vector2Copy(texcoord2f + 2*i, vertex[i].texcoord2f);
+       R_Mesh_PrepareVertices_Generic_Unlock();
+       R_Mesh_PrepareVertices_Generic(numvertices, vertex, NULL);
+}
+
+void R_Mesh_PrepareVertices_Generic(int numvertices, const r_vertexgeneric_t *vertex, const r_meshbuffer_t *vertexbuffer)
+{
+       // upload temporary vertexbuffer for this rendering
+       if (!gl_state.usevbo_staticvertex)
+               vertexbuffer = NULL;
+       if (!vertexbuffer && gl_state.usevbo_dynamicvertex)
+       {
+               if (gl_state.preparevertices_dynamicvertexbuffer)
+                       R_Mesh_UpdateMeshBuffer(gl_state.preparevertices_dynamicvertexbuffer, vertex, numvertices * sizeof(*vertex));
+               else
+                       gl_state.preparevertices_dynamicvertexbuffer = R_Mesh_CreateMeshBuffer(vertex, numvertices * sizeof(*vertex), "temporary", false, true);
+               vertexbuffer = gl_state.preparevertices_dynamicvertexbuffer;
+       }
+       if (vertexbuffer)
+       {
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               case RENDERPATH_GL13:
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               case RENDERPATH_GL11:
+                       R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , vertexbuffer, (int)((unsigned char *)vertex->vertex3f           - (unsigned char *)vertex));
+                       R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , vertexbuffer, (int)((unsigned char *)vertex->color4ub           - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoord2f        , vertexbuffer, (int)((unsigned char *)vertex->texcoord2f         - (unsigned char *)vertex));
+                       break;
+               }
+               return;
+       }
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(3, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+               R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+       case RENDERPATH_GL13:
+               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+       case RENDERPATH_GL11:
+               R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , NULL, 0);
+               R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoord2f        , NULL, 0);
+               break;
+       }
+}
+
+
+
+r_vertexmesh_t *R_Mesh_PrepareVertices_Mesh_Lock(int numvertices)
+{
+       size_t size;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               size = sizeof(r_vertexmesh_t) * numvertices;
+               if (gl_state.preparevertices_tempdatamaxsize < size)
+               {
+                       gl_state.preparevertices_tempdatamaxsize = size;
+                       gl_state.preparevertices_tempdata = Mem_Realloc(r_main_mempool, gl_state.preparevertices_tempdata, gl_state.preparevertices_tempdatamaxsize);
+               }
+               gl_state.preparevertices_vertexmesh = (r_vertexmesh_t *)gl_state.preparevertices_tempdata;
+               gl_state.preparevertices_numvertices = numvertices;
+               return gl_state.preparevertices_vertexmesh;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Mesh_Lock: unrecognized vid.renderpath\n");
+               return NULL;
+       }
+}
+
+qboolean R_Mesh_PrepareVertices_Mesh_Unlock(void)
+{
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               R_Mesh_PrepareVertices_Mesh(gl_state.preparevertices_numvertices, gl_state.preparevertices_vertexmesh, NULL);
+               gl_state.preparevertices_vertexmesh = NULL;
+               gl_state.preparevertices_numvertices = 0;
+               return true;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Mesh_Lock: unrecognized vid.renderpath\n");
+               return false;
+       }
+}
+
+void R_Mesh_PrepareVertices_Mesh_Arrays(int numvertices, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *color4f, const float *texcoordtexture2f, const float *texcoordlightmap2f)
+{
+       int i;
+       r_vertexmesh_t *vertex;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               if (gl_mesh_separatearrays.integer)
+               {
+                       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), color4f, NULL, 0);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), texcoordtexture2f, NULL, 0);
+                       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), svector3f, NULL, 0);
+                       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), tvector3f, NULL, 0);
+                       R_Mesh_TexCoordPointer(3, 3, GL_FLOAT, sizeof(float[3]), normal3f, NULL, 0);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), texcoordlightmap2f, NULL, 0);
+                       return;
+               }
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               if (gl_mesh_separatearrays.integer)
+               {
+                       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), vertex3f, NULL, 0);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), color4f, NULL, 0);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), texcoordtexture2f, NULL, 0);
+                       if (vid.texunits >= 2)
+                               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), texcoordlightmap2f, NULL, 0);
+                       if (vid.texunits >= 3)
+                               R_Mesh_TexCoordPointer(2, 2, GL_FLOAT, sizeof(float[2]), NULL, NULL, 0);
+                       return;
+               }
+               break;
+       default:
+               Sys_Error("R_Mesh_PrepareVertices_Position_Lock: unrecognized vid.renderpath\n");
+               return;
+       }
+
+       vertex = R_Mesh_PrepareVertices_Mesh_Lock(numvertices);
+       for (i = 0;i < numvertices;i++)
+               VectorCopy(vertex3f + 3*i, vertex[i].vertex3f);
+       if (svector3f)
+               for (i = 0;i < numvertices;i++)
+                       VectorCopy(svector3f + 3*i, vertex[i].svector3f);
+       if (tvector3f)
+               for (i = 0;i < numvertices;i++)
+                       VectorCopy(tvector3f + 3*i, vertex[i].tvector3f);
+       if (normal3f)
+               for (i = 0;i < numvertices;i++)
+                       VectorCopy(normal3f + 3*i, vertex[i].normal3f);
+       if (color4f)
+       {
+               for (i = 0;i < numvertices;i++)
+                       Vector4Scale(color4f + 4*i, 255.0f, vertex[i].color4ub);
+       }
+       else
+       {
+               float tempcolor4f[4];
+               unsigned char tempcolor4ub[4];
+               Vector4Scale(gl_state.color4f, 255.0f, tempcolor4f);
+               tempcolor4ub[0] = (unsigned char)bound(0.0f, tempcolor4f[0], 255.0f);
+               tempcolor4ub[1] = (unsigned char)bound(0.0f, tempcolor4f[1], 255.0f);
+               tempcolor4ub[2] = (unsigned char)bound(0.0f, tempcolor4f[2], 255.0f);
+               tempcolor4ub[3] = (unsigned char)bound(0.0f, tempcolor4f[3], 255.0f);
+               for (i = 0;i < numvertices;i++)
+                       Vector4Copy(tempcolor4ub, vertex[i].color4ub);
+       }
+       if (texcoordtexture2f)
+               for (i = 0;i < numvertices;i++)
+                       Vector2Copy(texcoordtexture2f + 2*i, vertex[i].texcoordtexture2f);
+       if (texcoordlightmap2f)
+               for (i = 0;i < numvertices;i++)
+                       Vector2Copy(texcoordlightmap2f + 2*i, vertex[i].texcoordlightmap2f);
+       R_Mesh_PrepareVertices_Mesh_Unlock();
+       R_Mesh_PrepareVertices_Mesh(numvertices, vertex, NULL);
+}
+
+void R_Mesh_PrepareVertices_Mesh(int numvertices, const r_vertexmesh_t *vertex, const r_meshbuffer_t *vertexbuffer)
+{
+       // upload temporary vertexbuffer for this rendering
+       if (!gl_state.usevbo_staticvertex)
+               vertexbuffer = NULL;
+       if (!vertexbuffer && gl_state.usevbo_dynamicvertex)
+       {
+               if (gl_state.preparevertices_dynamicvertexbuffer)
+                       R_Mesh_UpdateMeshBuffer(gl_state.preparevertices_dynamicvertexbuffer, vertex, numvertices * sizeof(*vertex));
+               else
+                       gl_state.preparevertices_dynamicvertexbuffer = R_Mesh_CreateMeshBuffer(vertex, numvertices * sizeof(*vertex), "temporary", false, true);
+               vertexbuffer = gl_state.preparevertices_dynamicvertexbuffer;
+       }
+       if (vertexbuffer)
+       {
+               switch(vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+               case RENDERPATH_CGGL:
+                       R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , vertexbuffer, (int)((unsigned char *)vertex->vertex3f           - (unsigned char *)vertex));
+                       R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , vertexbuffer, (int)((unsigned char *)vertex->color4ub           - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordtexture2f , vertexbuffer, (int)((unsigned char *)vertex->texcoordtexture2f  - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT        , sizeof(*vertex), vertex->svector3f         , vertexbuffer, (int)((unsigned char *)vertex->svector3f          - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT        , sizeof(*vertex), vertex->tvector3f         , vertexbuffer, (int)((unsigned char *)vertex->tvector3f          - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(3, 4, GL_FLOAT        , sizeof(*vertex), vertex->normal3f          , vertexbuffer, (int)((unsigned char *)vertex->normal3f           - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordlightmap2f, vertexbuffer, (int)((unsigned char *)vertex->texcoordlightmap2f - (unsigned char *)vertex));
+                       break;
+               case RENDERPATH_GL13:
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordlightmap2f, vertexbuffer, (int)((unsigned char *)vertex->texcoordlightmap2f - (unsigned char *)vertex));
+               case RENDERPATH_GL11:
+                       R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , vertexbuffer, (int)((unsigned char *)vertex->vertex3f           - (unsigned char *)vertex));
+                       R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , vertexbuffer, (int)((unsigned char *)vertex->color4ub           - (unsigned char *)vertex));
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordtexture2f , vertexbuffer, (int)((unsigned char *)vertex->texcoordtexture2f  - (unsigned char *)vertex));
+                       break;
+               }
+               return;
+       }
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL20:
+       case RENDERPATH_CGGL:
+               R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , NULL, 0);
+               R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordtexture2f , NULL, 0);
+               R_Mesh_TexCoordPointer(1, 3, GL_FLOAT        , sizeof(*vertex), vertex->svector3f         , NULL, 0);
+               R_Mesh_TexCoordPointer(2, 3, GL_FLOAT        , sizeof(*vertex), vertex->tvector3f         , NULL, 0);
+               R_Mesh_TexCoordPointer(3, 4, GL_FLOAT        , sizeof(*vertex), vertex->normal3f          , NULL, 0);
+               R_Mesh_TexCoordPointer(4, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordlightmap2f, NULL, 0);
+               break;
+       case RENDERPATH_GL13:
+               R_Mesh_TexCoordPointer(1, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordlightmap2f, NULL, 0);
+       case RENDERPATH_GL11:
+               R_Mesh_VertexPointer(     3, GL_FLOAT        , sizeof(*vertex), vertex->vertex3f          , NULL, 0);
+               R_Mesh_ColorPointer(      4, GL_UNSIGNED_BYTE, sizeof(*vertex), vertex->color4ub          , NULL, 0);
+               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT        , sizeof(*vertex), vertex->texcoordtexture2f , NULL, 0);
+               break;
+       }
+}
index cea789c..2e93c89 100644 (file)
@@ -59,28 +59,36 @@ void R_Mesh_Start(void);
 // (only valid after R_Mesh_Start)
 void R_Mesh_Finish(void);
 
-// allocates a static element array buffer object
-// (storing triangle data in video memory)
-int R_Mesh_CreateStaticEBO(void *data, size_t size);
-// frees an element array buffer object
-void R_Mesh_DestroyEBO(int bufferobject);
-// allocates a static vertex/element array buffer object
-// (storing vertex or element data in video memory)
-// target is GL_ELEMENT_ARRAY_BUFFER_ARB (triangle elements)
-// or GL_ARRAY_BUFFER_ARB (vertex data)
-int R_Mesh_CreateStaticBufferObject(unsigned int target, void *data, size_t size, const char *name);
-// frees a vertex/element array buffer object
-void R_Mesh_DestroyBufferObject(int bufferobject);
+
+// vertex buffer and index buffer creation/updating/freeing
+r_meshbuffer_t *R_Mesh_CreateMeshBuffer(const void *data, size_t size, const char *name, qboolean isindexbuffer, qboolean isdynamic);
+void R_Mesh_UpdateMeshBuffer(r_meshbuffer_t *buffer, const void *data, size_t size);
+void R_Mesh_DestroyMeshBuffer(r_meshbuffer_t *buffer);
 void GL_Mesh_ListVBOs(qboolean printeach);
 
+r_vertexposition_t *R_Mesh_PrepareVertices_Position_Lock(int numvertices);
+qboolean R_Mesh_PrepareVertices_Position_Unlock(void); // if this returns false, you need to prepare the mesh again!
+void R_Mesh_PrepareVertices_Position_Arrays(int numvertices, const float *vertex3f);
+void R_Mesh_PrepareVertices_Position(int numvertices, const r_vertexposition_t *vertex, const r_meshbuffer_t *buffer);
+
+r_vertexgeneric_t *R_Mesh_PrepareVertices_Generic_Lock(int numvertices);
+qboolean R_Mesh_PrepareVertices_Generic_Unlock(void);
+void R_Mesh_PrepareVertices_Generic_Arrays(int numvertices, const float *vertex3f, const float *color4f, const float *texcoord2f);
+void R_Mesh_PrepareVertices_Generic(int numvertices, const r_vertexgeneric_t *vertex, const r_meshbuffer_t *vertexbuffer);
+
+r_vertexmesh_t *R_Mesh_PrepareVertices_Mesh_Lock(int numvertices);
+qboolean R_Mesh_PrepareVertices_Mesh_Unlock(void); // if this returns false, you need to prepare the mesh again!
+void R_Mesh_PrepareVertices_Mesh_Arrays(int numvertices, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *color4f, const float *texcoordtexture2f, const float *texcoordlightmap2f);
+void R_Mesh_PrepareVertices_Mesh(int numvertices, const r_vertexmesh_t *vertex, const r_meshbuffer_t *buffer);
+
 // sets up the requested vertex transform matrix
 void R_EntityMatrix(const matrix4x4_t *matrix);
 // sets the vertex array pointer
-void R_Mesh_VertexPointer(const float *vertex3f, int bufferobject, size_t bufferoffset);
+void R_Mesh_VertexPointer(int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset);
 // sets the color array pointer (GL_Color only works when this is NULL)
-void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferoffset);
+void R_Mesh_ColorPointer(int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset);
 // sets the texcoord array pointer for an array unit
-void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, const float *texcoord, int bufferobject, size_t bufferoffset);
+void R_Mesh_TexCoordPointer(unsigned int unitnum, int components, int gltype, size_t stride, const void *pointer, const r_meshbuffer_t *vertexbuffer, size_t bufferoffset);
 // returns current texture bound to this identifier
 int R_Mesh_TexBound(unsigned int unitnum, int id);
 // copies a section of the framebuffer to a 2D texture
@@ -95,7 +103,7 @@ void R_Mesh_TexCombine(unsigned int unitnum, int combinergb, int combinealpha, i
 void R_Mesh_ResetTextureState(void);
 
 // renders a mesh
-void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int bufferobject3i, int bufferobject3s);
+void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const r_meshbuffer_t *element3i_indexbuffer, size_t element3i_bufferoffset, const unsigned short *element3s, const r_meshbuffer_t *element3s_indexbuffer, size_t element3s_bufferoffset);
 
 // saves a section of the rendered frame to a .tga or .jpg file
 qboolean SCR_ScreenShot(char *filename, unsigned char *buffer1, unsigned char *buffer2, unsigned char *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg, qboolean png, qboolean gammacorrect);
index de8bc08..84dfbd8 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -934,14 +934,19 @@ static void _DrawQ_ProcessDrawFlag(int flags)
 
 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
 {
-       float floats[20];
+       float floats[36];
 
        _DrawQ_ProcessDrawFlag(flags);
-       GL_Color(red, green, blue, alpha);
 
-       R_Mesh_VertexPointer(floats, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
+       floats[12] = 0.0f;floats[13] = 0.0f;
+       floats[14] = 1.0f;floats[15] = 0.0f;
+       floats[16] = 1.0f;floats[17] = 1.0f;
+       floats[18] = 0.0f;floats[19] = 1.0f;
+       floats[20] = floats[24] = floats[28] = floats[32] = red;
+       floats[21] = floats[25] = floats[29] = floats[33] = green;
+       floats[22] = floats[26] = floats[30] = floats[34] = blue;
+       floats[23] = floats[27] = floats[31] = floats[35] = alpha;
        if (pic)
        {
                if (width == 0)
@@ -949,14 +954,8 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo
                if (height == 0)
                        height = pic->height;
                R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
-
-#if 1
-               floats[12] = 0.0f;floats[13] = 0.0f;
-               floats[14] = 1.0f;floats[15] = 0.0f;
-               floats[16] = 1.0f;floats[17] = 1.0f;
-               floats[18] = 0.0f;floats[19] = 1.0f;
-#else
+
+#if 0
       // AK07: lets be texel correct on the corners
       {
          float horz_offset = 0.5f / pic->width;
@@ -978,12 +977,13 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo
        floats[3] = floats[6] = x + width;
        floats[7] = floats[10] = y + height;
 
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 }
 
 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
 {
-       float floats[20];
+       float floats[36];
        float af = DEG2RAD(-angle); // forward
        float ar = DEG2RAD(-angle + 90); // right
        float sinaf = sin(af);
@@ -992,10 +992,7 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height,
        float cosar = cos(ar);
 
        _DrawQ_ProcessDrawFlag(flags);
-       GL_Color(red, green, blue, alpha);
 
-       R_Mesh_VertexPointer(floats, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        if (pic)
        {
@@ -1004,12 +1001,6 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height,
                if (height == 0)
                        height = pic->height;
                R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
-
-               floats[12] = 0.0f;floats[13] = 0.0f;
-               floats[14] = 1.0f;floats[15] = 0.0f;
-               floats[16] = 1.0f;floats[17] = 1.0f;
-               floats[18] = 0.0f;floats[19] = 1.0f;
        }
        else
                R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
@@ -1032,18 +1023,25 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height,
        floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
        floats[10] = y - sinaf*org_x + sinar*(height-org_y);
 
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       floats[12] = 0.0f;floats[13] = 0.0f;
+       floats[14] = 1.0f;floats[15] = 0.0f;
+       floats[16] = 1.0f;floats[17] = 1.0f;
+       floats[18] = 0.0f;floats[19] = 1.0f;
+       floats[20] = floats[24] = floats[28] = floats[32] = red;
+       floats[21] = floats[25] = floats[29] = floats[33] = green;
+       floats[22] = floats[26] = floats[30] = floats[34] = blue;
+       floats[23] = floats[27] = floats[31] = floats[35] = alpha;
+
+       R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 }
 
 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
 {
-       float floats[12];
+       float floats[36];
 
        _DrawQ_ProcessDrawFlag(flags);
-       GL_Color(red, green, blue, alpha);
 
-       R_Mesh_VertexPointer(floats, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
 
@@ -1052,8 +1050,17 @@ void DrawQ_Fill(float x, float y, float width, float height, float red, float gr
        floats[1] = floats[4] = y;
        floats[3] = floats[6] = x + width;
        floats[7] = floats[10] = y + height;
-
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       floats[12] = 0.0f;floats[13] = 0.0f;
+       floats[14] = 1.0f;floats[15] = 0.0f;
+       floats[16] = 1.0f;floats[17] = 1.0f;
+       floats[18] = 0.0f;floats[19] = 1.0f;
+       floats[20] = floats[24] = floats[28] = floats[32] = red;
+       floats[21] = floats[25] = floats[29] = floats[33] = green;
+       floats[22] = floats[26] = floats[30] = floats[34] = blue;
+       floats[23] = floats[27] = floats[31] = floats[35] = alpha;
+
+       R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 }
 
 /// color tag printing
@@ -1358,12 +1365,9 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
 
        _DrawQ_ProcessDrawFlag(flags);
 
-       R_Mesh_ColorPointer(color4f, 0, 0);
        R_Mesh_ResetTextureState();
        if (!fontmap)
                R_Mesh_TexBind(0, fnt->tex);
-       R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
        R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
 
        ac = color4f;
@@ -1493,7 +1497,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                                                if (batchcount)
                                                {
                                                        // switching from freetype to non-freetype rendering
-                                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                                       R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
+                                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
                                                        batchcount = 0;
                                                        ac = color4f;
                                                        at = texcoord2f;
@@ -1530,7 +1535,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                                batchcount++;
                                if (batchcount >= QUADELEMENTS_MAXQUADS)
                                {
-                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
+                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
                                        batchcount = 0;
                                        ac = color4f;
                                        at = texcoord2f;
@@ -1544,7 +1550,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                                        if (batchcount)
                                        {
                                                // we need a different character map, render what we currently have:
-                                               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                               R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
+                                               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
                                                batchcount = 0;
                                                ac = color4f;
                                                at = texcoord2f;
@@ -1603,7 +1610,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                                batchcount++;
                                if (batchcount >= QUADELEMENTS_MAXQUADS)
                                {
-                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
+                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
                                        batchcount = 0;
                                        ac = color4f;
                                        at = texcoord2f;
@@ -1621,7 +1629,10 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma
                }
        }
        if (batchcount > 0)
-               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+       {
+               R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
+               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
+       }
 
        if (outcolor)
                *outcolor = colorindex;
@@ -1696,8 +1707,6 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height
 
        _DrawQ_ProcessDrawFlag(flags);
 
-       R_Mesh_VertexPointer(floats, 0, 0);
-       R_Mesh_ColorPointer(floats + 20, 0, 0);
        R_Mesh_ResetTextureState();
        if (pic)
        {
@@ -1706,11 +1715,6 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height
                if (height == 0)
                        height = pic->height;
                R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
-               floats[12] = s1;floats[13] = t1;
-               floats[14] = s2;floats[15] = t2;
-               floats[16] = s4;floats[17] = t4;
-               floats[18] = s3;floats[19] = t3;
        }
        else
                R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
@@ -1720,25 +1724,28 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height
        floats[1] = floats[4] = y;
        floats[3] = floats[6] = x + width;
        floats[7] = floats[10] = y + height;
+       floats[12] = s1;floats[13] = t1;
+       floats[14] = s2;floats[15] = t2;
+       floats[16] = s4;floats[17] = t4;
+       floats[18] = s3;floats[19] = t3;
        floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
        floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
        floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
        floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
 
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
 }
 
 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
 {
        _DrawQ_ProcessDrawFlag(flags);
 
-       R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
-       R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
        R_Mesh_ResetTextureState();
-       R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
        R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
 
-       R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
+       R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
 }
 
 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
@@ -1824,8 +1831,6 @@ void R_DrawGamma(void)
                break;
        }
        // all the blends ignore depth
-       R_Mesh_VertexPointer(blendvertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
        GL_DepthMask(true);
@@ -1845,8 +1850,9 @@ void R_DrawGamma(void)
                GL_BlendFunc(GL_DST_COLOR, GL_ONE);
                while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
                {
-                       GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
-                       R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
+                       GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
+                       R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
+                       R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        VectorScale(c, 0.5, c);
                }
        }
@@ -1861,8 +1867,9 @@ void R_DrawGamma(void)
        if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
        {
                GL_BlendFunc(GL_ONE, GL_ONE);
-               GL_Color(c[0], c[1], c[2], 1);
-               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
+               GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
+               R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
+               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        }
 }
 
index 29acd58..d07ad15 100644 (file)
@@ -168,7 +168,6 @@ cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces
 cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture (for level designers)"};
 
 cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
-cvar_t r_batchmode = {0, "r_batchmode", "1", "selects method of rendering multiple surfaces with one driver call (values are 0, 1, 2, etc...)"};
 cvar_t r_track_sprites = {CVAR_SAVE, "r_track_sprites", "1", "track SPR_LABEL* sprites by putting them as indicator at the screen border to rotate to"};
 cvar_t r_track_sprites_flags = {CVAR_SAVE, "r_track_sprites_flags", "1", "1: Rotate sprites accodringly, 2: Make it a continuous rotation"};
 cvar_t r_track_sprites_scalew = {CVAR_SAVE, "r_track_sprites_scalew", "1", "width scaling of tracked sprites"};
@@ -655,7 +654,7 @@ static const char *builtinshaderstring =
 "      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
 "      TexCoord1 = gl_MultiTexCoord0.xy;\n"
 "#ifdef USEBLOOM\n"
-"      TexCoord2 = gl_MultiTexCoord1.xy;\n"
+"      TexCoord2 = gl_MultiTexCoord4.xy;\n"
 "#endif\n"
 "}\n"
 "#endif\n"
@@ -1908,7 +1907,7 @@ const char *builtincgshaderstring =
 "float4 gl_Vertex : POSITION,\n"
 "uniform float4x4 ModelViewProjectionMatrix,\n"
 "float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"float4 gl_MultiTexCoord1 : TEXCOORD1,\n"
+"float4 gl_MultiTexCoord1 : TEXCOORD4,\n"
 "out float4 gl_Position : POSITION,\n"
 "out float2 TexCoord1 : TEXCOORD0,\n"
 "out float2 TexCoord2 : TEXCOORD1\n"
@@ -4547,7 +4546,8 @@ extern rtexture_t *r_shadow_prepassgeometrydepthtexture;
 extern rtexture_t *r_shadow_prepassgeometrynormalmaptexture;
 extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
 extern rtexture_t *r_shadow_prepasslightingspeculartexture;
-void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass)
+extern cvar_t gl_mesh_separatearrays;
+void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
@@ -4568,12 +4568,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        mode = SHADERMODE_GENERIC;
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                }
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest(false);
                GL_BlendFunc(GL_ONE, GL_ZERO);
        }
@@ -4600,15 +4594,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                mode = SHADERMODE_DEFERREDGEOMETRY;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest(false);
                GL_BlendFunc(GL_ONE, GL_ZERO);
        }
@@ -4667,24 +4652,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               //R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        }
@@ -4733,24 +4700,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_REFLECTION;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -4807,21 +4756,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -4871,21 +4805,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
@@ -4948,11 +4867,6 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                if (r_shadow_glossexact.integer)
                                        permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
                        }
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
                else if (r_glsl_deluxemapping.integer >= 2)
                {
@@ -4965,41 +4879,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                if (r_shadow_glossexact.integer)
                                        permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
                        }
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
                else if (rsurface.uselightmaptexture)
                {
                        // ordinary lightmapping (q1bsp, q3bsp)
                        mode = SHADERMODE_LIGHTMAP;
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
                else
                {
                        // ordinary vertex coloring (q3bsp)
                        mode = SHADERMODE_VERTEXCOLOR;
-                       R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               }
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
                }
                GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
@@ -5007,6 +4896,22 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
+               if (gl_mesh_separatearrays.integer)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_VertexPointer(     3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
+                       R_Mesh_ColorPointer(      4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchsvector3f, rsurface.batchsvector3f_vertexbuffer, rsurface.batchsvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(3, 4, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
+               }
+               else
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer);
+               }
                R_SetupShader_SetPermutationGLSL(mode, permutation);
                if (r_glsl_permutation->loc_ModelToReflectCube >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelToReflectCube, 1, false, m16f);}
                if (mode == SHADERMODE_LIGHTSOURCE)
@@ -5137,6 +5042,22 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                break;
        case RENDERPATH_CGGL:
 #ifdef SUPPORTCG
+               if (gl_mesh_separatearrays.integer)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_VertexPointer(     3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
+                       R_Mesh_ColorPointer(      4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchsvector3f, rsurface.batchsvector3f_vertexbuffer, rsurface.batchsvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(3, 4, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
+               }
+               else
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer);
+               }
                R_SetupShader_SetPermutationCG(mode, permutation);
                if (r_cg_permutation->fp_ModelToReflectCube) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);cgGLSetMatrixParameterfc(r_cg_permutation->fp_ModelToReflectCube, m16f);}CHECKCGERROR
                if (mode == SHADERMODE_LIGHTSOURCE)
@@ -6491,7 +6412,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&developer_texturelogging);
        Cvar_RegisterVariable(&gl_lightmaps);
        Cvar_RegisterVariable(&r_test);
-       Cvar_RegisterVariable(&r_batchmode);
        Cvar_RegisterVariable(&r_glsl_saturation);
        Cvar_RegisterVariable(&r_framedatasize);
        if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
@@ -6759,6 +6679,41 @@ void R_AnimCache_ClearCache(void)
                ent->animcache_normal3f = NULL;
                ent->animcache_svector3f = NULL;
                ent->animcache_tvector3f = NULL;
+               ent->animcache_vertexposition = NULL;
+               ent->animcache_vertexmesh = NULL;
+               ent->animcache_vertexpositionbuffer = NULL;
+               ent->animcache_vertexmeshbuffer = NULL;
+       }
+}
+
+void R_AnimCache_UpdateEntityMeshBuffers(entity_render_t *ent, int numvertices)
+{
+       int i;
+       if (!ent->animcache_vertexmesh && ent->animcache_normal3f)
+               ent->animcache_vertexmesh = R_FrameData_Alloc(sizeof(r_vertexmesh_t)*numvertices);
+       if (!ent->animcache_vertexposition)
+               ent->animcache_vertexposition = R_FrameData_Alloc(sizeof(r_vertexposition_t)*numvertices);
+       if (ent->animcache_vertexposition)
+       {
+               for (i = 0;i < numvertices;i++)
+                       VectorCopy(ent->animcache_vertex3f + 3*i, ent->animcache_vertexposition[i].vertex3f);
+               // TODO: upload vertex buffer?
+       }
+       if (ent->animcache_vertexmesh)
+       {
+               memcpy(ent->animcache_vertexmesh, ent->model->surfmesh.vertexmesh, sizeof(r_vertexmesh_t)*numvertices);
+               for (i = 0;i < numvertices;i++)
+                       VectorCopy(ent->animcache_vertex3f + 3*i, ent->animcache_vertexmesh[i].vertex3f);
+               if (ent->animcache_svector3f)
+                       for (i = 0;i < numvertices;i++)
+                               VectorCopy(ent->animcache_svector3f + 3*i, ent->animcache_vertexmesh[i].svector3f);
+               if (ent->animcache_tvector3f)
+                       for (i = 0;i < numvertices;i++)
+                               VectorCopy(ent->animcache_tvector3f + 3*i, ent->animcache_vertexmesh[i].tvector3f);
+               if (ent->animcache_normal3f)
+                       for (i = 0;i < numvertices;i++)
+                               VectorCopy(ent->animcache_normal3f + 3*i, ent->animcache_vertexmesh[i].normal3f);
+               // TODO: upload vertex buffer?
        }
 }
 
@@ -6769,7 +6724,7 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
        // see if it's already cached this frame
        if (ent->animcache_vertex3f)
        {
-               // add normals/tangents if needed
+               // add normals/tangents if needed (this only happens with multiple views, reflections, cameras, etc)
                if (wantnormals || wanttangents)
                {
                        if (ent->animcache_normal3f)
@@ -6787,7 +6742,10 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                                        ent->animcache_tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
                                }
                                if (!r_framedata_failed)
+                               {
                                        model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? ent->animcache_normal3f : NULL, wanttangents ? ent->animcache_svector3f : NULL, wanttangents ? ent->animcache_tvector3f : NULL);
+                                       R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices);
+                               }
                        }
                }
        }
@@ -6807,7 +6765,10 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                        ent->animcache_tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
                }
                if (!r_framedata_failed)
+               {
                        model->AnimateVertices(model, ent->frameblend, ent->skeleton, ent->animcache_vertex3f, ent->animcache_normal3f, ent->animcache_svector3f, ent->animcache_tvector3f);
+                       R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices);
+               }
        }
        return !r_framedata_failed;
 }
@@ -6815,7 +6776,7 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
 void R_AnimCache_CacheVisibleEntities(void)
 {
        int i;
-       qboolean wantnormals = !r_showsurfaces.integer;
+       qboolean wantnormals = true;
        qboolean wanttangents = !r_showsurfaces.integer;
 
        switch(vid.renderpath)
@@ -6829,6 +6790,9 @@ void R_AnimCache_CacheVisibleEntities(void)
                break;
        }
 
+       if (r_shownormals.integer)
+               wanttangents = wantnormals = true;
+
        // TODO: thread this
        // NOTE: R_PrepareRTLights() also caches entities
 
@@ -7853,10 +7817,10 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        R_SetViewport(&r_bloomstate.viewport);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_Color(colorscale, colorscale, colorscale, 1);
-       // TODO: optimize with multitexture or GLSL
-       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+       // TODO: do boxfilter scale-down in shader?
        R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1);
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
        // we now have a bloom image in the framebuffer
@@ -7879,8 +7843,6 @@ void R_Bloom_MakeTexture(void)
        r_refdef.stats.bloom++;
 
        R_ResetViewRendering2D();
-       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
 
        // we have a bloom image in the framebuffer
        CHECKGLERROR
@@ -7891,10 +7853,10 @@ void R_Bloom_MakeTexture(void)
                x *= 2;
                r = bound(0, r_bloom_colorexponent.value / x, 1);
                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
-               GL_Color(r, r, r, 1);
+               GL_Color(r,r,r,1);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
                R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                // copy the vertically blurred bloom view to a texture
@@ -7910,7 +7872,6 @@ void R_Bloom_MakeTexture(void)
        if(range >= 1)
                brighten *= (3 * range) / (2 * range - 1); // compensate for the "dot particle"
        R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.offsettexcoord2f, 0, 0);
 
        for (dir = 0;dir < 2;dir++)
        {
@@ -7942,7 +7903,8 @@ void R_Bloom_MakeTexture(void)
                        if(range >= 1)
                                r *= (1 - x*x/(float)(range*range));
                        GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.offsettexcoord2f);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                        GL_BlendFunc(GL_ONE, GL_ONE);
                }
@@ -7957,18 +7919,18 @@ void R_Bloom_MakeTexture(void)
        if (r_bloom_colorsubtract.value > 0 && vid.support.ext_blend_subtract)
        {
                GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_Color(1,1,1,1);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
                R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               GL_Color(1, 1, 1, 1);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                GL_BlendFunc(GL_ONE, GL_ONE);
                qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
                R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
                GL_Color(r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 1);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                qglBlendEquationEXT(GL_FUNC_ADD_EXT);
 
@@ -8054,8 +8016,6 @@ static void R_BlendView(void)
                        if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
 
                        R_ResetViewRendering2D();
-                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                       R_Mesh_ColorPointer(NULL, 0, 0);
 
                        if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
                        {
@@ -8090,9 +8050,9 @@ static void R_BlendView(void)
                                {
                                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                                        GL_Color(1, 1, 1, cl.motionbluralpha);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
                                        R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1);
-                                       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
-                                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                                        r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                                }
                        }
@@ -8108,12 +8068,11 @@ static void R_BlendView(void)
                        {
                                // apply a color tint to the whole view
                                R_ResetViewRendering2D();
-                               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                               R_Mesh_ColorPointer(NULL, 0, 0);
+                               GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
                                R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-                               GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        }
                        break; // no screen processing, no bloom, skip it
                }
@@ -8137,12 +8096,9 @@ static void R_BlendView(void)
                sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
 
                R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                GL_Color(1, 1, 1, 1);
+               R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
-               R_Mesh_TexCoordPointer(1, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
 
                switch(vid.renderpath)
                {
@@ -8179,7 +8135,7 @@ static void R_BlendView(void)
                default:
                        break;
                }
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                break;
        case RENDERPATH_GL13:
@@ -8188,12 +8144,11 @@ static void R_BlendView(void)
                {
                        // apply a color tint to the whole view
                        R_ResetViewRendering2D();
-                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                       R_Mesh_ColorPointer(NULL, 0, 0);
+                       GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
                        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-                       GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                }
                break;
        }
@@ -8785,11 +8740,10 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
                        c[2] = c[2] * f1 + r_refdef.fogcolor[2] * f2;
                }
        }
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
-       R_Mesh_ColorPointer(color4f, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(8, vertex3f, color4f, NULL);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-       R_Mesh_Draw(0, 8, 0, 12, NULL, bboxelements, 0, 0);
+       R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
 static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
@@ -8935,9 +8889,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        GL_DepthTest(!(rsurface.ent_flags & RENDER_NODEPTHTEST));
        GL_CullFace((rsurface.ent_flags & RENDER_DOUBLESIDED) ? GL_NONE : r_refdef.view.cullface_back);
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-       R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
        memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-       R_Mesh_ColorPointer(color4f, 0, 0);
        for (i = 0, c = color4f;i < 6;i++, c += 4)
        {
                c[0] *= rsurface.colormod[0];
@@ -8949,7 +8901,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        {
                for (i = 0, c = color4f;i < 6;i++, c += 4)
                {
-                       f1 = RSurf_FogVertex(rsurface.vertex3f + 3*i);
+                       f1 = RSurf_FogVertex(nomodelvertex3f + 3*i);
                        f2 = 1 - f1;
                        c[0] = (c[0] * f1 + r_refdef.fogcolor[0] * f2);
                        c[1] = (c[1] * f1 + r_refdef.fogcolor[1] * f2);
@@ -8957,7 +8909,8 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
                }
        }
        R_Mesh_ResetTextureState();
-       R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, nomodelelement3s, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(6, nomodelvertex3f, color4f, NULL);
+       R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, NULL, 0, nomodelelement3s, NULL, 0);
 }
 
 void R_DrawNoModel(entity_render_t *ent)
@@ -9396,7 +9349,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        }
        else
        {
-               t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
+               t->backgroundbasetexture = r_texture_white;
                t->backgroundnmaptexture = r_texture_blanknormalmap;
                t->backgroundglosstexture = r_texture_black;
                t->backgroundglowtexture = NULL;
@@ -9543,24 +9496,51 @@ rsurfacestate_t rsurface;
 
 void R_Mesh_ResizeArrays(int newvertices)
 {
-       float *base;
+       unsigned char *base;
+       size_t size;
        if (rsurface.array_size >= newvertices)
                return;
-       if (rsurface.array_modelvertex3f)
-               Mem_Free(rsurface.array_modelvertex3f);
+       if (rsurface.array_base)
+               Mem_Free(rsurface.array_base);
        rsurface.array_size = (newvertices + 1023) & ~1023;
-       base = (float *)Mem_Alloc(r_main_mempool, rsurface.array_size * sizeof(float[33]));
-       rsurface.array_modelvertex3f     = base + rsurface.array_size * 0;
-       rsurface.array_modelsvector3f    = base + rsurface.array_size * 3;
-       rsurface.array_modeltvector3f    = base + rsurface.array_size * 6;
-       rsurface.array_modelnormal3f     = base + rsurface.array_size * 9;
-       rsurface.array_deformedvertex3f  = base + rsurface.array_size * 12;
-       rsurface.array_deformedsvector3f = base + rsurface.array_size * 15;
-       rsurface.array_deformedtvector3f = base + rsurface.array_size * 18;
-       rsurface.array_deformednormal3f  = base + rsurface.array_size * 21;
-       rsurface.array_texcoord3f        = base + rsurface.array_size * 24;
-       rsurface.array_color4f           = base + rsurface.array_size * 27;
-       rsurface.array_generatedtexcoordtexture2f = base + rsurface.array_size * 31;
+       size = 0;
+       size += rsurface.array_size * sizeof(*rsurface.array_modelvertexmesh);
+       size += rsurface.array_size * sizeof(*rsurface.array_batchvertexmesh);
+       size += rsurface.array_size * sizeof(*rsurface.array_modelvertexposition);
+       size += rsurface.array_size * sizeof(*rsurface.array_batchvertexposition);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[3]);
+       size += rsurface.array_size * sizeof(float[4]);
+       size += rsurface.array_size * sizeof(float[2]);
+       size += rsurface.array_size * sizeof(float[2]);
+       size += rsurface.array_size * sizeof(float[4]);
+       size += rsurface.array_size * sizeof(int[3]);
+       size += rsurface.array_size * sizeof(unsigned short[3]);
+       rsurface.array_base = base = (unsigned char *)Mem_Alloc(r_main_mempool, size);
+       rsurface.array_modelvertexmesh         = (r_vertexmesh_t     *)base;base += rsurface.array_size * sizeof(*rsurface.array_modelvertexmesh);
+       rsurface.array_batchvertexmesh         = (r_vertexmesh_t     *)base;base += rsurface.array_size * sizeof(*rsurface.array_batchvertexmesh);
+       rsurface.array_modelvertexposition     = (r_vertexposition_t *)base;base += rsurface.array_size * sizeof(*rsurface.array_modelvertexposition);
+       rsurface.array_batchvertexposition     = (r_vertexposition_t *)base;base += rsurface.array_size * sizeof(*rsurface.array_batchvertexposition);
+       rsurface.array_modelvertex3f           = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_modelsvector3f          = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_modeltvector3f          = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_modelnormal3f           = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_batchvertex3f           = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_batchsvector3f          = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_batchtvector3f          = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_batchnormal3f           = (float              *)base;base += rsurface.array_size * sizeof(float[3]);
+       rsurface.array_batchlightmapcolor4f    = (float              *)base;base += rsurface.array_size * sizeof(float[4]);
+       rsurface.array_batchtexcoordtexture2f  = (float              *)base;base += rsurface.array_size * sizeof(float[2]);
+       rsurface.array_batchtexcoordlightmap2f = (float              *)base;base += rsurface.array_size * sizeof(float[2]);
+       rsurface.array_passcolor4f             = (float              *)base;base += rsurface.array_size * sizeof(float[4]);
+       rsurface.array_batchelement3i          = (int                *)base;base += rsurface.array_size * sizeof(int[3]);
+       rsurface.array_batchelement3s          = (unsigned short     *)base;base += rsurface.array_size * sizeof(unsigned short[3]);
 }
 
 void RSurf_ActiveWorldEntity(void)
@@ -9601,48 +9581,80 @@ void RSurf_ActiveWorldEntity(void)
        rsurface.basepolygonfactor = r_refdef.polygonfactor;
        rsurface.basepolygonoffset = r_refdef.polygonoffset;
        rsurface.modelvertex3f  = model->surfmesh.data_vertex3f;
-       rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
        rsurface.modelsvector3f = model->surfmesh.data_svector3f;
-       rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelsvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
        rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
-       rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
        rsurface.modelnormal3f  = model->surfmesh.data_normal3f;
-       rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelnormal3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
        rsurface.modellightmapcolor4f  = model->surfmesh.data_lightmapcolor4f;
-       rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+       rsurface.modellightmapcolor4f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
        rsurface.modeltexcoordtexture2f  = model->surfmesh.data_texcoordtexture2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
        rsurface.modeltexcoordlightmap2f  = model->surfmesh.data_texcoordlightmap2f;
-       rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
+       rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer;
+       rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset;
        rsurface.modelelement3s = model->surfmesh.data_element3s;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
-       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
+       rsurface.modelelement3s_indexbuffer = model->surfmesh.data_element3s_indexbuffer;
+       rsurface.modelelement3s_bufferoffset = model->surfmesh.data_element3s_bufferoffset;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
-       rsurface.modelnum_vertices = model->surfmesh.num_vertices;
-       rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+       rsurface.modelnumvertices = model->surfmesh.num_vertices;
+       rsurface.modelnumtriangles = model->surfmesh.num_triangles;
        rsurface.modelsurfaces = model->data_surfaces;
-       rsurface.generatedvertex = false;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.modelvertexmesh = model->surfmesh.vertexmesh;
+       rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer;
+       rsurface.modelvertexposition = model->surfmesh.vertexposition;
+       rsurface.modelvertexpositionbuffer = model->surfmesh.vertexpositionbuffer;
+       rsurface.modelgeneratedvertex = false;
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertexposition = NULL;
+       rsurface.batchvertexpositionbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents, qboolean prepass)
@@ -9694,6 +9706,10 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modelsvector3f = wanttangents ? ent->animcache_svector3f : NULL;
                        rsurface.modeltvector3f = wanttangents ? ent->animcache_tvector3f : NULL;
                        rsurface.modelnormal3f = wantnormals ? ent->animcache_normal3f : NULL;
+                       rsurface.modelvertexmesh = ent->animcache_vertexmesh;
+                       rsurface.modelvertexmeshbuffer = ent->animcache_vertexmeshbuffer;
+                       rsurface.modelvertexposition = ent->animcache_vertexposition;
+                       rsurface.modelvertexpositionbuffer = ent->animcache_vertexpositionbuffer;
                }
                else if (wanttangents)
                {
@@ -9702,6 +9718,10 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modeltvector3f = rsurface.array_modeltvector3f;
                        rsurface.modelnormal3f = rsurface.array_modelnormal3f;
                        model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertexposition = NULL;
+                       rsurface.modelvertexpositionbuffer = NULL;
                }
                else if (wantnormals)
                {
@@ -9710,6 +9730,10 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modeltvector3f = NULL;
                        rsurface.modelnormal3f = rsurface.array_modelnormal3f;
                        model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, NULL, NULL);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertexposition = NULL;
+                       rsurface.modelvertexpositionbuffer = NULL;
                }
                else
                {
@@ -9718,77 +9742,115 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modeltvector3f = NULL;
                        rsurface.modelnormal3f = NULL;
                        model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, NULL, NULL, NULL);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertexposition = NULL;
+                       rsurface.modelvertexpositionbuffer = NULL;
                }
-               rsurface.modelvertex3f_bufferobject = 0;
+               rsurface.modelvertex3f_vertexbuffer = 0;
                rsurface.modelvertex3f_bufferoffset = 0;
-               rsurface.modelsvector3f_bufferobject = 0;
+               rsurface.modelsvector3f_vertexbuffer = 0;
                rsurface.modelsvector3f_bufferoffset = 0;
-               rsurface.modeltvector3f_bufferobject = 0;
+               rsurface.modeltvector3f_vertexbuffer = 0;
                rsurface.modeltvector3f_bufferoffset = 0;
-               rsurface.modelnormal3f_bufferobject = 0;
+               rsurface.modelnormal3f_vertexbuffer = 0;
                rsurface.modelnormal3f_bufferoffset = 0;
-               rsurface.generatedvertex = true;
+               rsurface.modelgeneratedvertex = true;
        }
        else
        {
                rsurface.modelvertex3f  = model->surfmesh.data_vertex3f;
-               rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
                rsurface.modelsvector3f = model->surfmesh.data_svector3f;
-               rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelsvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
                rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
-               rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modeltvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
                rsurface.modelnormal3f  = model->surfmesh.data_normal3f;
-               rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelnormal3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
-               rsurface.generatedvertex = false;
+               rsurface.modelvertexmesh = model->surfmesh.vertexmesh;
+               rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer;
+               rsurface.modelvertexposition = model->surfmesh.vertexposition;
+               rsurface.modelvertexpositionbuffer = model->surfmesh.vertexpositionbuffer;
+               rsurface.modelgeneratedvertex = false;
        }
        rsurface.modellightmapcolor4f  = model->surfmesh.data_lightmapcolor4f;
-       rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+       rsurface.modellightmapcolor4f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
        rsurface.modeltexcoordtexture2f  = model->surfmesh.data_texcoordtexture2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
        rsurface.modeltexcoordlightmap2f  = model->surfmesh.data_texcoordlightmap2f;
-       rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
+       rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer;
+       rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset;
        rsurface.modelelement3s = model->surfmesh.data_element3s;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
-       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
+       rsurface.modelelement3s_indexbuffer = model->surfmesh.data_element3s_indexbuffer;
+       rsurface.modelelement3s_bufferoffset = model->surfmesh.data_element3s_bufferoffset;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
-       rsurface.modelnum_vertices = model->surfmesh.num_vertices;
-       rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+       rsurface.modelnumvertices = model->surfmesh.num_vertices;
+       rsurface.modelnumtriangles = model->surfmesh.num_triangles;
        rsurface.modelsurfaces = model->data_surfaces;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertexposition = NULL;
+       rsurface.batchvertexpositionbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
 void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qboolean wantnormals, qboolean wanttangents)
 {
+       int i;
+
        rsurface.entity = r_refdef.scene.worldentity;
        rsurface.skeleton = NULL;
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = shadertime;
        rsurface.ent_flags = entflags;
-       rsurface.modelnum_vertices = numvertices;
-       rsurface.modelnum_triangles = numtriangles;
-       if (rsurface.array_size < rsurface.modelnum_vertices)
-               R_Mesh_ResizeArrays(rsurface.modelnum_vertices);
+       rsurface.modelnumvertices = numvertices;
+       rsurface.modelnumtriangles = numtriangles;
+       if (rsurface.array_size < rsurface.modelnumvertices)
+               R_Mesh_ResizeArrays(rsurface.modelnumvertices);
        rsurface.matrix = *matrix;
        rsurface.inversematrix = *inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -9833,50 +9895,108 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
                rsurface.modeltvector3f = NULL;
                rsurface.modelnormal3f = NULL;
        }
-       rsurface.modelvertex3f_bufferobject = 0;
+       rsurface.modelvertexmesh = NULL;
+       rsurface.modelvertexmeshbuffer = NULL;
+       rsurface.modelvertexposition = NULL;
+       rsurface.modelvertexpositionbuffer = NULL;
+       rsurface.modelvertex3f_vertexbuffer = 0;
        rsurface.modelvertex3f_bufferoffset = 0;
-       rsurface.modelsvector3f_bufferobject = 0;
+       rsurface.modelsvector3f_vertexbuffer = 0;
        rsurface.modelsvector3f_bufferoffset = 0;
-       rsurface.modeltvector3f_bufferobject = 0;
+       rsurface.modeltvector3f_vertexbuffer = 0;
        rsurface.modeltvector3f_bufferoffset = 0;
-       rsurface.modelnormal3f_bufferobject = 0;
+       rsurface.modelnormal3f_vertexbuffer = 0;
        rsurface.modelnormal3f_bufferoffset = 0;
-       rsurface.generatedvertex = true;
+       rsurface.modelgeneratedvertex = true;
        rsurface.modellightmapcolor4f  = color4f;
-       rsurface.modellightmapcolor4f_bufferobject = 0;
+       rsurface.modellightmapcolor4f_vertexbuffer = 0;
        rsurface.modellightmapcolor4f_bufferoffset = 0;
        rsurface.modeltexcoordtexture2f  = texcoord2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = 0;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = 0;
        rsurface.modeltexcoordtexture2f_bufferoffset = 0;
        rsurface.modeltexcoordlightmap2f  = NULL;
-       rsurface.modeltexcoordlightmap2f_bufferobject = 0;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = 0;
        rsurface.modeltexcoordlightmap2f_bufferoffset = 0;
        rsurface.modelelement3i = element3i;
+       rsurface.modelelement3i_indexbuffer = NULL;
+       rsurface.modelelement3i_bufferoffset = 0;
        rsurface.modelelement3s = element3s;
-       rsurface.modelelement3i_bufferobject = 0;
-       rsurface.modelelement3s_bufferobject = 0;
+       rsurface.modelelement3s_indexbuffer = NULL;
+       rsurface.modelelement3s_bufferoffset = 0;
        rsurface.modellightmapoffsets = NULL;
        rsurface.modelsurfaces = NULL;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
-
-       if (rsurface.modelnum_vertices && rsurface.modelelement3i)
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertexposition = NULL;
+       rsurface.batchvertexpositionbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
+
+       if (rsurface.modelnumvertices && rsurface.modelelement3i)
        {
                if ((wantnormals || wanttangents) && !normal3f)
-                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
+               {
+                       Mod_BuildNormals(0, rsurface.modelnumvertices, rsurface.modelnumtriangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       rsurface.modelnormal3f = rsurface.array_modelnormal3f;
+               }
                if (wanttangents && !svector3f)
-                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
+               {
+                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnumvertices, rsurface.modelnumtriangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
+                       rsurface.modelsvector3f = rsurface.array_modelsvector3f;
+                       rsurface.modeltvector3f = rsurface.array_modeltvector3f;
+               }
+       }
+
+       // now convert arrays into vertexmesh structs
+       for (i = 0;i < numvertices;i++)
+       {
+               VectorCopy(rsurface.modelvertex3f + 3*i, rsurface.array_modelvertexposition[i].vertex3f);
+               VectorCopy(rsurface.modelvertex3f + 3*i, rsurface.array_modelvertexmesh[i].vertex3f);
+               if (rsurface.modelsvector3f)
+                       VectorCopy(rsurface.modelsvector3f + 3*i, rsurface.array_modelvertexmesh[i].svector3f);
+               if (rsurface.modeltvector3f)
+                       VectorCopy(rsurface.modeltvector3f + 3*i, rsurface.array_modelvertexmesh[i].tvector3f);
+               if (rsurface.modelnormal3f)
+                       VectorCopy(rsurface.modelnormal3f + 3*i, rsurface.array_modelvertexmesh[i].normal3f);
+               if (rsurface.modellightmapcolor4f)
+                       Vector4Scale(rsurface.modellightmapcolor4f + 4*i, 255.0f, rsurface.array_modelvertexmesh[i].color4ub);
+               if (rsurface.modeltexcoordtexture2f)
+                       Vector2Copy(rsurface.modeltexcoordtexture2f + 2*i, rsurface.array_modelvertexmesh[i].texcoordtexture2f);
+               if (rsurface.modeltexcoordlightmap2f)
+                       Vector2Copy(rsurface.modeltexcoordlightmap2f + 2*i, rsurface.array_modelvertexmesh[i].texcoordlightmap2f);
        }
 }
 
@@ -9912,70 +10032,456 @@ float RSurf_FogVertex(const float *v)
        return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
 }
 
+void RSurf_RenumberElements(const int *inelement3i, int *outelement3i, int numelements, int adjust)
+{
+       int i;
+       for (i = 0;i < numelements;i++)
+               outelement3i[i] = inelement3i[i] + adjust;
+}
+
 static const int quadedges[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
-void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, const msurface_t **texturesurfacelist)
+extern cvar_t gl_vbo;
+void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int deformindex;
-       int texturesurfaceindex;
+       int firsttriangle;
+       int numtriangles;
+       int firstvertex;
+       int endvertex;
+       int numvertices;
+       int surfacefirsttriangle;
+       int surfacenumtriangles;
+       int surfacefirstvertex;
+       int surfaceendvertex;
+       int surfacenumvertices;
+       int surfaceadjustvertex;
+       int needsupdate;
        int i, j;
+       qboolean gaps;
+       qboolean dynamicvertex;
        float amplitude;
        float animpos;
        float scale;
-       const float *v1, *in_tc;
-       float *out_tc;
        float center[3], forward[3], right[3], up[3], v[3], newforward[3], newright[3], newup[3];
        float waveparms[4];
        q3shaderinfo_deform_t *deform;
-       // if vertices are dynamic (animated models), generate them into the temporary rsurface.array_model* arrays and point rsurface.model* at them instead of the static data from the model itself
-       if (rsurface.generatedvertex)
+       const msurface_t *surface, *firstsurface;
+       r_vertexposition_t *vertexposition;
+       r_vertexmesh_t *vertexmesh;
+       if (!texturenumsurfaces)
+               return;
+       // find vertex range of this surface batch
+       gaps = false;
+       firstsurface = texturesurfacelist[0];
+       firsttriangle = firstsurface->num_firsttriangle;
+       numtriangles = 0;
+       firstvertex = endvertex = firstsurface->num_firstvertex;
+       for (i = 0;i < texturenumsurfaces;i++)
+       {
+               surface = texturesurfacelist[i];
+               if (surface != firstsurface + i)
+                       gaps = true;
+               surfacefirstvertex = surface->num_firstvertex;
+               surfaceendvertex = surfacefirstvertex + surface->num_vertices;
+               surfacenumtriangles = surface->num_triangles;
+               if (firstvertex > surfacefirstvertex)
+                       firstvertex = surfacefirstvertex;
+               if (endvertex < surfaceendvertex)
+                       endvertex = surfaceendvertex;
+               numtriangles += surfacenumtriangles;
+       }
+       if (!numtriangles)
+               return;
+
+       // we now know the vertex range used, and if there are any gaps in it
+       rsurface.batchfirstvertex = firstvertex;
+       rsurface.batchnumvertices = endvertex - firstvertex;
+       rsurface.batchfirsttriangle = firsttriangle;
+       rsurface.batchnumtriangles = numtriangles;
+
+       // this variable holds flags for which properties have been updated that
+       // may require regenerating vertexmesh or vertexposition arrays...
+       needsupdate = 0;
+
+       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
+               needsupdate |= BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_NOGAPS;
+       for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform;deformindex++, deform++)
        {
-               if (rsurface.texture->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
-                       generatenormals = true;
-               for (i = 0;i < Q3MAXDEFORMS;i++)
+               switch (deform->deform)
                {
-                       if (rsurface.texture->deforms[i].deform == Q3DEFORM_AUTOSPRITE)
+               default:
+               case Q3DEFORM_PROJECTIONSHADOW:
+               case Q3DEFORM_TEXT0:
+               case Q3DEFORM_TEXT1:
+               case Q3DEFORM_TEXT2:
+               case Q3DEFORM_TEXT3:
+               case Q3DEFORM_TEXT4:
+               case Q3DEFORM_TEXT5:
+               case Q3DEFORM_TEXT6:
+               case Q3DEFORM_TEXT7:
+               case Q3DEFORM_NONE:
+                       break;
+               case Q3DEFORM_AUTOSPRITE:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXPOSITION | BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_AUTOSPRITE2:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXPOSITION | BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_NORMAL:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_WAVE:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXPOSITION | BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_BULGE:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXPOSITION | BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_MOVE:
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXPOSITION | BATCHNEED_VERTEXMESH_VERTEX;
+                       break;
+               }
+       }
+       switch(rsurface.texture->tcgen.tcgen)
+       {
+       default:
+       case Q3TCGEN_TEXTURE:
+               break;
+       case Q3TCGEN_LIGHTMAP:
+               batchneed |= BATCHNEED_ARRAY_LIGHTMAP | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP;
+               break;
+       case Q3TCGEN_VECTOR:
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       case Q3TCGEN_ENVIRONMENT:
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       }
+       if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
+       {
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+       }
+
+       // check if any dynamic vertex processing must occur
+       dynamicvertex = false;
+
+       if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_NOGAPS;
+               needsupdate |= (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP));
+       }
+
+       if (needsupdate & batchneed & BATCHNEED_VERTEXPOSITION)
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= (batchneed & BATCHNEED_VERTEXPOSITION);
+       }
+
+       if (dynamicvertex || gaps || rsurface.batchfirstvertex)
+       {
+               // when copying, we need to consider the regeneration of vertexmesh, any dependencies it may have must be set...
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEX)      batchneed |= BATCHNEED_ARRAY_VERTEX;
+               if (batchneed & BATCHNEED_VERTEXMESH_NORMAL)      batchneed |= BATCHNEED_ARRAY_NORMAL;
+               if (batchneed & BATCHNEED_VERTEXMESH_VECTOR)      batchneed |= BATCHNEED_ARRAY_VECTOR;
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEXCOLOR) batchneed |= BATCHNEED_ARRAY_VERTEXCOLOR;
+               if (batchneed & BATCHNEED_VERTEXMESH_TEXCOORD)    batchneed |= BATCHNEED_ARRAY_TEXCOORD;
+               if (batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP)    batchneed |= BATCHNEED_ARRAY_LIGHTMAP;
+       }
+
+       // when the model data has no vertex buffer (dynamic mesh), we need to
+       // eliminate gaps
+       if (!rsurface.modelvertexmeshbuffer || (!gl_vbo.integer && !vid.forcevbo))
+               batchneed |= BATCHNEED_NOGAPS;
+
+       // if needsupdate, we have to do a dynamic vertex batch for sure
+       if (needsupdate & batchneed)
+               dynamicvertex = true;
+
+       // see if we need to build vertexmesh from arrays
+       if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
+               dynamicvertex = true;
+
+       // see if we need to build vertexposition from arrays
+       if (!rsurface.modelvertexposition && (batchneed & BATCHNEED_VERTEXPOSITION))
+               dynamicvertex = true;
+
+       // if gaps are unacceptable, and there are gaps, it's a dynamic batch...
+       if ((batchneed & BATCHNEED_NOGAPS) && (gaps || firstvertex))
+               dynamicvertex = true;
+
+       // if there is a chance of animated vertex colors, it's a dynamic batch
+       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
+               dynamicvertex = true;
+
+       rsurface.batchvertex3f = rsurface.modelvertex3f;
+       rsurface.batchvertex3f_vertexbuffer = rsurface.modelvertex3f_vertexbuffer;
+       rsurface.batchvertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+       rsurface.batchsvector3f = rsurface.modelsvector3f;
+       rsurface.batchsvector3f_vertexbuffer = rsurface.modelsvector3f_vertexbuffer;
+       rsurface.batchsvector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+       rsurface.batchtvector3f = rsurface.modeltvector3f;
+       rsurface.batchtvector3f_vertexbuffer = rsurface.modeltvector3f_vertexbuffer;
+       rsurface.batchtvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+       rsurface.batchnormal3f = rsurface.modelnormal3f;
+       rsurface.batchnormal3f_vertexbuffer = rsurface.modelnormal3f_vertexbuffer;
+       rsurface.batchnormal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+       rsurface.batchlightmapcolor4f = rsurface.modellightmapcolor4f;
+       rsurface.batchlightmapcolor4f_vertexbuffer  = rsurface.modellightmapcolor4f_vertexbuffer;
+       rsurface.batchlightmapcolor4f_bufferoffset  = rsurface.modellightmapcolor4f_bufferoffset;
+       rsurface.batchtexcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.batchtexcoordtexture2f_vertexbuffer  = rsurface.modeltexcoordtexture2f_vertexbuffer;
+       rsurface.batchtexcoordtexture2f_bufferoffset  = rsurface.modeltexcoordtexture2f_bufferoffset;
+       rsurface.batchtexcoordlightmap2f = rsurface.modeltexcoordlightmap2f;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = rsurface.modeltexcoordlightmap2f_vertexbuffer;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
+       rsurface.batchvertexposition = rsurface.modelvertexposition;
+       rsurface.batchvertexpositionbuffer = rsurface.modelvertexpositionbuffer;
+       rsurface.batchvertexmesh = rsurface.modelvertexmesh;
+       rsurface.batchvertexmeshbuffer = rsurface.modelvertexmeshbuffer;
+       rsurface.batchelement3i = rsurface.modelelement3i;
+       rsurface.batchelement3i_indexbuffer = rsurface.modelelement3i_indexbuffer;
+       rsurface.batchelement3i_bufferoffset = rsurface.modelelement3i_bufferoffset;
+       rsurface.batchelement3s = rsurface.modelelement3s;
+       rsurface.batchelement3s_indexbuffer = rsurface.modelelement3s_indexbuffer;
+       rsurface.batchelement3s_bufferoffset = rsurface.modelelement3s_bufferoffset;
+
+       // if any dynamic vertex processing has to occur in software, we copy the
+       // entire surface list together before processing to rebase the vertices
+       // to start at 0 (otherwise we waste a lot of room in a vertex buffer).
+       //
+       // if any gaps exist and we do not have a static vertex buffer, we have to
+       // copy the surface list together to avoid wasting upload bandwidth on the
+       // vertices in the gaps.
+       //
+       // if gaps exist and we have a static vertex buffer, we still have to
+       // combine the index buffer ranges into one dynamic index buffer.
+       //
+       // in all cases we end up with data that can be drawn in one call.
+
+       if (!dynamicvertex)
+       {
+               // static vertex data, just set pointers...
+               rsurface.batchgeneratedvertex = false;
+               // if there are gaps, we want to build a combined index buffer,
+               // otherwise use the original static buffer with an appropriate offset
+               if (gaps)
+               {
+                       firsttriangle = 0;
+                       numtriangles = 0;
+                       for (i = 0;i < texturenumsurfaces;i++)
+                       {
+                               surfacefirsttriangle = texturesurfacelist[i]->num_firsttriangle;
+                               surfacenumtriangles = texturesurfacelist[i]->num_triangles;
+                               memcpy(rsurface.array_batchelement3i + 3*numtriangles, rsurface.modelelement3i + 3*surfacefirsttriangle, surfacenumtriangles*sizeof(int[3]));
+                               numtriangles += surfacenumtriangles;
+                       }
+                       rsurface.batchelement3i = rsurface.array_batchelement3i;
+                       rsurface.batchelement3i_indexbuffer = NULL;
+                       rsurface.batchelement3i_bufferoffset = 0;
+                       rsurface.batchelement3s = NULL;
+                       rsurface.batchelement3s_indexbuffer = NULL;
+                       rsurface.batchelement3s_bufferoffset = 0;
+                       if (endvertex <= 65536)
+                       {
+                               rsurface.batchelement3s = rsurface.array_batchelement3s;
+                               for (i = 0;i < numtriangles*3;i++)
+                                       rsurface.array_batchelement3s[i] = rsurface.array_batchelement3i[i];
+                       }
+                       rsurface.batchfirsttriangle = firsttriangle;
+                       rsurface.batchnumtriangles = numtriangles;
+               }
+               return;
+       }
+
+       // something needs software processing, do it for real...
+       // we only directly handle interleaved array data in this case...
+       rsurface.batchgeneratedvertex = true;
+
+       // now copy the vertex data into a combined array and make an index array
+       // (this is what Quake3 does all the time)
+       //if (gaps || rsurface.batchfirstvertex)
+       {
+               rsurface.batchvertexposition = NULL;
+               rsurface.batchvertexpositionbuffer = NULL;
+               rsurface.batchvertexmesh = NULL;
+               rsurface.batchvertexmeshbuffer = NULL;
+               rsurface.batchvertex3f = NULL;
+               rsurface.batchvertex3f_vertexbuffer = NULL;
+               rsurface.batchvertex3f_bufferoffset = 0;
+               rsurface.batchsvector3f = NULL;
+               rsurface.batchsvector3f_vertexbuffer = NULL;
+               rsurface.batchsvector3f_bufferoffset = 0;
+               rsurface.batchtvector3f = NULL;
+               rsurface.batchtvector3f_vertexbuffer = NULL;
+               rsurface.batchtvector3f_bufferoffset = 0;
+               rsurface.batchnormal3f = NULL;
+               rsurface.batchnormal3f_vertexbuffer = NULL;
+               rsurface.batchnormal3f_bufferoffset = 0;
+               rsurface.batchlightmapcolor4f = NULL;
+               rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+               rsurface.batchlightmapcolor4f_bufferoffset = 0;
+               rsurface.batchtexcoordtexture2f = NULL;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               rsurface.batchtexcoordlightmap2f = NULL;
+               rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+               rsurface.batchelement3i = rsurface.array_batchelement3i;
+               rsurface.batchelement3i_indexbuffer = NULL;
+               rsurface.batchelement3i_bufferoffset = 0;
+               rsurface.batchelement3s = NULL;
+               rsurface.batchelement3s_indexbuffer = NULL;
+               rsurface.batchelement3s_bufferoffset = 0;
+               // we'll only be setting up certain arrays as needed
+               if (batchneed & BATCHNEED_VERTEXPOSITION)
+                       rsurface.batchvertexposition = rsurface.array_batchvertexposition;
+               if (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))
+                       rsurface.batchvertexmesh = rsurface.array_batchvertexmesh;
+               if (batchneed & BATCHNEED_ARRAY_VERTEX)
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+               if (batchneed & BATCHNEED_ARRAY_NORMAL)
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+               if (batchneed & BATCHNEED_ARRAY_VECTOR)
+               {
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+               }
+               if (batchneed & BATCHNEED_ARRAY_VERTEXCOLOR)
+                       rsurface.batchlightmapcolor4f = rsurface.array_batchlightmapcolor4f;
+               if (batchneed & BATCHNEED_ARRAY_TEXCOORD)
+                       rsurface.batchtexcoordtexture2f = rsurface.array_batchtexcoordtexture2f;
+               if (batchneed & BATCHNEED_ARRAY_LIGHTMAP)
+                       rsurface.batchtexcoordlightmap2f = rsurface.array_batchtexcoordlightmap2f;
+               numvertices = 0;
+               numtriangles = 0;
+               for (i = 0;i < texturenumsurfaces;i++)
+               {
+                       surfacefirstvertex = texturesurfacelist[i]->num_firstvertex;
+                       surfacenumvertices = texturesurfacelist[i]->num_vertices;
+                       surfacefirsttriangle = texturesurfacelist[i]->num_firsttriangle;
+                       surfaceadjustvertex = numvertices - surfacefirstvertex;
+                       surfacenumtriangles = texturesurfacelist[i]->num_triangles;
+                       // copy only the data requested
+                       if ((batchneed & BATCHNEED_VERTEXPOSITION) && rsurface.modelvertexposition)
+                               memcpy(rsurface.array_batchvertexposition + numvertices, rsurface.modelvertexposition + surfacefirstvertex, surfacenumvertices * sizeof(rsurface.batchvertexposition[0]));
+                       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)) && rsurface.modelvertexmesh)
+                               memcpy(rsurface.array_batchvertexmesh + numvertices, rsurface.modelvertexmesh + surfacefirstvertex, surfacenumvertices * sizeof(rsurface.batchvertexmesh[0]));
+                       if (batchneed & (BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_ARRAY_LIGHTMAP))
+                       {
+                               if (batchneed & BATCHNEED_ARRAY_VERTEX)
+                                       memcpy(rsurface.array_batchvertex3f + 3*numvertices, rsurface.modelvertex3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                               if ((batchneed & BATCHNEED_ARRAY_NORMAL) && rsurface.modelnormal3f)
+                                       memcpy(rsurface.array_batchnormal3f + 3*numvertices, rsurface.modelnormal3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                               if ((batchneed & BATCHNEED_ARRAY_VECTOR) && rsurface.modelsvector3f)
+                               {
+                                       memcpy(rsurface.array_batchsvector3f + 3*numvertices, rsurface.modelsvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                       memcpy(rsurface.array_batchtvector3f + 3*numvertices, rsurface.modeltvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                               }
+                               if ((batchneed & BATCHNEED_ARRAY_VERTEXCOLOR) && rsurface.modellightmapcolor4f)
+                                       memcpy(rsurface.array_batchlightmapcolor4f + 4*numvertices, rsurface.modellightmapcolor4f + 4*surfacefirstvertex, surfacenumvertices * sizeof(float[4]));
+                               if ((batchneed & BATCHNEED_ARRAY_TEXCOORD) && rsurface.modeltexcoordtexture2f)
+                                       memcpy(rsurface.array_batchtexcoordtexture2f + 2*numvertices, rsurface.modeltexcoordtexture2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
+                               if ((batchneed & BATCHNEED_ARRAY_LIGHTMAP) && rsurface.modeltexcoordlightmap2f)
+                                       memcpy(rsurface.array_batchtexcoordlightmap2f + 2*numvertices, rsurface.modeltexcoordlightmap2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
+                       }
+                       RSurf_RenumberElements(rsurface.modelelement3i + 3*surfacefirsttriangle, rsurface.array_batchelement3i + 3*numtriangles, 3*surfacenumtriangles, numvertices - surfacefirstvertex);
+                       numvertices += surfacenumvertices;
+                       numtriangles += surfacenumtriangles;
+               }
+
+               // generate a 16bit index array as well if possible
+               // (in general, dynamic batches fit)
+               if (numvertices <= 65536)
+               {
+                       rsurface.batchelement3s = rsurface.array_batchelement3s;
+                       for (i = 0;i < numtriangles*3;i++)
+                               rsurface.array_batchelement3s[i] = rsurface.array_batchelement3i[i];
+               }
+
+               // since we've copied everything, the batch now starts at 0
+               rsurface.batchfirstvertex = 0;
+               rsurface.batchnumvertices = numvertices;
+               rsurface.batchfirsttriangle = 0;
+               rsurface.batchnumtriangles = numtriangles;
+       }
+
+       // q1bsp surfaces rendered in vertex color mode have to have colors
+       // calculated based on lightstyles
+       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
+       {
+               // generate color arrays for the surfaces in this list
+               int c[4];
+               int scale;
+               int size3;
+               const int *offsets;
+               const unsigned char *lm;
+               numvertices = 0;
+               rsurface.batchlightmapcolor4f = rsurface.array_batchlightmapcolor4f;
+               rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+               rsurface.batchlightmapcolor4f_bufferoffset = 0;
+               for (i = 0;i < texturenumsurfaces;i++)
+               {
+                       surface = texturesurfacelist[i];
+                       offsets = rsurface.modellightmapoffsets + surface->num_firstvertex;
+                       surfacenumvertices = surface->num_vertices;
+                       if (surface->lightmapinfo->samples)
+                       {
+                               for (j = 0;j < surfacenumvertices;j++)
+                               {
+                                       lm = surface->lightmapinfo->samples + offsets[j];
+                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[0]];
+                                       VectorScale(lm, scale, c);
+                                       if (surface->lightmapinfo->styles[1] != 255)
+                                       {
+                                               size3 = ((surface->lightmapinfo->extents[0]>>4)+1)*((surface->lightmapinfo->extents[1]>>4)+1)*3;
+                                               lm += size3;
+                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[1]];
+                                               VectorMA(c, scale, lm, c);
+                                               if (surface->lightmapinfo->styles[2] != 255)
+                                               {
+                                                       lm += size3;
+                                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[2]];
+                                                       VectorMA(c, scale, lm, c);
+                                                       if (surface->lightmapinfo->styles[3] != 255)
+                                                       {
+                                                               lm += size3;
+                                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[3]];
+                                                               VectorMA(c, scale, lm, c);
+                                                       }
+                                               }
+                                       }
+                                       c[0] >>= 15;
+                                       c[1] >>= 15;
+                                       c[2] >>= 15;
+                                       Vector4Set(rsurface.array_batchlightmapcolor4f + 4*numvertices, min(c[0], 255) * (1.0f / 255.0f), min(c[1], 255) * (1.0f / 255.0f), min(c[2], 255) * (1.0f / 255.0f), 1);
+                                       numvertices++;
+                               }
+                       }
+                       else
                        {
-                               generatetangents = true;
-                               generatenormals = true;
+                               for (j = 0;j < surfacenumvertices;j++)
+                               {
+                                       Vector4Set(rsurface.array_batchlightmapcolor4f + 4*numvertices, 0, 0, 0, 1);
+                                       numvertices++;
+                               }
                        }
-                       if (rsurface.texture->deforms[i].deform != Q3DEFORM_NONE)
-                               generatenormals = true;
-               }
-               if (generatenormals && !rsurface.modelnormal3f)
-               {
-                       rsurface.normal3f = rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset = 0;
-                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
-               }
-               if (generatetangents && !rsurface.modelsvector3f)
-               {
-                       rsurface.svector3f = rsurface.modelsvector3f = rsurface.array_modelsvector3f;
-                       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.modeltvector3f = rsurface.array_modeltvector3f;
-                       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset = 0;
-                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
-               }
-       }
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+               }
+       }
+
        // if vertices are deformed (sprite flares and things in maps, possibly
-       // water waves, bulges and other deformations), generate them into
-       // rsurface.deform* arrays from whatever the rsurface.* arrays point to
-       // (may be static model data or generated data for an animated model, or
-       //  the previous deform pass)
+       // water waves, bulges and other deformations), modify the copied vertices
+       // in place
        for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform;deformindex++, deform++)
        {
                switch (deform->deform)
@@ -9999,41 +10505,36 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        VectorNormalize(newforward);
                        VectorNormalize(newright);
                        VectorNormalize(newup);
-                       // make deformed versions of only the model vertices used by the specified surfaces
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       // a single autosprite surface can contain multiple sprites...
+                       for (j = 0;j < rsurface.batchnumvertices - 3;j += 4)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               // a single autosprite surface can contain multiple sprites...
-                               for (j = 0;j < surface->num_vertices - 3;j += 4)
+                               VectorClear(center);
+                               for (i = 0;i < 4;i++)
+                                       VectorAdd(center, rsurface.batchvertex3f + 3*(j+i), center);
+                               VectorScale(center, 0.25f, center);
+                               VectorCopy(rsurface.batchnormal3f + 3*j, forward);
+                               VectorCopy(rsurface.batchsvector3f + 3*j, right);
+                               VectorCopy(rsurface.batchtvector3f + 3*j, up);
+                               for (i = 0;i < 4;i++)
                                {
-                                       VectorClear(center);
-                                       for (i = 0;i < 4;i++)
-                                               VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
-                                       VectorScale(center, 0.25f, center);
-                                       VectorCopy((rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, forward);
-                                       VectorCopy((rsurface.svector3f + 3 * surface->num_firstvertex) + j*3, right);
-                                       VectorCopy((rsurface.tvector3f + 3 * surface->num_firstvertex) + j*3, up);
-                                       for (i = 0;i < 4;i++)
-                                       {
-                                               VectorSubtract((rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, center, v);
-                                               VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
-                                       }
+                                       VectorSubtract(rsurface.batchvertex3f + 3*(j+i), center, v);
+                                       VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.array_batchvertex3f + 3*(j+i));
                                }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+                       rsurface.batchvertex3f_vertexbuffer = NULL;
+                       rsurface.batchvertex3f_bufferoffset = 0;
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchsvector3f_vertexbuffer = NULL;
+                       rsurface.batchsvector3f_bufferoffset = 0;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+                       rsurface.batchtvector3f_vertexbuffer = NULL;
+                       rsurface.batchtvector3f_bufferoffset = 0;
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_AUTOSPRITE2:
                        Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
@@ -10042,10 +10543,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        VectorNormalize(newforward);
                        VectorNormalize(newright);
                        VectorNormalize(newup);
-                       // make deformed versions of only the model vertices used by the specified surfaces
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                const float *v1, *v2;
                                vec3_t start, end;
                                float f, l;
@@ -10058,25 +10556,18 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                shortest[2];
                                memset(shortest, 0, sizeof(shortest));
                                // a single autosprite surface can contain multiple sprites...
-                               for (j = 0;j < surface->num_vertices - 3;j += 4)
+                               for (j = 0;j < rsurface.batchnumvertices - 3;j += 4)
                                {
                                        VectorClear(center);
                                        for (i = 0;i < 4;i++)
-                                               VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
+                                               VectorAdd(center, rsurface.batchvertex3f + 3*(j+i), center);
                                        VectorScale(center, 0.25f, center);
                                        // find the two shortest edges, then use them to define the
                                        // axis vectors for rotating around the central axis
                                        for (i = 0;i < 6;i++)
                                        {
-                                               v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][0]);
-                                               v2 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][1]);
-#if 0
-                                               Debug_PolygonBegin(NULL, 0);
-                                               Debug_PolygonVertex(v1[0], v1[1], v1[2], 0, 0, 1, 0, 0, 1);
-                                               Debug_PolygonVertex((v1[0] + v2[0]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, (v1[1] + v2[1]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1], (v1[2] + v2[2]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2], 0, 0, 1, 1, 0, 1);
-                                               Debug_PolygonVertex(v2[0], v2[1], v2[2], 0, 0, 1, 0, 0, 1);
-                                               Debug_PolygonEnd();
-#endif
+                                               v1 = rsurface.batchvertex3f + 3*(j+quadedges[i][0]);
+                                               v2 = rsurface.batchvertex3f + 3*(j+quadedges[i][1]);
                                                l = VectorDistance2(v1, v2);
                                                // this length bias tries to make sense of square polygons, assuming they are meant to be upright
                                                if (v1[2] != v2[2])
@@ -10097,13 +10588,6 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        }
                                        VectorLerp(shortest[0].v1, 0.5f, shortest[0].v2, start);
                                        VectorLerp(shortest[1].v1, 0.5f, shortest[1].v2, end);
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(start[0], start[1], start[2], 0, 0, 1, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 4, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 4, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(end[0], end[1], end[2], 0, 0, 0, 1, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
                                        // this calculates the right vector from the shortest edge
                                        // and the up vector from the edge midpoints
                                        VectorSubtract(shortest[0].v1, shortest[0].v2, right);
@@ -10118,20 +10602,6 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        VectorNormalize(forward);
                                        CrossProduct(up, forward, newright);
                                        VectorNormalize(newright);
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 8, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 8, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 8, 0, 0, 1, 0, 0, 1);
-                                       Debug_PolygonVertex(center[0] + right[0] * 8, center[1] + right[1] * 8, center[2] + right[2] * 8, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + up   [0] * 8, center[1] + up   [1] * 8, center[2] + up   [2] * 8, 0, 0, 0, 0, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(center[0] + forward [0] * 8, center[1] + forward [1] * 8, center[2] + forward [2] * 8, 0, 0, 1, 0, 0, 1);
-                                       Debug_PolygonVertex(center[0] + newright[0] * 8, center[1] + newright[1] * 8, center[2] + newright[2] * 8, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + up      [0] * 8, center[1] + up      [1] * 8, center[2] + up      [2] * 8, 0, 0, 0, 0, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
                                        // rotate the quad around the up axis vector, this is made
                                        // especially easy by the fact we know the quad is flat,
                                        // so we only have to subtract the center position and
@@ -10145,54 +10615,49 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        l = DotProduct(right, center);
                                        for (i = 0;i < 4;i++)
                                        {
-                                               v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + j + i);
+                                               v1 = rsurface.batchvertex3f + 3*(j+i);
                                                f = DotProduct(right, v1) - l;
-                                               VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
+                                               VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_batchvertex3f + 3*(j+i));
                                        }
                                }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+                       rsurface.batchvertex3f_vertexbuffer = NULL;
+                       rsurface.batchvertex3f_bufferoffset = 0;
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchsvector3f_vertexbuffer = NULL;
+                       rsurface.batchsvector3f_bufferoffset = 0;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+                       rsurface.batchtvector3f_vertexbuffer = NULL;
+                       rsurface.batchtvector3f_bufferoffset = 0;
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_NORMAL:
                        // deform the normals to make reflections wavey
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       for (j = 0;j < rsurface.batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                               {
-                                       float vertex[3];
-                                       float *normal = (rsurface.array_deformednormal3f  + 3 * surface->num_firstvertex) + j*3;
-                                       VectorScale((rsurface.vertex3f  + 3 * surface->num_firstvertex) + j*3, 0.98f, vertex);
-                                       VectorCopy((rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, normal);
-                                       normal[0] += deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       normal[1] += deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       normal[2] += deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       VectorNormalize(normal);
-                               }
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
+                               float vertex[3];
+                               float *normal = rsurface.array_batchnormal3f + 3*j;
+                               VectorScale(rsurface.batchvertex3f + 3*j, 0.98f, vertex);
+                               normal[0] = rsurface.batchnormal3f[j*3+0] + deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               normal[1] = rsurface.batchnormal3f[j*3+1] + deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               normal[2] = rsurface.batchnormal3f[j*3+2] + deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               VectorNormalize(normal);
                        }
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchsvector3f_vertexbuffer = NULL;
+                       rsurface.batchsvector3f_bufferoffset = 0;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+                       rsurface.batchtvector3f_vertexbuffer = NULL;
+                       rsurface.batchtvector3f_bufferoffset = 0;
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_WAVE:
                        // deform vertex array to make wavey water and flags and such
@@ -10203,122 +10668,119 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        // this is how a divisor of vertex influence on deformation
                        animpos = deform->parms[0] ? 1.0f / deform->parms[0] : 100.0f;
                        scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       for (j = 0;j < rsurface.batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
+                               // if the wavefunc depends on time, evaluate it per-vertex
+                               if (waveparms[3])
                                {
-                                       float *vertex = (rsurface.array_deformedvertex3f  + 3 * surface->num_firstvertex) + j*3;
-                                       VectorCopy((rsurface.vertex3f  + 3 * surface->num_firstvertex) + j*3, vertex);
-                                       // if the wavefunc depends on time, evaluate it per-vertex
-                                       if (waveparms[3])
-                                       {
-                                               waveparms[2] = deform->waveparms[2] + (vertex[0] + vertex[1] + vertex[2]) * animpos;
-                                               scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
-                                       }
-                                       VectorMA(vertex, scale, (rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, vertex);
+                                       waveparms[2] = deform->waveparms[2] + (rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+1] + rsurface.batchvertex3f[j*3+2]) * animpos;
+                                       scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
                                }
+                               VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.array_batchvertex3f + 3*j);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+                       rsurface.batchvertex3f_vertexbuffer = NULL;
+                       rsurface.batchvertex3f_bufferoffset = 0;
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchsvector3f_vertexbuffer = NULL;
+                       rsurface.batchsvector3f_bufferoffset = 0;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+                       rsurface.batchtvector3f_vertexbuffer = NULL;
+                       rsurface.batchtvector3f_bufferoffset = 0;
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_BULGE:
                        // deform vertex array to make the surface have moving bulges
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       for (j = 0;j < rsurface.batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                               {
-                                       scale = sin((rsurface.modeltexcoordtexture2f[2 * (surface->num_firstvertex + j)] * deform->parms[0] + r_refdef.scene.time * deform->parms[2])) * deform->parms[1];
-                                       VectorMA(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), scale, rsurface.normal3f + 3 * (surface->num_firstvertex + j), rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
-                               }
+                               scale = sin(rsurface.batchtexcoordtexture2f[j*2+0] * deform->parms[0] + r_refdef.scene.time * deform->parms[2]) * deform->parms[1];
+                               VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.array_batchvertex3f + 3*j);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+                       rsurface.batchvertex3f_vertexbuffer = NULL;
+                       rsurface.batchvertex3f_bufferoffset = 0;
+                       rsurface.batchsvector3f = rsurface.array_batchsvector3f;
+                       rsurface.batchsvector3f_vertexbuffer = NULL;
+                       rsurface.batchsvector3f_bufferoffset = 0;
+                       rsurface.batchtvector3f = rsurface.array_batchtvector3f;
+                       rsurface.batchtvector3f_vertexbuffer = NULL;
+                       rsurface.batchtvector3f_bufferoffset = 0;
+                       rsurface.batchnormal3f = rsurface.array_batchnormal3f;
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_MOVE:
                        // deform vertex array
                        scale = R_EvaluateQ3WaveFunc(deform->wavefunc, deform->waveparms);
                        VectorScale(deform->parms, scale, waveparms);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                                       VectorAdd(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), waveparms, rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
-                       }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
+                       for (j = 0;j < rsurface.batchnumvertices;j++)
+                               VectorAdd(rsurface.batchvertex3f + 3*j, waveparms, rsurface.array_batchvertex3f + 3*j);
+                       rsurface.batchvertex3f = rsurface.array_batchvertex3f;
+                       rsurface.batchvertex3f_vertexbuffer = NULL;
+                       rsurface.batchvertex3f_bufferoffset = 0;
                        break;
                }
        }
+
        // generate texcoords based on the chosen texcoord source
        switch(rsurface.texture->tcgen.tcgen)
        {
        default:
        case Q3TCGEN_TEXTURE:
-               rsurface.texcoordtexture2f               = rsurface.modeltexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = rsurface.modeltexcoordtexture2f_bufferobject;
-               rsurface.texcoordtexture2f_bufferoffset  = rsurface.modeltexcoordtexture2f_bufferoffset;
                break;
        case Q3TCGEN_LIGHTMAP:
-               rsurface.texcoordtexture2f               = rsurface.modeltexcoordlightmap2f;
-               rsurface.texcoordtexture2f_bufferobject  = rsurface.modeltexcoordlightmap2f_bufferobject;
-               rsurface.texcoordtexture2f_bufferoffset  = rsurface.modeltexcoordlightmap2f_bufferoffset;
+               if (rsurface.batchtexcoordlightmap2f)
+                       memcpy(rsurface.array_batchtexcoordlightmap2f, rsurface.batchtexcoordtexture2f, rsurface.batchnumvertices * sizeof(float[2]));
+               rsurface.batchtexcoordtexture2f = rsurface.array_batchtexcoordtexture2f;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
                break;
        case Q3TCGEN_VECTOR:
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               for (j = 0;j < rsurface.batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, out_tc += 2)
-                       {
-                               out_tc[0] = DotProduct(v1, rsurface.texture->tcgen.parms);
-                               out_tc[1] = DotProduct(v1, rsurface.texture->tcgen.parms + 3);
-                       }
+                       rsurface.array_batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms);
+                       rsurface.array_batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms + 3);
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
+               rsurface.batchtexcoordtexture2f = rsurface.array_batchtexcoordtexture2f;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
                break;
        case Q3TCGEN_ENVIRONMENT:
                // make environment reflections using a spheremap
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               for (j = 0;j < rsurface.batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       const float *vertex = rsurface.modelvertex3f + 3 * surface->num_firstvertex;
-                       const float *normal = rsurface.modelnormal3f + 3 * surface->num_firstvertex;
-                       float *out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;
-                       for (j = 0;j < surface->num_vertices;j++, vertex += 3, normal += 3, out_tc += 2)
-                       {
-                               // identical to Q3A's method, but executed in worldspace so
-                               // carried models can be shiny too
+                       // identical to Q3A's method, but executed in worldspace so
+                       // carried models can be shiny too
 
-                               float viewer[3], d, reflected[3], worldreflected[3];
+                       float viewer[3], d, reflected[3], worldreflected[3];
 
-                               VectorSubtract(rsurface.localvieworigin, vertex, viewer);
-                               // VectorNormalize(viewer);
+                       VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer);
+                       // VectorNormalize(viewer);
 
-                               d = DotProduct(normal, viewer);
+                       d = DotProduct(rsurface.batchnormal3f + 3*j, viewer);
 
-                               reflected[0] = normal[0]*2*d - viewer[0];
-                               reflected[1] = normal[1]*2*d - viewer[1];
-                               reflected[2] = normal[2]*2*d - viewer[2];
-                               // note: this is proportinal to viewer, so we can normalize later
+                       reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0];
+                       reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1];
+                       reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2];
+                       // note: this is proportinal to viewer, so we can normalize later
 
-                               Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
-                               VectorNormalize(worldreflected);
+                       Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
+                       VectorNormalize(worldreflected);
 
-                               // note: this sphere map only uses world x and z!
-                               // so positive and negative y will LOOK THE SAME.
-                               out_tc[0] = 0.5 + 0.5 * worldreflected[1];
-                               out_tc[1] = 0.5 - 0.5 * worldreflected[2];
-                       }
+                       // note: this sphere map only uses world x and z!
+                       // so positive and negative y will LOOK THE SAME.
+                       rsurface.array_batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1];
+                       rsurface.array_batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2];
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
+               rsurface.batchtexcoordtexture2f = rsurface.array_batchtexcoordtexture2f;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
                break;
        }
        // the only tcmod that needs software vertex processing is turbulent, so
@@ -10330,116 +10792,86 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
        {
                amplitude = rsurface.texture->tcmods[0].parms[1];
                animpos = rsurface.texture->tcmods[0].parms[2] + r_refdef.scene.time * rsurface.texture->tcmods[0].parms[3];
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               for (j = 0;j < rsurface.batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, in_tc = rsurface.texcoordtexture2f + 2 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, in_tc += 2, out_tc += 2)
-                       {
-                               out_tc[0] = in_tc[0] + amplitude * sin(((v1[0] + v1[2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
-                               out_tc[1] = in_tc[1] + amplitude * sin(((v1[1]        ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
-                       }
+                       rsurface.array_batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+                       rsurface.array_batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1]                                ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
+               rsurface.batchtexcoordtexture2f = rsurface.array_batchtexcoordtexture2f;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
        }
-       rsurface.texcoordlightmap2f              = rsurface.modeltexcoordlightmap2f;
-       rsurface.texcoordlightmap2f_bufferobject = rsurface.modeltexcoordlightmap2f_bufferobject;
-       rsurface.texcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
-       R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
-}
 
-void RSurf_DrawBatch_Simple(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int i, j;
-       const msurface_t *surface = texturesurfacelist[0];
-       const msurface_t *surface2;
-       int firstvertex;
-       int endvertex;
-       int numvertices;
-       int numtriangles;
-       // TODO: lock all array ranges before render, rather than on each surface
-       if (texturenumsurfaces == 1)
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       else if (r_batchmode.integer == 2)
+       if (needsupdate & batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))
        {
-               #define MAXBATCHTRIANGLES 65536
-               int batchtriangles = 0;
-               static int batchelements[MAXBATCHTRIANGLES*3];
-               for (i = 0;i < texturenumsurfaces;i = j)
+               // convert the modified arrays to vertex structs
+               rsurface.batchvertexmesh = rsurface.array_batchvertexmesh;
+               rsurface.batchvertexmeshbuffer = NULL;
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEX)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
+                               VectorCopy(rsurface.batchvertex3f + 3*j, vertexmesh->vertex3f);
+               if (batchneed & BATCHNEED_VERTEXMESH_NORMAL)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
+                               VectorCopy(rsurface.batchnormal3f + 3*j, vertexmesh->normal3f);
+               if (batchneed & BATCHNEED_VERTEXMESH_VECTOR)
                {
-                       surface = texturesurfacelist[i];
-                       j = i + 1;
-                       if (surface->num_triangles > MAXBATCHTRIANGLES)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
                        {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                               continue;
-                       }
-                       memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
-                       batchtriangles = surface->num_triangles;
-                       firstvertex = surface->num_firstvertex;
-                       endvertex = surface->num_firstvertex + surface->num_vertices;
-                       for (;j < texturenumsurfaces;j++)
-                       {
-                               surface2 = texturesurfacelist[j];
-                               if (batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
-                                       break;
-                               memcpy(batchelements + batchtriangles * 3, rsurface.modelelement3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
-                               batchtriangles += surface2->num_triangles;
-                               firstvertex = min(firstvertex, surface2->num_firstvertex);
-                               endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
+                               VectorCopy(rsurface.batchsvector3f + 3*j, vertexmesh->svector3f);
+                               VectorCopy(rsurface.batchtvector3f + 3*j, vertexmesh->tvector3f);
                        }
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
                }
+               if ((batchneed & BATCHNEED_VERTEXMESH_VERTEXCOLOR) && rsurface.batchlightmapcolor4f)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
+                               Vector4Scale(rsurface.batchlightmapcolor4f + 4*j, 255.0f, vertexmesh->color4ub);
+               if (batchneed & BATCHNEED_VERTEXMESH_TEXCOORD)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
+                               Vector2Copy(rsurface.batchtexcoordtexture2f + 2*j, vertexmesh->texcoordtexture2f);
+               if ((batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP) && rsurface.batchtexcoordlightmap2f)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
+                               Vector2Copy(rsurface.batchtexcoordlightmap2f + 2*j, vertexmesh->texcoordlightmap2f);
        }
-       else if (r_batchmode.integer == 1)
-       {
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2)
-                                       break;
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
-                       numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-       }
-       else
+
+       if (needsupdate & batchneed & BATCHNEED_VERTEXPOSITION)
        {
-               for (i = 0;i < texturenumsurfaces;i++)
-               {
-                       surface = texturesurfacelist[i];
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
+               // convert the modified arrays to vertex structs
+               rsurface.batchvertexposition = rsurface.array_batchvertexposition;
+               rsurface.batchvertexpositionbuffer = NULL;
+               if (sizeof(r_vertexposition_t) == sizeof(float[3]))
+                       memcpy(rsurface.array_batchvertexposition, rsurface.batchvertex3f, rsurface.batchnumvertices * sizeof(r_vertexposition_t));
+               else
+                       for (j = 0, vertexposition = rsurface.array_batchvertexposition;j < rsurface.batchnumvertices;j++, vertexposition++)
+                               VectorCopy(rsurface.batchvertex3f + 3*j, vertexposition->vertex3f);
        }
 }
 
-static void RSurf_BindLightmapForSurface(const msurface_t *surface)
+void RSurf_DrawBatch(void)
+{
+       R_Mesh_Draw(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchfirsttriangle, rsurface.batchnumtriangles, rsurface.batchelement3i, rsurface.batchelement3i_indexbuffer, rsurface.batchelement3i_bufferoffset, rsurface.batchelement3s, rsurface.batchelement3s_indexbuffer, rsurface.batchelement3s_bufferoffset);
+}
+
+static void RSurf_BindLightmapForBatch(void)
 {
        switch(vid.renderpath)
        {
        case RENDERPATH_CGGL:
 #ifdef SUPPORTCG
-               if (r_cg_permutation->fp_Texture_Lightmap ) CG_BindTexture(r_cg_permutation->fp_Texture_Lightmap , surface->lightmaptexture );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Deluxemap) CG_BindTexture(r_cg_permutation->fp_Texture_Deluxemap, surface->deluxemaptexture);CHECKCGERROR
+               if (r_cg_permutation->fp_Texture_Lightmap ) CG_BindTexture(r_cg_permutation->fp_Texture_Lightmap , rsurface.lightmaptexture );CHECKCGERROR
+               if (r_cg_permutation->fp_Texture_Deluxemap) CG_BindTexture(r_cg_permutation->fp_Texture_Deluxemap, rsurface.deluxemaptexture);CHECKCGERROR
 #endif
                break;
        case RENDERPATH_GL20:
-               if (r_glsl_permutation->loc_Texture_Lightmap  >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP , surface->lightmaptexture );
-               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(GL20TU_DELUXEMAP, surface->deluxemaptexture);
+               if (r_glsl_permutation->loc_Texture_Lightmap  >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP , rsurface.lightmaptexture );
+               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(GL20TU_DELUXEMAP, rsurface.deluxemaptexture);
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
-               R_Mesh_TexBind(0, surface->lightmaptexture);
+               R_Mesh_TexBind(0, rsurface.lightmaptexture);
                break;
        }
 }
 
-static void RSurf_BindReflectionForSurface(const msurface_t *surface)
+static void RSurf_BindReflectionForBatch(void)
 {
        // pick the closest matching water plane and bind textures
        int planeindex, vertexindex;
@@ -10454,7 +10886,7 @@ static void RSurf_BindReflectionForSurface(const msurface_t *surface)
                if(p->camera_entity != rsurface.texture->camera_entity)
                        continue;
                d = 0;
-               for (vertexindex = 0, v = rsurface.modelvertex3f + surface->num_firstvertex * 3;vertexindex < surface->num_vertices;vertexindex++, v += 3)
+               for (vertexindex = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3;vertexindex < rsurface.batchnumvertices;vertexindex++, v += 3)
                {
                        Matrix4x4_Transform(&rsurface.matrix, v, vert);
                        d += fabs(PlaneDiff(vert, &p->plane));
@@ -10485,376 +10917,170 @@ static void RSurf_BindReflectionForSurface(const msurface_t *surface)
        }
 }
 
-static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(void)
 {
        int i;
-       const msurface_t *surface;
-       if (r_waterstate.renderingscene)
-               return;
-       for (i = 0;i < texturenumsurfaces;i++)
-       {
-               surface = texturesurfacelist[i];
-               RSurf_BindLightmapForSurface(surface);
-               RSurf_BindReflectionForSurface(surface);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       }
+       for (i = 0;i < rsurface.batchnumvertices;i++)
+               Vector4Set(rsurface.array_passcolor4f + 4*i, 0.5f, 0.5f, 0.5f, 1.0f);
+       rsurface.passcolor4f = rsurface.array_passcolor4f;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyFog(void)
 {
-       int i;
-       int j;
-       const msurface_t *surface = texturesurfacelist[0];
-       const msurface_t *surface2;
-       int firstvertex;
-       int endvertex;
-       int numvertices;
-       int numtriangles;
-       if (texturenumsurfaces == 1)
-       {
-               RSurf_BindLightmapForSurface(surface);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       }
-       else if (r_batchmode.integer == 2)
-       {
-               int batchtriangles = 0;
-               static int batchelements[MAXBATCHTRIANGLES*3];
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       j = i + 1;
-                       if (surface->num_triangles > MAXBATCHTRIANGLES)
-                       {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                               continue;
-                       }
-                       memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
-                       batchtriangles = surface->num_triangles;
-                       firstvertex = surface->num_firstvertex;
-                       endvertex = surface->num_firstvertex + surface->num_vertices;
-                       for (;j < texturenumsurfaces;j++)
-                       {
-                               surface2 = texturesurfacelist[j];
-                               if (surface2->lightmaptexture != surface->lightmaptexture || batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
-                                       break;
-                               memcpy(batchelements + batchtriangles * 3, rsurface.modelelement3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
-                               batchtriangles += surface2->num_triangles;
-                               firstvertex = min(firstvertex, surface2->num_firstvertex);
-                               endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
-                       }
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
-               }
-       }
-       else if (r_batchmode.integer == 1)
-       {
-#if 0
-               Con_Printf("%s batch sizes ignoring lightmap:", rsurface.texture->name);
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2)
-                                       break;
-                       Con_Printf(" %i", j - i);
-               }
-               Con_Printf("\n");
-               Con_Printf("%s batch sizes honoring lightmap:", rsurface.texture->name);
-#endif
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2 || texturesurfacelist[j]->lightmaptexture != surface->lightmaptexture)
-                                       break;
-#if 0
-                       Con_Printf(" %i", j - i);
-#endif
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
-                       numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-#if 0
-               Con_Printf("\n");
-#endif
-       }
-       else
-       {
-               for (i = 0;i < texturenumsurfaces;i++)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-       }
-}
-
-static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int j;
-       int texturesurfaceindex;
-       if (r_showsurfaces.integer == 2)
-       {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0;j < surface->num_triangles;j++)
-                       {
-                               float f = ((j + surface->num_firsttriangle) & 31) * (1.0f / 31.0f) * r_refdef.view.colorscale;
-                               GL_Color(f, f, f, 1);
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle + j, 1, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                       }
-               }
-       }
-       else
-       {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       int k = (int)(((size_t)surface) / sizeof(msurface_t));
-                       GL_Color((k & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 4) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 8) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, 1);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-       }
-}
-
-static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int texturesurfaceindex;
-       int i;
-       const float *v;
-       float *c2;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-       {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
-               {
-                       c2[0] = 0.5;
-                       c2[1] = 0.5;
-                       c2[2] = 0.5;
-                       c2[3] = 1;
-               }
-       }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
-}
-
-static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int texturesurfaceindex;
        int i;
        float f;
        const float *v;
        const float *c;
        float *c2;
-       if (rsurface.lightmapcolor4f)
+       if (rsurface.passcolor4f)
        {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               // generate color arrays
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4, c2 = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
-                       {
-                               f = RSurf_FogVertex(v);
-                               c2[0] = c[0] * f;
-                               c2[1] = c[1] * f;
-                               c2[2] = c[2] * f;
-                               c2[3] = c[3];
-                       }
+                       f = RSurf_FogVertex(v);
+                       c2[0] = c[0] * f;
+                       c2[1] = c[1] * f;
+                       c2[2] = c[2] * f;
+                       c2[3] = c[3];
                }
        }
        else
        {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c2 = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c2 += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
-                       {
-                               f = RSurf_FogVertex(v);
-                               c2[0] = f;
-                               c2[1] = f;
-                               c2[2] = f;
-                               c2[3] = 1;
-                       }
+                       f = RSurf_FogVertex(v);
+                       c2[0] = f;
+                       c2[1] = f;
+                       c2[2] = f;
+                       c2[3] = 1;
                }
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
+       rsurface.passcolor4f = rsurface.array_passcolor4f;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(void)
 {
-       int texturesurfaceindex;
        int i;
        float f;
        const float *v;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       // generate color arrays for the surfaces in this list
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4, c2 = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
-               {
-                       f = RSurf_FogVertex(v);
-                       c2[0] = c[0] * f + r_refdef.fogcolor[0] * (1 - f);
-                       c2[1] = c[1] * f + r_refdef.fogcolor[1] * (1 - f);
-                       c2[2] = c[2] * f + r_refdef.fogcolor[2] * (1 - f);
-                       c2[3] = c[3];
-               }
+               f = RSurf_FogVertex(v);
+               c2[0] = c[0] * f + r_refdef.fogcolor[0] * (1 - f);
+               c2[1] = c[1] * f + r_refdef.fogcolor[1] * (1 - f);
+               c2[2] = c[2] * f + r_refdef.fogcolor[2] * (1 - f);
+               c2[3] = c[3];
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
+       rsurface.passcolor4f = rsurface.array_passcolor4f;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a)
+static void RSurf_DrawBatch_GL11_ApplyColor(float r, float g, float b, float a)
 {
-       int texturesurfaceindex;
        int i;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       for (i = 0, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4, c2 = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
-               {
-                       c2[0] = c[0] * r;
-                       c2[1] = c[1] * g;
-                       c2[2] = c[2] * b;
-                       c2[3] = c[3] * a;
-               }
+               c2[0] = c[0] * r;
+               c2[1] = c[1] * g;
+               c2[2] = c[2] * b;
+               c2[3] = c[3] * a;
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
+       rsurface.passcolor4f = rsurface.array_passcolor4f;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyAmbient(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyAmbient(void)
 {
-       int texturesurfaceindex;
        int i;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       for (i = 0, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4, c2 = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
-               {
-                       c2[0] = c[0] + r_refdef.scene.ambient;
-                       c2[1] = c[1] + r_refdef.scene.ambient;
-                       c2[2] = c[2] + r_refdef.scene.ambient;
-                       c2[3] = c[3];
-               }
+               c2[0] = c[0] + r_refdef.scene.ambient;
+               c2[1] = c[1] + r_refdef.scene.ambient;
+               c2[2] = c[2] + r_refdef.scene.ambient;
+               c2[3] = c[3];
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
+       rsurface.passcolor4f = rsurface.array_passcolor4f;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_Lightmap(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize
-       rsurface.lightmapcolor4f = NULL;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
+       RSurf_BindLightmapForBatch();
+       RSurf_DrawBatch();
 }
 
-static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_Unlit(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize applyfog && applycolor case
        // just apply fog if necessary, and tint the fog color array if necessary
-       rsurface.lightmapcolor4f = NULL;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch();
 }
 
-static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_VertexColor(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
-       int texturesurfaceindex;
-       int i;
-       float *c;
        // TODO: optimize
-       if (texturesurfacelist[0]->lightmapinfo)
-       {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, c = rsurface.array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
-                       {
-                               if (surface->lightmapinfo->samples)
-                               {
-                                       const unsigned char *lm = surface->lightmapinfo->samples + (rsurface.modellightmapoffsets + surface->num_firstvertex)[i];
-                                       float scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[0]] * (1.0f / 32768.0f);
-                                       VectorScale(lm, scale, c);
-                                       if (surface->lightmapinfo->styles[1] != 255)
-                                       {
-                                               int size3 = ((surface->lightmapinfo->extents[0]>>4)+1)*((surface->lightmapinfo->extents[1]>>4)+1)*3;
-                                               lm += size3;
-                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[1]] * (1.0f / 32768.0f);
-                                               VectorMA(c, scale, lm, c);
-                                               if (surface->lightmapinfo->styles[2] != 255)
-                                               {
-                                                       lm += size3;
-                                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[2]] * (1.0f / 32768.0f);
-                                                       VectorMA(c, scale, lm, c);
-                                                       if (surface->lightmapinfo->styles[3] != 255)
-                                                       {
-                                                               lm += size3;
-                                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[3]] * (1.0f / 32768.0f);
-                                                               VectorMA(c, scale, lm, c);
-                                                       }
-                                               }
-                                       }
-                               }
-                               else
-                                       VectorClear(c);
-                               c[3] = 1;
-                       }
-               }
-               rsurface.lightmapcolor4f = rsurface.array_color4f;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
-       }
-       else
+       rsurface.passcolor4f = rsurface.batchlightmapcolor4f;
+       rsurface.passcolor4f_vertexbuffer = rsurface.batchlightmapcolor4f_vertexbuffer;
+       rsurface.passcolor4f_bufferoffset = rsurface.batchlightmapcolor4f_bufferoffset;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
+       GL_Color(r, g, b, a);
+       RSurf_DrawBatch();
+}
+
+static void RSurf_DrawBatch_GL11_ClampColor(void)
+{
+       int i;
+       const float *c1;
+       float *c2;
+       if (!rsurface.passcolor4f)
+               return;
+       for (i = 0, c1 = rsurface.passcolor4f + 4*rsurface.batchfirstvertex, c2 = rsurface.array_passcolor4f + 4*rsurface.batchfirstvertex;i < rsurface.batchnumvertices;i++, c1 += 4, c2 += 4)
        {
-               rsurface.lightmapcolor4f = rsurface.modellightmapcolor4f;
-               rsurface.lightmapcolor4f_bufferobject = rsurface.modellightmapcolor4f_bufferobject;
-               rsurface.lightmapcolor4f_bufferoffset = rsurface.modellightmapcolor4f_bufferoffset;
+               c2[0] = bound(0.0f, c1[0], 1.0f);
+               c2[1] = bound(0.0f, c1[1], 1.0f);
+               c2[2] = bound(0.0f, c1[2], 1.0f);
+               c2[3] = bound(0.0f, c1[3], 1.0f);
        }
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
-       GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const msurface_t **texturesurfacelist, float *r, float *g, float *b, float *a, qboolean *applycolor)
+static void RSurf_DrawBatch_GL11_ApplyVertexShade(float *r, float *g, float *b, float *a, qboolean *applycolor)
 {
-       int texturesurfaceindex;
        int i;
        float f;
        float alpha;
@@ -10875,33 +11101,24 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const
        diffusecolor[1] = rsurface.modellight_diffuse[1] * *g * f;
        diffusecolor[2] = rsurface.modellight_diffuse[2] * *b * f;
        alpha = *a;
-       if (VectorLength2(diffusecolor) > 0 && rsurface.normal3f)
+       if (VectorLength2(diffusecolor) > 0)
        {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               // q3-style directional shading
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, n = rsurface.batchnormal3f + rsurface.batchfirstvertex * 3, c = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, n += 3, c += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       int numverts = surface->num_vertices;
-                       v = rsurface.vertex3f + 3 * surface->num_firstvertex;
-                       n = rsurface.normal3f + 3 * surface->num_firstvertex;
-                       c = rsurface.array_color4f + 4 * surface->num_firstvertex;
-                       // q3-style directional shading
-                       for (i = 0;i < numverts;i++, v += 3, n += 3, c += 4)
-                       {
-                               if ((f = DotProduct(n, lightdir)) > 0)
-                                       VectorMA(ambientcolor, f, diffusecolor, c);
-                               else
-                                       VectorCopy(ambientcolor, c);
-                               c[3] = alpha;
-                       }
+                       if ((f = DotProduct(n, lightdir)) > 0)
+                               VectorMA(ambientcolor, f, diffusecolor, c);
+                       else
+                               VectorCopy(ambientcolor, c);
+                       c[3] = alpha;
                }
                *r = 1;
                *g = 1;
                *b = 1;
                *a = 1;
-               rsurface.lightmapcolor4f = rsurface.array_color4f;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
+               rsurface.passcolor4f = rsurface.array_passcolor4f;
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
                *applycolor = false;
        }
        else
@@ -10909,20 +11126,36 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const
                *r = ambientcolor[0];
                *g = ambientcolor[1];
                *b = ambientcolor[2];
-               rsurface.lightmapcolor4f = NULL;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
+               rsurface.passcolor4f = NULL;
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
        }
 }
 
-static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_VertexShade(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
-       RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &r, &g, &b, &a, &applycolor);
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       RSurf_DrawBatch_GL11_ApplyVertexShade(&r, &g, &b, &a, &applycolor);
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch();
+}
+
+static void RSurf_DrawBatch_GL11_MakeFogColor(float r, float g, float b, float a)
+{
+       int i;
+       float f;
+       const float *v;
+       float *c;
+       for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.array_passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4)
+       {
+               f = 1 - RSurf_FogVertex(v);
+               c[0] = r;
+               c[1] = g;
+               c[2] = b;
+               c[3] = f * a;
+       }
 }
 
 void RSurf_SetupDepthAndCulling(void)
@@ -10953,8 +11186,6 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        // level, so don't use it then either.
        if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis)
        {
-               GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                R_Mesh_ResetTextureState();
                if (skyrendermasked)
                {
@@ -10964,15 +11195,19 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                        // just to make sure that braindead drivers don't draw
                        // anything despite that colormask...
                        GL_BlendFunc(GL_ZERO, GL_ONE);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXPOSITION, texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_PrepareVertices_Position(rsurface.batchnumvertices, rsurface.batchvertexposition, rsurface.batchvertexpositionbuffer);
                }
                else
                {
                        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                        // fog sky
                        GL_BlendFunc(GL_ONE, GL_ZERO);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
+                       GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
+                       R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, NULL, NULL);
                }
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch();
                if (skyrendermasked)
                        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        }
@@ -10986,50 +11221,50 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
 {
        if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
                return;
-       RSurf_PrepareVerticesForBatch(true, true, texturenumsurfaces, texturesurfacelist);
        if (prepass)
        {
                // render screenspace normalmap to texture
                GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch();
        }
        else if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA)) && !r_waterstate.renderingscene)
        {
                // render water or distortion background, then blend surface on top
                GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND);
-               RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(texturenumsurfaces, texturesurfacelist);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, texturenumsurfaces, texturesurfacelist);
+               RSurf_BindReflectionForBatch();
+               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+                       RSurf_BindLightmapForBatch();
+               RSurf_DrawBatch();
                GL_DepthMask(false);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist);
                if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-                       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
-               else
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       RSurf_BindLightmapForBatch();
+               RSurf_DrawBatch();
        }
        else
        {
                // render surface normally
                GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist);
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
-                       RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(texturenumsurfaces, texturesurfacelist);
-               else if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-                       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
-               else
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       RSurf_BindReflectionForBatch();
+               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+                       RSurf_BindLightmapForBatch();
+               RSurf_DrawBatch();
        }
 }
 
 static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.3 path - anything not completely ancient
-       int texturesurfaceindex;
        qboolean applycolor;
        qboolean applyfog;
        int layerindex;
        const texturelayer_t *layer;
-       RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | ((!rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.modeltexcoordlightmap2f ? BATCHNEED_ARRAY_LIGHTMAP : 0) | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
 
        for (layerindex = 0, layer = rsurface.texture->currentlayers;layerindex < rsurface.texture->currentnumlayers;layerindex++, layer++)
        {
@@ -11064,7 +11299,7 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                }
                layercolor[3] = layer->color[3];
                applycolor = layercolor[0] != 1 || layercolor[1] != 1 || layercolor[2] != 1 || layercolor[3] != 1;
-               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, 0, 0);
                applyfog = r_refdef.fogenabled && (rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED);
                switch (layer->type)
                {
@@ -11073,27 +11308,27 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                        R_Mesh_TexBind(0, r_texture_white);
                        R_Mesh_TexMatrix(0, NULL);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
                        R_Mesh_TexBind(1, layer->texture);
                        R_Mesh_TexMatrix(1, &layer->texmatrix);
                        R_Mesh_TexCombine(1, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                       R_Mesh_TexCoordPointer(1, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                               RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_VertexShade(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        else if (rsurface.uselightmaptexture)
-                               RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_Lightmap(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        else
-                               RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_VertexColor(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        break;
                case TEXTURELAYERTYPE_TEXTURE:
                        // singletexture unlit texture with transparency support
                        R_Mesh_TexBind(0, layer->texture);
                        R_Mesh_TexMatrix(0, &layer->texmatrix);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        R_Mesh_TexBind(1, 0);
-                       R_Mesh_TexCoordPointer(1, 2, NULL, 0, 0);
-                       RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
+                       RSurf_DrawBatch_GL11_Unlit(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        break;
                case TEXTURELAYERTYPE_FOG:
                        // singletexture fogging
@@ -11102,34 +11337,19 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        }
                        else
                        {
                                R_Mesh_TexBind(0, 0);
-                               R_Mesh_TexCoordPointer(0, 2, NULL, 0, 0);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        }
                        R_Mesh_TexBind(1, 0);
-                       R_Mesh_TexCoordPointer(1, 2, NULL, 0, 0);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        // generate a color array for the fog pass
-                       R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               int i;
-                               float f;
-                               const float *v;
-                               float *c;
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
-                               {
-                                       f = 1 - RSurf_FogVertex(v);
-                                       c[0] = layercolor[0];
-                                       c[1] = layercolor[1];
-                                       c[2] = layercolor[2];
-                                       c[3] = f * layercolor[3];
-                               }
-                       }
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.array_passcolor4f, 0, 0);
+                       RSurf_DrawBatch_GL11_MakeFogColor(layercolor[0], layercolor[1], layercolor[2], layercolor[3]);
+                       RSurf_DrawBatch();
                        break;
                default:
                        Con_Printf("R_DrawTextureSurfaceList: unknown layer type %i\n", layer->type);
@@ -11146,11 +11366,11 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
 static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.1 - crusty old voodoo path
-       int texturesurfaceindex;
        qboolean applyfog;
        int layerindex;
        const texturelayer_t *layer;
-       RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | ((!rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.modeltexcoordlightmap2f ? BATCHNEED_ARRAY_LIGHTMAP : 0) | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
 
        for (layerindex = 0, layer = rsurface.texture->currentlayers;layerindex < rsurface.texture->currentnumlayers;layerindex++, layer++)
        {
@@ -11166,7 +11386,7 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                }
                GL_DepthMask(layer->depthmask && writedepth);
                GL_BlendFunc(layer->blendfunc1, layer->blendfunc2);
-               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, 0, 0);
                applyfog = r_refdef.fogenabled && (rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED);
                switch (layer->type)
                {
@@ -11178,20 +11398,20 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, r_texture_white);
                                R_Mesh_TexMatrix(0, NULL);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
                                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                                       RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_VertexShade(1, 1, 1, 1, false, false);
                                else if (rsurface.uselightmaptexture)
-                                       RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_Lightmap(1, 1, 1, 1, false, false);
                                else
-                                       RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_VertexColor(1, 1, 1, 1, false, false);
                                // then apply the texture to it
                                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-                               RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layer->color[0] * 0.5f, layer->color[1] * 0.5f, layer->color[2] * 0.5f, layer->color[3], layer->color[0] != 2 || layer->color[1] != 2 || layer->color[2] != 2 || layer->color[3] != 1, false);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                               RSurf_DrawBatch_GL11_Unlit(layer->color[0] * 0.5f, layer->color[1] * 0.5f, layer->color[2] * 0.5f, layer->color[3], layer->color[0] != 2 || layer->color[1] != 2 || layer->color[2] != 2 || layer->color[3] != 1, false);
                        }
                        else
                        {
@@ -11199,11 +11419,11 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                                       RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                                       RSurf_DrawBatch_GL11_VertexShade(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                                else
-                                       RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                                       RSurf_DrawBatch_GL11_VertexColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                        }
                        break;
                case TEXTURELAYERTYPE_TEXTURE:
@@ -11211,8 +11431,8 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                        R_Mesh_TexBind(0, layer->texture);
                        R_Mesh_TexMatrix(0, &layer->texmatrix);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-                       RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                       RSurf_DrawBatch_GL11_Unlit(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                        break;
                case TEXTURELAYERTYPE_FOG:
                        // singletexture fogging
@@ -11221,32 +11441,17 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        }
                        else
                        {
                                R_Mesh_TexBind(0, 0);
-                               R_Mesh_TexCoordPointer(0, 2, NULL, 0, 0);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        }
                        // generate a color array for the fog pass
-                       R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               int i;
-                               float f;
-                               const float *v;
-                               float *c;
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
-                               {
-                                       f = 1 - RSurf_FogVertex(v);
-                                       c[0] = layer->color[0];
-                                       c[1] = layer->color[1];
-                                       c[2] = layer->color[2];
-                                       c[3] = f * layer->color[3];
-                               }
-                       }
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.array_passcolor4f, 0, 0);
+                       RSurf_DrawBatch_GL11_MakeFogColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3]);
+                       RSurf_DrawBatch();
                        break;
                default:
                        Con_Printf("R_DrawTextureSurfaceList: unknown layer type %i\n", layer->type);
@@ -11260,12 +11465,14 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
        }
 }
 
-static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
+       int vi;
+       int j;
+       r_vertexgeneric_t *batchvertex;
        float c[4];
 
        GL_AlphaTest(false);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
 
@@ -11323,55 +11530,127 @@ static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, const
                GL_DepthMask(writedepth);
        }
 
-       rsurface.lightmapcolor4f = NULL;
-
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+       if (r_showsurfaces.integer == 3)
        {
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               rsurface.passcolor4f = NULL;
 
-               rsurface.lightmapcolor4f = NULL;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
-       }
-       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-       {
-               qboolean applycolor = true;
-               float one = 1.0;
+               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
 
-               RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+                       rsurface.passcolor4f = NULL;
+                       rsurface.passcolor4f_vertexbuffer = 0;
+                       rsurface.passcolor4f_bufferoffset = 0;
+               }
+               else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
+               {
+                       qboolean applycolor = true;
+                       float one = 1.0;
 
-               r_refdef.lightmapintensity = 1;
-               RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &one, &one, &one, &one, &applycolor);
-               r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
-       }
-       else
-       {
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
 
-               rsurface.lightmapcolor4f = rsurface.modellightmapcolor4f;
-               rsurface.lightmapcolor4f_bufferobject = rsurface.modellightmapcolor4f_bufferobject;
-               rsurface.lightmapcolor4f_bufferoffset = rsurface.modellightmapcolor4f_bufferoffset;
-       }
+                       r_refdef.lightmapintensity = 1;
+                       RSurf_DrawBatch_GL11_ApplyVertexShade(&one, &one, &one, &one, &applycolor);
+                       r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
+               }
+               else
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
 
-       if(!rsurface.lightmapcolor4f)
-               RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(texturenumsurfaces, texturesurfacelist);
+                       rsurface.passcolor4f = rsurface.batchlightmapcolor4f;
+                       rsurface.passcolor4f_vertexbuffer = rsurface.batchlightmapcolor4f_vertexbuffer;
+                       rsurface.passcolor4f_bufferoffset = rsurface.batchlightmapcolor4f_bufferoffset;
+               }
+
+               if(!rsurface.passcolor4f)
+                       RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray();
 
-       RSurf_DrawBatch_GL11_ApplyAmbient(texturenumsurfaces, texturesurfacelist);
-       RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, c[0], c[1], c[2], c[3]);
-       if(r_refdef.fogenabled)
-               RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch_GL11_ApplyAmbient();
+               RSurf_DrawBatch_GL11_ApplyColor(c[0], c[1], c[2], c[3]);
+               if(r_refdef.fogenabled)
+                       RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors();
+               RSurf_DrawBatch_GL11_ClampColor();
 
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.passcolor4f, NULL);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               RSurf_DrawBatch();
+       }
+       else if (!r_refdef.view.showdebug)
+       {
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices);
+               for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++)
+               {
+                       VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                       Vector4Set(batchvertex[vi].color4ub, 0, 0, 0, 255);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
+       else if (r_showsurfaces.integer == 4)
+       {
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices);
+               for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++)
+               {
+                       unsigned char c = vi << 3;
+                       VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                       Vector4Set(batchvertex[vi].color4ub, c, c, c, 255);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
+       else if (r_showsurfaces.integer == 2)
+       {
+               const int *e;
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(3*rsurface.batchnumtriangles);
+               for (j = 0, e = rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle;j < rsurface.batchnumtriangles;j++, e += 3)
+               {
+                       unsigned char c = (j + rsurface.batchfirsttriangle) << 3;
+                       VectorCopy(rsurface.batchvertex3f + 3*e[0], batchvertex[j*3+0].vertex3f);
+                       VectorCopy(rsurface.batchvertex3f + 3*e[1], batchvertex[j*3+1].vertex3f);
+                       VectorCopy(rsurface.batchvertex3f + 3*e[2], batchvertex[j*3+2].vertex3f);
+                       Vector4Set(batchvertex[j*3+0].color4ub, c, c, c, 255);
+                       Vector4Set(batchvertex[j*3+1].color4ub, c, c, c, 255);
+                       Vector4Set(batchvertex[j*3+2].color4ub, c, c, c, 255);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               R_Mesh_Draw(0, rsurface.batchnumtriangles*3, 0, rsurface.batchnumtriangles, NULL, NULL, 0, NULL, NULL, 0);
+       }
+       else
+       {
+               int texturesurfaceindex;
+               int k;
+               const msurface_t *surface;
+               unsigned char surfacecolor4ub[4];
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchfirstvertex + rsurface.batchnumvertices);
+               vi = 0;
+               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               {
+                       surface = texturesurfacelist[texturesurfaceindex];
+                       k = (int)(((size_t)surface) / sizeof(msurface_t));
+                       Vector4Set(surfacecolor4ub, (k & 0xF) << 4, (k & 0xF0), (k & 0xF00) >> 4, 255);
+                       for (j = 0;j < surface->num_vertices;j++)
+                       {
+                               VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                               Vector4Copy(surfacecolor4ub, batchvertex[vi].color4ub);
+                               vi++;
+                       }
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
 }
 
 static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
-       if (r_showsurfaces.integer == 3 && !prepass && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY))
+       if (r_showsurfaces.integer)
        {
-               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+               R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
                return;
        }
        switch (vid.renderpath)
@@ -11394,9 +11673,9 @@ static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
-       if (r_showsurfaces.integer == 3 && !prepass && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY))
+       if (r_showsurfaces.integer)
        {
-               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+               R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
                return;
        }
        switch (vid.renderpath)
@@ -11455,7 +11734,9 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        surface = rsurface.modelsurfaces + surfacelist[i];
                        texture = surface->texture;
                        rsurface.texture = R_GetCurrentTexture(texture);
-                       rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
+                       rsurface.lightmaptexture = NULL;
+                       rsurface.deluxemaptexture = NULL;
+                       rsurface.uselightmaptexture = false;
                        // scan ahead until we find a different texture
                        endsurface = min(i + 1024, numsurfaces);
                        texturenumsurfaces = 0;
@@ -11463,7 +11744,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        for (;j < endsurface;j++)
                        {
                                surface = rsurface.modelsurfaces + surfacelist[j];
-                               if (texture != surface->texture || rsurface.uselightmaptexture != (surface->lightmaptexture != NULL))
+                               if (texture != surface->texture)
                                        break;
                                texturesurfacelist[texturenumsurfaces++] = surface;
                        }
@@ -11479,13 +11760,13 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_BlendFunc(GL_ONE, GL_ZERO);
                                GL_DepthMask(true);
                                GL_AlphaTest(false);
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                                R_Mesh_ResetTextureState();
                                R_SetupShader_DepthOrShadow();
                        }
                        RSurf_SetupDepthAndCulling();
-                       RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXPOSITION, texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_PrepareVertices_Position(rsurface.batchnumvertices, rsurface.batchvertexposition, rsurface.batchvertexpositionbuffer);
+                       RSurf_DrawBatch();
                }
                if (setup)
                        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
@@ -11497,6 +11778,8 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                surface = rsurface.modelsurfaces + surfacelist[i];
                texture = surface->texture;
                rsurface.texture = R_GetCurrentTexture(texture);
+               rsurface.lightmaptexture = surface->lightmaptexture;
+               rsurface.deluxemaptexture = surface->deluxemaptexture;
                rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
                // scan ahead until we find a different texture
                endsurface = min(i + MAXBATCH_TRANSPARENTSURFACES, numsurfaces);
@@ -11505,7 +11788,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                for (;j < endsurface;j++)
                {
                        surface = rsurface.modelsurfaces + surfacelist[j];
-                       if (texture != surface->texture || rsurface.uselightmaptexture != (surface->lightmaptexture != NULL))
+                       if (texture != surface->texture || rsurface.lightmaptexture != surface->lightmaptexture)
                                break;
                        texturesurfacelist[texturenumsurfaces++] = surface;
                }
@@ -11542,20 +11825,24 @@ static void R_ProcessTransparentTextureSurfaceList(int texturenumsurfaces, const
        }
 }
 
+static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+{
+       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
+               return;
+       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
+               return;
+       RSurf_SetupDepthAndCulling();
+       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXPOSITION, texturenumsurfaces, texturesurfacelist);
+       R_Mesh_PrepareVertices_Position(rsurface.batchnumvertices, rsurface.batchvertexposition, rsurface.batchvertexpositionbuffer);
+       RSurf_DrawBatch();
+}
+
 static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, qboolean prepass)
 {
        const entity_render_t *queueentity = r_refdef.scene.worldentity;
        CHECKGLERROR
        if (depthonly)
-       {
-               if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
-                       return;
-               if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
-                       return;
-               RSurf_SetupDepthAndCulling();
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
+               R_DrawTextureSurfaceList_DepthOnly(texturenumsurfaces, texturesurfacelist);
        else if (prepass)
        {
                if (!rsurface.texture->currentnumlayers)
@@ -11565,34 +11852,7 @@ static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, const msurf
                else
                        R_DrawWorldTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
        }
-       else if (r_showsurfaces.integer && !r_refdef.view.showdebug && !prepass)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_Color(0, 0, 0, 1);
-               GL_DepthTest(writedepth);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3 && !prepass)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_DepthTest(true);
-               RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY)
+       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) && !r_showsurfaces.integer)
                R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
        else if (!rsurface.texture->currentnumlayers)
                return;
@@ -11624,6 +11884,8 @@ void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, in
                // use skin 1 instead)
                texture = surfacelist[i]->texture;
                rsurface.texture = R_GetCurrentTexture(texture);
+               rsurface.lightmaptexture = surfacelist[i]->lightmaptexture;
+               rsurface.deluxemaptexture = surfacelist[i]->deluxemaptexture;
                rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL && !depthonly && !prepass;
                if (!(rsurface.texture->currentmaterialflags & flagsmask) || (rsurface.texture->currentmaterialflags & MATERIALFLAG_NODRAW))
                {
@@ -11633,7 +11895,7 @@ void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, in
                        continue;
                }
                // simply scan ahead until we find a different texture or lightmap state
-               for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
+               for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.lightmaptexture == surfacelist[j]->lightmaptexture;j++)
                        ;
                // render the range of surfaces
                R_ProcessWorldTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, prepass);
@@ -11644,15 +11906,7 @@ static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurf
 {
        CHECKGLERROR
        if (depthonly)
-       {
-               if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
-                       return;
-               if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
-                       return;
-               RSurf_SetupDepthAndCulling();
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
+               R_DrawTextureSurfaceList_DepthOnly(texturenumsurfaces, texturesurfacelist);
        else if (prepass)
        {
                if (!rsurface.texture->currentnumlayers)
@@ -11662,34 +11916,7 @@ static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurf
                else
                        R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
        }
-       else if (r_showsurfaces.integer && !r_refdef.view.showdebug)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-