]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
speed up decal creation in newdecalsystem by using BIH for finding the affected triangles
[xonotic/darkplaces.git] / gl_rmain.c
index be299bf2fa4ece6c8d77db1e132a25878ae97ef6..0234fdfb76bf4c3d18d44a889dd8e2a4e9c44261 100644 (file)
@@ -25,6 +25,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "polygon.h"
 #include "image.h"
 #include "ft2.h"
+#include "csprogs.h"
+#include "cl_video.h"
 
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
@@ -74,7 +76,9 @@ cvar_t r_showcollisionbrushes_polygonoffset = {0, "r_showcollisionbrushes_polygo
 cvar_t r_showdisabledepthtest = {0, "r_showdisabledepthtest", "0", "disables depth testing on r_show* cvars, allowing you to see what hidden geometry the graphics card is processing"};
 cvar_t r_drawportals = {0, "r_drawportals", "0", "shows portals (separating polygons) in world interior in quake1 maps"};
 cvar_t r_drawentities = {0, "r_drawentities","1", "draw entities (doors, players, projectiles, etc)"};
+cvar_t r_drawworld = {0, "r_drawworld","1", "draw world (most static stuff)"};
 cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"};
+cvar_t r_drawexteriormodel = {0, "r_drawexteriormodel","1", "draw your player model (e.g. in chase cam, reflections)"};
 cvar_t r_cullentities_trace = {0, "r_cullentities_trace", "1", "probabistically cull invisible entities"};
 cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "number of samples to test for entity culling (in addition to center sample)"};
 cvar_t r_cullentities_trace_tempentitysamples = {0, "r_cullentities_trace_tempentitysamples", "-1", "number of samples to test for entity culling of temp entities (including all CSQC entities), -1 disables trace culling on these entities to prevent flicker (pvs still applies)"};
@@ -165,7 +169,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"};
@@ -216,6 +219,7 @@ rtexture_t *r_texture_notexture;
 rtexture_t *r_texture_whitecube;
 rtexture_t *r_texture_normalizationcube;
 rtexture_t *r_texture_fogattenuation;
+rtexture_t *r_texture_fogheighttexture;
 rtexture_t *r_texture_gammaramps;
 unsigned int r_texture_gammaramps_serial;
 //rtexture_t *r_texture_fogintensity;
@@ -302,6 +306,7 @@ void FOG_clear(void)
        r_refdef.fog_end = 16384;
        r_refdef.fog_height = 1<<30;
        r_refdef.fog_fadedepth = 128;
+       memset(r_refdef.fog_height_texturename, 0, sizeof(r_refdef.fog_height_texturename));
 }
 
 static void R_BuildBlankTextures(void)
@@ -311,22 +316,22 @@ static void R_BuildBlankTextures(void)
        data[1] = 128; // normal Y
        data[0] = 255; // normal Z
        data[3] = 128; // height
-       r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 255;
        data[1] = 255;
        data[2] = 255;
        data[3] = 255;
-       r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 128;
        data[1] = 128;
        data[2] = 128;
        data[3] = 255;
-       r_texture_grey128 = R_LoadTexture2D(r_main_texturepool, "blankgrey128", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_grey128 = R_LoadTexture2D(r_main_texturepool, "blankgrey128", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 0;
        data[1] = 0;
        data[2] = 0;
        data[3] = 255;
-       r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildNoTexture(void)
@@ -354,14 +359,14 @@ static void R_BuildNoTexture(void)
                        }
                }
        }
-       r_texture_notexture = R_LoadTexture2D(r_main_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_BGRA, TEXF_MIPMAP | TEXF_PERSISTENT, NULL);
+       r_texture_notexture = R_LoadTexture2D(r_main_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_BGRA, TEXF_MIPMAP | TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildWhiteCube(void)
 {
        unsigned char data[6*1*1*4];
        memset(data, 255, sizeof(data));
-       r_texture_whitecube = R_LoadTextureCubeMap(r_main_texturepool, "whitecube", 1, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+       r_texture_whitecube = R_LoadTextureCubeMap(r_main_texturepool, "whitecube", 1, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildNormalizationCube(void)
@@ -422,7 +427,7 @@ static void R_BuildNormalizationCube(void)
                        }
                }
        }
-       r_texture_normalizationcube = R_LoadTextureCubeMap(r_main_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+       r_texture_normalizationcube = R_LoadTextureCubeMap(r_main_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
        Mem_Free(data);
 }
 
@@ -477,11 +482,79 @@ static void R_BuildFogTexture(void)
        }
        else
        {
-               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
+               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, -1, NULL);
                //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALLOWUPDATES, NULL);
        }
 }
 
+static void R_BuildFogHeightTexture(void)
+{
+       unsigned char *inpixels;
+       int size;
+       int x;
+       int y;
+       int j;
+       float c[4];
+       float f;
+       inpixels = NULL;
+       strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
+       if (r_refdef.fogheighttexturename[0])
+               inpixels = loadimagepixelsbgra(r_refdef.fogheighttexturename, true, false, false, NULL);
+       if (!inpixels)
+       {
+               r_refdef.fog_height_tablesize = 0;
+               if (r_texture_fogheighttexture)
+                       R_FreeTexture(r_texture_fogheighttexture);
+               r_texture_fogheighttexture = NULL;
+               if (r_refdef.fog_height_table2d)
+                       Mem_Free(r_refdef.fog_height_table2d);
+               r_refdef.fog_height_table2d = NULL;
+               if (r_refdef.fog_height_table1d)
+                       Mem_Free(r_refdef.fog_height_table1d);
+               r_refdef.fog_height_table1d = NULL;
+               return;
+       }
+       size = image_width;
+       r_refdef.fog_height_tablesize = size;
+       r_refdef.fog_height_table1d = Mem_Alloc(r_main_mempool, size * 4);
+       r_refdef.fog_height_table2d = Mem_Alloc(r_main_mempool, size * size * 4);
+       memcpy(r_refdef.fog_height_table1d, inpixels, size * 4);
+       Mem_Free(inpixels);
+       // LordHavoc: now the magic - what is that table2d for?  it is a cooked
+       // average fog color table accounting for every fog layer between a point
+       // and the camera.  (Note: attenuation is handled separately!)
+       for (y = 0;y < size;y++)
+       {
+               for (x = 0;x < size;x++)
+               {
+                       Vector4Clear(c);
+                       f = 0;
+                       if (x < y)
+                       {
+                               for (j = x;j <= y;j++)
+                               {
+                                       Vector4Add(c, r_refdef.fog_height_table1d + j*4, c);
+                                       f++;
+                               }
+                       }
+                       else
+                       {
+                               for (j = x;j >= y;j--)
+                               {
+                                       Vector4Add(c, r_refdef.fog_height_table1d + j*4, c);
+                                       f++;
+                               }
+                       }
+                       f = 1.0f / f;
+                       r_refdef.fog_height_table2d[(y*size+x)*4+0] = (unsigned char)(c[0] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+1] = (unsigned char)(c[1] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+2] = (unsigned char)(c[2] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+3] = (unsigned char)(c[3] * f);
+               }
+       }
+       r_texture_fogheighttexture = R_LoadTexture2D(r_main_texturepool, "fogheighttable", size, size, r_refdef.fog_height_table2d, TEXTYPE_BGRA, TEXF_ALPHA | TEXF_CLAMP, -1, NULL);
+}
+
 //=======================================================================================================================================================
 
 static const char *builtinshaderstring =
@@ -489,7 +562,7 @@ static const char *builtinshaderstring =
 "// written by Forest 'LordHavoc' Hale\n"
 "// shadowmapping enhancements by Lee 'eihrul' Salzman\n"
 "\n"
-"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE)\n"
+"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE) || defined(USEFOGHEIGHTTEXTURE)\n"
 "# define USEFOG\n"
 "#endif\n"
 "#if defined(MODE_LIGHTMAP) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
@@ -582,7 +655,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"
@@ -603,7 +676,7 @@ static const char *builtinshaderstring =
 "#endif\n"
 "//uncomment these if you want to use them:\n"
 "uniform vec4 UserVec1;\n"
-"// uniform vec4 UserVec2;\n"
+"uniform vec4 UserVec2;\n"
 "// uniform vec4 UserVec3;\n"
 "// uniform vec4 UserVec4;\n"
 "// uniform float ClientTime;\n"
@@ -621,12 +694,42 @@ static const char *builtinshaderstring =
 "#ifdef USEPOSTPROCESSING\n"
 "// do r_glsl_dumpshader, edit glsl/default.glsl, and replace this by your own postprocessing if you want\n"
 "// this code does a blur with the radius specified in the first component of r_glsl_postprocess_uservec1 and blends it using the second component\n"
+"      float sobel = 1.0;\n"
+"      // vec2 ts = textureSize(Texture_First, 0);\n"
+"      // vec2 px = vec2(1/ts.x, 1/ts.y);\n"
+"      vec2 px = PixelSize;\n"
+"      vec3 x1 = texture2D(Texture_First, TexCoord1 + vec2(-px.x, px.y)).rgb;\n"
+"      vec3 x2 = texture2D(Texture_First, TexCoord1 + vec2(-px.x,  0.0)).rgb;\n"
+"      vec3 x3 = texture2D(Texture_First, TexCoord1 + vec2(-px.x,-px.y)).rgb;\n"
+"      vec3 x4 = texture2D(Texture_First, TexCoord1 + vec2( px.x, px.y)).rgb;\n"
+"      vec3 x5 = texture2D(Texture_First, TexCoord1 + vec2( px.x,  0.0)).rgb;\n"
+"      vec3 x6 = texture2D(Texture_First, TexCoord1 + vec2( px.x,-px.y)).rgb;\n"
+"      vec3 y1 = texture2D(Texture_First, TexCoord1 + vec2( px.x,-px.y)).rgb;\n"
+"      vec3 y2 = texture2D(Texture_First, TexCoord1 + vec2(  0.0,-px.y)).rgb;\n"
+"      vec3 y3 = texture2D(Texture_First, TexCoord1 + vec2(-px.x,-px.y)).rgb;\n"
+"      vec3 y4 = texture2D(Texture_First, TexCoord1 + vec2( px.x, px.y)).rgb;\n"
+"      vec3 y5 = texture2D(Texture_First, TexCoord1 + vec2(  0.0, px.y)).rgb;\n"
+"      vec3 y6 = texture2D(Texture_First, TexCoord1 + vec2(-px.x, px.y)).rgb;\n"
+"      float px1 = -1.0 * dot(vec3(0.3, 0.59, 0.11), x1);\n"
+"      float px2 = -2.0 * dot(vec3(0.3, 0.59, 0.11), x2);\n"
+"      float px3 = -1.0 * dot(vec3(0.3, 0.59, 0.11), x3);\n"
+"      float px4 =  1.0 * dot(vec3(0.3, 0.59, 0.11), x4);\n"
+"      float px5 =  2.0 * dot(vec3(0.3, 0.59, 0.11), x5);\n"
+"      float px6 =  1.0 * dot(vec3(0.3, 0.59, 0.11), x6);\n"
+"      float py1 = -1.0 * dot(vec3(0.3, 0.59, 0.11), y1);\n"
+"      float py2 = -2.0 * dot(vec3(0.3, 0.59, 0.11), y2);\n"
+"      float py3 = -1.0 * dot(vec3(0.3, 0.59, 0.11), y3);\n"
+"      float py4 =  1.0 * dot(vec3(0.3, 0.59, 0.11), y4);\n"
+"      float py5 =  2.0 * dot(vec3(0.3, 0.59, 0.11), y5);\n"
+"      float py6 =  1.0 * dot(vec3(0.3, 0.59, 0.11), y6);\n"
+"      sobel = 0.25 * abs(px1 + px2 + px3 + px4 + px5 + px6) + 0.25 * abs(py1 + py2 + py3 + py4 + py5 + py6);\n"
 "      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.987688, -0.156434)) * UserVec1.y;\n"
 "      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.156434, -0.891007)) * UserVec1.y;\n"
 "      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2( 0.891007, -0.453990)) * UserVec1.y;\n"
 "      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2( 0.707107,  0.707107)) * UserVec1.y;\n"
 "      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.453990,  0.891007)) * UserVec1.y;\n"
-"      gl_FragColor /= (1 + 5 * UserVec1.y);\n"
+"      gl_FragColor /= (1.0 + 5.0 * UserVec1.y);\n"
+"      gl_FragColor.rgb = gl_FragColor.rgb * (1.0 + UserVec2.x) + vec3(max(0.0, sobel - UserVec2.z))*UserVec2.y;\n"
 "#endif\n"
 "\n"
 "#ifdef USESATURATION\n"
