]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
significant surface renderer optimizations
[xonotic/darkplaces.git] / gl_rmain.c
index 505bcaf582bfd820e39ca583bdc42334f5e44955..6eae7af2061a0c76c7a3b4b8021b10856168e3b2 100644 (file)
@@ -48,9 +48,6 @@ qboolean r_rtdlight;
 qboolean r_rtdlightshadows;
 
 
-// forces all rendering to draw triangle outlines
-int r_showtrispass;
-
 // view origin
 vec3_t r_vieworigin;
 vec3_t r_viewforward;
@@ -64,15 +61,18 @@ int r_view_width;
 int r_view_height;
 int r_view_depth;
 matrix4x4_t r_view_matrix;
-
+float r_polygonfactor;
+float r_polygonoffset;
+float r_shadowpolygonfactor;
+float r_shadowpolygonoffset;
 //
 // screen size info
 //
 refdef_t r_refdef;
 
 cvar_t r_nearclip = {0, "r_nearclip", "1", "distance from camera of nearclip plane" };
+cvar_t r_showsurfaces = {0, "r_showsurfaces", "0", "shows surfaces as different colors"};
 cvar_t r_showtris = {0, "r_showtris", "0", "shows triangle outlines, value controls brightness (can be above 1)"};
-cvar_t r_showtris_polygonoffset = {0, "r_showtris_polygonoffset", "-10", "nudges triangle outlines in hardware depth units, used to make outlines appear infront of walls"};
 cvar_t r_shownormals = {0, "r_shownormals", "0", "shows per-vertex surface normals and tangent vectors for bumpmapped lighting"};
 cvar_t r_showlighting = {0, "r_showlighting", "0", "shows areas lit by lights, useful for finding out why some areas of a map render slowly (bright orange = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
 cvar_t r_showshadowvolumes = {0, "r_showshadowvolumes", "0", "shows areas shadowed by lights, useful for finding out why some areas of a map render slowly (bright blue = lots of passes = slow), a value of 2 disables depth testing which can be interesting but not very useful"};
@@ -87,6 +87,7 @@ cvar_t r_fullbright = {0, "r_fullbright","0", "make everything bright cheat (not
 cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1", "opacity of water polygons"};
 cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1", "enables dynamic lights (rocket glow and such)"};
 cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1", "enables glowing pixels in quake textures (changes need r_restart to take effect)"};
+cvar_t r_q1bsp_skymasking = {0, "r_qb1sp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"};
 
 cvar_t gl_fogenable = {0, "gl_fogenable", "0", "nehahra fog enable (for Nehahra compatibility only)"};
 cvar_t gl_fogdensity = {0, "gl_fogdensity", "0.25", "nehahra fog density (recommend values below 0.1) (for Nehahra compatibility only)"};
@@ -99,11 +100,10 @@ cvar_t gl_fogend = {0, "gl_fogend","0", "nehahra fog end distance (for Nehahra c
 cvar_t r_textureunits = {0, "r_textureunits", "32", "number of hardware texture units reported by driver (note: setting this to 1 turns off gl_combine)"};
 
 cvar_t r_glsl = {0, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
-cvar_t r_glsl_offsetmapping = {0, "r_glsl_offsetmapping", "0", "enables offset mapping effect (also known as parallax mapping or sometimes as virtual displacement mapping, not as good as relief mapping or silohuette mapping but much faster), can cause strange artifacts on many textures, requires bumpmaps for depth information (normalmaps can have depth information as alpha channel, but most do not)"};
-cvar_t r_glsl_offsetmapping_scale = {0, "r_glsl_offsetmapping_scale", "-0.04", "how deep the offset mapping effect is, and whether it is inward or outward"};
-cvar_t r_glsl_offsetmapping_bias = {0, "r_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
-cvar_t r_glsl_usehalffloat = {0, "r_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
-cvar_t r_glsl_surfacenormalize = {0, "r_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+cvar_t r_glsl_offsetmapping = {0, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"};
+cvar_t r_glsl_offsetmapping_reliefmapping = {0, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
+cvar_t r_glsl_offsetmapping_scale = {0, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"};
+cvar_t r_glsl_deluxemapping = {0, "r_glsl_deluxemapping", "1", "use per pixel lighting on deluxemap-compiled q3bsp maps (or a value of 2 forces deluxemap shading even without deluxemaps)"};
 
 cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1", "enables animation smoothing on sprites (requires r_lerpmodels 1)"};
 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
@@ -132,7 +132,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_fogintensity;
+//rtexture_t *r_texture_fogintensity;
 
 // information about each possible shader permutation
 r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_COUNT];
@@ -370,9 +370,9 @@ static void R_BuildNormalizationCube(void)
                                        break;
                                }
                                intensity = 127.0f / sqrt(DotProduct(v, v));
-                               data[side][y][x][0] = 128.0f + intensity * v[0];
-                               data[side][y][x][1] = 128.0f + intensity * v[1];
-                               data[side][y][x][2] = 128.0f + intensity * v[2];
+                               data[side][y][x][0] = (unsigned char)(128.0f + intensity * v[0]);
+                               data[side][y][x][1] = (unsigned char)(128.0f + intensity * v[1]);
+                               data[side][y][x][2] = (unsigned char)(128.0f + intensity * v[2]);
                                data[side][y][x][3] = 255;
                        }
                }
@@ -386,7 +386,7 @@ static void R_BuildFogTexture(void)
        double r, alpha;
 #define FOGWIDTH 64
        unsigned char data1[FOGWIDTH][4];
-       unsigned char data2[FOGWIDTH][4];
+       //unsigned char data2[FOGWIDTH][4];
        r = (-1.0/256.0) * (FOGWIDTH * FOGWIDTH);
        for (x = 0;x < FOGWIDTH;x++)
        {
@@ -399,13 +399,13 @@ static void R_BuildFogTexture(void)
                data1[x][1] = 255 - b;
                data1[x][2] = 255 - b;
                data1[x][3] = 255;
-               data2[x][0] = b;
-               data2[x][1] = b;
-               data2[x][2] = b;
-               data2[x][3] = 255;
+               //data2[x][0] = b;
+               //data2[x][1] = b;
+               //data2[x][2] = b;
+               //data2[x][3] = 255;
        }
        r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
-       r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+       //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
 }
 
 static const char *builtinshaderstring =
@@ -414,52 +414,42 @@ static const char *builtinshaderstring =
 "\n"
 "// common definitions between vertex shader and fragment shader:\n"
 "\n"
-"// use half floats if available for math performance\n"
-"#ifdef GEFORCEFX\n"
-"#define myhalf half\n"
-"#define myhvec2 hvec2\n"
-"#define myhvec3 hvec3\n"
-"#define myhvec4 hvec4\n"
-"#else\n"
-"#define myhalf float\n"
-"#define myhvec2 vec2\n"
-"#define myhvec3 vec3\n"
-"#define myhvec4 vec4\n"
-"#endif\n"
-"\n"
 "varying vec2 TexCoord;\n"
+"varying vec2 TexCoordLightmap;\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
-"varying myhvec3 CubeVector;\n"
+"varying vec3 CubeVector;\n"
 "varying vec3 LightVector;\n"
-"#endif\n"
-"\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
 "varying vec3 EyeVector;\n"
+"#ifdef USEFOG\n"
+"varying vec3 EyeVectorModelSpace;\n"
 "#endif\n"
 "\n"
+"varying vec3 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n"
+"varying vec3 VectorT; // direction of T texcoord (sometimes crudely called binormal)\n"
+"varying vec3 VectorR; // direction of R texcoord (surface normal)\n"
+"\n"
 "\n"
 "\n"
 "\n"
 "// vertex shader specific:\n"
 "#ifdef VERTEX_SHADER\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
 "uniform vec3 LightPosition;\n"
-"#endif\n"
-"\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
 "uniform vec3 EyePosition;\n"
-"#endif\n"
+"uniform vec3 LightDir;\n"
 "\n"
 "// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
 "\n"
 "void main(void)\n"
 "{\n"
+"      gl_FrontColor = gl_Color;\n"
 "      // copy the surface texcoord\n"
 "      TexCoord = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\n"
+"#if !defined(MODE_LIGHTSOURCE) && !defined(MODE_LIGHTDIRECTION)\n"
+"      TexCoordLightmap = vec2(gl_MultiTexCoord4);\n"
+"#endif\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "      // transform vertex position into light attenuation/cubemap space\n"
 "      // (-1 to +1 across the light box)\n"
 "      CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
@@ -473,12 +463,25 @@ static const char *builtinshaderstring =
 "      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
 "#endif\n"
 "\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
+"#ifdef MODE_LIGHTDIRECTION\n"
+"      LightVector.x = dot(LightDir, gl_MultiTexCoord1.xyz);\n"
+"      LightVector.y = dot(LightDir, gl_MultiTexCoord2.xyz);\n"
+"      LightVector.z = dot(LightDir, gl_MultiTexCoord3.xyz);\n"
+"#endif\n"
+"\n"
 "      // transform unnormalized eye direction into tangent space\n"
-"      vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
+"#ifndef USEFOG\n"
+"      vec3 EyeVectorModelSpace;\n"
+"#endif\n"
+"      EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
+"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
+"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
+"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
+"\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+"      VectorS = gl_MultiTexCoord1.xyz;\n"
+"      VectorT = gl_MultiTexCoord2.xyz;\n"
+"      VectorR = gl_MultiTexCoord3.xyz;\n"
 "#endif\n"
 "\n"
 "      // transform vertex to camera space, using ftransform to match non-VS\n"
@@ -486,7 +489,7 @@ static const char *builtinshaderstring =
 "      gl_Position = ftransform();\n"
 "}\n"
 "\n"
-"#endif\n"
+"#endif // VERTEX_SHADER\n"
 "\n"
 "\n"
 "\n"
@@ -494,88 +497,105 @@ static const char *builtinshaderstring =
 "// fragment shader specific:\n"
 "#ifdef FRAGMENT_SHADER\n"
 "\n"
-"uniform myhvec3 LightColor;\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform myhalf OffsetMapping_Scale;\n"
-"uniform myhalf OffsetMapping_Bias;\n"
-"#endif\n"
-"\n"
-"#if defined(USELIGHTSOURCE) || defined(USEDELUXEMAPPING)\n"
 "uniform sampler2D Texture_Normal;\n"
-"#endif\n"
-"\n"
 "uniform sampler2D Texture_Color;\n"
-"\n"
-"#ifdef USECOLORMAPPING\n"
+"uniform sampler2D Texture_Gloss;\n"
+"uniform samplerCube Texture_Cube;\n"
+"uniform sampler2D Texture_FogMask;\n"
 "uniform sampler2D Texture_Pants;\n"
 "uniform sampler2D Texture_Shirt;\n"
-"uniform myhvec3 Color_Pants;\n"
-"uniform myhvec3 Color_Shirt;\n"
-"#endif\n"
+"uniform sampler2D Texture_Lightmap;\n"
+"uniform sampler2D Texture_Deluxemap;\n"
+"uniform sampler2D Texture_Glow;\n"
 "\n"
-"uniform myhalf AmbientScale;\n"
-"uniform myhalf DiffuseScale;\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf SpecularScale;\n"
-"uniform myhalf SpecularPower;\n"
-"uniform sampler2D Texture_Gloss;\n"
-"#endif\n"
+"uniform vec3 LightColor;\n"
+"uniform vec3 AmbientColor;\n"
+"uniform vec3 DiffuseColor;\n"
+"uniform vec3 SpecularColor;\n"
+"uniform vec3 Color_Pants;\n"
+"uniform vec3 Color_Shirt;\n"
+"uniform vec3 FogColor;\n"
 "\n"
-"#ifdef USECUBEFILTER\n"
-"uniform samplerCube Texture_Cube;\n"
-"#endif\n"
+"uniform float OffsetMapping_Scale;\n"
+"uniform float OffsetMapping_Bias;\n"
+"uniform float FogRangeRecip;\n"
 "\n"
-"#ifdef USEFOG\n"
-"uniform myhalf FogRangeRecip;\n"
-"uniform sampler2D Texture_FogMask;\n"
-"#endif\n"
+"uniform float AmbientScale;\n"
+"uniform float DiffuseScale;\n"
+"uniform float SpecularScale;\n"
+"uniform float SpecularPower;\n"
 "\n"
 "void main(void)\n"
 "{\n"
 "      // apply offsetmapping\n"
 "#ifdef USEOFFSETMAPPING\n"
-"      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-"      myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      myhvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
+"      vec2 TexCoordOffset = TexCoord;\n"
 "#define TexCoord TexCoordOffset\n"
+"\n"
+"      vec3 eyedir = vec3(normalize(EyeVector));\n"
+"      float depthbias = 1.0 - eyedir.z; // should this be a -?\n"
+"      depthbias = 1.0 - depthbias * depthbias;\n"
+"\n"
+"#ifdef USEOFFSETMAPPING_RELIEFMAPPING\n"
+"      // 14 sample relief mapping: linear search and then binary search\n"
+"      vec3 OffsetVector = vec3(EyeVector.xy * (1.0 / EyeVector.z) * depthbias * OffsetMapping_Scale * vec2(-0.1, 0.1), -0.1);\n"
+"      vec3 RT = vec3(TexCoord - OffsetVector.xy * 10.0, 1.0) + OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
+"      TexCoord = RT.xy;\n"
+"#else\n"
+"      // 3 sample offset mapping (only 3 samples because of ATI Radeon 9500-9800/X300 limits)\n"
+"      vec2 OffsetVector = vec2((EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * OffsetMapping_Scale * vec2(-0.333, 0.333));\n"
+"      //TexCoord += OffsetVector * 3.0;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"#endif\n"
 "#endif\n"
 "\n"
 "      // combine the diffuse textures (base, pants, shirt)\n"
-"      myhvec4 color = myhvec4(texture2D(Texture_Color, TexCoord));\n"
+"      vec4 color = vec4(texture2D(Texture_Color, TexCoord));\n"
 "#ifdef USECOLORMAPPING\n"
-"      color.rgb += myhvec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhvec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
+"      color.rgb += vec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + vec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
 "#endif\n"
 "\n"
 "\n"
 "\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "      // light source\n"
 "\n"
 "      // get the surface normal and light normal\n"
-"#ifdef SURFACENORMALIZE\n"
-"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"#else\n"
-"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
-"#endif\n"
-"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
+"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      vec3 diffusenormal = vec3(normalize(LightVector));\n"
 "\n"
 "      // calculate directional shading\n"
 "      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
-"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
+"      color.rgb += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
 "      // apply light cubemap filter\n"
-"      color.rgb *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
+"      //color.rgb *= normalize(CubeVector) * 0.5 + 0.5;//vec3(textureCube(Texture_Cube, CubeVector));\n"
+"      color.rgb *= vec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
 "      // apply light color\n"
-"      color.rgb = color.rgb * LightColor;\n"
+"      color.rgb *= LightColor;\n"
 "\n"
 "      // apply attenuation\n"
 "      //\n"
@@ -590,61 +610,93 @@ static const char *builtinshaderstring =
 "\n"
 "\n"
 "\n"
-"#else\n"
-"#ifdef USEDELUXEMAPPING\n"
-"      // deluxemap lightmapping\n"
+"#elif defined(MODE_LIGHTDIRECTION)\n"
+"      // directional model lighting\n"
 "\n"
 "      // get the surface normal and light normal\n"
-"#ifdef SURFACENORMALIZE\n"
-"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      myhvec3 diffusenormal = normalize(myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)));\n"
-"#else\n"
-"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
-"      myhvec3 diffusenormal = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap));\n"
+"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      vec3 diffusenormal = vec3(normalize(LightVector));\n"
+"\n"
+"      // calculate directional shading\n"
+"      color.rgb *= AmbientColor + DiffuseColor * max(dot(surfacenormal, diffusenormal), 0.0);\n"
+"#ifdef USESPECULAR\n"
+"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
+"      color.rgb += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
+"\n"
+"\n"
+"\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE)\n"
+"      // deluxemap lightmapping using light vectors in modelspace (evil q3map2)\n"
+"\n"
+"      // get the surface normal and light normal\n"
+"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      vec3 diffusenormal_modelspace = vec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5;\n"
+"      vec3 diffusenormal = normalize(vec3(dot(diffusenormal_modelspace, VectorS), dot(diffusenormal_modelspace, VectorT), dot(diffusenormal_modelspace, VectorR)));\n"
+"\n"
 "      // calculate directional shading\n"
-"      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      vec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
-"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
+"      tempcolor += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
 "      // apply lightmap color\n"
-"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap));\n"
+"      color.rgb = tempcolor * vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * vec3(AmbientScale);\n"
 "\n"
 "\n"
 "\n"
 "\n"
-"#else\n"
-"      // apply lightmap color\n"
-"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale;\n"
-"#endif\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
+"      // deluxemap lightmapping using light vectors in tangentspace\n"
+"\n"
+"      // get the surface normal and light normal\n"
+"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      vec3 diffusenormal = normalize(vec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5);\n"
+"\n"
+"      // calculate directional shading\n"
+"      vec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"#ifdef USESPECULAR\n"
+"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
+"      tempcolor += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
+"      // apply lightmap color\n"
+"      color.rgb = tempcolor * vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * vec3(AmbientScale);\n"
+"\n"
 "\n"
 "\n"
 "\n"
+"#else // MODE none (lightmap)\n"
+"      // apply lightmap color\n"
+"      color.rgb *= vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale + vec3(AmbientScale);\n"
+"#endif // MODE\n"
+"\n"
+"      color *= gl_Color;\n"
+"\n"
+"#ifdef USEGLOW\n"
+"      color.rgb += vec3(texture2D(Texture_Glow, TexCoord));\n"
+"#endif\n"
+"\n"
 "#ifdef USEFOG\n"
 "      // apply fog\n"
-"      myhalf fog = texture2D(Texture_FogMask, myhvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
-"      color.rgb = color.rgb * (1 - fog) + FogColor * fog;\n"
+"      float fog = texture2D(Texture_FogMask, vec2(length(EyeVectorModelSpace)*FogRangeRecip, 0.0)).x;\n"
+"      color.rgb = color.rgb * fog + FogColor * (1.0 - fog);\n"
 "#endif\n"
 "\n"
 "      gl_FragColor = color;\n"
 "}\n"
 "\n"
-"#endif\n"
+"#endif // FRAGMENT_SHADER\n"
 ;
 
-// the loaded GLSL shader file for compiling shader permutations as needed
-static char *shaderstring = NULL;
-
 void R_GLSL_CompilePermutation(int permutation)
 {
        r_glsl_permutation_t *p = r_glsl_permutations + permutation;
        int vertstrings_count;
        int fragstrings_count;
+       char *shaderstring;
        const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
        const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
        char permutationname[256];
@@ -656,17 +708,35 @@ void R_GLSL_CompilePermutation(int permutation)
        vertstrings_count = 1;
        fragstrings_count = 1;
        permutationname[0] = 0;
-       if (permutation & SHADERPERMUTATION_DELUXEMAPPING)
+       if (permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE)
        {
-               vertstrings_list[vertstrings_count++] = "#define USEDELUXEMAP\n";
-               fragstrings_list[fragstrings_count++] = "#define USEDELUXEMAP\n";
-               strlcat(permutationname, " deluxemap", sizeof(permutationname));
+               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTSOURCE\n";
+               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTSOURCE\n";
+               strlcat(permutationname, " lightsource", sizeof(permutationname));
        }
-       if (permutation & SHADERPERMUTATION_LIGHTSOURCE)
+       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE)
        {
-               vertstrings_list[vertstrings_count++] = "#define USELIGHTSOURCE\n";
-               fragstrings_list[fragstrings_count++] = "#define USELIGHTSOURCE\n";
-               strlcat(permutationname, " lightsource", sizeof(permutationname));
+               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
+               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
+               strlcat(permutationname, " lightdirectionmap_modelspace", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)
+       {
+               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
+               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
+               strlcat(permutationname, " lightdirectionmap_tangentspace", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
+       {
+               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTION\n";
+               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTION\n";
+               strlcat(permutationname, " lightdirection", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_GLOW)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USEGLOW\n";
+               fragstrings_list[fragstrings_count++] = "#define USEGLOW\n";
+               strlcat(permutationname, " glow", sizeof(permutationname));
        }
        if (permutation & SHADERPERMUTATION_COLORMAPPING)
        {
@@ -698,20 +768,16 @@ void R_GLSL_CompilePermutation(int permutation)
                fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
                strlcat(permutationname, " offsetmapping", sizeof(permutationname));
        }
-       if (permutation & SHADERPERMUTATION_SURFACENORMALIZE)
-       {
-               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
-               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
-               strlcat(permutationname, " surfacenormalize", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_GEFORCEFX)
+       if (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING)
        {
-               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
-               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
-               strlcat(permutationname, " halffloat", sizeof(permutationname));
+               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
+               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
+               strlcat(permutationname, " OFFSETMAPPING_RELIEFMAPPING", sizeof(permutationname));
        }
+       shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
        if (shaderstring)
        {
+               Con_DPrintf("GLSL shader text loaded from disk\n");
                vertstrings_list[vertstrings_count++] = shaderstring;
                fragstrings_list[fragstrings_count++] = shaderstring;
        }
@@ -732,6 +798,9 @@ void R_GLSL_CompilePermutation(int permutation)
                p->loc_Texture_FogMask     = qglGetUniformLocationARB(p->program, "Texture_FogMask");
                p->loc_Texture_Pants       = qglGetUniformLocationARB(p->program, "Texture_Pants");
                p->loc_Texture_Shirt       = qglGetUniformLocationARB(p->program, "Texture_Shirt");
+               p->loc_Texture_Lightmap    = qglGetUniformLocationARB(p->program, "Texture_Lightmap");
+               p->loc_Texture_Deluxemap   = qglGetUniformLocationARB(p->program, "Texture_Deluxemap");
+               p->loc_Texture_Glow        = qglGetUniformLocationARB(p->program, "Texture_Glow");
                p->loc_FogColor            = qglGetUniformLocationARB(p->program, "FogColor");
                p->loc_LightPosition       = qglGetUniformLocationARB(p->program, "LightPosition");
                p->loc_EyePosition         = qglGetUniformLocationARB(p->program, "EyePosition");
@@ -744,49 +813,88 @@ void R_GLSL_CompilePermutation(int permutation)
                p->loc_SpecularPower       = qglGetUniformLocationARB(p->program, "SpecularPower");
                p->loc_SpecularScale       = qglGetUniformLocationARB(p->program, "SpecularScale");
                p->loc_OffsetMapping_Scale = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
-               p->loc_OffsetMapping_Bias  = qglGetUniformLocationARB(p->program, "OffsetMapping_Bias");
-               if (p->loc_Texture_Normal)   qglUniform1iARB(p->loc_Texture_Normal, 0);
-               if (p->loc_Texture_Color)    qglUniform1iARB(p->loc_Texture_Color, 1);
-               if (p->loc_Texture_Gloss)    qglUniform1iARB(p->loc_Texture_Gloss, 2);
-               if (p->loc_Texture_Cube)     qglUniform1iARB(p->loc_Texture_Cube, 3);
-               if (p->loc_Texture_FogMask)  qglUniform1iARB(p->loc_Texture_FogMask, 4);
-               if (p->loc_Texture_Pants)    qglUniform1iARB(p->loc_Texture_Pants, 5);
-               if (p->loc_Texture_Shirt)    qglUniform1iARB(p->loc_Texture_Shirt, 6);
-               if (p->loc_Texture_Lightmap) qglUniform1iARB(p->loc_Texture_Lightmap, 7);
-               if (p->loc_Texture_Deluxemap) qglUniform1iARB(p->loc_Texture_Deluxemap, 8);
+               p->loc_AmbientColor        = qglGetUniformLocationARB(p->program, "AmbientColor");
+               p->loc_DiffuseColor        = qglGetUniformLocationARB(p->program, "DiffuseColor");
+               p->loc_SpecularColor       = qglGetUniformLocationARB(p->program, "SpecularColor");
+               p->loc_LightDir            = qglGetUniformLocationARB(p->program, "LightDir");
+               if (p->loc_Texture_Normal >= 0)    qglUniform1iARB(p->loc_Texture_Normal, 0);
+               if (p->loc_Texture_Color >= 0)     qglUniform1iARB(p->loc_Texture_Color, 1);
+               if (p->loc_Texture_Gloss >= 0)     qglUniform1iARB(p->loc_Texture_Gloss, 2);
+               if (p->loc_Texture_Cube >= 0)      qglUniform1iARB(p->loc_Texture_Cube, 3);
+               if (p->loc_Texture_FogMask >= 0)   qglUniform1iARB(p->loc_Texture_FogMask, 4);
+               if (p->loc_Texture_Pants >= 0)     qglUniform1iARB(p->loc_Texture_Pants, 5);
+               if (p->loc_Texture_Shirt >= 0)     qglUniform1iARB(p->loc_Texture_Shirt, 6);
+               if (p->loc_Texture_Lightmap >= 0)  qglUniform1iARB(p->loc_Texture_Lightmap, 7);
+               if (p->loc_Texture_Deluxemap >= 0) qglUniform1iARB(p->loc_Texture_Deluxemap, 8);
+               if (p->loc_Texture_Glow >= 0)      qglUniform1iARB(p->loc_Texture_Glow, 9);
                qglUseProgramObjectARB(0);
                CHECKGLERROR
        }
        else
                Con_Printf("permutation%s failed for shader %s, some features may not work properly!\n", permutationname, "glsl/default.glsl");
+       if (shaderstring)
+               Mem_Free(shaderstring);
 }
 
-void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+void R_GLSL_Restart_f(void)
+{
+       int i;
+       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
+               if (r_glsl_permutations[i].program)
+                       GL_Backend_FreeProgram(r_glsl_permutations[i].program);
+       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
+}
+
+int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
        // minimum features necessary to avoid wasting rendering time in the
        // fragment shader on features that are not being used
        int permutation = 0;
+       float specularscale = rsurface_texture->specularscale;
        r_glsl_permutation = NULL;
        if (r_shadow_rtlight)
-               permutation |= SHADERPERMUTATION_LIGHTSOURCE;
-       else if (false)
-               permutation |= SHADERPERMUTATION_DELUXEMAPPING;
+       {
+               permutation |= SHADERPERMUTATION_MODE_LIGHTSOURCE;
+               specularscale *= r_shadow_rtlight->specularscale;
+               if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+                       permutation |= SHADERPERMUTATION_CUBEFILTER;
+       }
+       else
+       {
+               if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+               {
+                       if (modellighting)
+                               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
+                       else
+                       {
+                               if (r_glsl_deluxemapping.integer >= 1 && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping && rsurface_lightmaptexture)
+                               {
+                                       if (r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
+                                               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
+                                       else
+                                               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+                               }
+                               else if (r_glsl_deluxemapping.integer >= 2) // fake mode
+                                       permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+                       }
+               }
+               if (rsurface_texture->skin.glow)
+                       permutation |= SHADERPERMUTATION_GLOW;
+       }
+       if (specularscale > 0)
+               permutation |= SHADERPERMUTATION_SPECULAR;
        if (fogenabled)
                permutation |= SHADERPERMUTATION_FOG;
-       if ((dopants || doshirt))
+       if (rsurface_texture->colormapping)
                permutation |= SHADERPERMUTATION_COLORMAPPING;
-       if (specularscale > 0)
-               permutation |= SHADERPERMUTATION_SPECULAR;
-       if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
-               permutation |= SHADERPERMUTATION_CUBEFILTER;
        if (r_glsl_offsetmapping.integer)
+       {
                permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-       if (r_glsl_surfacenormalize.integer)
-               permutation |= SHADERPERMUTATION_SURFACENORMALIZE;
-       if (r_glsl_usehalffloat.integer)
-               permutation |= SHADERPERMUTATION_GEFORCEFX;
+               if (r_glsl_offsetmapping_reliefmapping.integer)
+                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+       }
        if (!r_glsl_permutations[permutation].program)
        {
                if (!r_glsl_permutations[permutation].compiled)
@@ -806,49 +914,107 @@ void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture,
                                if (r_glsl_permutations[permutation].program)
                                        break;
                                if (!i)
-                                       return; // utterly failed
+                                       return 0; // utterly failed
                        }
                }
        }
        r_glsl_permutation = r_glsl_permutations + permutation;
        CHECKGLERROR
        qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
-       R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
-       if (r_shadow_rtlight)
-               R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
-       if (r_glsl_permutation->loc_Texture_Normal) R_Mesh_TexBind(0, R_GetTexture(normalmaptexture));
-       if (r_glsl_permutation->loc_Texture_Color) R_Mesh_TexBind(1, R_GetTexture(basetexture));
-       if (r_glsl_permutation->loc_FogColor)
+       R_Mesh_TexMatrix(0, &rsurface_texture->currenttexmatrix);
+       if (permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE)
+       {
+               if (r_glsl_permutation->loc_Texture_Cube >= 0 && r_shadow_rtlight) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
+               if (r_glsl_permutation->loc_LightPosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);
+               if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+               if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_shadow_rtlight->ambientscale);
+               if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_shadow_rtlight->diffusescale);
+               if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale);
+       }
+       else if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
+       {
+               if (rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+               {
+                       if (r_glsl_permutation->loc_AmbientColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, 1, 1, 1);
+                       if (r_glsl_permutation->loc_DiffuseColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, 0, 0, 0);
+                       if (r_glsl_permutation->loc_SpecularColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, 0, 0, 0);
+                       if (r_glsl_permutation->loc_LightDir >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_LightDir, 0, 0, -1);
+               }
+               else
+               {
+                       if (r_glsl_permutation->loc_AmbientColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, rsurface_entity->modellight_ambient[0], rsurface_entity->modellight_ambient[1], rsurface_entity->modellight_ambient[2]);
+                       if (r_glsl_permutation->loc_DiffuseColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, rsurface_entity->modellight_diffuse[0], rsurface_entity->modellight_diffuse[1], rsurface_entity->modellight_diffuse[2]);
+                       if (r_glsl_permutation->loc_SpecularColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, rsurface_entity->modellight_diffuse[0] * rsurface_texture->specularscale, rsurface_entity->modellight_diffuse[1] * rsurface_texture->specularscale, rsurface_entity->modellight_diffuse[2] * rsurface_texture->specularscale);
+                       if (r_glsl_permutation->loc_LightDir >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_LightDir, rsurface_entity->modellight_lightdir[0], rsurface_entity->modellight_lightdir[1], rsurface_entity->modellight_lightdir[2]);
+               }
+       }
+       else
+       {
+               if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_ambient.value * 2.0f / 128.0f);
+               if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_lightmapintensity * 2.0f);
+               if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, r_lightmapintensity * specularscale * 2.0f);
+       }
+       if (r_glsl_permutation->loc_Texture_Normal >= 0) R_Mesh_TexBind(0, R_GetTexture(rsurface_texture->skin.nmap));
+       if (r_glsl_permutation->loc_Texture_Color >= 0) R_Mesh_TexBind(1, R_GetTexture(rsurface_texture->basetexture));
+       if (r_glsl_permutation->loc_Texture_Gloss >= 0) R_Mesh_TexBind(2, R_GetTexture(rsurface_texture->glosstexture));
+       //if (r_glsl_permutation->loc_Texture_Cube >= 0 && permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
+       if (r_glsl_permutation->loc_Texture_FogMask >= 0) R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
+       if (r_glsl_permutation->loc_Texture_Pants >= 0) R_Mesh_TexBind(5, R_GetTexture(rsurface_texture->skin.pants));
+       if (r_glsl_permutation->loc_Texture_Shirt >= 0) R_Mesh_TexBind(6, R_GetTexture(rsurface_texture->skin.shirt));
+       //if (r_glsl_permutation->loc_Texture_Lightmap >= 0) R_Mesh_TexBind(7, R_GetTexture(r_texture_white));
+       //if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
+       if (r_glsl_permutation->loc_Texture_Glow >= 0) R_Mesh_TexBind(9, R_GetTexture(rsurface_texture->skin.glow));
+       if (r_glsl_permutation->loc_FogColor >= 0)
        {
                // additive passes are only darkened by fog, not tinted
-               if (r_shadow_rtlight || (texture->currentmaterialflags & MATERIALFLAG_ADD))
+               if (r_shadow_rtlight || (rsurface_texture->currentmaterialflags & MATERIALFLAG_ADD))
                        qglUniform3fARB(r_glsl_permutation->loc_FogColor, 0, 0, 0);
                else
                        qglUniform3fARB(r_glsl_permutation->loc_FogColor, fogcolor[0], fogcolor[1], fogcolor[2]);
        }