@@ -928,6 +1031,9 @@ static const char *builtinshaderstring =
 "uniform sampler2D Texture_Shirt;\n"
 "#endif\n"
 "#ifdef USEFOG\n"
+"#ifdef USEFOGHEIGHTTEXTURE\n"
+"uniform sampler2D Texture_FogHeightTexture;\n"
+"#endif\n"
 "uniform sampler2D Texture_FogMask;\n"
 "#endif\n"
 "#ifdef USELIGHTMAP\n"
@@ -957,17 +1063,23 @@ static const char *builtinshaderstring =
 "uniform float FogRangeRecip;\n"
 "uniform float FogPlaneViewDist;\n"
 "uniform float FogHeightFade;\n"
-"float FogVertex(void)\n"
+"vec3 FogVertex(vec3 surfacecolor)\n"
 "{\n"
 "      vec3 EyeVectorModelSpace = EyeVectorModelSpaceFogPlaneVertexDist.xyz;\n"
 "      float FogPlaneVertexDist = EyeVectorModelSpaceFogPlaneVertexDist.w;\n"
 "      float fogfrac;\n"
-"#ifdef USEFOGOUTSIDE\n"
-"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
+"#ifdef USEFOGHEIGHTTEXTURE\n"
+"      vec4 fogheightpixel = texture2D(Texture_FogHeightTexture, vec2(1,1) + vec2(FogPlaneVertexDist, FogPlaneViewDist) * (-2.0 * FogHeightFade));\n"
+"      fogfrac = fogheightpixel.a;\n"
+"      return mix(fogheightpixel.rgb * FogColor, surfacecolor, texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)).r);\n"
 "#else\n"
+"# ifdef USEFOGOUTSIDE\n"
+"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
+"# else\n"
 "      fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);\n"
+"# endif\n"
+"      return mix(FogColor, surfacecolor, texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)).r);\n"
 "#endif\n"
-"      return float(texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)));\n"
 "}\n"
 "#endif\n"
 "\n"
@@ -1088,7 +1200,7 @@ static const char *builtinshaderstring =
 "vec4 GetShadowMapTCCube(vec3 dir)\n"
 "{\n"
 "      vec3 adir = abs(dir);\n"
-"      return vec4(dir, ShadowMap_Parameters.z + ShadowMap_Parameters.w / max(max(adir.x, adir.y), adir.z));\n"
+"      return vec4(dir, ShadowMap_Parameters.w + ShadowMap_Parameters.y / max(max(adir.x, adir.y), adir.z));\n"
 "}\n"
 "#endif\n"
 "\n"
@@ -1162,8 +1274,28 @@ static const char *builtinshaderstring =
 "#      else\n"
 "#        define texval(x, y) texture4(Texture_ShadowMap2D, center + vec2(x, y)*ShadowMap_TextureScale)\n"
 "#      endif\n"
-"      vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
-"      center *= ShadowMap_TextureScale;\n"
+"      vec2 offset = fract(shadowmaptc.xy - 0.5), center = (shadowmaptc.xy - offset)*ShadowMap_TextureScale;\n"
+"#      if USESHADOWMAPPCF > 1\n"
+"   vec4 group1 = step(shadowmaptc.z, texval(-2.0, -2.0));\n"
+"   vec4 group2 = step(shadowmaptc.z, texval( 0.0, -2.0));\n"
+"   vec4 group3 = step(shadowmaptc.z, texval( 2.0, -2.0));\n"
+"   vec4 group4 = step(shadowmaptc.z, texval(-2.0,  0.0));\n"
+"   vec4 group5 = step(shadowmaptc.z, texval( 0.0,  0.0));\n"
+"   vec4 group6 = step(shadowmaptc.z, texval( 2.0,  0.0));\n"
+"   vec4 group7 = step(shadowmaptc.z, texval(-2.0,  2.0));\n"
+"   vec4 group8 = step(shadowmaptc.z, texval( 0.0,  2.0));\n"
+"   vec4 group9 = step(shadowmaptc.z, texval( 2.0,  2.0));\n"
+"      vec4 locols = vec4(group1.ab, group3.ab);\n"
+"      vec4 hicols = vec4(group7.rg, group9.rg);\n"
+"      locols.yz += group2.ab;\n"
+"      hicols.yz += group8.rg;\n"
+"      vec4 midcols = vec4(group1.rg, group3.rg) + vec4(group7.ab, group9.ab) +\n"
+"                              vec4(group4.rg, group6.rg) + vec4(group4.ab, group6.ab) +\n"
+"                              mix(locols, hicols, offset.y);\n"
+"      vec4 cols = group5 + vec4(group2.rg, group8.ab);\n"
+"      cols.xyz += mix(midcols.xyz, midcols.yzw, offset.x);\n"
+"      f = dot(cols, vec4(1.0/25.0));\n"
+"#      else\n"
 "      vec4 group1 = step(shadowmaptc.z, texval(-1.0, -1.0));\n"
 "      vec4 group2 = step(shadowmaptc.z, texval( 1.0, -1.0));\n"
 "      vec4 group3 = step(shadowmaptc.z, texval(-1.0,  1.0));\n"
@@ -1171,6 +1303,7 @@ static const char *builtinshaderstring =
 "      vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) +\n"
 "                              mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y);\n"
 "      f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
+"#      endif\n"
 "#     else\n"
 "#      ifdef GL_EXT_gpu_shader4\n"
 "#        define texval(x, y) texture2DOffset(Texture_ShadowMap2D, center, ivec2(x, y)).r\n"
@@ -1661,11 +1794,7 @@ static const char *builtinshaderstring =
 "#endif\n"
 "\n"
 "#ifdef USEFOG\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      color.rgb *= myhalf(FogVertex());\n"
-"#else\n"
-"      color.rgb = mix(FogColor, color.rgb, FogVertex());\n"
-"#endif\n"
+"      color.rgb = FogVertex(color.rgb);\n"
 "#endif\n"
 "\n"
 "      // reflection must come last because it already contains exactly the correct fog (the reflection render preserves camera distance from the plane, it only flips the side) and ContrastBoost/SceneBrightness\n"
@@ -1735,7 +1864,12 @@ const char *builtincgshaderstring =
 "// written by Forest 'LordHavoc' Hale\n"
 "// shadowmapping enhancements by Lee 'eihrul' Salzman\n"
 "\n"
-"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE)\n"
+"// FIXME: we need to get rid of ModelViewProjectionPosition to make room for the texcoord for this\n"
+"#if defined(USEREFLECTION)\n"
+"#undef USESHADOWMAPORTHO\n"
+"#endif\n"
+"\n"
+"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE) || defined(USEFOGHEIGHTTEXTURE)\n"
 "# define USEFOG\n"
 "#endif\n"
 "#if defined(MODE_LIGHTMAP) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
@@ -1745,6 +1879,10 @@ const char *builtincgshaderstring =
 "#define USEEYEVECTOR\n"
 "#endif\n"
 "\n"
+"#ifdef FRAGMENT_SHADER\n"
+"#define texDepth2D(tex,texcoord) tex2D(tex,texcoord).r\n"
+"#endif\n"
+"\n"
 "#ifdef MODE_DEPTH_OR_SHADOW\n"
 "#ifdef VERTEX_SHADER\n"
 "void main\n"
@@ -1800,7 +1938,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"
@@ -2122,15 +2260,21 @@ const char *builtincgshaderstring =
 "#ifdef FRAGMENT_SHADER\n"
 "\n"
 "#ifdef USEFOG\n"
-"float FogVertex(float3 EyeVectorModelSpace, float FogPlaneVertexDist, float FogRangeRecip, float FogPlaneViewDist, float FogHeightFade, sampler2D Texture_FogMask)\n"
+"float3 FogVertex(float3 surfacecolor, float3 FogColor, float3 EyeVectorModelSpace, float FogPlaneVertexDist, float FogRangeRecip, float FogPlaneViewDist, float FogHeightFade, sampler2D Texture_FogMask, sampler2D Texture_FogHeightTexture)\n"
 "{\n"
 "      float fogfrac;\n"
-"#ifdef USEFOGOUTSIDE\n"
-"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
+"#ifdef USEFOGHEIGHTTEXTURE\n"
+"      float4 fogheightpixel = tex2D(Texture_FogHeightTexture, float2(1,1) + float2(FogPlaneVertexDist, FogPlaneViewDist) * (-2.0 * FogHeightFade));\n"
+"      fogfrac = fogheightpixel.a;\n"
+"      return lerp(fogheightpixel.rgb * FogColor, surfacecolor, tex2D(Texture_FogMask, float2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)).r);\n"
 "#else\n"
+"# ifdef USEFOGOUTSIDE\n"
+"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
+"# else\n"
 "      fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);\n"
+"# endif\n"
+"      return lerp(FogColor, surfacecolor, tex2D(Texture_FogMask, float2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)).r);\n"
 "#endif\n"
-"      return float(tex2D(Texture_FogMask, half2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)));\n"
 "}\n"
 "#endif\n"
 "\n"
@@ -2189,8 +2333,8 @@ const char *builtincgshaderstring =
 "{\n"
 "      float3 adir = abs(dir);\n"
 "      float2 aparams = ShadowMap_Parameters.xy / max(max(adir.x, adir.y), adir.z);\n"
-"      float4 proj = texCUBEe(Texture_CubeProjection, dir);\n"
-"      return float3(mix(dir.xy, dir.zz, proj.xy) * aparams.x + proj.zw * ShadowMap_Parameters.z, aparams.y + ShadowMap_Parameters.w);\n"
+"      float4 proj = texCUBE(Texture_CubeProjection, dir);\n"
+"      return float3(lerp(dir.xy, dir.zz, proj.xy) * aparams.x + proj.zw * ShadowMap_Parameters.z, aparams.y + ShadowMap_Parameters.w);\n"
 "}\n"
 "#  else\n"
 "float3 GetShadowMapTC2D(float3 dir, float4 ShadowMap_Parameters)\n"
@@ -2205,13 +2349,13 @@ const char *builtincgshaderstring =
 "}\n"
 "#  endif\n"
 "# endif\n"
-"#endif // defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D) || defined(USESHADOWMAPORTHO)\n"
+"#endif // defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
 "\n"
 "#ifdef USESHADOWMAPCUBE\n"
 "float4 GetShadowMapTCCube(float3 dir, float4 ShadowMap_Parameters)\n"
 "{\n"
 "    float3 adir = abs(dir);\n"
-"    return float4(dir, ShadowMap_Parameters.z + ShadowMap_Parameters.w / max(max(adir.x, adir.y), adir.z));\n"
+"    return float4(dir, ShadowMap_Parameters.w + ShadowMap_Parameters.y / max(max(adir.x, adir.y), adir.z));\n"
 "}\n"
 "#endif\n"
 "\n"
@@ -2509,6 +2653,10 @@ const char *builtincgshaderstring =
 "uniform sampler2D Texture_ScreenDepth,\n"
 "uniform sampler2D Texture_ScreenNormalMap,\n"
 "\n"
+"#ifdef USECUBEFILTER\n"
+"uniform samplerCUBE Texture_Cube,\n"
+"#endif\n"
+"\n"
 "#ifdef USESHADOWMAPRECT\n"
 "# ifdef USESHADOWSAMPLER\n"
 "uniform samplerRECTShadow Texture_ShadowMapRect,\n"
@@ -2548,7 +2696,7 @@ const char *builtincgshaderstring =
 "{\n"
 "      // calculate viewspace pixel position\n"
 "      float2 ScreenTexCoord = Pixel * PixelToScreenTexCoord;\n"
-"      ScreenTexCoord.y = ScreenTexCoord.y * -1 + 1; // Cg is opposite?\n"
+"      //ScreenTexCoord.y = ScreenTexCoord.y * -1 + 1; // Cg is opposite?\n"
 "      float3 position;\n"
 "      position.z = ScreenToDepth.y / (texDepth2D(Texture_ScreenDepth, ScreenTexCoord) + ScreenToDepth.x);\n"
 "      position.xy = ModelViewPosition.xy * (position.z / ModelViewPosition.z);\n"
@@ -2669,7 +2817,7 @@ const char *builtincgshaderstring =
 "out float4 EyeVectorModelSpaceFogPlaneVertexDist : TEXCOORD4,\n"
 "#endif\n"
 "#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)\n"
-"out float3 LightVector : TEXCOORD5,\n"
+"out float3 LightVector : TEXCOORD1,\n"
 "#endif\n"
 "#ifdef MODE_LIGHTSOURCE\n"
 "out float3 CubeVector : TEXCOORD3,\n"
@@ -2680,7 +2828,7 @@ const char *builtincgshaderstring =
 "out float3 VectorR : TEXCOORD7, // direction of R texcoord (surface normal)\n"
 "#endif\n"
 "#ifdef USESHADOWMAPORTHO\n"
-"out float3 ShadowMapTC : TEXCOORD8,\n"
+"out float3 ShadowMapTC : TEXCOORD3, // CONFLICTS WITH USEREFLECTION!\n"
 "#endif\n"
 "out float4 gl_Position : POSITION\n"
 ")\n"
@@ -2775,7 +2923,7 @@ const char *builtincgshaderstring =
 "float4 EyeVectorModelSpaceFogPlaneVertexDist : TEXCOORD4,\n"
 "#endif\n"
 "#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)\n"
-"float3 LightVector : TEXCOORD5,\n"
+"float3 LightVector : TEXCOORD1,\n"
 "#endif\n"
 "#ifdef MODE_LIGHTSOURCE\n"
 "float3 CubeVector : TEXCOORD3,\n"
@@ -2789,7 +2937,7 @@ const char *builtincgshaderstring =
 "float3 VectorR : TEXCOORD7, // direction of R texcoord (surface normal)\n"
 "#endif\n"
 "#ifdef USESHADOWMAPORTHO\n"
-"float3 ShadowMapTC : TEXCOORD8\n"
+"float3 ShadowMapTC : TEXCOORD3, // CONFLICTS WITH USEREFLECTION!\n"
 "#endif\n"
 "\n"
 "uniform sampler2D Texture_Normal,\n"
@@ -2815,6 +2963,7 @@ const char *builtincgshaderstring =
 "uniform sampler2D Texture_Shirt,\n"
 "#endif\n"
 "#ifdef USEFOG\n"
+"uniform sampler2D Texture_FogHeightTexture,\n"
 "uniform sampler2D Texture_FogMask,\n"
 "#endif\n"
 "#ifdef USELIGHTMAP\n"
@@ -2973,9 +3122,9 @@ const char *builtincgshaderstring =
 "#endif\n"
 "\n"
 "#ifdef USEREFLECTCUBE\n"
-"      vec3 TangentReflectVector = reflect(-EyeVector, surfacenormal);\n"
-"      vec3 ModelReflectVector = TangentReflectVector.x * VectorS + TangentReflectVector.y * VectorT + TangentReflectVector.z * VectorR;\n"
-"      vec3 ReflectCubeTexCoord = float3(mul(ModelToReflectCube, float4(ModelReflectVector, 0)));\n"
+"      float3 TangentReflectVector = reflect(-EyeVector, surfacenormal);\n"
+"      float3 ModelReflectVector = TangentReflectVector.x * VectorS + TangentReflectVector.y * VectorT + TangentReflectVector.z * VectorR;\n"
+"      float3 ReflectCubeTexCoord = float3(mul(ModelToReflectCube, float4(ModelReflectVector, 0)));\n"
 "      diffusetex += half3(tex2D(Texture_ReflectMask, TexCoord)) * half3(texCUBE(Texture_ReflectCube, ReflectCubeTexCoord));\n"
 "#endif\n"
 "\n"
@@ -3124,11 +3273,7 @@ const char *builtincgshaderstring =
 "#endif\n"
 "\n"
 "#ifdef USEFOG\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      color.rgb *= half(FogVertex(EyeVectorModelSpaceFogPlaneVertexDist.xyz, EyeVectorModelSpaceFogPlaneVertexDist.w, FogRangeRecip, FogPlaneViewDist, FogHeightFade, Texture_FogMask));\n"
-"#else\n"
-"      color.rgb = lerp(FogColor, float3(color.rgb), FogVertex(EyeVectorModelSpaceFogPlaneVertexDist.xyz, EyeVectorModelSpaceFogPlaneVertexDist.w, FogRangeRecip, FogPlaneViewDist, FogHeightFade, Texture_FogMask));\n"
-"#endif\n"
+"      color.rgb = FogVertex(color.rgb, FogColor, EyeVectorModelSpaceFogPlaneVertexDist.xyz, EyeVectorModelSpaceFogPlaneVertexDist.w, FogRangeRecip, FogPlaneViewDist, FogHeightFade, Texture_FogMask, Texture_FogHeightTexture);\n"
 "#endif\n"
 "\n"
 "      // reflection must come last because it already contains exactly the correct fog (the reflection render preserves camera distance from the plane, it only flips the side) and ContrastBoost/SceneBrightness\n"
@@ -3196,29 +3341,30 @@ typedef enum shaderpermutation_e
        SHADERPERMUTATION_SATURATION = 1<<4, ///< saturation (postprocessing only)
        SHADERPERMUTATION_FOGINSIDE = 1<<5, ///< tint the color by fog color or black if using additive blend mode
        SHADERPERMUTATION_FOGOUTSIDE = 1<<6, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_GAMMARAMPS = 1<<7, ///< gamma (postprocessing only)
-       SHADERPERMUTATION_CUBEFILTER = 1<<8, ///< (lightsource) use cubemap light filter
-       SHADERPERMUTATION_GLOW = 1<<9, ///< (lightmap) blend in an additive glow texture
-       SHADERPERMUTATION_BLOOM = 1<<10, ///< bloom (postprocessing only)
-       SHADERPERMUTATION_SPECULAR = 1<<11, ///< (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_POSTPROCESSING = 1<<12, ///< user defined postprocessing (postprocessing only)
-       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<13, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
-       SHADERPERMUTATION_REFLECTION = 1<<14, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<15, ///< adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<16, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_SHADOWMAPRECT = 1<<17, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<18, ///< (lightsource) use shadowmap cubemap texture as light filter
-       SHADERPERMUTATION_SHADOWMAP2D = 1<<19, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPPCF = 1<<20, ///< (lightsource) use percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<21, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWSAMPLER = 1<<22, ///< (lightsource) use hardware shadowmap test
-       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<23, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<24, //< (lightsource) use orthographic shadowmap projection
-       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<25, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
-       SHADERPERMUTATION_ALPHAKILL = 1<<26, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
-       SHADERPERMUTATION_REFLECTCUBE = 1<<27, ///< fake reflections using global cubemap (not HDRI light probe)
-       SHADERPERMUTATION_LIMIT = 1<<28, ///< size of permutations array
-       SHADERPERMUTATION_COUNT = 28 ///< size of shaderpermutationinfo array
+       SHADERPERMUTATION_FOGHEIGHTTEXTURE = 1<<7, ///< fog color and density determined by texture mapped on vertical axis
+       SHADERPERMUTATION_GAMMARAMPS = 1<<8, ///< gamma (postprocessing only)
+       SHADERPERMUTATION_CUBEFILTER = 1<<9, ///< (lightsource) use cubemap light filter
+       SHADERPERMUTATION_GLOW = 1<<10, ///< (lightmap) blend in an additive glow texture
+       SHADERPERMUTATION_BLOOM = 1<<11, ///< bloom (postprocessing only)
+       SHADERPERMUTATION_SPECULAR = 1<<12, ///< (lightsource or deluxemapping) render specular effects
+       SHADERPERMUTATION_POSTPROCESSING = 1<<13, ///< user defined postprocessing (postprocessing only)
+       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<14, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
+       SHADERPERMUTATION_REFLECTION = 1<<15, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+       SHADERPERMUTATION_OFFSETMAPPING = 1<<16, ///< adjust texcoords to roughly simulate a displacement mapped surface
+       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<17, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+       SHADERPERMUTATION_SHADOWMAPRECT = 1<<18, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<19, ///< (lightsource) use shadowmap cubemap texture as light filter
+       SHADERPERMUTATION_SHADOWMAP2D = 1<<20, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPPCF = 1<<21, ///< (lightsource) use percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<22, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWSAMPLER = 1<<23, ///< (lightsource) use hardware shadowmap test
+       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<24, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<25, //< (lightsource) use orthographic shadowmap projection
+       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<26, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
+       SHADERPERMUTATION_ALPHAKILL = 1<<27, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
+       SHADERPERMUTATION_REFLECTCUBE = 1<<28, ///< fake reflections using global cubemap (not HDRI light probe)
+       SHADERPERMUTATION_LIMIT = 1<<29, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 29 ///< size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -3232,6 +3378,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USESATURATION\n", " saturation"},
        {"#define USEFOGINSIDE\n", " foginside"},
        {"#define USEFOGOUTSIDE\n", " fogoutside"},
+       {"#define USEFOGHEIGHTTEXTURE\n", " fogheighttexture"},
        {"#define USEGAMMARAMPS\n", " gammaramps"},
        {"#define USECUBEFILTER\n", " cubefilter"},
        {"#define USEGLOW\n", " glow"},
@@ -3344,6 +3491,7 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_SecondaryGlow;
        int loc_Texture_Pants;
        int loc_Texture_Shirt;
+       int loc_Texture_FogHeightTexture;
        int loc_Texture_FogMask;
        int loc_Texture_Lightmap;
        int loc_Texture_Deluxemap;
@@ -3570,6 +3718,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                p->loc_Texture_SecondaryGlow      = qglGetUniformLocationARB(p->program, "Texture_SecondaryGlow");
                p->loc_Texture_Pants              = qglGetUniformLocationARB(p->program, "Texture_Pants");
                p->loc_Texture_Shirt              = qglGetUniformLocationARB(p->program, "Texture_Shirt");
+               p->loc_Texture_FogHeightTexture   = qglGetUniformLocationARB(p->program, "Texture_FogHeightTexture");
                p->loc_Texture_FogMask            = qglGetUniformLocationARB(p->program, "Texture_FogMask");
                p->loc_Texture_Lightmap           = qglGetUniformLocationARB(p->program, "Texture_Lightmap");
                p->loc_Texture_Deluxemap          = qglGetUniformLocationARB(p->program, "Texture_Deluxemap");
@@ -3652,6 +3801,7 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                if (p->loc_Texture_SecondaryGlow   >= 0) qglUniform1iARB(p->loc_Texture_SecondaryGlow  , GL20TU_SECONDARY_GLOW);
                if (p->loc_Texture_Pants           >= 0) qglUniform1iARB(p->loc_Texture_Pants          , GL20TU_PANTS);
                if (p->loc_Texture_Shirt           >= 0) qglUniform1iARB(p->loc_Texture_Shirt          , GL20TU_SHIRT);
+               if (p->loc_Texture_FogHeightTexture>= 0) qglUniform1iARB(p->loc_Texture_FogHeightTexture, GL20TU_FOGHEIGHTTEXTURE);
                if (p->loc_Texture_FogMask         >= 0) qglUniform1iARB(p->loc_Texture_FogMask        , GL20TU_FOGMASK);
                if (p->loc_Texture_Lightmap        >= 0) qglUniform1iARB(p->loc_Texture_Lightmap       , GL20TU_LIGHTMAP);
                if (p->loc_Texture_Deluxemap       >= 0) qglUniform1iARB(p->loc_Texture_Deluxemap      , GL20TU_DELUXEMAP);
@@ -3768,6 +3918,7 @@ typedef struct r_cg_permutation_s
        CGparameter fp_Texture_SecondaryGlow;
        CGparameter fp_Texture_Pants;
        CGparameter fp_Texture_Shirt;
+       CGparameter fp_Texture_FogHeightTexture;
        CGparameter fp_Texture_FogMask;
        CGparameter fp_Texture_Lightmap;
        CGparameter fp_Texture_Deluxemap;
@@ -4066,6 +4217,7 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
                p->fp_Texture_SecondaryGlow      = cgGetNamedParameter(p->fprogram, "Texture_SecondaryGlow");
                p->fp_Texture_Pants              = cgGetNamedParameter(p->fprogram, "Texture_Pants");
                p->fp_Texture_Shirt              = cgGetNamedParameter(p->fprogram, "Texture_Shirt");
+               p->fp_Texture_FogHeightTexture   = cgGetNamedParameter(p->fprogram, "Texture_FogHeightTexture");
                p->fp_Texture_FogMask            = cgGetNamedParameter(p->fprogram, "Texture_FogMask");
                p->fp_Texture_Lightmap           = cgGetNamedParameter(p->fprogram, "Texture_Lightmap");
                p->fp_Texture_Deluxemap          = cgGetNamedParameter(p->fprogram, "Texture_Deluxemap");
@@ -4425,7 +4577,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
@@ -4439,14 +4592,13 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                // distorted background
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERSHADER)
                        mode = SHADERMODE_WATER;