-       if (r_glsl_permutation->loc_LightPosition) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);
-       if (r_glsl_permutation->loc_EyePosition) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);
-       if (r_glsl_permutation->loc_LightColor) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
-       if (r_glsl_permutation->loc_Texture_Pants) R_Mesh_TexBind(5, R_GetTexture(pantstexture));
-       if (r_glsl_permutation->loc_Texture_Shirt) R_Mesh_TexBind(6, R_GetTexture(shirttexture));
-       if (r_glsl_permutation->loc_Color_Pants) qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);
-       if (r_glsl_permutation->loc_Color_Shirt) qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);
-       if (r_glsl_permutation->loc_FogRangeRecip) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, fograngerecip);
-       if (r_glsl_permutation->loc_AmbientScale) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_shadow_rtlight->ambientscale);
-       if (r_glsl_permutation->loc_DiffuseScale) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_shadow_rtlight->diffusescale);
-       if (r_glsl_permutation->loc_Texture_Gloss) R_Mesh_TexBind(2, R_GetTexture(glosstexture));
-       if (r_glsl_permutation->loc_SpecularPower) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, 8);
-       if (r_glsl_permutation->loc_SpecularScale) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale);
-       if (r_glsl_permutation->loc_OffsetMapping_Scale) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
-       if (r_glsl_permutation->loc_OffsetMapping_Bias) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Bias, r_glsl_offsetmapping_bias.value);
+       if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, rsurface_modelorg[0], rsurface_modelorg[1], rsurface_modelorg[2]);
+       if (r_glsl_permutation->loc_Color_Pants >= 0)
+       {
+               if (rsurface_texture->skin.pants)
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, rsurface_entity->colormap_pantscolor[0], rsurface_entity->colormap_pantscolor[1], rsurface_entity->colormap_pantscolor[2]);
+               else
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, 0, 0, 0);
+       }
+       if (r_glsl_permutation->loc_Color_Shirt >= 0)
+       {
+               if (rsurface_texture->skin.shirt)
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, rsurface_entity->colormap_shirtcolor[0], rsurface_entity->colormap_shirtcolor[1], rsurface_entity->colormap_shirtcolor[2]);
+               else
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, 0, 0, 0);
+       }
+       if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, fograngerecip);
+       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface_texture->specularpower);
+       if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
        CHECKGLERROR
+       return permutation;
+}
+
+void R_SwitchSurfaceShader(int permutation)
+{
+       if (r_glsl_permutation != r_glsl_permutations + permutation)
+       {
+               r_glsl_permutation = r_glsl_permutations + permutation;
+               CHECKGLERROR
+               qglUseProgramObjectARB(r_glsl_permutation->program);
+               CHECKGLERROR
+       }
 }
 
 void gl_main_start(void)
 {
-       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
-       if (gl_support_half_float)
-               Cvar_SetValue("r_glsl_usehalffloat", 1);
        r_main_texturepool = R_AllocTexturePool();
        r_bloom_texture_screen = NULL;
        r_bloom_texture_bloom = NULL;
@@ -860,29 +1026,11 @@ void gl_main_start(void)
                R_BuildNormalizationCube();
        }
        R_BuildFogTexture();
-       shaderstring = NULL;
-       if (gl_support_fragment_shader)
-       {
-               shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
-               if (shaderstring)
-                       Con_Printf("GLSL shader text loaded from disk\n");
-               // if we couldn't load the shader file, fall back to builtin shader
-               if (!shaderstring)
-               {
-                       if (shaderstring)
-                               Con_Printf("GLSL shader text loaded from fallback\n");
-                       shaderstring = Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
-                       strcpy(shaderstring, builtinshaderstring);
-               }
-       }
-       if (shaderstring)
-               Con_Printf("GLSL shader text loaded\n");
        memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
 void gl_main_shutdown(void)
 {
-       int i;
        R_FreeTexturePool(&r_main_texturepool);
        r_bloom_texture_screen = NULL;
        r_bloom_texture_bloom = NULL;
@@ -891,13 +1039,7 @@ void gl_main_shutdown(void)
        r_texture_black = NULL;
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
-       if (shaderstring)
-               Mem_Free(shaderstring);
-       shaderstring = NULL;
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               if (r_glsl_permutations[i].program)
-                       GL_Backend_FreeProgram(r_glsl_permutations[i].program);
-       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
+       R_GLSL_Restart_f();
 }
 
 extern void CL_ParseEntityLump(char *entitystring);
@@ -930,10 +1072,11 @@ void GL_Main_Init(void)
 {
        r_main_mempool = Mem_AllocPool("Renderer", 0, NULL);
 
+       Cmd_AddCommand("r_glsl_restart", R_GLSL_Restart_f, "unloads GLSL shaders, they will then be reloaded as needed\n");
        FOG_registercvars(); // FIXME: move this fog stuff to client?
        Cvar_RegisterVariable(&r_nearclip);
+       Cvar_RegisterVariable(&r_showsurfaces);
        Cvar_RegisterVariable(&r_showtris);
-       Cvar_RegisterVariable(&r_showtris_polygonoffset);
        Cvar_RegisterVariable(&r_shownormals);
        Cvar_RegisterVariable(&r_showlighting);
        Cvar_RegisterVariable(&r_showshadowvolumes);
@@ -948,13 +1091,13 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_wateralpha);
        Cvar_RegisterVariable(&r_dynamic);
        Cvar_RegisterVariable(&r_fullbright);
+       Cvar_RegisterVariable(&r_q1bsp_skymasking);
        Cvar_RegisterVariable(&r_textureunits);
        Cvar_RegisterVariable(&r_glsl);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_scale);
-       Cvar_RegisterVariable(&r_glsl_offsetmapping_bias);
-       Cvar_RegisterVariable(&r_glsl_usehalffloat);
-       Cvar_RegisterVariable(&r_glsl_surfacenormalize);
+       Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_lerpsprites);
        Cvar_RegisterVariable(&r_lerpmodels);
        Cvar_RegisterVariable(&r_waterscroll);
@@ -1113,6 +1256,26 @@ int R_CullBox(const vec3_t mins, const vec3_t maxs)
 
 //==================================================================================
 
+static void R_UpdateEntityLighting(entity_render_t *ent)
+{
+       vec3_t tempdiffusenormal;
+       VectorSet(ent->modellight_ambient, r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f), r_ambient.value * (2.0f / 128.0f));
+       VectorClear(ent->modellight_diffuse);
+       VectorClear(ent->modellight_lightdir);
+       if ((ent->flags & RENDER_LIGHT) && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint)
+               r_refdef.worldmodel->brush.LightPoint(r_refdef.worldmodel, ent->origin, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+       else // highly rare
+               VectorSet(ent->modellight_ambient, 1, 1, 1);
+       Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir);
+       VectorNormalize(ent->modellight_lightdir);
+       ent->modellight_ambient[0] *= ent->colormod[0] * r_lightmapintensity;
+       ent->modellight_ambient[1] *= ent->colormod[1] * r_lightmapintensity;
+       ent->modellight_ambient[2] *= ent->colormod[2] * r_lightmapintensity;
+       ent->modellight_diffuse[0] *= ent->colormod[0] * r_lightmapintensity;
+       ent->modellight_diffuse[1] *= ent->colormod[1] * r_lightmapintensity;
+       ent->modellight_diffuse[2] *= ent->colormod[2] * r_lightmapintensity;
+}
+
 static void R_MarkEntities (void)
 {
        int i, renderimask;
@@ -1135,8 +1298,8 @@ static void R_MarkEntities (void)
                        ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
                        if (!(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && ((ent->effects & EF_NODEPTHTEST) || r_refdef.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.worldmodel, r_worldleafvisible, ent->mins, ent->maxs)))
                        {
-                               R_UpdateEntLights(ent);
                                ent->visframe = r_framecount;
+                               R_UpdateEntityLighting(ent);
                        }
                }
        }
@@ -1152,8 +1315,8 @@ static void R_MarkEntities (void)
                        ent->scale = Matrix4x4_ScaleFromMatrix(&ent->matrix);
                        if (!(ent->flags & renderimask) && !R_CullBox(ent->mins, ent->maxs) && (ent->effects & EF_NODEPTHTEST))
                        {
-                               R_UpdateEntLights(ent);
                                ent->visframe = r_framecount;
+                               R_UpdateEntityLighting(ent);
                        }
                }
        }
@@ -1329,7 +1492,8 @@ static void R_BlendView(void)
        int screenwidth, screenheight;
        qboolean dobloom;
        qboolean doblend;
-       rmeshstate_t m;
+       float vertex3f[12];
+       float texcoord2f[3][8];
 
        // set the (poorly named) screenwidth and screenheight variables to
        // a power of 2 at least as large as the screen, these will define the
@@ -1348,13 +1512,16 @@ static void R_BlendView(void)
        GL_DepthTest(false);
        R_Mesh_Matrix(&identitymatrix);
        // vertex coordinates for a quad that covers the screen exactly