-               else
+               else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFRACTION)
                        mode = SHADERMODE_REFRACTION;
-               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);
+               else
+               {
+                       mode = SHADERMODE_GENERIC;
+                       permutation |= SHADERPERMUTATION_DIFFUSE;
+               }
                GL_AlphaTest(false);
                GL_BlendFunc(GL_ONE, GL_ZERO);
        }
@@ -4454,9 +4606,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4466,15 +4625,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);
        }
@@ -4482,9 +4632,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4503,7 +4660,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
                }
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaprect || r_shadow_usingshadowmap2d || r_shadow_usingshadowmapcube)
@@ -4526,24 +4683,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);
        }
@@ -4551,9 +4690,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4563,7 +4709,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
@@ -4585,24 +4731,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);
        }
@@ -4610,9 +4738,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4628,7 +4763,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
                }
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
@@ -4652,21 +4787,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);
        }
@@ -4674,9 +4794,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4685,7 +4812,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
@@ -4709,21 +4836,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);
        }
@@ -4731,9 +4843,16 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4741,7 +4860,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
                if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
@@ -4779,11 +4898,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)
                {
@@ -4796,41 +4910,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);
@@ -4838,6 +4927,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 | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | 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 | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | 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)
@@ -4922,7 +5027,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (r_glsl_permutation->loc_FogPlaneViewDist >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogPlaneViewDist, rsurface.fogplaneviewdist);
                if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, rsurface.fograngerecip);
                if (r_glsl_permutation->loc_FogHeightFade >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogHeightFade, rsurface.fogheightfade);
-               if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
+               if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale);
                if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2fARB(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
                if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2fARB(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
 
@@ -4941,9 +5046,10 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (r_glsl_permutation->loc_Texture_Shirt           >= 0) R_Mesh_TexBind(GL20TU_SHIRT             , rsurface.texture->shirttexture                      );
                if (r_glsl_permutation->loc_Texture_ReflectMask     >= 0) R_Mesh_TexBind(GL20TU_REFLECTMASK       , rsurface.texture->reflectmasktexture                );
                if (r_glsl_permutation->loc_Texture_ReflectCube     >= 0) R_Mesh_TexBind(GL20TU_REFLECTCUBE       , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
+               if (r_glsl_permutation->loc_Texture_FogHeightTexture>= 0) R_Mesh_TexBind(GL20TU_FOGHEIGHTTEXTURE  , r_texture_fogheighttexture                          );
                if (r_glsl_permutation->loc_Texture_FogMask         >= 0) R_Mesh_TexBind(GL20TU_FOGMASK           , r_texture_fogattenuation                            );
                if (r_glsl_permutation->loc_Texture_Lightmap        >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP          , r_texture_white                                     );
-               if (r_glsl_permutation->loc_Texture_Deluxemap       >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP          , r_texture_blanknormalmap                            );
+               if (r_glsl_permutation->loc_Texture_Deluxemap       >= 0) R_Mesh_TexBind(GL20TU_DELUXEMAP         , r_texture_blanknormalmap                            );
                if (r_glsl_permutation->loc_Texture_Attenuation     >= 0) R_Mesh_TexBind(GL20TU_ATTENUATION       , r_shadow_attenuationgradienttexture                 );
                if (r_glsl_permutation->loc_Texture_Refraction      >= 0) R_Mesh_TexBind(GL20TU_REFRACTION        , r_texture_white                                     );
                if (r_glsl_permutation->loc_Texture_Reflection      >= 0) R_Mesh_TexBind(GL20TU_REFLECTION        , r_texture_white                                     );
@@ -4967,6 +5073,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 | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | 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 | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | 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)
@@ -5086,6 +5208,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (r_cg_permutation->fp_Texture_Shirt          ) CG_BindTexture(r_cg_permutation->fp_Texture_Shirt          , rsurface.texture->shirttexture                      );CHECKCGERROR
                if (r_cg_permutation->fp_Texture_ReflectMask    ) CG_BindTexture(r_cg_permutation->fp_Texture_ReflectMask    , rsurface.texture->reflectmasktexture                );CHECKCGERROR
                if (r_cg_permutation->fp_Texture_ReflectCube    ) CG_BindTexture(r_cg_permutation->fp_Texture_ReflectCube    , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);CHECKCGERROR
+               if (r_cg_permutation->fp_Texture_FogHeightTexture) CG_BindTexture(r_cg_permutation->fp_Texture_FogHeightTexture, r_texture_fogheighttexture                         );CHECKCGERROR
                if (r_cg_permutation->fp_Texture_FogMask        ) CG_BindTexture(r_cg_permutation->fp_Texture_FogMask        , r_texture_fogattenuation                            );CHECKCGERROR
                if (r_cg_permutation->fp_Texture_Lightmap       ) CG_BindTexture(r_cg_permutation->fp_Texture_Lightmap       , r_texture_white                                     );CHECKCGERROR
                if (r_cg_permutation->fp_Texture_Deluxemap      ) CG_BindTexture(r_cg_permutation->fp_Texture_Deluxemap      , r_texture_blanknormalmap                            );CHECKCGERROR
@@ -5384,6 +5507,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                skinframe->avgcolor[3] = avgcolor[4] / (255.0 * cnt); \
        }
 
+extern cvar_t gl_picmip;
 skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
 {
        int j;
@@ -5397,6 +5521,9 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        qboolean ddshasalpha = false;
        float ddsavgcolor[4];
        char basename[MAX_QPATH];
+       int miplevel = R_PicmipForFlags(textureflags);
+       int savemiplevel = miplevel;
+       int mymiplevel;
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -5411,13 +5538,15 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        Image_StripImageExtension(name, basename, sizeof(basename));
 
        // check for DDS texture file first
-       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), textureflags, &ddshasalpha, ddsavgcolor)))
+       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), textureflags, &ddshasalpha, ddsavgcolor, miplevel)))
        {
-               basepixels = loadimagepixelsbgra(name, complain, true, r_texture_convertsRGB_skin.integer);
+               basepixels = loadimagepixelsbgra(name, complain, true, r_texture_convertsRGB_skin.integer, &miplevel);
                if (basepixels == NULL)
                        return NULL;
        }
 
+       // FIXME handle miplevel
+
        if (developer_loading.integer)
                Con_Printf("loading skin \"%s\"\n", name);
 
@@ -5442,14 +5571,14 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                skinframe->hasalpha = ddshasalpha;
                VectorCopy(ddsavgcolor, skinframe->avgcolor);
                if (r_loadfog && skinframe->hasalpha)
-                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL);
+                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL, miplevel);
                //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
        }
        else
        {
                basepixels_width = image_width;
                basepixels_height = image_height;
-               skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), miplevel, NULL);
                if (textureflags & TEXF_ALPHA)
                {
                        for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
@@ -5471,7 +5600,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                                        pixels[j+2] = 255;
                                        pixels[j+3] = basepixels[j+3];
                                }
-                               skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                               skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), miplevel, NULL);
                                Mem_Free(pixels);
                        }
                }
@@ -5485,30 +5614,32 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
 
        if (r_loaddds)
        {
+               mymiplevel = savemiplevel;
                if (r_loadnormalmap)
-                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL);
-               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), textureflags, NULL, NULL);
+                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL, mymiplevel);
+               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
                if (r_loadgloss)
-                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), textureflags, NULL, NULL);
+                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
        }
 
        // _norm is the name used by tenebrae and has been adopted as standard
        if (r_loadnormalmap && skinframe->nmap == NULL)
        {
-               if ((pixels = loadimagepixelsbgra(va("%s_norm", skinframe->basename), false, false, false)) != NULL)
+               mymiplevel = savemiplevel;
+               if ((pixels = loadimagepixelsbgra(va("%s_norm", skinframe->basename), false, false, false, &mymiplevel)) != NULL)
                {
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                        pixels = NULL;
                }
-               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixelsbgra(va("%s_bump", skinframe->basename), false, false, false)) != NULL)
+               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixelsbgra(va("%s_bump", skinframe->basename), false, false, false, &mymiplevel)) != NULL)
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
                        Image_HeightmapToNormalmap_BGRA(bumppixels, pixels, image_width, image_height, false, r_shadow_bumpscale_bumpmap.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                        Mem_Free(bumppixels);
                }
@@ -5516,7 +5647,7 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, basepixels_width * basepixels_height * 4);
                        Image_HeightmapToNormalmap_BGRA(basepixels, pixels, basepixels_width, basepixels_height, false, r_shadow_bumpscale_basetexture.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                }
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->nmap)
@@ -5525,44 +5656,49 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
 
        // _luma is supported only for tenebrae compatibility
        // _glow is the preferred name
-       if (skinframe->glow == NULL && ((pixels = loadimagepixelsbgra(va("%s_glow",  skinframe->basename), false, false, r_texture_convertsRGB_skin.integer)) || (pixels = loadimagepixelsbgra(va("%s_luma", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer))))
+       mymiplevel = savemiplevel;
+       if (skinframe->glow == NULL && ((pixels = loadimagepixelsbgra(va("%s_glow",  skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel)) || (pixels = loadimagepixelsbgra(va("%s_luma", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel))))
        {
-               skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_glow.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_glow.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->glow)
                        R_SaveTextureDDSFile(skinframe->glow, va("dds/%s_glow.dds", skinframe->basename), true);
                Mem_Free(pixels);pixels = NULL;
        }
 
-       if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer)))
+       mymiplevel = savemiplevel;
+       if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel)))
        {
-               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_gloss.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_gloss.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->gloss)
                        R_SaveTextureDDSFile(skinframe->gloss, va("dds/%s_gloss.dds", skinframe->basename), true);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->pants == NULL && (pixels = loadimagepixelsbgra(va("%s_pants", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer)))
+       mymiplevel = savemiplevel;
+       if (skinframe->pants == NULL && (pixels = loadimagepixelsbgra(va("%s_pants", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel)))
        {
-               skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->pants)
                        R_SaveTextureDDSFile(skinframe->pants, va("dds/%s_pants.dds", skinframe->basename), true);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->shirt == NULL && (pixels = loadimagepixelsbgra(va("%s_shirt", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer)))
+       mymiplevel = savemiplevel;
+       if (skinframe->shirt == NULL && (pixels = loadimagepixelsbgra(va("%s_shirt", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel)))
        {
-               skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->shirt)
                        R_SaveTextureDDSFile(skinframe->shirt, va("dds/%s_shirt.dds", skinframe->basename), true);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->reflect == NULL && (pixels = loadimagepixelsbgra(va("%s_reflect", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer)))
+       mymiplevel = savemiplevel;
+       if (skinframe->reflect == NULL && (pixels = loadimagepixelsbgra(va("%s_reflect", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer, &mymiplevel)))
        {
-               skinframe->reflect = R_LoadTexture2D (r_main_texturepool, va("%s_reflect", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_reflectmask.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->reflect = R_LoadTexture2D (r_main_texturepool, va("%s_reflect", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_reflectmask.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->reflect)
                        R_SaveTextureDDSFile(skinframe->reflect, va("dds/%s_reflect.dds", skinframe->basename), true);
                Mem_Free(pixels);
@@ -5614,10 +5750,10 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
                temp2 = temp1 + width * height * 4;
                Image_HeightmapToNormalmap_BGRA(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, NULL);
+               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, -1, NULL);
                Mem_Free(temp1);
        }
-       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_BGRA, skinframe->textureflags, NULL);
+       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_BGRA, skinframe->textureflags, -1, NULL);
        if (textureflags & TEXF_ALPHA)
        {
                for (i = 3;i < width * height * 4;i += 4)
@@ -5634,7 +5770,7 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                        memcpy(fogpixels, skindata, width * height * 4);
                        for (i = 0;i < width * height * 4;i += 4)
                                fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
-                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_BGRA, skinframe->textureflags, NULL);
+                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_BGRA, skinframe->textureflags, -1, NULL);
                        Mem_Free(fogpixels);
                }
        }