-       varray_vertex3f[0] = 0;varray_vertex3f[1] = 0;varray_vertex3f[2] = 0;
-       varray_vertex3f[3] = 1;varray_vertex3f[4] = 0;varray_vertex3f[5] = 0;
-       varray_vertex3f[6] = 1;varray_vertex3f[7] = 1;varray_vertex3f[8] = 0;
-       varray_vertex3f[9] = 0;varray_vertex3f[10] = 1;varray_vertex3f[11] = 0;
+       vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
+       vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
+       vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
+       vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+       R_Mesh_VertexPointer(vertex3f);
+       R_Mesh_ColorPointer(NULL);
+       R_Mesh_ResetTextureState();
        if (dobloom)
        {
-               int bloomwidth, bloomheight, x, dobloomblend, range;
+               int bloomwidth, bloomheight, x, range;
                float xoffset, yoffset, r;
                renderstats.bloom++;
                // allocate textures as needed
@@ -1368,29 +1535,26 @@ static void R_BlendView(void)
                bloomheight = min(r_view_height, bloomwidth * r_view_height / r_view_width);
                // set up a texcoord array for the full resolution screen image
                // (we have to keep this around to copy back during final render)
-               varray_texcoord2f[0][0] = 0;
-               varray_texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
-               varray_texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
-               varray_texcoord2f[0][3] = (float)r_view_height / (float)screenheight;
-               varray_texcoord2f[0][4] = (float)r_view_width / (float)screenwidth;
-               varray_texcoord2f[0][5] = 0;
-               varray_texcoord2f[0][6] = 0;
-               varray_texcoord2f[0][7] = 0;
+               texcoord2f[0][0] = 0;
+               texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
+               texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
+               texcoord2f[0][3] = (float)r_view_height / (float)screenheight;
+               texcoord2f[0][4] = (float)r_view_width / (float)screenwidth;
+               texcoord2f[0][5] = 0;
+               texcoord2f[0][6] = 0;
+               texcoord2f[0][7] = 0;
                // set up a texcoord array for the reduced resolution bloom image
                // (which will be additive blended over the screen image)
-               varray_texcoord2f[1][0] = 0;
-               varray_texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
-               varray_texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
-               varray_texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
-               varray_texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
-               varray_texcoord2f[1][5] = 0;
-               varray_texcoord2f[1][6] = 0;
-               varray_texcoord2f[1][7] = 0;
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-               m.tex[0] = R_GetTexture(r_bloom_texture_screen);
-               R_Mesh_State(&m);
+               texcoord2f[1][0] = 0;
+               texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
+               texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
+               texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
+               texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
+               texcoord2f[1][5] = 0;
+               texcoord2f[1][6] = 0;
+               texcoord2f[1][7] = 0;
+               R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
+               R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
                // copy view into the full resolution screen image texture
                GL_ActiveTexture(0);
                qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
@@ -1413,11 +1577,8 @@ static void R_BlendView(void)
                }
                // we now have a darkened bloom image in the framebuffer, copy it into
                // the bloom image texture for more processing
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
-               m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
-               m.pointer_texcoord[0] = varray_texcoord2f[2];
-               R_Mesh_State(&m);
+               R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
+               R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]);
                GL_ActiveTexture(0);
                qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.height - (r_view_y + bloomheight), bloomwidth, bloomheight);
                renderstats.bloom_copypixels += bloomwidth * bloomheight;
@@ -1430,14 +1591,14 @@ static void R_BlendView(void)
                        xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
                        yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
                        // compute a texcoord array with the specified x and y offset
-                       varray_texcoord2f[2][0] = xoffset+0;
-                       varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][5] = yoffset+0;
-                       varray_texcoord2f[2][6] = xoffset+0;
-                       varray_texcoord2f[2][7] = yoffset+0;
+                       texcoord2f[2][0] = xoffset+0;
+                       texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+                       texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+                       texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+                       texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+                       texcoord2f[2][5] = yoffset+0;
+                       texcoord2f[2][6] = xoffset+0;
+                       texcoord2f[2][7] = yoffset+0;
                        // this r value looks like a 'dot' particle, fading sharply to
                        // black at the edges
                        // (probably not realistic but looks good enough)
@@ -1463,14 +1624,14 @@ static void R_BlendView(void)
                        xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
                        yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
                        // compute a texcoord array with the specified x and y offset
-                       varray_texcoord2f[2][0] = xoffset+0;
-                       varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][5] = yoffset+0;
-                       varray_texcoord2f[2][6] = xoffset+0;
-                       varray_texcoord2f[2][7] = yoffset+0;
+                       texcoord2f[2][0] = xoffset+0;
+                       texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+                       texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+                       texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+                       texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+                       texcoord2f[2][5] = yoffset+0;
+                       texcoord2f[2][6] = xoffset+0;
+                       texcoord2f[2][7] = yoffset+0;
                        // this r value looks like a 'dot' particle, fading sharply to
                        // black at the edges
                        // (probably not realistic but looks good enough)
@@ -1490,49 +1651,33 @@ static void R_BlendView(void)
                qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
                // put the original screen image back in place and blend the bloom
                // texture on it
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
-               m.tex[0] = R_GetTexture(r_bloom_texture_screen);
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
-#if 0
-               dobloomblend = false;
-#else
+               GL_Color(1,1,1,1);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
                // do both in one pass if possible
+               R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
+               R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
                if (r_textureunits.integer >= 2 && gl_combine.integer)
                {
-                       dobloomblend = false;
-                       m.texcombinergb[1] = GL_ADD;
-                       m.tex[1] = R_GetTexture(r_bloom_texture_bloom);
-                       m.pointer_texcoord[1] = varray_texcoord2f[1];
+                       R_Mesh_TexCombine(1, GL_ADD, GL_ADD, 1, 1);
+                       R_Mesh_TexBind(1, R_GetTexture(r_bloom_texture_bloom));
+                       R_Mesh_TexCoordPointer(1, 2, texcoord2f[1]);
                }
                else
-                       dobloomblend = true;
-#endif
-               R_Mesh_State(&m);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_Color(1,1,1,1);
-               R_Mesh_Draw(0, 4, 2, polygonelements);
-               renderstats.bloom_drawpixels += r_view_width * r_view_height;
-               // now blend on the bloom texture if multipass
-               if (dobloomblend)
                {
-                       memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = varray_vertex3f;
-                       m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
-                       m.pointer_texcoord[0] = varray_texcoord2f[1];
-                       R_Mesh_State(&m);
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-                       GL_Color(1,1,1,1);
                        R_Mesh_Draw(0, 4, 2, polygonelements);
                        renderstats.bloom_drawpixels += r_view_width * r_view_height;
+                       // now blend on the bloom texture
+                       GL_BlendFunc(GL_ONE, GL_ONE);
+                       R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
+                       R_Mesh_TexCoordPointer(0, 2, texcoord2f[1]);
                }
+               R_Mesh_Draw(0, 4, 2, polygonelements);
+               renderstats.bloom_drawpixels += r_view_width * r_view_height;
        }
        if (doblend)
        {
                // apply a color tint to the whole view
-               memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
-               R_Mesh_State(&m);
+               R_Mesh_ResetTextureState();
                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, 2, polygonelements);
@@ -1566,6 +1711,18 @@ void R_RenderView(void)
        r_rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer;
        r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil;
        r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+       r_polygonfactor = 0;
+       r_polygonoffset = 0;
+       r_shadowpolygonfactor = r_polygonfactor + r_shadow_shadow_polygonfactor.value;
+       r_shadowpolygonoffset = r_polygonoffset + r_shadow_shadow_polygonoffset.value;
+       if (r_showsurfaces.integer)
+       {
+               r_rtworld = false;
+               r_rtworldshadows = false;
+               r_rtdlight = false;
+               r_rtdlightshadows = false;
+               r_lightmapintensity = 0;
+       }
 
        // GL is weird because it's bottom to top, r_view_y is top to bottom
        qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
@@ -1579,12 +1736,12 @@ void R_RenderView(void)
                R_TimeReport("setup");
 
        qglDepthFunc(GL_LEQUAL);
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        qglEnable(GL_POLYGON_OFFSET_FILL);
 
        R_RenderScene();
 
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        qglDisable(GL_POLYGON_OFFSET_FILL);
 
        R_BlendView();
@@ -1614,6 +1771,18 @@ void CSQC_R_ClearScreen (void)
        r_rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer;
        r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil;
        r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+       r_polygonfactor = 0;
+       r_polygonoffset = 0;
+       r_shadowpolygonfactor = r_polygonfactor + r_shadow_shadow_polygonfactor.value;
+       r_shadowpolygonoffset = r_polygonoffset + r_shadow_shadow_polygonoffset.value;
+       if (r_showsurfaces.integer)
+       {
+               r_rtworld = false;
+               r_rtworldshadows = false;
+               r_rtdlight = false;
+               r_rtdlightshadows = false;
+               r_lightmapintensity = 0;
+       }
 
        // GL is weird because it's bottom to top, r_view_y is top to bottom
        qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height);
@@ -1631,12 +1800,12 @@ void CSQC_R_ClearScreen (void)
 void CSQC_R_RenderScene (void)
 {
        qglDepthFunc(GL_LEQUAL);
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        qglEnable(GL_POLYGON_OFFSET_FILL);
 
        R_RenderScene();
 
-       qglPolygonOffset(0, 0);
+       qglPolygonOffset(r_polygonfactor, r_polygonoffset);
        qglDisable(GL_POLYGON_OFFSET_FILL);
 
        R_BlendView();
@@ -1659,6 +1828,9 @@ void R_RenderScene(void)
 
        r_framecount++;
 
+       if (gl_support_fragment_shader)
+               qglUseProgramObjectARB(0);
+
        R_MeshQueue_BeginScene();
 
        R_SetFrustum();
@@ -1687,129 +1859,94 @@ void R_RenderScene(void)
 
        R_Shadow_UpdateWorldLightSelection();
 
-       for (r_showtrispass = 0;r_showtrispass <= (r_showtris.value > 0);r_showtrispass++)
+       if (cl.csqc_vidvars.drawworld)
        {
-               if (r_showtrispass)
-               {
-                       rmeshstate_t m;
-                       r_showtrispass = 0;
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-                       GL_DepthTest(!r_showdisabledepthtest.integer);
-                       GL_DepthMask(GL_FALSE);
-                       memset(&m, 0, sizeof(m));
-                       R_Mesh_State(&m);
-                       //qglEnable(GL_LINE_SMOOTH);
-                       qglEnable(GL_POLYGON_OFFSET_LINE);
-                       qglPolygonOffset(0, r_showtris_polygonoffset.value);
-                       r_showtrispass = 1;
-               }
-
-               if (cl.csqc_vidvars.drawworld)
-               {
-                       // don't let sound skip if going slow
-                       if (r_refdef.extraupdate)
-                               S_ExtraUpdate ();
-
-                       if (r_showtrispass)
-                               GL_ShowTrisColor(0.025, 0.025, 0, 1);
-                       if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
-                       {
-                               r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
-                               if (r_timereport_active)
-                                       R_TimeReport("worldsky");
-                       }
-
-                       if (R_DrawBrushModelsSky() && r_timereport_active)
-                               R_TimeReport("bmodelsky");
-
-                       if (r_showtrispass)
-                               GL_ShowTrisColor(0.05, 0.05, 0.05, 1);
-                       if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
-                       {
-                               r_refdef.worldmodel->Draw(r_refdef.worldentity);
-                               if (r_timereport_active)
-                                       R_TimeReport("world");
-                       }
-               }
-
                // don't let sound skip if going slow
                if (r_refdef.extraupdate)
                        S_ExtraUpdate ();
 
-               if (r_showtrispass)
-                       GL_ShowTrisColor(0, 0.015, 0, 1);
-
-               R_DrawModels();
-               if (r_timereport_active)
-                       R_TimeReport("models");
+               if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky)
+               {
+                       r_refdef.worldmodel->DrawSky(r_refdef.worldentity);
+                       if (r_timereport_active)
+                               R_TimeReport("worldsky");
+               }
 
-               // don't let sound skip if going slow
-               if (r_refdef.extraupdate)
-                       S_ExtraUpdate ();
+               if (R_DrawBrushModelsSky() && r_timereport_active)
+                       R_TimeReport("bmodelsky");
 
-               if (r_showtrispass)
-                       GL_ShowTrisColor(0, 0, 0.033, 1);
-               R_ShadowVolumeLighting(false);
-               if (r_timereport_active)
-                       R_TimeReport("rtlights");
+               if (r_refdef.worldmodel && r_refdef.worldmodel->Draw)
+               {
+                       r_refdef.worldmodel->Draw(r_refdef.worldentity);
+                       if (r_timereport_active)
+                               R_TimeReport("world");
+               }
+       }
 
-               // don't let sound skip if going slow
-               if (r_refdef.extraupdate)
-                       S_ExtraUpdate ();
+       // don't let sound skip if going slow
+       if (r_refdef.extraupdate)
+               S_ExtraUpdate ();
 
-               if (r_showtrispass)
-                       GL_ShowTrisColor(0.1, 0, 0, 1);
+       R_DrawModels();
+       if (r_timereport_active)
+               R_TimeReport("models");
 
-               if (cl.csqc_vidvars.drawworld)
-               {
-                       R_DrawLightningBeams();
-                       if (r_timereport_active)
-                               R_TimeReport("lightning");
+       // don't let sound skip if going slow
+       if (r_refdef.extraupdate)
+               S_ExtraUpdate ();
 
-                       R_DrawParticles();
-                       if (r_timereport_active)
-                               R_TimeReport("particles");
+       R_ShadowVolumeLighting(false);
+       if (r_timereport_active)
+               R_TimeReport("rtlights");
 
-                       R_DrawExplosions();
-                       if (r_timereport_active)
-                               R_TimeReport("explosions");
-               }
+       // don't let sound skip if going slow
+       if (r_refdef.extraupdate)
+               S_ExtraUpdate ();
 
-               R_MeshQueue_RenderTransparent();
+       if (cl.csqc_vidvars.drawworld)
+       {
+               R_DrawLightningBeams();
                if (r_timereport_active)
-                       R_TimeReport("drawtrans");
+                       R_TimeReport("lightning");
 
-               if (cl.csqc_vidvars.drawworld)
-               {
-                       R_DrawCoronas();
-                       if (r_timereport_active)
-                               R_TimeReport("coronas");
-               }
-               if(cl.csqc_vidvars.drawcrosshair)
-               {
-                       R_DrawWorldCrosshair();
-                       if (r_timereport_active)
-                               R_TimeReport("crosshair");
-               }
+               R_DrawParticles();
+               if (r_timereport_active)
+                       R_TimeReport("particles");
 
-               VM_AddPolygonsToMeshQueue();
+               R_DrawExplosions();
+               if (r_timereport_active)
+                       R_TimeReport("explosions");
+       }
 
-               R_MeshQueue_Render();
+       R_MeshQueue_RenderTransparent();
+       if (r_timereport_active)
+               R_TimeReport("drawtrans");
 