@@ -5737,27 +5873,27 @@ static void R_SkinFrame_GenerateTexturesFromQPixels(skinframe_t *skinframe, qboo
                // use either a custom palette or the quake palette
                Image_Copy8bitBGRA(skindata, temp1, width * height, palette_bgra_complete);
                Image_HeightmapToNormalmap_BGRA(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, NULL);
+               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, -1, NULL);
                Mem_Free(temp1);
        }
 
        if (skinframe->qgenerateglow)
        {
                skinframe->qgenerateglow = false;
-               skinframe->glow = R_LoadTexture2D(r_main_texturepool, va("%s_glow", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_onlyfullbrights); // glow
+               skinframe->glow = R_LoadTexture2D(r_main_texturepool, va("%s_glow", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_onlyfullbrights); // glow
        }
 
        if (colormapped)
        {
                skinframe->qgeneratebase = false;
-               skinframe->base  = R_LoadTexture2D(r_main_texturepool, va("%s_nospecial", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, skinframe->glow ? palette_bgra_nocolormapnofullbrights : palette_bgra_nocolormap);
-               skinframe->pants = R_LoadTexture2D(r_main_texturepool, va("%s_pants", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_pantsaswhite);
-               skinframe->shirt = R_LoadTexture2D(r_main_texturepool, va("%s_shirt", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_shirtaswhite);
+               skinframe->base  = R_LoadTexture2D(r_main_texturepool, va("%s_nospecial", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nocolormapnofullbrights : palette_bgra_nocolormap);
+               skinframe->pants = R_LoadTexture2D(r_main_texturepool, va("%s_pants", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_pantsaswhite);
+               skinframe->shirt = R_LoadTexture2D(r_main_texturepool, va("%s_shirt", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_shirtaswhite);
        }
        else
        {
                skinframe->qgeneratemerged = false;
-               skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete);
+               skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete);
        }
 
        if (!skinframe->qgeneratemerged && !skinframe->qgeneratebase)
@@ -5799,7 +5935,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
        if (developer_loading.integer)
                Con_Printf("loading embedded 8bit image \"%s\"\n", name);
 
-       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette);
+       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette);
        if (textureflags & TEXF_ALPHA)
        {
                for (i = 0;i < width * height;i++)
@@ -5811,7 +5947,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
                        }
                }
                if (r_loadfog && skinframe->hasalpha)
-                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, alphapalette);
+                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, alphapalette);
        }
 
        R_SKINFRAME_LOAD_AVERAGE_COLORS(width * height, ((unsigned char *)palette)[skindata[pix]*4 + comp]);
@@ -5904,7 +6040,7 @@ rtexture_t *R_LoadCubemap(const char *basename)
                        // generate an image name based on the base and and suffix
                        dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
                        // load it
-                       if ((image_buffer = loadimagepixelsbgra(name, false, false, r_texture_convertsRGB_cubemap.integer)))
+                       if ((image_buffer = loadimagepixelsbgra(name, false, false, r_texture_convertsRGB_cubemap.integer, NULL)))
                        {
                                // an image loaded, make sure width and height are equal
                                if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
@@ -5933,7 +6069,7 @@ rtexture_t *R_LoadCubemap(const char *basename)
                if (developer_loading.integer)
                        Con_Printf("loading cubemap \"%s\"\n", basename);
 
-               cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0) | TEXF_FORCELINEAR, NULL);
+               cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0) | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                Mem_Free(cubemappixels);
        }
        else
@@ -6040,6 +6176,7 @@ void gl_main_start(void)
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        r_texture_numcubemaps = 0;
 
@@ -6099,14 +6236,17 @@ void gl_main_start(void)
                R_BuildNormalizationCube();
        }
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        //r_texture_fogintensity = NULL;
        memset(&r_bloomstate, 0, sizeof(r_bloomstate));
        memset(&r_waterstate, 0, sizeof(r_waterstate));
+       r_glsl_permutation = NULL;
        memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
        Mem_ExpandableArray_NewArray(&r_glsl_permutationarray, r_main_mempool, sizeof(r_glsl_permutation_t), 256);
        glslshaderstring = NULL;
 #ifdef SUPPORTCG
+       r_cg_permutation = NULL;
        memset(r_cg_permutationhash, 0, sizeof(r_cg_permutationhash));
        Mem_ExpandableArray_NewArray(&r_cg_permutationarray, r_main_mempool, sizeof(r_cg_permutation_t), 256);
        cgshaderstring = NULL;
@@ -6149,11 +6289,20 @@ void gl_main_shutdown(void)
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        r_texture_numcubemaps = 0;
        //r_texture_fogintensity = NULL;
        memset(&r_bloomstate, 0, sizeof(r_bloomstate));
        memset(&r_waterstate, 0, sizeof(r_waterstate));
+       r_glsl_permutation = NULL;
+       memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
+       glslshaderstring = NULL;
+#ifdef SUPPORTCG
+       r_cg_permutation = NULL;
+       memset(r_cg_permutationhash, 0, sizeof(r_cg_permutationhash));
+       cgshaderstring = NULL;
+#endif
        R_GLSL_Restart_f();
 }
 
@@ -6161,7 +6310,6 @@ extern void CL_ParseEntityLump(char *entitystring);
 void gl_main_newmap(void)
 {
        // FIXME: move this code to client
-       int l;
        char *entities, entname[MAX_QPATH];
        if (r_qwskincache)
                Mem_Free(r_qwskincache);
@@ -6169,17 +6317,12 @@ void gl_main_newmap(void)
        r_qwskincache_size = 0;
        if (cl.worldmodel)
        {
-               strlcpy(entname, cl.worldmodel->name, sizeof(entname));
-               l = (int)strlen(entname) - 4;
-               if (l >= 0 && !strcmp(entname + l, ".bsp"))
+               dpsnprintf(entname, sizeof(entname), "%s.ent", cl.worldnamenoextension);
+               if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
                {
-                       memcpy(entname + l, ".ent", 5);
-                       if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
-                       {
-                               CL_ParseEntityLump(entities);
-                               Mem_Free(entities);
-                               return;
-                       }
+                       CL_ParseEntityLump(entities);
+                       Mem_Free(entities);
+                       return;
                }
                if (cl.worldmodel->brush.entities)
                        CL_ParseEntityLump(cl.worldmodel->brush.entities);
@@ -6236,12 +6379,14 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_showdisabledepthtest);
        Cvar_RegisterVariable(&r_drawportals);
        Cvar_RegisterVariable(&r_drawentities);
+       Cvar_RegisterVariable(&r_drawworld);
        Cvar_RegisterVariable(&r_cullentities_trace);
        Cvar_RegisterVariable(&r_cullentities_trace_samples);
        Cvar_RegisterVariable(&r_cullentities_trace_tempentitysamples);
        Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
        Cvar_RegisterVariable(&r_cullentities_trace_delay);
        Cvar_RegisterVariable(&r_drawviewmodel);
+       Cvar_RegisterVariable(&r_drawexteriormodel);
        Cvar_RegisterVariable(&r_speeds);
        Cvar_RegisterVariable(&r_fullbrights);
        Cvar_RegisterVariable(&r_wateralpha);
@@ -6306,7 +6451,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)
@@ -6574,6 +6718,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?
        }
 }
 
@@ -6584,7 +6763,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)
@@ -6602,7 +6781,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);
+                               }
                        }
                }
        }
@@ -6622,7 +6804,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;
 }
@@ -6630,7 +6815,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)
@@ -6644,6 +6829,9 @@ void R_AnimCache_CacheVisibleEntities(void)
                break;
        }
 
+       if (r_shownormals.integer)
+               wanttangents = wantnormals = true;
+
        // TODO: thread this
        // NOTE: R_PrepareRTLights() also caches entities
 
@@ -6660,7 +6848,7 @@ static void R_View_UpdateEntityLighting (void)
        entity_render_t *ent;
        vec3_t tempdiffusenormal, avg;
        vec_t f, fa, fd, fdd;
-       qboolean skipunseen = r_shadows.integer != 1 || R_Shadow_ShadowMappingEnabled();
+       qboolean skipunseen = r_shadows.integer != 1; //|| R_Shadow_ShadowMappingEnabled();
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
        {
@@ -6798,6 +6986,8 @@ static void R_View_UpdateEntityVisible (void)
                :                                                          RENDER_EXTERIORMODEL;
        if (!r_drawviewmodel.integer)
                renderimask |= RENDER_VIEWMODEL;
+       if (!r_drawexteriormodel.integer)
+               renderimask |= RENDER_EXTERIORMODEL;
        if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs)
        {
                // worldmodel can check visibility
@@ -6810,7 +7000,8 @@ static void R_View_UpdateEntityVisible (void)
                        if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
                                r_refdef.viewcache.entityvisible[i] = true;
                }
-               if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight)
+               if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane)
+                       // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling
                {
                        for (i = 0;i < r_refdef.scene.numentities;i++)
                        {
@@ -7203,13 +7394,29 @@ void R_ResetViewRendering3D(void)
        GL_CullFace(r_refdef.view.cullface_back);
 }
 
+/*
+================
+R_RenderView_UpdateViewVectors
+================
+*/
+static void R_RenderView_UpdateViewVectors(void)
+{
+       // break apart the view matrix into vectors for various purposes
+       // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
+       // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
+       Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
+       VectorNegate(r_refdef.view.left, r_refdef.view.right);
+       // make an inverted copy of the view matrix for tracking sprites
+       Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
+}
+
 void R_RenderScene(void);
 void R_RenderWaterPlanes(void);
 
 static void R_Water_StartFrame(void)
 {
        int i;
-       int waterwidth, waterheight, texturewidth, textureheight;
+       int waterwidth, waterheight, texturewidth, textureheight, camerawidth, cameraheight;
        r_waterstate_waterplane_t *p;
 
        if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
@@ -7233,20 +7440,24 @@ static void R_Water_StartFrame(void)
        // calculate desired texture sizes
        // can't use water if the card does not support the texture size
        if (!r_water.integer || r_showsurfaces.integer)
-               texturewidth = textureheight = waterwidth = waterheight = 0;
+               texturewidth = textureheight = waterwidth = waterheight = camerawidth = cameraheight = 0;
        else if (vid.support.arb_texture_non_power_of_two)
        {
                texturewidth = waterwidth;
                textureheight = waterheight;
+               camerawidth = waterwidth;
+               cameraheight = waterheight;
        }
        else
        {
                for (texturewidth   = 1;texturewidth   < waterwidth ;texturewidth   *= 2);
                for (textureheight  = 1;textureheight  < waterheight;textureheight  *= 2);
+               for (camerawidth    = 1;camerawidth   <= waterwidth; camerawidth    *= 2); camerawidth  /= 2;
+               for (cameraheight   = 1;cameraheight  <= waterheight;cameraheight   *= 2); cameraheight /= 2;
        }
 
        // allocate textures as needed
-       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight || r_waterstate.camerawidth != camerawidth || r_waterstate.cameraheight != cameraheight)
        {
                r_waterstate.maxwaterplanes = MAX_WATERPLANES;
                for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
@@ -7257,10 +7468,15 @@ static void R_Water_StartFrame(void)
                        if (p->texture_reflection)
                                R_FreeTexture(p->texture_reflection);
                        p->texture_reflection = NULL;
+                       if (p->texture_camera)
+                               R_FreeTexture(p->texture_camera);
+                       p->texture_camera = NULL;
                }
                memset(&r_waterstate, 0, sizeof(r_waterstate));
                r_waterstate.texturewidth = texturewidth;
                r_waterstate.textureheight = textureheight;
+               r_waterstate.camerawidth = camerawidth;
+               r_waterstate.cameraheight = cameraheight;
        }
 
        if (r_waterstate.texturewidth)
@@ -7290,8 +7506,13 @@ void R_Water_AddWaterPlane(msurface_t *surface)
        vec3_t normal;
        vec3_t center;
        mplane_t plane;
+       int cam_ent;
        r_waterstate_waterplane_t *p;
        texture_t *t = R_GetCurrentTexture(surface->texture);
+       cam_ent = t->camera_entity;
+       if(!(t->currentmaterialflags & MATERIALFLAG_CAMERA))
+               cam_ent = 0;
+
        // just use the first triangle with a valid normal for any decisions
        VectorClear(normal);
        for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
@@ -7321,8 +7542,9 @@ void R_Water_AddWaterPlane(msurface_t *surface)
 
        // find a matching plane if there is one
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
-               if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
-                       break;
+               if(p->camera_entity == t->camera_entity)
+                       if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
+                               break;
        if (planeindex >= r_waterstate.maxwaterplanes)
                return; // nothing we can do, out of planes
 
@@ -7335,16 +7557,20 @@ void R_Water_AddWaterPlane(msurface_t *surface)
                // clear materialflags and pvs
                p->materialflags = 0;
                p->pvsvalid = false;
+               p->camera_entity = t->camera_entity;
        }
        // merge this surface's materialflags into the waterplane
        p->materialflags |= t->currentmaterialflags;
-       // merge this surface's PVS into the waterplane
-       VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
-       if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
-        && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+       if(!(p->materialflags & MATERIALFLAG_CAMERA))
        {
-               r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
-               p->pvsvalid = true;
+               // merge this surface's PVS into the waterplane
+               VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
+               if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
+                && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+               {
+                       r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
+                       p->pvsvalid = true;
+               }
        }
 }
 
@@ -7354,6 +7580,7 @@ static void R_Water_ProcessPlanes(void)
        r_refdef_view_t myview;
        int planeindex;
        r_waterstate_waterplane_t *p;
+       vec3_t visorigin;
 
        originalview = r_refdef.view;
 
@@ -7363,15 +7590,22 @@ static void R_Water_ProcessPlanes(void)
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                {
                        if (!p->texture_refraction)
-                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                        if (!p->texture_refraction)
                                goto error;
                }
+               else if (p->materialflags & MATERIALFLAG_CAMERA)
+               {
+                       if (!p->texture_camera)
+                               p->texture_camera = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_camera", planeindex), r_waterstate.camerawidth, r_waterstate.cameraheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR, -1, NULL);
+                       if (!p->texture_camera)
+                               goto error;
+               }
 
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
                {
                        if (!p->texture_reflection)
-                               p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                               p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                        if (!p->texture_reflection)
                                goto error;
                }
@@ -7421,9 +7655,20 @@ static void R_Water_ProcessPlanes(void)
                {
                        r_waterstate.renderingrefraction = true;
                        r_refdef.view = myview;
+
                        r_refdef.view.clipplane = p->plane;
                        VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
                        r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+                       if((p->materialflags & MATERIALFLAG_CAMERA) && p->camera_entity)
+                       {
+                               // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+                               r_waterstate.renderingrefraction = false; // we don't want to hide the player model from these ones
+                               CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+                               R_RenderView_UpdateViewVectors();
+                               r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+                       }
+
                        PlaneClassify(&r_refdef.view.clipplane);
 
                        R_ResetViewRendering3D();
@@ -7434,6 +7679,47 @@ static void R_Water_ProcessPlanes(void)
                        R_Mesh_CopyToTexture(p->texture_refraction, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
                        r_waterstate.renderingrefraction = false;
                }
+               else if (p->materialflags & MATERIALFLAG_CAMERA)
+               {
+                       r_refdef.view = myview;
+
+                       r_refdef.view.clipplane = p->plane;
+                       VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
+                       r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+                       r_refdef.view.width = r_waterstate.camerawidth;
+                       r_refdef.view.height = r_waterstate.cameraheight;
+                       r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
+                       r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
+
+                       if(p->camera_entity)
+                       {
+                               // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+                               CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+                       }
+
+                       // reverse the cullface settings for this render
+                       r_refdef.view.cullface_front = GL_FRONT;
+                       r_refdef.view.cullface_back = GL_BACK;
+                       // also reverse the view matrix
+                       Matrix4x4_ConcatScale3(&r_refdef.view.matrix, 1, 1, -1); // this serves to invert texcoords in the result, as the copied texture is mapped the wrong way round
+                       R_RenderView_UpdateViewVectors();
+                       if(p->camera_entity)
+                               r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+                       
+                       // camera needs no clipplane
+                       r_refdef.view.useclipplane = false;
+
+                       PlaneClassify(&r_refdef.view.clipplane);
+
+                       R_ResetViewRendering3D();
+                       R_ClearScreen(r_refdef.fogenabled);
+                       R_View_Update();
+                       R_RenderScene();
+
+                       R_Mesh_CopyToTexture(p->texture_camera, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
+                       r_waterstate.renderingrefraction = false;
+               }
 
        }
        r_waterstate.renderingscene = false;
@@ -7510,7 +7796,7 @@ void R_Bloom_StartFrame(void)
                r_bloomstate.screentexturewidth = screentexturewidth;
                r_bloomstate.screentextureheight = screentextureheight;
                if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight)
-                       r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCENEAREST | TEXF_CLAMP, NULL);
+                       r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL);
        }
        if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight)
        {
@@ -7520,7 +7806,7 @@ void R_Bloom_StartFrame(void)
                r_bloomstate.bloomtexturewidth = bloomtexturewidth;
                r_bloomstate.bloomtextureheight = bloomtextureheight;
                if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight)
-                       r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                       r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
        }
 
        // when doing a reduced render (HDR) we want to use a smaller area
@@ -7570,10 +7856,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
@@ -7596,8 +7882,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
@@ -7608,10 +7892,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
@@ -7627,7 +7911,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++)
        {
@@ -7659,7 +7942,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);
                }
@@ -7674,18 +7958,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);
 
@@ -7771,8 +8055,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))
                        {
@@ -7803,13 +8085,13 @@ static void R_BlendView(void)
                                cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
                                cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
                                // apply the blur
-                               if (cl.motionbluralpha > 0)
+                               if (cl.motionbluralpha > 0 && !r_refdef.envmap)
                                {
                                        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;
                                }
                        }
@@ -7825,12 +8107,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
                }
@@ -7854,12 +8135,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)
                {
@@ -7896,7 +8174,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:
@@ -7905,12 +8183,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;
        }
@@ -8024,10 +8301,14 @@ void R_UpdateVariables(void)
                r_refdef.fogrange = bound(r_refdef.fog_start, r_refdef.fogrange, r_refdef.fog_end);
                r_refdef.fograngerecip = 1.0f / r_refdef.fogrange;
                r_refdef.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * r_refdef.fograngerecip;
+               if (strcmp(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename))
+                       R_BuildFogHeightTexture();
                // fog color was already set
                // update the fog texture
                if (r_refdef.fogmasktable_start != r_refdef.fog_start || r_refdef.fogmasktable_alpha != r_refdef.fog_alpha || r_refdef.fogmasktable_density != r_refdef.fog_density || r_refdef.fogmasktable_range != r_refdef.fogrange)
                        R_BuildFogTexture();
+               r_refdef.fog_height_texcoordscale = 1.0f / max(0.125f, r_refdef.fog_fadedepth);
+               r_refdef.fog_height_tablescale = r_refdef.fog_height_tablesize * r_refdef.fog_height_texcoordscale;
        }
        else
                r_refdef.fogenabled = false;
@@ -8062,7 +8343,7 @@ void R_UpdateVariables(void)
                                }
                                else
                                {
-                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
+                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, -1, NULL);
                                }
                        }
                }
@@ -8144,18 +8425,12 @@ void R_RenderView(void)
                return;
        }
 
-       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0 || !r_renderview.integer/* || !r_refdef.scene.worldmodel*/)
+       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0 || !r_renderview.integer || cl_videoplaying/* || !r_refdef.scene.worldmodel*/)
                return; //Host_Error ("R_RenderView: NULL worldmodel");
 
        r_refdef.view.colorscale = r_hdr_scenebrightness.value;
 
-       // break apart the view matrix into vectors for various purposes
-       // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
-       // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
-       Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
-       VectorNegate(r_refdef.view.left, r_refdef.view.right);
-       // make an inverted copy of the view matrix for tracking sprites
-       Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
+       R_RenderView_UpdateViewVectors();
 
        R_Shadow_UpdateWorldLightSelection();
 
@@ -8246,6 +8521,9 @@ void R_RenderScene(void)
 {
        qboolean shadowmapping = false;
 
+       if (r_timereport_active)
+               R_TimeReport("beginscene");
+
        r_refdef.stats.renders++;
 
        R_UpdateFogColor();
@@ -8260,6 +8538,9 @@ void R_RenderScene(void)
 
        Matrix4x4_CreateTranslate(&r_waterscrollmatrix, sin(r_refdef.scene.time) * 0.025 * r_waterscroll.value, sin(r_refdef.scene.time * 0.8f) * 0.025 * r_waterscroll.value, 0);
 
+       if (r_timereport_active)
+               R_TimeReport("skystartframe");
+
        if (cl.csqc_vidvars.drawworld)
        {
                // don't let sound skip if going slow
@@ -8316,7 +8597,7 @@ void R_RenderScene(void)
                        R_TimeReport("modeldepth");
        }
 
-       if (r_shadows.integer > 0 && shadowmapping && r_refdef.lightmapintensity > 0)
+       if (r_shadows.integer >= 2 && shadowmapping && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadowMaps();
                R_ResetViewRendering3D();
@@ -8344,7 +8625,7 @@ void R_RenderScene(void)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
-       if (r_shadows.integer > 0 && !shadowmapping && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
+       if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadows();
                R_ResetViewRendering3D();
@@ -8364,7 +8645,7 @@ void R_RenderScene(void)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
-       if (r_shadows.integer > 0 && !shadowmapping && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
+       if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadows();
                R_ResetViewRendering3D();
@@ -8498,11 +8779,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)
@@ -8648,9 +8928,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];
@@ -8662,7 +8940,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);
@@ -8670,7 +8948,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)
@@ -8843,12 +9122,22 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1,
        layer->color[3] = a;
 }
 
+static qboolean R_TestQ3WaveFunc(q3wavefunc_t func, const float *parms)
+{
+       if(parms[0] == 0 && parms[1] == 0)
+               return false;
+       if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
+               if(rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)] == 0)
+                       return false;
+       return true;
+}
+
 static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
 {
        double index, f;
        index = parms[2] + r_refdef.scene.time * parms[3];
        index -= floor(index);
-       switch (func)
+       switch (func & ((1 << Q3WAVEFUNC_USER_SHIFT) - 1))
        {
        default:
        case Q3WAVEFUNC_NONE:
@@ -8873,7 +9162,10 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
                        f = -(1 - f);
                break;
        }
-       return (float)(parms[0] + parms[1] * f);
+       f = parms[0] + parms[1] * f;
+       if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
+               f *= rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)];
+       return (float) f;
 }
 
 void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags)
@@ -8974,6 +9266,11 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        t->update_lastrenderframe = r_textureframe;
        t->update_lastrenderentity = (void *)ent;
 
+       if(ent && ent->entitynumber >= MAX_EDICTS && ent->entitynumber < 2 * MAX_EDICTS)
+               t->camera_entity = ent->entitynumber;
+       else
+               t->camera_entity = 0;
+
        // switch to an alternate material if this is a q1bsp animated material
        {
                texture_t *texture = t;
@@ -9030,7 +9327,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
                t->currentalpha *= t->r_water_wateralpha;
        if(!r_waterstate.enabled || r_refdef.view.isoverlay)
-               t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION);
+               t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA);
        if (!(rsurface.ent_flags & RENDER_LIGHT))
                t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
        else if (rsurface.modeltexcoordlightmap2f == NULL && !(t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
@@ -9053,11 +9350,11 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
        if (t->currentmaterialflags & MATERIALFLAG_BLENDED)
        {
-               if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER))
+               if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA))
                        t->currentmaterialflags &= ~MATERIALFLAG_BLENDED;
        }
        else
-               t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER);
+               t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA);
        if ((t->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)) == MATERIALFLAG_BLENDED && r_transparentdepthmasking.integer && !(t->basematerialflags & MATERIALFLAG_BLENDED))
                t->currentmaterialflags |= MATERIALFLAG_TRANSDEPTH;
 
@@ -9104,7 +9401,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;
@@ -9251,24 +9548,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)
@@ -9278,6 +9602,7 @@ void RSurf_ActiveWorldEntity(void)
        //      return;
        rsurface.entity = r_refdef.scene.worldentity;
        rsurface.skeleton = NULL;
+       memset(rsurface.userwavefunc_param, 0, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = 0;
@@ -9309,48 +9634,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)
@@ -9360,6 +9717,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        //      return;
        rsurface.entity = (entity_render_t *)ent;
        rsurface.skeleton = ent->skeleton;
+       memcpy(rsurface.userwavefunc_param, ent->userwavefunc_param, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = ent->skinnum;
        rsurface.ent_qwskin = (ent->entitynumber <= cl.maxclients && ent->entitynumber >= 1 && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[ent->entitynumber - 1].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl")) ? (ent->entitynumber - 1) : -1;
        rsurface.ent_shadertime = ent->shadertime;
@@ -9402,6 +9760,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)
                {
@@ -9410,6 +9772,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)
                {
@@ -9418,6 +9784,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
                {
@@ -9426,77 +9796,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);
@@ -9541,50 +9949,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);
        }
 }
 