-               if (r_showtrispass)
-               {
-                       //qglDisable(GL_LINE_SMOOTH);
-                       qglDisable(GL_POLYGON_OFFSET_LINE);
-               }
+       if (cl.csqc_vidvars.drawworld)
+       {
+               R_DrawCoronas();
+               if (r_timereport_active)
+                       R_TimeReport("coronas");
+       }
+       if(cl.csqc_vidvars.drawcrosshair)
+       {
+               R_DrawWorldCrosshair();
+               if (r_timereport_active)
+                       R_TimeReport("crosshair");
        }
 
-       r_showtrispass = 0;
+       VM_AddPolygonsToMeshQueue();
+
+       R_MeshQueue_Render();
 
        R_MeshQueue_EndScene();
 
        // don't let sound skip if going slow
        if (r_refdef.extraupdate)
                S_ExtraUpdate ();
+
+       if (gl_support_fragment_shader)
+               qglUseProgramObjectARB(0);
 }
 
 /*
@@ -1817,7 +1954,6 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
 {
        int i;
        float *v, *c, f1, f2, diff[3], vertex3f[8*3], color4f[8*4];
-       rmeshstate_t m;
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_DepthMask(false);
        GL_DepthTest(true);
@@ -1843,10 +1979,9 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
                        c[2] = c[2] * f1 + fogcolor[2] * f2;
                }
        }
-       memset(&m, 0, sizeof(m));
-       m.pointer_vertex = vertex3f;
-       m.pointer_color = color;
-       R_Mesh_State(&m);
+       R_Mesh_VertexPointer(vertex3f);
+       R_Mesh_ColorPointer(color);
+       R_Mesh_ResetTextureState();
        R_Mesh_Draw(8, 12);
 }
 */
@@ -1888,12 +2023,8 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, int surfacenu
        int i;
        float f1, f2, *c;
        float color4f[6*4];
-       rmeshstate_t m;
        R_Mesh_Matrix(&ent->matrix);
 
-       memset(&m, 0, sizeof(m));
-       m.pointer_vertex = nomodelvertex3f;
-
        if (ent->flags & EF_ADDITIVE)
        {
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
@@ -1910,10 +2041,11 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, int surfacenu
                GL_DepthMask(true);
        }
        GL_DepthTest(!(ent->effects & EF_NODEPTHTEST));
+       R_Mesh_VertexPointer(nomodelvertex3f);
        if (fogenabled)
        {
                memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-               m.pointer_color = color4f;
+               R_Mesh_ColorPointer(color4f);
                f2 = VERTEXFOGTABLE(VectorDistance(ent->origin, r_vieworigin));
                f1 = 1 - f2;
                for (i = 0, c = color4f;i < 6;i++, c += 4)
@@ -1927,13 +2059,13 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, int surfacenu
        else if (ent->alpha != 1)
        {
                memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-               m.pointer_color = color4f;
+               R_Mesh_ColorPointer(color4f);
                for (i = 0, c = color4f;i < 6;i++, c += 4)
                        c[3] *= ent->alpha;
        }
        else
-               m.pointer_color = nomodelcolor4f;
-       R_Mesh_State(&m);
+               R_Mesh_ColorPointer(nomodelcolor4f);
+       R_Mesh_ResetTextureState();
        R_Mesh_Draw(0, 6, 8, nomodelelements);
 }
 
@@ -1980,7 +2112,7 @@ float spritetexcoord2f[4*2] = {0, 1, 0, 0, 1, 0, 1, 1};
 void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_t *fogtexture, int depthdisable, const vec3_t origin, const vec3_t left, const vec3_t up, float scalex1, float scalex2, float scaley1, float scaley2, float cr, float cg, float cb, float ca)
 {
        float fog = 0.0f, ifog;
-       rmeshstate_t m;
+       float vertex3f[12];
 
        if (fogenabled)
                fog = VERTEXFOGTABLE(VectorDistance(origin, r_vieworigin));
@@ -1991,24 +2123,24 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        GL_DepthMask(false);
        GL_DepthTest(!depthdisable);
 
-       varray_vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
-       varray_vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
-       varray_vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
-       varray_vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
-       varray_vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
-       varray_vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
-       varray_vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
-       varray_vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
-       varray_vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
-       varray_vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
-       varray_vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
-       varray_vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
-
-       memset(&m, 0, sizeof(m));
-       m.tex[0] = R_GetTexture(texture);
-       m.pointer_texcoord[0] = spritetexcoord2f;
-       m.pointer_vertex = varray_vertex3f;
-       R_Mesh_State(&m);
+       vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
+       vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
+       vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
+       vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
+       vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
+       vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
+       vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
+       vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
+       vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
+       vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
+       vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
+       vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
+
+       R_Mesh_VertexPointer(vertex3f);
+       R_Mesh_ColorPointer(NULL);
+       R_Mesh_ResetTextureState();
+       R_Mesh_TexBind(0, R_GetTexture(texture));
+       R_Mesh_TexCoordPointer(0, 2, spritetexcoord2f);
        GL_Color(cr * ifog, cg * ifog, cb * ifog, ca);
        R_Mesh_Draw(0, 4, 2, polygonelements);
 
@@ -2088,6 +2220,30 @@ void R_Mesh_AddBrushMeshFromPlanes(rmesh_t *mesh, int numplanes, mplane_t *plane
        }
 }
 
+static void R_DrawCollisionBrush(const colbrushf_t *brush)
+{
+       int i;
+       R_Mesh_VertexPointer(brush->points->v);
+       i = (int)(((size_t)brush) / sizeof(colbrushf_t));
+       GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+       GL_LockArrays(0, brush->numpoints);
+       R_Mesh_Draw(0, brush->numpoints, brush->numtriangles, brush->elements);
+       GL_LockArrays(0, 0);
+}
+
+static void R_DrawCollisionSurface(const entity_render_t *ent, const msurface_t *surface)
+{
+       int i;
+       if (!surface->num_collisiontriangles)
+               return;
+       R_Mesh_VertexPointer(surface->data_collisionvertex3f);
+       i = (int)(((size_t)surface) / sizeof(msurface_t));
+       GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+       GL_LockArrays(0, surface->num_collisionvertices);
+       R_Mesh_Draw(0, surface->num_collisionvertices, surface->num_collisiontriangles, surface->data_collisionelement3i);
+       GL_LockArrays(0, 0);
+}
+
 static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1, int blendfunc2, texturelayertype_t type, rtexture_t *texture, const matrix4x4_t *matrix, float r, float g, float b, float a)
 {
        texturelayer_t *layer;
@@ -2136,52 +2292,69 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
        if (!(ent->flags & RENDER_LIGHT))
                t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
        if (ent->effects & EF_ADDITIVE)
-               t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
+               t->currentmaterialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
        else if (t->currentalpha < 1)
-               t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
+               t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
        if (ent->effects & EF_NODEPTHTEST)
                t->currentmaterialflags |= MATERIALFLAG_NODEPTHTEST;
        if (t->currentmaterialflags & MATERIALFLAG_WATER && r_waterscroll.value != 0)
                t->currenttexmatrix = r_waterscrollmatrix;
        else
                t->currenttexmatrix = identitymatrix;
+
+       t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
+       t->basetexture = (!t->colormapping && t->skin.merged) ? t->skin.merged : t->skin.base;
+       t->glosstexture = r_texture_white;
+       t->specularpower = 8;
+       t->specularscale = 0;
+       if (r_shadow_gloss.integer > 0)
+       {
+               if (t->skin.gloss)
+               {
+                       if (r_shadow_glossintensity.value > 0)
+                       {
+                               t->glosstexture = t->skin.gloss;
+                               t->specularscale = r_shadow_glossintensity.value;
+                       }
+               }
+               else if (r_shadow_gloss.integer >= 2 && r_shadow_gloss2intensity.value > 0)
+                       t->specularscale = r_shadow_gloss2intensity.value;
+       }
+
        t->currentnumlayers = 0;
        if (!(t->currentmaterialflags & MATERIALFLAG_NODRAW))
        {
                if (gl_lightmaps.integer)
                        R_Texture_AddLayer(t, true, GL_ONE, GL_ZERO, TEXTURELAYERTYPE_LITTEXTURE_MULTIPASS, r_texture_white, &identitymatrix, 1, 1, 1, 1);
-               else if (t->currentmaterialflags & MATERIALFLAG_SKY)
-               {
-                       // transparent sky would be ridiculous
-                       if (!(t->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
-                               R_Texture_AddLayer(t, true, GL_ONE, GL_ZERO, TEXTURELAYERTYPE_SKY, r_texture_white, &identitymatrix, fogcolor[0], fogcolor[1], fogcolor[2], 1);
-               }
-               else
+               else if (!(t->currentmaterialflags & MATERIALFLAG_SKY))
                {
                        int blendfunc1, blendfunc2, depthmask;
                        if (t->currentmaterialflags & MATERIALFLAG_ADD)
                        {
                                blendfunc1 = GL_SRC_ALPHA;
                                blendfunc2 = GL_ONE;
-                               depthmask = false;
                        }
                        else if (t->currentmaterialflags & MATERIALFLAG_ALPHA)
                        {
                                blendfunc1 = GL_SRC_ALPHA;
                                blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
-                               depthmask = false;
+                       }
+                       else if (t->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
+                       {
+                               blendfunc1 = t->customblendfunc[0];
+                               blendfunc2 = t->customblendfunc[1];
                        }
                        else
                        {
                                blendfunc1 = GL_ONE;
                                blendfunc2 = GL_ZERO;
-                               depthmask = true;
                        }
+                       depthmask = !(t->currentmaterialflags & MATERIALFLAG_BLENDED);
                        if (t->currentmaterialflags & (MATERIALFLAG_WATER | MATERIALFLAG_WALL))
                        {
                                rtexture_t *currentbasetexture;
                                int layerflags = 0;
-                               if (fogenabled && (t->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                               if (fogenabled && (t->currentmaterialflags & MATERIALFLAG_BLENDED))
                                        layerflags |= TEXTURELAYERFLAG_FOGDARKEN;
                                currentbasetexture = (VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) < (1.0f / 1048576.0f) && t->skin.merged) ? t->skin.merged : t->skin.base;
                                if (t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
@@ -2198,14 +2371,14 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                                        float colorscale;
                                        colorscale = 2;
                                        // q3bsp has no lightmap updates, so the lightstylevalue that
-                                       // would normally be baked into the lightmaptexture must be
+                                       // would normally be baked into the lightmap must be
                                        // applied to the color
                                        if (ent->model->type == mod_brushq3)
                                                colorscale *= r_refdef.lightstylevalue[0] * (1.0f / 256.0f);
                                        colorscale *= r_lightmapintensity;
                                        if (r_textureunits.integer >= 2 && gl_combine.integer)
                                                R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_LITTEXTURE_COMBINE, currentbasetexture, &t->currenttexmatrix, ent->colormod[0] * colorscale, ent->colormod[1] * colorscale, ent->colormod[2] * colorscale, t->currentalpha);
-                                       else if ((t->currentmaterialflags & MATERIALFLAG_TRANSPARENT) == 0)
+                                       else if ((t->currentmaterialflags & MATERIALFLAG_BLENDED) == 0)
                                                R_Texture_AddLayer(t, true, GL_ONE, GL_ZERO, TEXTURELAYERTYPE_LITTEXTURE_MULTIPASS, currentbasetexture, &t->currenttexmatrix, ent->colormod[0] * colorscale * 0.5f, ent->colormod[1] * colorscale * 0.5f, ent->colormod[2] * colorscale * 0.5f, t->currentalpha);
                                        else
                                                R_Texture_AddLayer(t, depthmask, blendfunc1, blendfunc2, TEXTURELAYERTYPE_LITTEXTURE_VERTEX, currentbasetexture, &t->currenttexmatrix, ent->colormod[0] * colorscale, ent->colormod[1] * colorscale, ent->colormod[2] * colorscale, t->currentalpha);
@@ -2239,7 +2412,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                                        // were darkened by fog already, and we should not add fog color
                                        // (because the background was not darkened, there is no fog color
                                        // that was lost behind it).
-                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, (t->currentmaterialflags & MATERIALFLAG_TRANSPARENT) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA, TEXTURELAYERTYPE_FOG, t->skin.fog, &identitymatrix, fogcolor[0], fogcolor[1], fogcolor[2], t->currentalpha);
+                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, (t->currentmaterialflags & MATERIALFLAG_BLENDED) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA, TEXTURELAYERTYPE_FOG, t->skin.fog, &identitymatrix, fogcolor[0], fogcolor[1], fogcolor[2], t->currentalpha);
                                }
                        }
                }
@@ -2254,116 +2427,222 @@ void R_UpdateAllTextureInfo(entity_render_t *ent)
                        R_UpdateTextureInfo(ent, ent->model->data_textures + i);
 }
 
+int rsurface_array_size = 0;
+float *rsurface_array_modelvertex3f = NULL;
+float *rsurface_array_modelsvector3f = NULL;
+float *rsurface_array_modeltvector3f = NULL;
+float *rsurface_array_modelnormal3f = NULL;
+float *rsurface_array_deformedvertex3f = NULL;
+float *rsurface_array_deformedsvector3f = NULL;
+float *rsurface_array_deformedtvector3f = NULL;
+float *rsurface_array_deformednormal3f = NULL;
+float *rsurface_array_color4f = NULL;
+float *rsurface_array_texcoord3f = NULL;
+
+void R_Mesh_ResizeArrays(int newvertices)
+{
+       float *base;
+       if (rsurface_array_size >= newvertices)
+               return;
+       if (rsurface_array_modelvertex3f)
+               Mem_Free(rsurface_array_modelvertex3f);
+       rsurface_array_size = (newvertices + 1023) & ~1023;
+       base = (float *)Mem_Alloc(r_main_mempool, rsurface_array_size * sizeof(float[31]));
+       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;
+}
+
+float *rsurface_modelvertex3f;
+float *rsurface_modelsvector3f;
+float *rsurface_modeltvector3f;
+float *rsurface_modelnormal3f;
 float *rsurface_vertex3f;
 float *rsurface_svector3f;
 float *rsurface_tvector3f;
 float *rsurface_normal3f;
 float *rsurface_lightmapcolor4f;
+vec3_t rsurface_modelorg;
+qboolean rsurface_generatedvertex;
+const entity_render_t *rsurface_entity;
+const model_t *rsurface_model;
+texture_t *rsurface_texture;
+rtexture_t *rsurface_lightmaptexture;
+rsurfmode_t rsurface_mode;
+texture_t *rsurface_glsl_texture;
+
+void RSurf_ActiveEntity(const entity_render_t *ent)
+{
+       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, rsurface_modelorg);
+       rsurface_entity = ent;
+       rsurface_model = ent->model;
+       if (rsurface_array_size < rsurface_model->surfmesh.num_vertices)
+               R_Mesh_ResizeArrays(rsurface_model->surfmesh.num_vertices);
+       R_Mesh_Matrix(&ent->matrix);
+       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, rsurface_modelorg);
+       if ((rsurface_entity->frameblend[0].lerp != 1 || rsurface_entity->frameblend[0].frame != 0) && (rsurface_model->surfmesh.data_morphvertex3f || rsurface_model->surfmesh.data_vertexboneweights))
+       {
+               rsurface_modelvertex3f = rsurface_array_modelvertex3f;
+               rsurface_modelsvector3f = NULL;
+               rsurface_modeltvector3f = NULL;
+               rsurface_modelnormal3f = NULL;
+               Mod_Alias_GetMesh_Vertex3f(rsurface_model, rsurface_entity->frameblend, rsurface_array_modelvertex3f);
+               rsurface_generatedvertex = true;
+       }
+       else
+       {
+               rsurface_modelvertex3f  = rsurface_model->surfmesh.data_vertex3f;
+               rsurface_modelsvector3f = rsurface_model->surfmesh.data_svector3f;
+               rsurface_modeltvector3f = rsurface_model->surfmesh.data_tvector3f;
+               rsurface_modelnormal3f  = rsurface_model->surfmesh.data_normal3f;
+               rsurface_generatedvertex = false;
+       }
+       rsurface_vertex3f  = rsurface_modelvertex3f;
+       rsurface_svector3f = rsurface_modelsvector3f;
+       rsurface_tvector3f = rsurface_modeltvector3f;
+       rsurface_normal3f  = rsurface_modelnormal3f;
+       rsurface_mode = RSURFMODE_NONE;
+}
 