@@ -9620,70 +10086,82 @@ 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++)
        {
-               if (rsurface.texture->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
-                       generatenormals = true;
-               for (i = 0;i < Q3MAXDEFORMS;i++)
-               {
-                       if (rsurface.texture->deforms[i].deform == Q3DEFORM_AUTOSPRITE)
-                       {
-                               generatetangents = true;
-                               generatenormals = true;
-                       }
-                       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)
+               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;
+
+       // check if any dynamic vertex processing must occur
+       dynamicvertex = false;
+
+       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++)
        {
                switch (deform->deform)
@@ -9701,59 +10179,439 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                case Q3DEFORM_NONE:
                        break;
                case Q3DEFORM_AUTOSPRITE:
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
-                       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];
-                               // 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.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);
-                                       }
-                               }
-                               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;
+                       dynamicvertex = true;
+                       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:
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
+                       dynamicvertex = true;
+                       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:
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_WAVE:
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, ignore this transform
+                       dynamicvertex = true;
+                       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_BULGE:
+                       dynamicvertex = true;
+                       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:
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, ignore this transform
+                       dynamicvertex = true;
+                       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:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_LIGHTMAP | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP;
+               break;
+       case Q3TCGEN_VECTOR:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       case Q3TCGEN_ENVIRONMENT:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       }
+       if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+       }
+
+       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
+                       {
+                               for (j = 0;j < surfacenumvertices;j++)
+                               {
+                                       Vector4Set(rsurface.array_batchlightmapcolor4f + 4*numvertices, 0, 0, 0, 1);
+                                       numvertices++;
+                               }
+                       }
+               }
+       }
+
+       // if vertices are deformed (sprite flares and things in maps, possibly
+       // 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)
+               {
+               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:
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
+                       VectorNormalize(newforward);
+                       VectorNormalize(newright);
+                       VectorNormalize(newup);
+                       // a single autosprite surface can contain multiple sprites...
+                       for (j = 0;j < rsurface.batchnumvertices - 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++)
+                               {
+                                       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(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);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
                        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;
@@ -9766,25 +10624,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])
@@ -9805,13 +10656,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);
@@ -9826,20 +10670,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
@@ -9853,54 +10683,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
@@ -9908,125 +10733,126 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        waveparms[1] = deform->waveparms[1];
                        waveparms[2] = deform->waveparms[2];
                        waveparms[3] = deform->waveparms[3];
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, waveparms))
+                               break; // if wavefunc is a nop, don't make a dynamic vertex array
                        // 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
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, don't make a dynamic 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
@@ -10038,149 +10864,130 @@ 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 4096
-               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)
-                       {
-                               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++)
+                       for (j = 0, vertexmesh = rsurface.array_batchvertexmesh;j < rsurface.batchnumvertices;j++, vertexmesh++)
                        {
-                               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);
-               }
-       }
-       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);
                }
+               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 (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 int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
 {
-       // pick the closest matching water plane and bind textures
-       int planeindex, vertexindex;
+       // pick the closest matching water plane
+       int planeindex, vertexindex, bestplaneindex = -1;
        float d, bestd;
        vec3_t vert;
        const float *v;
-       r_waterstate_waterplane_t *p, *bestp;
+       r_waterstate_waterplane_t *p;
        bestd = 0;
-       bestp = NULL;
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
        {
+               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)
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, 1, &surface);
+               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));
                }
-               if (bestd > d || !bestp)
+               if (bestd > d || bestplaneindex < 0)
                {
                        bestd = d;
-                       bestp = p;
+                       bestplaneindex = planeindex;
                }
        }
+       return bestplaneindex;
+}
+
+static void RSurf_BindReflectionForBatch(int planeindex)
+{
+       // pick the closest matching water plane and bind textures
+       r_waterstate_waterplane_t *bestp = planeindex >= 0 ? r_waterstate.waterplanes + planeindex : NULL;
        switch(vid.renderpath)
        {
        case RENDERPATH_CGGL:
 #ifdef SUPPORTCG
-               if (r_cg_permutation->fp_Texture_Refraction) CG_BindTexture(r_cg_permutation->fp_Texture_Refraction, bestp ? bestp->texture_refraction : r_texture_black);CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Reflection) CG_BindTexture(r_cg_permutation->fp_Texture_Reflection, bestp ? bestp->texture_reflection : r_texture_black);CHECKCGERROR
+               if (r_cg_permutation->fp_Texture_Refraction) {CG_BindTexture(r_cg_permutation->fp_Texture_Refraction, bestp ? bestp->texture_refraction : r_texture_black);CHECKCGERROR}
+               else if (r_cg_permutation->fp_Texture_First) {CG_BindTexture(r_cg_permutation->fp_Texture_First, bestp ? bestp->texture_camera : r_texture_black);CHECKCGERROR}
+               if (r_cg_permutation->fp_Texture_Reflection) {CG_BindTexture(r_cg_permutation->fp_Texture_Reflection, bestp ? bestp->texture_reflection : r_texture_black);CHECKCGERROR}
 #endif
                break;
        case RENDERPATH_GL20:
                if (r_glsl_permutation->loc_Texture_Refraction >= 0) R_Mesh_TexBind(GL20TU_REFRACTION, bestp ? bestp->texture_refraction : r_texture_black);
+               else if (r_glsl_permutation->loc_Texture_First >= 0) R_Mesh_TexBind(GL20TU_FIRST, bestp ? bestp->texture_camera : r_texture_black);
                if (r_glsl_permutation->loc_Texture_Reflection >= 0) R_Mesh_TexBind(GL20TU_REFLECTION, bestp ? bestp->texture_reflection : r_texture_black);
                break;
        case RENDERPATH_GL13:
@@ -10189,377 +10996,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)
-       {
-#define MAXBATCHTRIANGLES 4096
-               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;
@@ -10580,33 +11180,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
@@ -10614,20 +11205,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)
@@ -10658,8 +11265,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)
                {
@@ -10669,15 +11274,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);
        }
@@ -10689,52 +11298,74 @@ extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
 extern rtexture_t *r_shadow_prepasslightingspeculartexture;
 static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
 {
-       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION)))
+       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);
-       }
-       else if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION)) && !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);
-               GL_DepthMask(false);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
-               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-                       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
-               else
-                       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();
+               return;
        }
-       else
+
+       // bind lightmap texture
+
+       // water/refraction/reflection/camera surfaces have to be handled specially
+       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA | MATERIALFLAG_REFLECTION)) && !r_waterstate.renderingscene)
        {
-               // 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);
-               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);
+               int start, end, startplaneindex;
+               for (start = 0;start < texturenumsurfaces;start = end)
+               {
+                       startplaneindex = RSurf_FindWaterPlaneForSurface(texturesurfacelist[start]);
+                       for (end = start + 1;end < texturenumsurfaces && startplaneindex == RSurf_FindWaterPlaneForSurface(texturesurfacelist[end]);end++)
+                               ;
+                       // now that we have a batch using the same planeindex, render it
+                       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA)) && !r_waterstate.renderingscene)
+                       {
+                               // render water or distortion background
+                               GL_DepthMask(true);
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start);
+                               RSurf_BindReflectionForBatch(startplaneindex);
+                               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+                                       RSurf_BindLightmapForBatch();
+                               RSurf_DrawBatch();
+                               // blend surface on top
+                               GL_DepthMask(false);
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start);
+                               RSurf_DrawBatch();
+                       }
+                       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION) && !r_waterstate.renderingscene)
+                       {
+                               // render surface with reflection texture as input
+                               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, end-start, texturesurfacelist + start);
+                               RSurf_BindReflectionForBatch(startplaneindex);
+                               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+                                       RSurf_BindLightmapForBatch();
+                               RSurf_DrawBatch();
+                       }
+               }
+               return;
        }
+
+       // render surface batch 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, texturenumsurfaces, texturesurfacelist);
+       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++)
        {
@@ -10769,7 +11400,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)
                {
@@ -10778,27 +11409,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
@@ -10807,34 +11438,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);
@@ -10851,11 +11467,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++)
        {
@@ -10871,7 +11487,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)
                {
@@ -10883,20 +11499,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
                        {
@@ -10904,11 +11520,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:
@@ -10916,8 +11532,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
@@ -10926,32 +11542,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);
@@ -10965,12 +11566,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);
 
@@ -11028,55 +11631,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);
+
+                       rsurface.passcolor4f = rsurface.batchlightmapcolor4f;
+                       rsurface.passcolor4f_vertexbuffer = rsurface.batchlightmapcolor4f_vertexbuffer;
+                       rsurface.passcolor4f_bufferoffset = rsurface.batchlightmapcolor4f_bufferoffset;
+               }
 
-       if(!rsurface.lightmapcolor4f)
-               RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(texturenumsurfaces, texturesurfacelist);
+               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)
@@ -11099,9 +11774,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)
@@ -11126,7 +11801,8 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        int texturenumsurfaces, endsurface;
        texture_t *texture;
        const msurface_t *surface;
-       const msurface_t *texturesurfacelist[256];
+#define MAXBATCH_TRANSPARENTSURFACES 256
+       const msurface_t *texturesurfacelist[MAXBATCH_TRANSPARENTSURFACES];
 
        // if the model is static it doesn't matter what value we give for
        // wantnormals and wanttangents, so this logic uses only rules applicable
@@ -11159,7 +11835,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;
@@ -11167,7 +11845,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;
                        }
@@ -11183,13 +11861,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);
@@ -11201,15 +11879,17 @@ 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 + 1024, numsurfaces);
+               endsurface = min(i + MAXBATCH_TRANSPARENTSURFACES, numsurfaces);
                texturenumsurfaces = 0;
                texturesurfacelist[texturenumsurfaces++] = surface;
                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;
                }
@@ -11246,20 +11926,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)
@@ -11269,34 +11953,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;
@@ -11328,6 +11985,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))
                {
@@ -11337,7 +11996,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);
@@ -11348,15 +12007,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)
@@ -11366,34 +12017,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);
-               GL_Color(0, 0, 0, 1);
-               GL_DepthTest(writedepth);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
-       {
-               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;
@@ -11425,6 +12049,8 @@ void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurfa
                // 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))
                {
@@ -11434,7 +12060,7 @@ void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurfa
                        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_ProcessModelTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, ent, prepass);
@@ -11476,10 +12102,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
        GL_CullFace(GL_NONE);
        R_EntityMatrix(&identitymatrix);
 
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
 
        i = surfacelist[0];
        GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f) * r_refdef.view.colorscale,
@@ -11502,7 +12125,9 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
                for (j = 0;j < 3;j++, i++)
                        vertex3f[i] = mins[j] + size[j] * locboxvertex3f[i];
 
-       R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, locboxelements, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(6*4, vertex3f, NULL, NULL);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, NULL, 0, locboxelements, NULL, 0);
 }
 
 void R_DrawLocs(void)
@@ -11601,13 +12226,92 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
 extern cvar_t cl_decals_bias;
 extern cvar_t cl_decals_models;
 extern cvar_t cl_decals_newsystem_intensitymultiplier;