-void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg, qboolean generatenormals, qboolean generatetangents)
+void RSurf_CleanUp(void)
 {
-       if ((ent->frameblend[0].lerp != 1 || ent->frameblend[0].frame != 0) && (surface->groupmesh->data_morphvertex3f || surface->groupmesh->data_vertexboneweights))
+       if (rsurface_mode == RSURFMODE_GLSL)
+               qglUseProgramObjectARB(0);
+       GL_AlphaTest(false);
+       rsurface_mode = RSURFMODE_NONE;
+       rsurface_lightmaptexture = NULL;
+       rsurface_texture = NULL;
+       rsurface_glsl_texture = NULL;
+}
+
+void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, msurface_t **texturesurfacelist)
+{
+       if (rsurface_generatedvertex)
        {
-               rsurface_vertex3f = varray_vertex3f;
-               Mod_Alias_GetMesh_Vertex3f(ent->model, ent->frameblend, surface->groupmesh, rsurface_vertex3f);
-               if (generatetangents || (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2)))
+               if (rsurface_texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+                       generatetangents = true;
+               if (generatetangents)
+                       generatenormals = true;
+               if (generatenormals && !rsurface_modelnormal3f)
                {
-                       rsurface_svector3f = varray_svector3f;
-                       rsurface_tvector3f = varray_tvector3f;
-                       rsurface_normal3f = varray_normal3f;
-                       Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
+                       rsurface_normal3f = rsurface_modelnormal3f = rsurface_array_modelnormal3f;
+                       Mod_BuildNormals(0, rsurface_model->surfmesh.num_vertices, rsurface_model->surfmesh.num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_element3i, rsurface_array_modelnormal3f, r_smoothnormals_areaweighting.integer);
                }
-               else
+               if (generatetangents && !rsurface_modelsvector3f)
                {
-                       rsurface_svector3f = NULL;
-                       rsurface_tvector3f = NULL;
-                       if (generatenormals)
-                       {
-                               rsurface_normal3f = varray_normal3f;
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
-                       }
-                       else
-                               rsurface_normal3f = NULL;
+                       rsurface_svector3f = rsurface_modelsvector3f = rsurface_array_modelsvector3f;
+                       rsurface_tvector3f = rsurface_modeltvector3f = rsurface_array_modeltvector3f;
+                       Mod_BuildTextureVectorsFromNormals(0, rsurface_model->surfmesh.num_vertices, rsurface_model->surfmesh.num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_texcoordtexture2f, rsurface_modelnormal3f, rsurface_model->surfmesh.data_element3i, rsurface_array_modelsvector3f, rsurface_array_modeltvector3f, r_smoothnormals_areaweighting.integer);
                }
        }
-       else
-       {
-               rsurface_vertex3f = surface->groupmesh->data_vertex3f;
-               rsurface_svector3f = surface->groupmesh->data_svector3f;
-               rsurface_tvector3f = surface->groupmesh->data_tvector3f;
-               rsurface_normal3f = surface->groupmesh->data_normal3f;
-       }
-       if (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+       if (rsurface_texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
        {
-               int i, j;
+               int texturesurfaceindex;
                float center[3], forward[3], right[3], up[3], v[4][3];
                matrix4x4_t matrix1, imatrix1;
-               Matrix4x4_Transform(&ent->inversematrix, r_viewforward, forward);
-               Matrix4x4_Transform(&ent->inversematrix, r_viewright, right);
-               Matrix4x4_Transform(&ent->inversematrix, r_viewup, up);
-               // a single autosprite surface can contain multiple sprites...
-               for (j = 0;j < surface->num_vertices - 3;j += 4)
+               Matrix4x4_Transform(&rsurface_entity->inversematrix, r_viewforward, forward);
+               Matrix4x4_Transform(&rsurface_entity->inversematrix, r_viewright, right);
+               Matrix4x4_Transform(&rsurface_entity->inversematrix, r_viewup, up);
+               // make deformed versions of only the vertices used by the specified surfaces
+               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                {
-                       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);
-                       // FIXME: calculate vectors from triangle edges instead of using texture vectors as an easy way out?
-                       Matrix4x4_FromVectors(&matrix1, (rsurface_normal3f + 3 * surface->num_firstvertex) + j*3, (rsurface_svector3f + 3 * surface->num_firstvertex) + j*3, (rsurface_tvector3f + 3 * surface->num_firstvertex) + j*3, center);
-                       Matrix4x4_Invert_Simple(&imatrix1, &matrix1);
-                       for (i = 0;i < 4;i++)
-                               Matrix4x4_Transform(&imatrix1, (rsurface_vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, v[i]);
-                       if (texture->textureflags & Q3TEXTUREFLAG_AUTOSPRITE2)
+                       int i, j;
+                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                       // a single autosprite surface can contain multiple sprites...
+                       for (j = 0;j < surface->num_vertices - 3;j += 4)
                        {
-                               forward[0] = modelorg[0] - center[0];
-                               forward[1] = modelorg[1] - center[1];
-                               forward[2] = 0;
-                               VectorNormalize(forward);
-                               right[0] = forward[1];
-                               right[1] = -forward[0];
-                               right[2] = 0;
-                               VectorSet(up, 0, 0, 1);
+                               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);
+                               if (rsurface_texture->textureflags & Q3TEXTUREFLAG_AUTOSPRITE2)
+                               {
+                                       forward[0] = rsurface_modelorg[0] - center[0];
+                                       forward[1] = rsurface_modelorg[1] - center[1];
+                                       forward[2] = 0;
+                                       VectorNormalize(forward);
+                                       right[0] = forward[1];
+                                       right[1] = -forward[0];
+                                       right[2] = 0;
+                                       VectorSet(up, 0, 0, 1);
+                               }
+                               // FIXME: calculate vectors from triangle edges instead of using texture vectors as an easy way out?
+                               Matrix4x4_FromVectors(&matrix1, (rsurface_normal3f + 3 * surface->num_firstvertex) + j*3, (rsurface_svector3f + 3 * surface->num_firstvertex) + j*3, (rsurface_tvector3f + 3 * surface->num_firstvertex) + j*3, center);
+                               Matrix4x4_Invert_Simple(&imatrix1, &matrix1);
+                               for (i = 0;i < 4;i++)
+                                       Matrix4x4_Transform(&imatrix1, (rsurface_vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, v[i]);
+                               for (i = 0;i < 4;i++)
+                                       VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, rsurface_array_modelvertex3f + (surface->num_firstvertex+i+j) * 3);
                        }
-                       for (i = 0;i < 4;i++)
-                               VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, varray_vertex3f + (surface->num_firstvertex+i+j) * 3);
+                       Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3, rsurface_array_deformednormal3f, r_smoothnormals_areaweighting.integer);
+                       Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_texcoordtexture2f, rsurface_array_deformednormal3f, rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3, rsurface_array_deformedsvector3f, rsurface_array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
                }
-               rsurface_vertex3f = varray_vertex3f;
-               rsurface_svector3f = varray_svector3f;
-               rsurface_tvector3f = varray_tvector3f;
-               rsurface_normal3f = varray_normal3f;
-               Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
+               rsurface_vertex3f = rsurface_array_deformedvertex3f;
+               rsurface_svector3f = rsurface_array_deformedsvector3f;
+               rsurface_tvector3f = rsurface_array_deformedtvector3f;
+               rsurface_normal3f = rsurface_array_deformednormal3f;
        }
        R_Mesh_VertexPointer(rsurface_vertex3f);
 }
 
-static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg, float r, float g, float b, float a, int lightmode, qboolean applycolor, qboolean applyfog)
+static void RSurf_Draw(const msurface_t *surface)
+{
+       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+}
+
+static void RSurf_DrawLightmap(const msurface_t *surface, float r, float g, float b, float a, int lightmode, qboolean applycolor, qboolean applyfog)
 {
        int i;
        float f;
        float *v, *c, *c2;
-       RSurf_SetVertexPointer(ent, texture, surface, modelorg, lightmode == 2, false);
        if (lightmode >= 2)
        {
                // model lighting
-               vec4_t ambientcolor4f;
+               vec3_t ambientcolor;
                vec3_t diffusecolor;
-               vec3_t diffusenormal;
-               if (R_LightModel(ambientcolor4f, diffusecolor, diffusenormal, ent, r*0.5f, g*0.5f, b*0.5f, a, false))
+               vec3_t lightdir;
+               VectorCopy(rsurface_entity->modellight_lightdir, lightdir);
+               ambientcolor[0] = rsurface_entity->modellight_ambient[0] * r * 0.5f;
+               ambientcolor[1] = rsurface_entity->modellight_ambient[1] * g * 0.5f;
+               ambientcolor[2] = rsurface_entity->modellight_ambient[2] * b * 0.5f;
+               diffusecolor[0] = rsurface_entity->modellight_diffuse[0] * r * 0.5f;
+               diffusecolor[1] = rsurface_entity->modellight_diffuse[1] * g * 0.5f;
+               diffusecolor[2] = rsurface_entity->modellight_diffuse[2] * b * 0.5f;
+               if (VectorLength2(diffusecolor) > 0)
                {
-                       rsurface_lightmapcolor4f = varray_color4f;
-                       R_LightModel_CalcVertexColors(ambientcolor4f, diffusecolor, diffusenormal, surface->groupmesh->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, rsurface_lightmapcolor4f + 4 * surface->num_firstvertex);
+                       int numverts = surface->num_vertices;
+                       v = rsurface_vertex3f + 3 * surface->num_firstvertex;
+                       c2 = 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, c2 += 3, c += 4)
+                       {
+                               if ((f = DotProduct(c2, lightdir)) > 0)
+                                       VectorMA(ambientcolor, f, diffusecolor, c);
+                               else
+                                       VectorCopy(ambientcolor, c);
+                               c[3] = a;
+                       }
                        r = 1;
                        g = 1;
                        b = 1;
                        a = 1;
                        applycolor = false;
+                       rsurface_lightmapcolor4f = rsurface_array_color4f;
                }
                else
                {
-                       r = ambientcolor4f[0];
-                       g = ambientcolor4f[1];
-                       b = ambientcolor4f[2];
-                       a = ambientcolor4f[3];
+                       r = ambientcolor[0];
+                       g = ambientcolor[1];
+                       b = ambientcolor[2];
                        rsurface_lightmapcolor4f = NULL;
                }
        }
@@ -2371,11 +2650,11 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
        {
                if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
                {
-                       for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+                       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 + (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i];
+                                       const unsigned char *lm = surface->lightmapinfo->samples + (rsurface_model->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i];
                                        float scale = r_refdef.lightstylevalue[surface->lightmapinfo->styles[0]] * (1.0f / 32768.0f);
                                        VectorScale(lm, scale, c);
                                        if (surface->lightmapinfo->styles[1] != 255)
@@ -2401,10 +2680,10 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                                else
                                        VectorClear(c);
                        }
-                       rsurface_lightmapcolor4f = varray_color4f;
+                       rsurface_lightmapcolor4f = rsurface_array_color4f;
                }
                else
-                       rsurface_lightmapcolor4f = surface->groupmesh->data_lightmapcolor4f;
+                       rsurface_lightmapcolor4f = rsurface_model->surfmesh.data_lightmapcolor4f;
        }
        else
                rsurface_lightmapcolor4f = NULL;
@@ -2412,9 +2691,9 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
        {
                if (rsurface_lightmapcolor4f)
                {
-                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
+                       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 = 1 - VERTEXFOGTABLE(VectorDistance(v, modelorg));
+                               f = 1 - VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                c2[0] = c[0] * f;
                                c2[1] = c[1] * f;
                                c2[2] = c[2] * f;
@@ -2423,66 +2702,208 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                }
                else
                {
-                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
+                       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 = 1 - VERTEXFOGTABLE(VectorDistance(v, modelorg));
+                               f = 1 - VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                c2[0] = f;
                                c2[1] = f;
                                c2[2] = f;
                                c2[3] = 1;
                        }
                }