+// baseparms, parms, temps
+static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, float g, float b, float a, float s1, float t1, float s2, float t2, int decalsequence, qboolean dynamic, float (*planes)[4], matrix4x4_t *projection, int triangleindex, int surfaceindex)
+{
+       int cornerindex;
+       int index;
+       float v[9][3];
+       const float *vertex3f;
+       int numpoints;
+       float points[2][9][3];
+       float temp[3];
+       float tc[9][2];
+       float f;
+       float c[9][4];
+       const int *e;
+
+       e = rsurface.modelelement3i + 3*triangleindex;
+
+       vertex3f = rsurface.modelvertex3f;
+
+       for (cornerindex = 0;cornerindex < 3;cornerindex++)
+       {
+               index = 3*e[cornerindex];
+               VectorCopy(vertex3f + index, v[cornerindex]);
+       }
+       // cull backfaces
+       //TriangleNormal(v[0], v[1], v[2], normal);
+       //if (DotProduct(normal, localnormal) < 0.0f)
+       //      continue;
+       // clip by each of the box planes formed from the projection matrix
+       // if anything survives, we emit the decal
+       numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
+       if (numpoints < 3)
+               return;
+       // some part of the triangle survived, so we have to accept it...
+       if (dynamic)
+       {
+               // dynamic always uses the original triangle
+               numpoints = 3;
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorCopy(vertex3f + index, v[cornerindex]);
+               }
+       }
+       for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
+       {
+               // convert vertex positions to texcoords
+               Matrix4x4_Transform(projection, v[cornerindex], temp);
+               tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
+               tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
+               // calculate distance fade from the projection origin
+               f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
+               f = bound(0.0f, f, 1.0f);
+               c[cornerindex][0] = r * f;
+               c[cornerindex][1] = g * f;
+               c[cornerindex][2] = b * f;
+               c[cornerindex][3] = 1.0f;
+               //VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
+       }
+       if (dynamic)
+               R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex, surfaceindex, decalsequence);
+       else
+               for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
+                       R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+}
 static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, int decalsequence)
 {
        matrix4x4_t projection;
        decalsystem_t *decalsystem;
        qboolean dynamic;
        dp_model_t *model;
-       const float *vertex3f;
        const msurface_t *surface;
        const msurface_t *surfaces;
        const int *surfacelist;
@@ -11617,24 +12321,18 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
        int surfacelistindex;
        int surfaceindex;
        int triangleindex;
-       int cornerindex;
-       int index;
-       int numpoints;
-       const int *e;
        float localorigin[3];
        float localnormal[3];
        float localmins[3];
        float localmaxs[3];
        float localsize;
-       float v[9][3];
-       float tc[9][2];
-       float c[9][4];
        //float normal[3];
        float planes[6][4];
-       float f;
-       float points[2][9][3];
        float angles[3];
-       float temp[3];
+       bih_t *bih;
+       int bih_triangles_count;
+       int bih_triangles[256];
+       int bih_surfaces[256];
 
        decalsystem = &ent->decalsystem;
        model = ent->model;
@@ -11713,86 +12411,57 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
 #endif
 
        dynamic = model->surfmesh.isanimated;
-       vertex3f = rsurface.modelvertex3f;
        numsurfacelist = model->nummodelsurfaces;
        surfacelist = model->sortedmodelsurfaces;
        surfaces = model->data_surfaces;
-       for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+
+       bih = NULL;
+       bih_triangles_count = -1;
+       if(!dynamic)
        {
-               surfaceindex = surfacelist[surfacelistindex];
-               surface = surfaces + surfaceindex;
-               // check cull box first because it rejects more than any other check
-               if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
-                       continue;
-               // skip transparent surfaces
-               texture = surface->texture;
-               if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
-                       continue;
-               if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
-                       continue;
-               numtriangles = surface->num_triangles;
-               for (triangleindex = 0, e = model->surfmesh.data_element3i + 3*surface->num_firsttriangle;triangleindex < numtriangles;triangleindex++, e += 3)
+               if(model->render_bih.numleafs)
+                       bih = &model->render_bih;
+               else if(model->collision_bih.numleafs)
+                       bih = &model->collision_bih;
+       }
+       if(bih)
+               bih_triangles_count = BIH_GetTriangleListForBox(bih, sizeof(bih_triangles) / sizeof(*bih_triangles), bih_triangles, bih_surfaces, localmins, localmaxs);
+       if(bih_triangles_count == 0)
+               return;
+       if(bih_triangles_count > (int) (sizeof(bih_triangles) / sizeof(*bih_triangles))) // hit too many, likely bad anyway
+               return;
+       if(bih_triangles_count > 0)
+       {
+               for (triangleindex = 0; triangleindex < bih_triangles_count; ++triangleindex)
                {
-                       for (cornerindex = 0;cornerindex < 3;cornerindex++)
-                       {
-                               index = 3*e[cornerindex];
-                               VectorCopy(vertex3f + index, v[cornerindex]);
-                       }
-                       // cull backfaces
-                       //TriangleNormal(v[0], v[1], v[2], normal);
-                       //if (DotProduct(normal, localnormal) < 0.0f)
-                       //      continue;
-                       // clip by each of the box planes formed from the projection matrix
-                       // if anything survives, we emit the decal
-                       numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
-                               continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-                       if (numpoints < 3)
+                       surfaceindex = bih_surfaces[triangleindex];
+                       surface = surfaces + surfaceindex;
+                       texture = surface->texture;
+                       if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
+                       if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-                       if (numpoints < 3)
+                       R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, bih_triangles[triangleindex], surfaceindex);
+               }
+       }
+       else
+       {
+               for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+               {
+                       surfaceindex = surfacelist[surfacelistindex];
+                       surface = surfaces + surfaceindex;
+                       // check cull box first because it rejects more than any other check
+                       if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
+                       // skip transparent surfaces
+                       texture = surface->texture;
+                       if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
-                       if (numpoints < 3)
+                       if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
                                continue;
-                       // some part of the triangle survived, so we have to accept it...
-                       if (dynamic)
-                       {
-                               // dynamic always uses the original triangle
-                               numpoints = 3;
-                               for (cornerindex = 0;cornerindex < 3;cornerindex++)
-                               {
-                                       index = 3*e[cornerindex];
-                                       VectorCopy(vertex3f + index, v[cornerindex]);
-                               }
-                       }
-                       for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
-                       {
-                               // convert vertex positions to texcoords
-                               Matrix4x4_Transform(&projection, v[cornerindex], temp);
-                               tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
-                               tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
-                               // calculate distance fade from the projection origin
-                               f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
-                               f = bound(0.0f, f, 1.0f);
-                               c[cornerindex][0] = r * f;
-                               c[cornerindex][1] = g * f;
-                               c[cornerindex][2] = b * f;
-                               c[cornerindex][3] = 1.0f;
-                               //VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
-                       }
-                       if (dynamic)
-                               R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex+surface->num_firsttriangle, surfaceindex, decalsequence);
-                       else
-                               for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
-                                       R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+                       numtriangles = surface->num_triangles;
+                       for (triangleindex = 0; triangleindex < numtriangles; triangleindex++)
+                               R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, triangleindex + surface->num_firsttriangle, surfaceindex);
                }
        }
 }
@@ -12021,12 +12690,12 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                t2f[5] = decal->texcoord2f[2][1];
 
                // update vertex positions for animated models
-               if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnum_triangles)
+               if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnumtriangles)
                {
                        e = rsurface.modelelement3i + 3*decal->triangleindex;
-                       VectorCopy(rsurface.vertex3f + 3*e[0], v3f);
-                       VectorCopy(rsurface.vertex3f + 3*e[1], v3f + 3);
-                       VectorCopy(rsurface.vertex3f + 3*e[2], v3f + 6);
+                       VectorCopy(rsurface.modelvertexposition[e[0]].vertex3f, v3f);
+                       VectorCopy(rsurface.modelvertexposition[e[1]].vertex3f, v3f + 3);
+                       VectorCopy(rsurface.modelvertexposition[e[2]].vertex3f, v3f + 6);
                }
                else
                {
@@ -12059,9 +12728,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                // (this assumes they all use one particle font texture!)
                RSurf_ActiveCustomEntity(&rsurface.matrix, &rsurface.inversematrix, rsurface.ent_flags, rsurface.ent_shadertime, 1, 1, 1, 1, numdecals*3, decalsystem->vertex3f, decalsystem->texcoord2f, NULL, NULL, NULL, decalsystem->color4f, numtris, decalsystem->element3i, decalsystem->element3s, false, false);
                R_Mesh_ResetTextureState();
-               R_Mesh_VertexPointer(decalsystem->vertex3f, 0, 0);
-               R_Mesh_TexCoordPointer(0, 2, decalsystem->texcoord2f, 0, 0);
-               R_Mesh_ColorPointer(decalsystem->color4f, 0, 0);
+               R_Mesh_PrepareVertices_Generic_Arrays(numtris * 3, decalsystem->vertex3f, decalsystem->color4f, decalsystem->texcoord2f);
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
                GL_PolygonOffset(rsurface.basepolygonfactor + r_polygonoffset_decals_factor.value, rsurface.basepolygonoffset + r_polygonoffset_decals_offset.value);
@@ -12069,7 +12736,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                GL_CullFace(GL_NONE);
                GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
                R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1);
-               R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, decalsystem->element3s, 0, 0);
+               R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, NULL, 0, decalsystem->element3s, NULL, 0);
        }
 }
 
@@ -12120,7 +12787,6 @@ void R_DrawDebugModel(void)
 
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
 
-       R_Mesh_ColorPointer(NULL, 0, 0);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
        GL_DepthRange(0, 1);
@@ -12149,9 +12815,9 @@ void R_DrawDebugModel(void)
                                brush = model->brush.data_brushes + bihleaf->itemindex;
                                if (brush->colbrushf && brush->colbrushf->numtriangles)
                                {
-                                       R_Mesh_VertexPointer(brush->colbrushf->points->v, 0, 0);
                                        GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                                       R_Mesh_Draw(0, brush->colbrushf->numpoints, 0, brush->colbrushf->numtriangles, brush->colbrushf->elements, NULL, 0, 0);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(brush->colbrushf->numpoints, brush->colbrushf->points->v, NULL, NULL);
+                                       R_Mesh_Draw(0, brush->colbrushf->numpoints, 0, brush->colbrushf->numtriangles, brush->colbrushf->elements, NULL, 0, NULL, NULL, 0);
                                }
                                break;
                        case BIH_COLLISIONTRIANGLE:
@@ -12159,18 +12825,18 @@ void R_DrawDebugModel(void)
                                VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+0], vertex3f[0]);
                                VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+1], vertex3f[1]);
                                VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+2], vertex3f[2]);
-                               R_Mesh_VertexPointer(vertex3f[0], 0, 0);
                                GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
+                               R_Mesh_PrepareVertices_Generic_Arrays(3, vertex3f[0], NULL, NULL);
+                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                                break;
                        case BIH_RENDERTRIANGLE:
                                triangleindex = bihleaf->itemindex;
                                VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+0], vertex3f[0]);
                                VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+1], vertex3f[1]);
                                VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+2], vertex3f[2]);
-                               R_Mesh_VertexPointer(vertex3f[0], 0, 0);
                                GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
+                               R_Mesh_PrepareVertices_Generic_Arrays(3, vertex3f[0], NULL, NULL);
+                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                                break;
                        }
                }
@@ -12197,7 +12863,7 @@ void R_DrawDebugModel(void)
                        rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
-                               RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
+                               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_NOGAPS, 1, &surface);
                                if (r_showtris.value > 0)
                                {
                                        if (!rsurface.texture->currentlayers->depthmask)
@@ -12206,12 +12872,9 @@ void R_DrawDebugModel(void)
                                                GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, r_showtris.value);
                                        else
                                                GL_Color(0, r_refdef.view.colorscale, 0, r_showtris.value);
-                                       R_Mesh_VertexPointer(rsurface.vertex3f, 0, 0);
-                                       R_Mesh_ColorPointer(NULL, 0, 0);
-                                       R_Mesh_TexCoordPointer(0, 0, NULL, 0, 0);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, NULL, NULL);
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-                                       //R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, model->surfmesh.data_element3i, NULL, 0, 0);
-                                       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);
+                                       RSurf_DrawBatch();
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                                        CHECKGLERROR
                                }
@@ -12220,25 +12883,25 @@ void R_DrawDebugModel(void)
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 0, 0, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, -r_shownormals.value, rsurface.svector3f + l * 3, v);
+                                               VectorMA(v, -r_shownormals.value, rsurface.batchsvector3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 1, 1, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
                                        CHECKGLERROR
                                }
-                               if (r_shownormals.value > 0)
+                               if (r_shownormals.value > 0 && rsurface.batchsvector3f)
                                {
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 0, 0, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.svector3f + l * 3, v);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchsvector3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 1, 1, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
@@ -12247,10 +12910,10 @@ void R_DrawDebugModel(void)
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(0, r_refdef.view.colorscale, 0, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.tvector3f + l * 3, v);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchtvector3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 1, 1, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
@@ -12259,10 +12922,10 @@ void R_DrawDebugModel(void)
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(0, 0, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.normal3f + l * 3, v);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchnormal3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 1, 1, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
@@ -12326,6 +12989,8 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                return;
        }
 
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
@@ -12456,6 +13121,8 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                return;
        }
 
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
@@ -12512,6 +13179,8 @@ void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, i
        texture.basematerialflags = materialflags | MATERIALFLAG_CUSTOMSURFACE | MATERIALFLAG_WALL;
        texture.currentskinframe = skinframe;
        texture.currenttexmatrix = *texmatrix; // requires MATERIALFLAG_CUSTOMSURFACE
+       texture.offsetmapping = OFFSETMAPPING_OFF;
+       texture.offsetscale = 1;
        texture.specularscalemod = 1;
        texture.specularpowermod = 1;
 
@@ -12523,6 +13192,8 @@ void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, i
 
        // now render it
        rsurface.texture = R_GetCurrentTexture(surface.texture);
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        R_DrawModelTextureSurfaceList(1, &surfacelist, writedepth, prepass);
 }
@@ -12542,6 +13213,8 @@ void R_DrawCustomSurface_Texture(texture_t *texture, const matrix4x4_t *texmatri
 
        // now render it
        rsurface.texture = R_GetCurrentTexture(surface.texture);
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        R_DrawModelTextureSurfaceList(1, &surfacelist, writedepth, prepass);
 }