-               rsurface_lightmapcolor4f = varray_color4f;
+               rsurface_lightmapcolor4f = rsurface_array_color4f;
        }
        if (applycolor && rsurface_lightmapcolor4f)
        {
-               for (i = 0, c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
+               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;
                }
-               rsurface_lightmapcolor4f = varray_color4f;
+               rsurface_lightmapcolor4f = rsurface_array_color4f;
        }
        R_Mesh_ColorPointer(rsurface_lightmapcolor4f);
        GL_Color(r, g, b, a);
-       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
-       GL_LockArrays(0, 0);
-}
-
-static void RSurf_Draw(const msurface_t *surface)
-{
-       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
-       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
-       GL_LockArrays(0, 0);
+       RSurf_Draw(surface);
 }
 
-static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *texture, int texturenumsurfaces, const msurface_t **texturesurfacelist, const vec3_t modelorg)
+static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
        int lightmode;
-       const msurface_t *surface;
        qboolean applycolor;
        qboolean applyfog;
        rmeshstate_t m;
-       if (texture->currentmaterialflags & MATERIALFLAG_NODRAW)
+       if (rsurface_texture->currentmaterialflags & MATERIALFLAG_NODRAW)
                return;
+       r_shadow_rtlight = NULL;
        renderstats.entities_surfaces += texturenumsurfaces;
-       // FIXME: identify models using a better check than ent->model->brush.shadowmesh
-       lightmode = ((ent->effects & EF_FULLBRIGHT) || ent->model->brush.shadowmesh) ? 0 : 2;
-       GL_DepthTest(!(texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
-       if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
+       // FIXME: identify models using a better check than rsurface_model->brush.shadowmesh
+       lightmode = ((rsurface_entity->effects & EF_FULLBRIGHT) || rsurface_model->brush.shadowmesh) ? 0 : 2;
+       GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
+       if ((rsurface_texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (rsurface_entity->flags & RENDER_NOCULLFACE))
                qglDisable(GL_CULL_FACE);
-       if (texture->currentnumlayers)
+       if (r_showsurfaces.integer)
+       {
+               if (rsurface_mode != RSURFMODE_SHOWSURFACES)
+               {
+                       rsurface_mode = RSURFMODE_SHOWSURFACES;
+                       GL_DepthMask(true);
+                       GL_BlendFunc(GL_ONE, GL_ZERO);
+                       R_Mesh_ColorPointer(NULL);
+                       R_Mesh_ResetTextureState();
+               }
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               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), ((k >> 4) & 15) * (1.0f / 16.0f), ((k >> 8) & 15) * (1.0f / 16.0f), 0.2f);
+                       RSurf_Draw(surface);
+               }
+       }
+       else if (rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY)
+       {
+               // transparent sky would be ridiculous
+               if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+               {
+                       if (rsurface_mode != RSURFMODE_SKY)
+                       {
+                               if (rsurface_mode == RSURFMODE_GLSL)
+                                       qglUseProgramObjectARB(0);
+                               rsurface_mode = RSURFMODE_SKY;
+                               if (skyrendernow)
+                               {
+                                       skyrendernow = false;
+                                       R_Sky();
+                                       // restore entity matrix
+                                       R_Mesh_Matrix(&rsurface_entity->matrix);
+                               }
+                               GL_DepthMask(true);
+                               // LordHavoc: HalfLife maps have freaky skypolys so don't use
+                               // skymasking on them, and Quake3 never did sky masking (unlike
+                               // software Quake and software Quake2), so disable the sky masking
+                               // in Quake3 maps as it causes problems with q3map2 sky tricks,
+                               // and skymasking also looks very bad when noclipping outside the
+                               // level, so don't use it then either.
+                               if (rsurface_model->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_worldnovis)
+                               {
+                                       GL_Color(fogcolor[0], fogcolor[1], fogcolor[2], 1);
+                                       R_Mesh_ColorPointer(NULL);
+                                       R_Mesh_ResetTextureState();
+                                       if (skyrendermasked)
+                                       {
+                                               // depth-only (masking)
+                                               GL_ColorMask(0,0,0,0);
+                                               // just to make sure that braindead drivers don't draw
+                                               // anything despite that colormask...
+                                               GL_BlendFunc(GL_ZERO, GL_ONE);
+                                       }
+                                       else
+                                       {
+                                               // fog sky
+                                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                                       }
+                               }
+                       }
+                       // LordHavoc: HalfLife maps have freaky skypolys so don't use
+                       // skymasking on them, and Quake3 never did sky masking (unlike
+                       // software Quake and software Quake2), so disable the sky masking
+                       // in Quake3 maps as it causes problems with q3map2 sky tricks,
+                       // and skymasking also looks very bad when noclipping outside the
+                       // level, so don't use it then either.
+                       if (rsurface_model->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_worldnovis)
+                       {
+                               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               {
+                                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                       RSurf_Draw(surface);
+                               }
+                               if (skyrendermasked)
+                                       GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
+                       }
+               }
+       }
+       else if (rsurface_texture->currentnumlayers && r_glsl.integer && gl_support_fragment_shader)
+       {
+               if (rsurface_mode != RSURFMODE_GLSL)
+               {
+                       rsurface_mode = RSURFMODE_GLSL;
+                       rsurface_glsl_texture = NULL;
+                       R_Mesh_ResetTextureState();
+               }
+               if (rsurface_glsl_texture != rsurface_texture)
+               {
+                       rsurface_glsl_texture = rsurface_texture;
+                       GL_BlendFunc(rsurface_texture->currentlayers[0].blendfunc1, rsurface_texture->currentlayers[0].blendfunc2);
+                       GL_DepthMask(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_BLENDED));
+                       GL_Color(rsurface_entity->colormod[0], rsurface_entity->colormod[1], rsurface_entity->colormod[2], rsurface_texture->currentalpha);
+                       R_SetupSurfaceShader(vec3_origin, lightmode == 2);
+                       //permutation_deluxemapping = permutation_lightmapping = R_SetupSurfaceShader(vec3_origin, lightmode == 2, false);
+                       //if (r_glsl_deluxemapping.integer)
+                       //      permutation_deluxemapping = R_SetupSurfaceShader(vec3_origin, lightmode == 2, true);
+                       R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
+                       if (lightmode != 2)
+                               R_Mesh_TexCoordPointer(4, 2, rsurface_model->surfmesh.data_texcoordlightmap2f);
+                       GL_AlphaTest((rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+               }
+               if (!r_glsl_permutation)
+                       return;
+               RSurf_PrepareVerticesForBatch(true, true, texturenumsurfaces, texturesurfacelist);
+               R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
+               R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
+               R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
+               if (lightmode != 2)
+               {
+                       if (rsurface_lightmaptexture)
+                       {
+                               R_Mesh_TexBind(7, R_GetTexture(rsurface_lightmaptexture));
+                               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
+                                       R_Mesh_TexBind(8, R_GetTexture(texturesurfacelist[0]->deluxemaptexture));
+                               R_Mesh_ColorPointer(NULL);
+                       }
+                       else
+                       {
+                               R_Mesh_TexBind(7, R_GetTexture(r_texture_white));
+                               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
+                                       R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
+                               R_Mesh_ColorPointer(rsurface_model->surfmesh.data_lightmapcolor4f);
+                       }
+               }
+               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               {
+                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                       RSurf_Draw(surface);
+               }
+       }
+       else if (rsurface_texture->currentnumlayers)
        {
                int layerindex;
-               texturelayer_t *layer;
-               for (layerindex = 0, layer = texture->currentlayers;layerindex < texture->currentnumlayers;layerindex++, layer++)
+               const texturelayer_t *layer;
+               if (rsurface_mode != RSURFMODE_MULTIPASS)
+               {
+                       if (rsurface_mode == RSURFMODE_GLSL)
+                               qglUseProgramObjectARB(0);
+                       rsurface_mode = RSURFMODE_MULTIPASS;
+               }
+               RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+               for (layerindex = 0, layer = rsurface_texture->currentlayers;layerindex < rsurface_texture->currentnumlayers;layerindex++, layer++)
                {
                        vec4_t layercolor;
                        int layertexrgbscale;
+                       if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
+                       {
+                               if (layerindex == 0)
+                                       GL_AlphaTest(true);
+                               else
+                               {
+                                       GL_AlphaTest(false);
+                                       qglDepthFunc(GL_EQUAL);
+                               }
+                       }
                        GL_DepthMask(layer->depthmask);
                        GL_BlendFunc(layer->blendfunc1, layer->blendfunc2);
                        if ((layer->color[0] > 2 || layer->color[1] > 2 || layer->color[2] > 2) && (gl_combine.integer || layer->depthmask))
@@ -2501,86 +2922,45 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                VectorScale(layer->color, 1.0f, layercolor);
                        }
                        layercolor[3] = layer->color[3];
+                       R_Mesh_ColorPointer(NULL);
                        GL_Color(layercolor[0], layercolor[1], layercolor[2], layercolor[3]);
                        applycolor = layercolor[0] != 1 || layercolor[1] != 1 || layercolor[2] != 1 || layercolor[3] != 1;
                        applyfog = (layer->flags & TEXTURELAYERFLAG_FOGDARKEN) != 0;
                        switch (layer->type)
                        {
-                       case TEXTURELAYERTYPE_SKY:
-                               if (skyrendernow)
-                               {
-                                       skyrendernow = false;
-                                       if (skyrendermasked)
-                                       {
-                                               R_Sky();
-                                               // restore entity matrix and GL_Color
-                                               R_Mesh_Matrix(&ent->matrix);
-                                               GL_Color(layercolor[0], layercolor[1], layercolor[2], layercolor[3]);
-                                       }
-                               }
-                               // LordHavoc: HalfLife maps have freaky skypolys...
-                               //if (!ent->model->brush.ishlbsp)
-                               {
-                                       if (skyrendermasked)
-                                       {
-                                               // depth-only (masking)
-                                               GL_ColorMask(0,0,0,0);
-                                               // just to make sure that braindead drivers don't draw anything
-                                               // despite that colormask...
-                                               GL_BlendFunc(GL_ZERO, GL_ONE);
-                                       }
-                                       else
-                                       {
-                                               // fog sky
-                                               GL_BlendFunc(GL_ONE, GL_ZERO);
-                                       }
-                                       memset(&m, 0, sizeof(m));
-                                       R_Mesh_State(&m);
-                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                                       {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               RSurf_SetVertexPointer(ent, texture, surface, modelorg, false, false);
-                                               RSurf_Draw(surface);
-                                       }
-                                       if (skyrendermasked)
-                                               GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
-                               }
-                               break;
                        case TEXTURELAYERTYPE_LITTEXTURE_COMBINE:
                                memset(&m, 0, sizeof(m));
+                               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordlightmap2f;
                                m.tex[1] = R_GetTexture(layer->texture);
                                m.texmatrix[1] = layer->texmatrix;
                                m.texrgbscale[1] = layertexrgbscale;
-                               m.pointer_color = varray_color4f;
-                               R_Mesh_State(&m);
+                               m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
+                               R_Mesh_TextureState(&m);
                                if (lightmode == 2)
                                {
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
-                                               R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                                R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
-                                               RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 2, applycolor, applyfog);
+                                               RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 2, applycolor, applyfog);
+                                       }
+                               }
+                               else if (rsurface_lightmaptexture)
+                               {
+                                       R_Mesh_TexBind(0, R_GetTexture(rsurface_lightmaptexture));
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, applyfog);
                                        }
                                }
                                else
                                {
+                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
-                                               R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
-                                               if (surface->lightmaptexture)
-                                               {
-                                                       R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
-                                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, applyfog);
-                                               }
-                                               else
-                                               {
-                                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
-                                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 1, applycolor, applyfog);
-                                               }
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 1, applycolor, applyfog);
                                        }
                                }
                                break;
@@ -2588,49 +2968,48 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
-                               R_Mesh_State(&m);
+                               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordlightmap2f;
+                               R_Mesh_TextureState(&m);
                                if (lightmode == 2)
                                {
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                                R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
-                                               RSurf_DrawLightmap(ent, texture, surface, modelorg, 1, 1, 1, 1, 2, false, false);
+                                               RSurf_DrawLightmap(surface, 1, 1, 1, 1, 2, false, false);
+                                       }
+                               }
+                               else if (rsurface_lightmaptexture)
+                               {
+                                       R_Mesh_TexBind(0, R_GetTexture(rsurface_lightmaptexture));
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, 1, 1, 1, 1, 0, false, false);
                                        }
                                }
                                else
                                {
+                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
-                                               if (surface->lightmaptexture)
-                                               {
-                                                       R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
-                                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, 1, 1, 1, 1, 0, false, false);
-                                               }
-                                               else
-                                               {
-                                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
-                                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, 1, 1, 1, 1, 1, false, false);
-                                               }
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, 1, 1, 1, 1, 1, false, false);
                                        }
                                }
+                               GL_LockArrays(0, 0);
                                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
-                               R_Mesh_State(&m);
+                               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+                               R_Mesh_TextureState(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, applyfog);
+                                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                       RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, false);
                                }
                                break;
                        case TEXTURELAYERTYPE_LITTEXTURE_VERTEX:
@@ -2638,24 +3017,22 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
                                m.texrgbscale[0] = layertexrgbscale;
-                               m.pointer_color = varray_color4f;
-                               R_Mesh_State(&m);
+                               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+                               R_Mesh_TextureState(&m);
                                if (lightmode == 2)
                                {
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                               RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 2, applycolor, applyfog);
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 2, applycolor, applyfog);
                                        }
                                }
                                else
                                {
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
-                                               surface = texturesurfacelist[texturesurfaceindex];
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                               RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 1, applycolor, applyfog);
+                                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 1, applycolor, applyfog);
                                        }
                                }
                                break;
@@ -2663,36 +3040,35 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
-                               R_Mesh_State(&m);
+                               m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+                               R_Mesh_TextureState(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                       RSurf_DrawLightmap(ent, texture, surface, modelorg, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, applyfog);
+                                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
+                                       RSurf_DrawLightmap(surface, layercolor[0], layercolor[1], layercolor[2], layercolor[3], 0, applycolor, applyfog);
                                }
                                break;
                        case TEXTURELAYERTYPE_FOG:
-                               memset(&m, 0, sizeof(m));
+                               R_Mesh_ColorPointer(rsurface_array_color4f);
                                if (layer->texture)
                                {
+                                       memset(&m, 0, sizeof(m));
                                        m.tex[0] = R_GetTexture(layer->texture);
                                        m.texmatrix[0] = layer->texmatrix;
+                                       m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+                                       R_Mesh_TextureState(&m);
                                }
-                               R_Mesh_State(&m);
+                               else
+                                       R_Mesh_ResetTextureState();
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        int i;
                                        float f, *v, *c;
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       RSurf_SetVertexPointer(ent, texture, surface, modelorg, false, false);
-                                       if (layer->texture)
-                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                       R_Mesh_ColorPointer(varray_color4f);
-                                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
+                                       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 = VERTEXFOGTABLE(VectorDistance(v, modelorg));
+                                               f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                                c[0] = layercolor[0];
                                                c[1] = layercolor[1];
                                                c[2] = layercolor[2];
@@ -2704,109 +3080,73 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                        default:
                                Con_Printf("R_DrawTextureSurfaceList: unknown layer type %i\n", layer->type);
                        }
+                       GL_LockArrays(0, 0);
                        // if trying to do overbright on first pass of an opaque surface
                        // when combine is not supported, brighten as a post process
                        if (layertexrgbscale > 1 && !gl_combine.integer && layer->depthmask)
                        {
                                int scale;
                                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+                               R_Mesh_ColorPointer(NULL);
                                GL_Color(1, 1, 1, 1);
-                               memset(&m, 0, sizeof(m));
-                               R_Mesh_State(&m);
+                               R_Mesh_ResetTextureState();
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       RSurf_SetVertexPointer(ent, texture, surface, modelorg, false, false);
+                                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                        for (scale = 1;scale < layertexrgbscale;scale <<= 1)
                                                RSurf_Draw(surface);
                                }
+                               GL_LockArrays(0, 0);
                        }
                }
-               if (r_shownormals.integer && !r_showtrispass)
+               if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
                {
-                       int j, k;
-                       float v[3];
-                       GL_DepthTest(!r_showdisabledepthtest.integer);
-                       GL_DepthMask(texture->currentlayers->depthmask);
-                       GL_BlendFunc(texture->currentlayers->blendfunc1, texture->currentlayers->blendfunc2);
-                       memset(&m, 0, sizeof(m));
-                       R_Mesh_State(&m);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               surface = texturesurfacelist[texturesurfaceindex];
-                               RSurf_SetVertexPointer(ent, texture, surface, modelorg, false, true);
-                               GL_Color(1, 0, 0, 1);
-                               qglBegin(GL_LINES);
-                               for (j = 0, k = surface->num_firstvertex;j < surface->num_vertices;j++, k++)
-                               {
-                                       VectorCopy(rsurface_vertex3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                                       VectorMA(v, 8, rsurface_svector3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                               }
-                               GL_Color(0, 0, 1, 1);
-                               for (j = 0, k = surface->num_firstvertex;j < surface->num_vertices;j++, k++)
-                               {
-                                       VectorCopy(rsurface_vertex3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                                       VectorMA(v, 8, rsurface_tvector3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                               }
-                               GL_Color(0, 1, 0, 1);
-                               for (j = 0, k = surface->num_firstvertex;j < surface->num_vertices;j++, k++)
-                               {
-                                       VectorCopy(rsurface_vertex3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                                       VectorMA(v, 8, rsurface_normal3f + k * 3, v);
-                                       qglVertex3f(v[0], v[1], v[2]);
-                               }
-                               qglEnd();
-                       }
+                       qglDepthFunc(GL_LEQUAL);
+                       GL_AlphaTest(false);
                }
        }
-       if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
+       GL_LockArrays(0, 0);
+       if ((rsurface_texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (rsurface_entity->flags & RENDER_NOCULLFACE))
                qglEnable(GL_CULL_FACE);
 }
 
 static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
 {
-       const msurface_t *surface = ent->model->data_surfaces + surfacenumber;
-       vec3_t modelorg;
-       texture_t *texture;
+       msurface_t *surface = ent->model->data_surfaces + surfacenumber;
 
-       texture = surface->texture;
-       if (texture->basematerialflags & MATERIALFLAG_SKY)
+       rsurface_texture = surface->texture;
+       if (rsurface_texture->basematerialflags & MATERIALFLAG_SKY)
                return; // transparent sky is too difficult
-       R_UpdateTextureInfo(ent, texture);
 
-       R_Mesh_Matrix(&ent->matrix);
-       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
-       R_DrawTextureSurfaceList(ent, texture->currentframe, 1, &surface, modelorg);
+       RSurf_ActiveEntity(ent);
+       R_UpdateTextureInfo(ent, rsurface_texture);
+       rsurface_texture = rsurface_texture->currentframe;
+       R_DrawTextureSurfaceList(1, &surface);
+       RSurf_CleanUp();
 }
 
-void R_QueueTextureSurfaceList(entity_render_t *ent, texture_t *texture, int texturenumsurfaces, const msurface_t **texturesurfacelist, const vec3_t modelorg)
+void R_QueueTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist)
 {
        int texturesurfaceindex;
-       const msurface_t *surface;
        vec3_t tempcenter, center;
-       if (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT)
+       if (rsurface_texture->currentmaterialflags & MATERIALFLAG_BLENDED)
        {
                // drawing sky transparently would be too difficult
-               if (!(texture->currentmaterialflags & MATERIALFLAG_SKY))
+               if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_SKY))
                {
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
-                               surface = texturesurfacelist[texturesurfaceindex];
+                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                                tempcenter[0] = (surface->mins[0] + surface->maxs[0]) * 0.5f;
                                tempcenter[1] = (surface->mins[1] + surface->maxs[1]) * 0.5f;
                                tempcenter[2] = (surface->mins[2] + surface->maxs[2]) * 0.5f;
-                               Matrix4x4_Transform(&ent->matrix, tempcenter, center);
-                               R_MeshQueue_AddTransparent(texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_vieworigin : center, R_DrawSurface_TransparentCallback, ent, surface - ent->model->data_surfaces, r_shadow_rtlight);
+                               Matrix4x4_Transform(&rsurface_entity->matrix, tempcenter, center);
+                               R_MeshQueue_AddTransparent(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_vieworigin : center, R_DrawSurface_TransparentCallback, rsurface_entity, surface - rsurface_model->data_surfaces, r_shadow_rtlight);
                        }
                }
        }
        else
-               R_DrawTextureSurfaceList(ent, texture, texturenumsurfaces, texturesurfacelist, modelorg);
+               R_DrawTextureSurfaceList(texturenumsurfaces, texturesurfacelist);
 }
 
 extern void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface);
@@ -2814,21 +3154,20 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
 {
        int i, j, f, flagsmask;
        int counttriangles = 0;
-       msurface_t *surface, **surfacechain;
-       texture_t *t, *texture;
+       texture_t *t;
        model_t *model = ent->model;
-       vec3_t modelorg;
        const int maxsurfacelist = 1024;
        int numsurfacelist = 0;
-       const msurface_t *surfacelist[1024];
+       msurface_t *surfacelist[1024];
        if (model == NULL)
                return;
-       R_Mesh_Matrix(&ent->matrix);
-       Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
+
+       RSurf_ActiveEntity(ent);
 
        // update light styles
        if (!skysurfaces && model->brushq1.light_styleupdatechains)
        {
+               msurface_t *surface, **surfacechain;
                for (i = 0;i < model->brushq1.light_styles;i++)
                {
                        if (model->brushq1.light_stylevalue[i] != r_refdef.lightstylevalue[model->brushq1.light_style[i]])
@@ -2845,24 +3184,27 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
        flagsmask = skysurfaces ? MATERIALFLAG_SKY : (MATERIALFLAG_WATER | MATERIALFLAG_WALL);
        f = 0;
        t = NULL;
-       texture = NULL;
+       rsurface_lightmaptexture = NULL;
+       rsurface_texture = NULL;
        numsurfacelist = 0;
        if (ent == r_refdef.worldentity)
        {
+               msurface_t *surface;
                for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
                {
                        if (!r_worldsurfacevisible[j])
                                continue;
-                       if (t != surface->texture)
+                       if (t != surface->texture || rsurface_lightmaptexture != surface->lightmaptexture)
                        {
                                if (numsurfacelist)
                                {
-                                       R_QueueTextureSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       R_QueueTextureSurfaceList(numsurfacelist, surfacelist);
                                        numsurfacelist = 0;
                                }
                                t = surface->texture;
-                               texture = t->currentframe;
-                               f = texture->currentmaterialflags & flagsmask;
+                               rsurface_lightmaptexture = surface->lightmaptexture;
+                               rsurface_texture = t->currentframe;
+                               f = rsurface_texture->currentmaterialflags & flagsmask;
                        }
                        if (f && surface->num_triangles)
                        {
@@ -2874,7 +3216,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                counttriangles += surface->num_triangles;
                                if (numsurfacelist >= maxsurfacelist)
                                {
-                                       R_QueueTextureSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       R_QueueTextureSurfaceList(numsurfacelist, surfacelist);
                                        numsurfacelist = 0;
                                }
                        }
@@ -2882,18 +3224,20 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
        }
        else
        {
+               msurface_t *surface;
                for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
                {
-                       if (t != surface->texture)
+                       if (t != surface->texture || rsurface_lightmaptexture != surface->lightmaptexture)
                        {
                                if (numsurfacelist)
                                {
-                                       R_QueueTextureSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       R_QueueTextureSurfaceList(numsurfacelist, surfacelist);
                                        numsurfacelist = 0;
                                }
                                t = surface->texture;
-                               texture = t->currentframe;
-                               f = texture->currentmaterialflags & flagsmask;
+                               rsurface_lightmaptexture = surface->lightmaptexture;
+                               rsurface_texture = t->currentframe;
+                               f = rsurface_texture->currentmaterialflags & flagsmask;
                        }
                        if (f && surface->num_triangles)
                        {
@@ -2905,15 +3249,115 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                counttriangles += surface->num_triangles;
                                if (numsurfacelist >= maxsurfacelist)
                                {
-                                       R_QueueTextureSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       R_QueueTextureSurfaceList(numsurfacelist, surfacelist);
                                        numsurfacelist = 0;
                                }
                        }
                }
        }
        if (numsurfacelist)
-               R_QueueTextureSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
-       if (!r_showtrispass)
-               renderstats.entities_triangles += counttriangles;
+               R_QueueTextureSurfaceList(numsurfacelist, surfacelist);
+       renderstats.entities_triangles += counttriangles;
+       RSurf_CleanUp();
+
+       if (r_showcollisionbrushes.integer && model->brush.num_brushes && !skysurfaces)
+       {
+               int i;
+               const msurface_t *surface;
+               q3mbrush_t *brush;
+               R_Mesh_Matrix(&ent->matrix);
+               R_Mesh_ColorPointer(NULL);
+               R_Mesh_ResetTextureState();
+               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               GL_DepthMask(false);
+               GL_DepthTest(!r_showdisabledepthtest.integer);
+               qglPolygonOffset(r_polygonfactor + r_showcollisionbrushes_polygonfactor.value, r_polygonoffset + r_showcollisionbrushes_polygonoffset.value);
+               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                       if (brush->colbrushf && brush->colbrushf->numtriangles)
+                               R_DrawCollisionBrush(brush->colbrushf);
+               for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                       if (surface->num_collisiontriangles)
+                               R_DrawCollisionSurface(ent, surface);
+               qglPolygonOffset(r_polygonfactor, r_polygonoffset);
+       }
+
+       if (r_showtris.integer || r_shownormals.integer)
+       {
+               int k, l;
+               msurface_t *surface;
+               const int *elements;
+               vec3_t v;
+               GL_DepthTest(true);
+               GL_DepthMask(true);
+               if (r_showdisabledepthtest.integer)
+                       qglDepthFunc(GL_ALWAYS);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               R_Mesh_ColorPointer(NULL);
+               R_Mesh_ResetTextureState();
+               for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
+               {
+                       if (ent == r_refdef.worldentity && !r_worldsurfacevisible[j])
+                               continue;
+                       rsurface_texture = surface->texture->currentframe;
+                       if ((rsurface_texture->currentmaterialflags & flagsmask) && surface->num_triangles)
+                       {
+                               RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
+                               if (r_showtris.integer)
+                               {
+                                       if (!rsurface_texture->currentlayers->depthmask)
+                                               GL_Color(r_showtris.value, 0, 0, 1);
+                                       else if (ent == r_refdef.worldentity)
+                                               GL_Color(r_showtris.value, r_showtris.value, r_showtris.value, 1);
+                                       else
+                                               GL_Color(0, r_showtris.value, 0, 1);
+                                       elements = (ent->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
+                                       qglBegin(GL_LINES);
+                                       for (k = 0;k < surface->num_triangles;k++, elements += 3)
+                                       {
+                                               qglArrayElement(elements[0]);qglArrayElement(elements[1]);
+                                               qglArrayElement(elements[1]);qglArrayElement(elements[2]);
+                                               qglArrayElement(elements[2]);qglArrayElement(elements[0]);
+                                       }
+                                       qglEnd();
+                               }
+                               if (r_shownormals.integer)
+                               {
+                                       GL_Color(r_shownormals.value, 0, 0, 1);
+                                       qglBegin(GL_LINES);
+                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       {
+                                               VectorCopy(rsurface_vertex3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                               VectorMA(v, 8, rsurface_svector3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                       }
+                                       qglEnd();
+                                       GL_Color(0, 0, r_shownormals.value, 1);
+                                       qglBegin(GL_LINES);
+                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       {
+                                               VectorCopy(rsurface_vertex3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                               VectorMA(v, 8, rsurface_tvector3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                       }
+                                       qglEnd();
+                                       GL_Color(0, r_shownormals.value, 0, 1);
+                                       qglBegin(GL_LINES);
+                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       {
+                                               VectorCopy(rsurface_vertex3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                               VectorMA(v, 8, rsurface_normal3f + l * 3, v);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                       }
+                                       qglEnd();
+                               }
+                       }
+               }
+               rsurface_texture = NULL;
+               if (r_showdisabledepthtest.integer)
+                       qglDepthFunc(GL_LEQUAL);
+       }
 }