]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
added r_renderview cvar (similar to scr_refresh but disables only 3D
[xonotic/darkplaces.git] / gl_rmain.c
index ab597a4a174e569d635868caf443f963f1590ac0..f11963bd0ee269a232e8ed028fe771c9cd28c23f 100644 (file)
@@ -28,11 +28,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
 
+static int r_frame = 0; ///< used only by R_GetCurrentTexture
+
 //
 // screen size info
 //
 r_refdef_t r_refdef;
 
+cvar_t r_motionblur = {CVAR_SAVE, "r_motionblur", "0", "motionblur value scale - 0.5 recommended"};
+cvar_t r_damageblur = {CVAR_SAVE, "r_damageblur", "0", "motionblur based on damage"};
+cvar_t r_motionblur_vmin = {CVAR_SAVE, "r_motionblur_vmin", "300", "minimum influence from velocity"};
+cvar_t r_motionblur_vmax = {CVAR_SAVE, "r_motionblur_vmax", "600", "maximum influence from velocity"};
+cvar_t r_motionblur_bmin = {CVAR_SAVE, "r_motionblur_bmin", "0.5", "velocity at which there is no blur yet (may be negative to always have some blur)"};
+cvar_t r_motionblur_vcoeff = {CVAR_SAVE, "r_motionblur_vcoeff", "0.05", "sliding average reaction time for velocity"};
+cvar_t r_motionblur_maxblur = {CVAR_SAVE, "r_motionblur_maxblur", "0.88", "cap for motionblur alpha value"};
+cvar_t r_motionblur_randomize = {CVAR_SAVE, "r_motionblur_randomize", "0.1", "randomizing coefficient to workaround ghosting"};
+
+cvar_t r_animcache = {CVAR_SAVE, "r_animcache", "1", "cache animation frames to save CPU usage, primarily optimizes shadows and reflections"};
+
 cvar_t r_depthfirst = {CVAR_SAVE, "r_depthfirst", "0", "renders a depth-only version of the scene before normal rendering begins to eliminate overdraw, values: 0 = off, 1 = world depth, 2 = world and model depth"};
 cvar_t r_useinfinitefarclip = {CVAR_SAVE, "r_useinfinitefarclip", "1", "enables use of a special kind of projection matrix that has an extremely large farclip"};
 cvar_t r_nearclip = {0, "r_nearclip", "1", "distance from camera of nearclip plane" };
@@ -58,8 +71,12 @@ cvar_t r_fullbright = {0, "r_fullbright","0", "makes map very bright and renders
 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_shadows = {CVAR_SAVE, "r_shadows", "0", "casts fake stencil shadows from models onto the world (rtlights are unaffected by this); when set to 2, always cast the shadows DOWN, otherwise use the model lighting"};
+cvar_t r_shadows = {CVAR_SAVE, "r_shadows", "0", "casts fake stencil shadows from models onto the world (rtlights are unaffected by this); when set to 2, always cast the shadows in the direction set by r_shadows_throwdirection, otherwise use the model lighting."};
+cvar_t r_shadows_darken = {CVAR_SAVE, "r_shadows_darken", "0.5", "how much shadowed areas will be darkened"};
 cvar_t r_shadows_throwdistance = {CVAR_SAVE, "r_shadows_throwdistance", "500", "how far to cast shadows from models"};
+cvar_t r_shadows_throwdirection = {CVAR_SAVE, "r_shadows_throwdirection", "0 0 -1", "override throwing direction for r_shadows 2"};
+cvar_t r_shadows_drawafterrtlighting = {CVAR_SAVE, "r_shadows_drawafterrtlighting", "0", "draw fake shadows AFTER realtime lightning is drawn. May be useful for simulating fast sunlight on large outdoor maps with only one noshadow rtlight. The price is less realistic appearance of dynamic light shadows."};
+cvar_t r_shadows_castfrombmodels = {CVAR_SAVE, "r_shadows_castfrombmodels", "0", "do cast shadows from bmodels"};
 cvar_t r_q1bsp_skymasking = {0, "r_q1bsp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"};
 cvar_t r_polygonoffset_submodel_factor = {0, "r_polygonoffset_submodel_factor", "0", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
 cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", "2", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
@@ -78,7 +95,6 @@ cvar_t gl_skyclip = {0, "gl_skyclip", "4608", "nehahra farclip distance - the re
 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 = {CVAR_SAVE, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
-cvar_t r_glsl_contrastboost = {CVAR_SAVE, "r_glsl_contrastboost", "1", "by how much to multiply the contrast in dark areas (1 is no change)"};
 cvar_t r_glsl_deluxemapping = {CVAR_SAVE, "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_glsl_offsetmapping = {CVAR_SAVE, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"};
 cvar_t r_glsl_offsetmapping_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
@@ -96,7 +112,7 @@ cvar_t r_water_resolutionmultiplier = {CVAR_SAVE, "r_water_resolutionmultiplier"
 cvar_t r_water_refractdistort = {CVAR_SAVE, "r_water_refractdistort", "0.01", "how much water refractions shimmer"};
 cvar_t r_water_reflectdistort = {CVAR_SAVE, "r_water_reflectdistort", "0.01", "how much water reflections shimmer"};
 
-cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1", "enables animation smoothing on sprites (requires r_lerpmodels 1)"};
+cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1", "enables animation smoothing on sprites"};
 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
 cvar_t r_lerplightstyles = {CVAR_SAVE, "r_lerplightstyles", "0", "enable animation smoothing on flickering lights"};
 cvar_t r_waterscroll = {CVAR_SAVE, "r_waterscroll", "1", "makes water scroll around, value controls how much"};
@@ -126,6 +142,7 @@ cvar_t r_track_sprites = {CVAR_SAVE, "r_track_sprites", "1", "track SPR_LABEL* s
 cvar_t r_track_sprites_flags = {CVAR_SAVE, "r_track_sprites_flags", "1", "1: Rotate sprites accodringly, 2: Make it a continuous rotation"};
 cvar_t r_track_sprites_scalew = {CVAR_SAVE, "r_track_sprites_scalew", "1", "width scaling of tracked sprites"};
 cvar_t r_track_sprites_scaleh = {CVAR_SAVE, "r_track_sprites_scaleh", "1", "height scaling of tracked sprites"};
+cvar_t r_glsl_saturation = {CVAR_SAVE, "r_glsl_saturation", "1", "saturation multiplier (only working in glsl!)"};
 
 extern cvar_t v_glslgamma;
 
@@ -139,7 +156,7 @@ static struct r_bloomstate_s
        int bloomwidth, bloomheight;
 
        int screentexturewidth, screentextureheight;
-       rtexture_t *texture_screen;
+       rtexture_t *texture_screen; /// \note also used for motion blur if enabled!
 
        int bloomtexturewidth, bloomtextureheight;
        rtexture_t *texture_bloom;
@@ -148,12 +165,14 @@ static struct r_bloomstate_s
        float screentexcoord2f[8];
        float bloomtexcoord2f[8];
        float offsettexcoord2f[8];
+
+       r_viewport_t viewport;
 }
 r_bloomstate;
 
 r_waterstate_t r_waterstate;
 
-// shadow volume bsp struct with automatically growing nodes buffer
+/// shadow volume bsp struct with automatically growing nodes buffer
 svbsp_t r_svbsp;
 
 rtexture_t *r_texture_blanknormalmap;
@@ -175,8 +194,8 @@ unsigned int r_maxqueries;
 char r_qwskincache[MAX_SCOREBOARD][MAX_QPATH];
 skinframe_t *r_qwskincache_skinframe[MAX_SCOREBOARD];
 
-// vertex coordinates for a quad that covers the screen exactly
-const static float r_screenvertex3f[12] =
+/// vertex coordinates for a quad that covers the screen exactly
+const float r_screenvertex3f[12] =
 {
        0, 0, 0,
        1, 0, 0,
@@ -429,12 +448,39 @@ static const char *builtinshaderstring =
 "// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
 "// written by Forest 'LordHavoc' Hale\n"
 "\n"
+"// enable various extensions depending on permutation:\n"
+"\n" 
+"#ifdef USESHADOWMAPRECT\n"
+"# extension GL_ARB_texture_rectangle : enable\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWMAP2D\n"
+"# ifdef GL_EXT_gpu_shader4\n"
+"#   extension GL_EXT_gpu_shader4 : enable\n"
+"# endif\n"
+"# ifdef GL_ARB_texture_gather\n"
+"#   extension GL_ARB_texture_gather : enable\n"
+"# else\n"
+"#   ifdef GL_AMD_texture_texture4\n"
+"#     extension GL_AMD_texture_texture4 : enable\n"
+"#   endif\n"
+"# endif\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWMAPCUBE\n"
+"# extension GL_EXT_gpu_shader4 : enable\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWSAMPLER\n"
+"# extension GL_ARB_shadow : enable\n"
+"#endif\n"
+"\n"
 "// common definitions between vertex shader and fragment shader:\n"
 "\n"
 "//#ifdef __GLSL_CG_DATA_TYPES\n"
 "//# define myhalf half\n"
 "//# define myhalf2 half2\n"
-"//# define myhalf3 half3\n"
+"//# define myhalf3half3\n"
 "//# define myhalf4 half4\n"
 "//#else\n"
 "# define myhalf float\n"
@@ -453,6 +499,22 @@ static const char *builtinshaderstring =
 "# endif\n"
 "\n"
 "#else\n"
+"#ifdef MODE_SHOWDEPTH\n"
+"# ifdef VERTEX_SHADER\n"
+"void main(void)\n"
+"{\n"
+"      gl_Position = ftransform();\n"
+"      gl_FrontColor = vec4(gl_Position.z, gl_Position.z, gl_Position.z, 1.0);\n"
+"}\n"
+"# endif\n"
+"# ifdef FRAGMENT_SHADER\n"
+"void main(void)\n"
+"{\n"
+"      gl_FragColor = gl_Color;\n"
+"}\n"
+"# endif\n"
+"\n"
+"#else // !MODE_SHOWDEPTH\n"
 "\n"
 "#ifdef MODE_POSTPROCESS\n"
 "# ifdef VERTEX_SHADER\n"
@@ -461,7 +523,7 @@ static const char *builtinshaderstring =
 "      gl_FrontColor = gl_Color;\n"
 "      gl_Position = ftransform();\n"
 "      gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n"
-"#ifdef USEGLOW\n"
+"#ifdef USEBLOOM\n"
 "      gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;\n"
 "#endif\n"
 "}\n"
@@ -469,17 +531,17 @@ static const char *builtinshaderstring =
 "# ifdef FRAGMENT_SHADER\n"
 "\n"
 "uniform sampler2D Texture_First;\n"
-"#ifdef USEGLOW\n"
+"#ifdef USEBLOOM\n"
 "uniform sampler2D Texture_Second;\n"
 "#endif\n"
 "#ifdef USEGAMMARAMPS\n"
 "uniform sampler2D Texture_GammaRamps;\n"
 "#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform vec4 TintColor;\n"
+"#ifdef USESATURATION\n"
+"uniform float Saturation;\n"
 "#endif\n"
-"#ifdef USECOLORMOD\n"
-"uniform vec3 Gamma;\n"
+"#ifdef USEVIEWTINT\n"
+"uniform vec4 TintColor;\n"
 "#endif\n"
 "//uncomment these if you want to use them:\n"
 "uniform vec4 UserVec1;\n"
@@ -491,10 +553,10 @@ static const char *builtinshaderstring =
 "void main(void)\n"
 "{\n"
 "      gl_FragColor = texture2D(Texture_First, gl_TexCoord[0].xy);\n"
-"#ifdef USEGLOW\n"
+"#ifdef USEBLOOM\n"
 "      gl_FragColor += texture2D(Texture_Second, gl_TexCoord[1].xy);\n"
 "#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
+"#ifdef USEVIEWTINT\n"
 "      gl_FragColor = mix(gl_FragColor, TintColor, TintColor.a);\n"
 "#endif\n"
 "\n"
@@ -509,6 +571,13 @@ static const char *builtinshaderstring =
 "      gl_FragColor /= (1 + 5 * UserVec1.y);\n"
 "#endif\n"
 "\n"
+"#ifdef USESATURATION\n"
+"      //apply saturation BEFORE gamma ramps, so v_glslgamma value does not matter\n"
+"      myhalf y = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));\n"
+"      //gl_FragColor = vec3(y) + (gl_FragColor.rgb - vec3(y)) * Saturation;\n"
+"      gl_FragColor.rgb = mix(vec3(y), gl_FragColor.rgb, Saturation);\n"
+"#endif\n"
+"\n"
 "#ifdef USEGAMMARAMPS\n"
 "      gl_FragColor.r = texture2D(Texture_GammaRamps, vec2(gl_FragColor.r, 0)).r;\n"
 "      gl_FragColor.g = texture2D(Texture_GammaRamps, vec2(gl_FragColor.g, 0)).g;\n"
@@ -567,6 +636,9 @@ static const char *builtinshaderstring =
 "#else // !MODE_GENERIC\n"
 "\n"
 "varying vec2 TexCoord;\n"
+"#ifdef USEVERTEXTEXTUREBLEND\n"
+"varying vec2 TexCoord2;\n"
+"#endif\n"
 "varying vec2 TexCoordLightmap;\n"
 "\n"
 "#ifdef MODE_LIGHTSOURCE\n"
@@ -617,6 +689,9 @@ static const char *builtinshaderstring =
 "      gl_FrontColor = gl_Color;\n"
 "      // copy the surface texcoord\n"
 "      TexCoord = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\n"
+"#ifdef USEVERTEXTEXTUREBLEND\n"
+"      TexCoord2 = vec2(gl_TextureMatrix[1] * gl_MultiTexCoord0);\n"
+"#endif\n"
 "#ifndef MODE_LIGHTSOURCE\n"
 "# ifndef MODE_LIGHTDIRECTION\n"
 "      TexCoordLightmap = vec2(gl_MultiTexCoord4);\n"
@@ -706,6 +781,36 @@ static const char *builtinshaderstring =
 "uniform sampler2D Texture_Attenuation;\n"
 "uniform samplerCube Texture_Cube;\n"
 "\n"
+"#define showshadowmap 0\n"
+"\n"
+"#ifdef USESHADOWMAPRECT\n"
+"# ifdef USESHADOWSAMPLER\n"
+"uniform sampler2DRectShadow Texture_ShadowMapRect;\n"
+"# else\n"
+"uniform sampler2DRect Texture_ShadowMapRect;\n"
+"# endif\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWMAP2D\n"
+"# ifdef USESHADOWSAMPLER\n"
+"uniform sampler2DShadow Texture_ShadowMap2D;\n"
+"# else\n"
+"uniform sampler2D Texture_ShadowMap2D;\n"
+"# endif\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWMAPVSDCT\n"
+"uniform samplerCube Texture_CubeProjection;\n"
+"#endif\n"
+"\n"
+"#ifdef USESHADOWMAPCUBE\n"
+"# ifdef USESHADOWSAMPLER\n"
+"uniform samplerCubeShadow Texture_ShadowMapCube;\n"
+"# else\n"
+"uniform samplerCube Texture_ShadowMapCube;\n"
+"# endif\n"
+"#endif\n"
+"\n"
 "uniform myhalf3 LightColor;\n"
 "uniform myhalf3 AmbientColor;\n"
 "uniform myhalf3 DiffuseColor;\n"
@@ -746,9 +851,6 @@ static const char *builtinshaderstring =
 "\n"
 "uniform myhalf GlowScale;\n"
 "uniform myhalf SceneBrightness;\n"
-"#ifdef USECONTRASTBOOST\n"
-"uniform myhalf ContrastBoostCoeff;\n"
-"#endif\n"
 "\n"
 "uniform float OffsetMapping_Scale;\n"
 "uniform float OffsetMapping_Bias;\n"
@@ -804,6 +906,203 @@ static const char *builtinshaderstring =
 "}\n"
 "#endif // USEOFFSETMAPPING\n"
 "\n"
+"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D) || defined(USESHADOWMAPCUBE)\n"
+"uniform vec2 ShadowMap_TextureScale;\n"
+"uniform vec4 ShadowMap_Parameters;\n"
+"#endif\n"
+"\n"
+"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
+"vec3 GetShadowMapTC2D(vec3 dir)\n"
+"{\n"
+"      vec3 adir = abs(dir);\n"
+"# ifndef USESHADOWMAPVSDCT\n"
+"      vec2 tc;\n"
+"      vec2 offset;\n"
+"      float ma;\n"
+"      if (adir.x > adir.y)\n"
+"      {\n"
+"              if (adir.x > adir.z) // X\n"
+"              {\n"
+"                      ma = adir.x;\n"
+"                      tc = dir.zy;\n"
+"                      offset = vec2(mix(0.5, 1.5, dir.x < 0.0), 0.5);\n"
+"              }\n"
+"              else // Z\n"
+"              {\n"
+"                      ma = adir.z;\n"
+"                      tc = dir.xy;\n"
+"                      offset = vec2(mix(0.5, 1.5, dir.z < 0.0), 2.5);\n"
+"              }\n"
+"      }\n"
+"      else\n"
+"      {\n"
+"              if (adir.y > adir.z) // Y\n"
+"              {\n"
+"                      ma = adir.y;\n"
+"                      tc = dir.xz;\n"
+"                      offset = vec2(mix(0.5, 1.5, dir.y < 0.0), 1.5);\n"
+"              }\n"
+"              else // Z\n"
+"              {\n"
+"                      ma = adir.z;\n"
+"                      tc = dir.xy;\n"
+"                      offset = vec2(mix(0.5, 1.5, dir.z < 0.0), 2.5);\n"
+"              }\n"
+"      }\n"
+"\n"
+"      vec3 stc = vec3(tc * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
+"      stc.xy += offset * ShadowMap_Parameters.y;\n"
+"      stc.z += ShadowMap_Parameters.z;\n"
+"#  if showshadowmap\n"
+"      stc.xy *= ShadowMap_TextureScale;\n"
+"#  endif\n"
+"      return stc;\n"
+"# else\n"
+"      vec4 proj = textureCube(Texture_CubeProjection, dir);\n"
+"      float ma = max(max(adir.x, adir.y), adir.z);\n"
+"      vec3 stc = vec3(mix(dir.xy, dir.zz, proj.xy) * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
+"      stc.xy += proj.zw * ShadowMap_Parameters.y;\n"
+"      stc.z += ShadowMap_Parameters.z;\n"
+"#  if showshadowmap\n"
+"      stc.xy *= ShadowMap_TextureScale;\n"
+"#  endif\n"
+"      return stc;\n"
+"# endif\n"
+"}\n"
+"#endif // defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
+"\n"
+"#ifdef USESHADOWMAPCUBE\n"
+"vec4 GetShadowMapTCCube(vec3 dir)\n"
+"{\n"
+"    vec3 adir = abs(dir);\n"
+"    return vec4(dir, ShadowMap_Parameters.z + ShadowMap_Parameters.w / max(max(adir.x, adir.y), adir.z));\n"
+"}\n"
+"#endif\n"
+"\n"
+"#if !showshadowmap\n"
+"# ifdef USESHADOWMAPRECT\n"
+"float ShadowMapCompare(vec3 dir)\n"
+"{\n"
+"      vec3 shadowmaptc = GetShadowMapTC2D(dir);\n"
+"      float f;\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"\n"
+"#    ifdef USESHADOWMAPPCF\n"
+"#      define texval(x, y) shadow2DRect(Texture_ShadowMapRect, shadowmaptc + vec3(x, y, 0.0)).r\n"
+"    f = dot(vec4(0.25), vec4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
+"#    else\n"
+"    f = shadow2DRect(Texture_ShadowMapRect, shadowmaptc).r;\n"
+"#    endif\n"
+"\n"
+"#  else\n"
+"\n"
+"#    ifdef USESHADOWMAPPCF\n"
+"#      if USESHADOWMAPPCF > 1\n"
+"#        define texval(x, y) texture2DRect(Texture_ShadowMapRect, center + vec2(x, y)).r\n"
+"    vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
+"    vec4 row1 = step(shadowmaptc.z, vec4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
+"    vec4 row2 = step(shadowmaptc.z, vec4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
+"    vec4 row3 = step(shadowmaptc.z, vec4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
+"    vec4 row4 = step(shadowmaptc.z, vec4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
+"    vec4 cols = row2 + row3 + mix(row1, row4, offset.y);\n"
+"    f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
+"#      else\n"
+"#        define texval(x, y) texture2DRect(Texture_ShadowMapRect, shadowmaptc.xy + vec2(x, y)).r\n"
+"    vec2 offset = fract(shadowmaptc.xy);\n"
+"    vec3 row1 = step(shadowmaptc.z, vec3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
+"    vec3 row2 = step(shadowmaptc.z, vec3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
+"    vec3 row3 = step(shadowmaptc.z, vec3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
+"    vec3 cols = row2 + mix(row1, row3, offset.y);\n"
+"    f = dot(mix(cols.xy, cols.yz, offset.x), vec2(0.25));\n"
+"#      endif\n"
+"#    else\n"
+"    f = step(shadowmaptc.z, texture2DRect(Texture_ShadowMapRect, shadowmaptc.xy).r);\n"
+"#    endif\n"
+"\n"
+"#  endif\n"
+"      return f;\n"
+"}\n"
+"# endif\n"
+"\n"
+"# ifdef USESHADOWMAP2D\n"
+"float ShadowMapCompare(vec3 dir)\n"
+"{\n"
+"    vec3 shadowmaptc = GetShadowMapTC2D(dir);\n"
+"    float f;\n"
+"\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"#    ifdef USESHADOWMAPPCF\n"
+"#      define texval(x, y) shadow2D(Texture_ShadowMap2D, vec3(center + vec2(x, y)*ShadowMap_TextureScale, shadowmaptc.z)).r  \n"
+"    vec2 center = shadowmaptc.xy*ShadowMap_TextureScale;\n"
+"    f = dot(vec4(0.25), vec4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
+"#    else\n"
+"    f = shadow2D(Texture_ShadowMap2D, vec3(shadowmaptc.xy*ShadowMap_TextureScale, shadowmaptc.z)).r;\n"
+"#    endif\n"
+"#  else\n"
+"#    ifdef USESHADOWMAPPCF\n"
+"#     if defined(GL_ARB_texture_gather) || defined(GL_AMD_texture_texture4)\n"
+"#      ifdef GL_ARB_texture_gather\n"
+"#        define texval(x, y) textureGatherOffset(Texture_ShadowMap2D, center, ivec(x, y))\n"
+"#      else\n"
+"#        define texval(x, y) texture4(Texture_ShadowMap2D, center + vec2(x,y)*ShadowMap_TextureScale)\n"
+"#      endif\n"
+"    vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
+"    center *= ShadowMap_TextureScale;\n"
+"    vec4 group1 = step(shadowmaptc.z, texval(-1.0, -1.0));\n"
+"    vec4 group2 = step(shadowmaptc.z, texval( 1.0, -1.0));\n"
+"    vec4 group3 = step(shadowmaptc.z, texval(-1.0,  1.0));\n"
+"    vec4 group4 = step(shadowmaptc.z, texval( 1.0,  1.0));\n"
+"    vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) +\n"
+"                mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y);\n"
+"    f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
+"#     else\n"
+"#      ifdef GL_EXT_gpu_shader4\n"
+"#        define texval(x, y) texture2DOffset(Texture_ShadowMap2D, center, ivec2(x, y)).r\n"
+"#      else\n"
+"#        define texval(x, y) texture2D(Texture_ShadowMap2D, center + vec2(x, y)*ShadowMap_TextureScale).r  \n"
+"#      endif\n"
+"#      if USESHADOWMAPPCF > 1\n"       
+"    vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
+"    center *= ShadowMap_TextureScale;\n"
+"    vec4 row1 = step(shadowmaptc.z, vec4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
+"    vec4 row2 = step(shadowmaptc.z, vec4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
+"    vec4 row3 = step(shadowmaptc.z, vec4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
+"    vec4 row4 = step(shadowmaptc.z, vec4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
+"    vec4 cols = row2 + row3 + mix(row1, row4, offset.y);\n"
+"    f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
+"#      else\n"
+"    vec2 center = shadowmaptc.xy*ShadowMap_TextureScale, offset = fract(shadowmaptc.xy);\n"
+"    vec3 row1 = step(shadowmaptc.z, vec3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
+"    vec3 row2 = step(shadowmaptc.z, vec3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
+"    vec3 row3 = step(shadowmaptc.z, vec3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
+"    vec3 cols = row2 + mix(row1, row3, offset.y);\n"
+"    f = dot(mix(cols.xy, cols.yz, offset.x), vec2(0.25));\n"
+"#      endif\n"      
+"#     endif\n"
+"#    else\n"
+"    f = step(shadowmaptc.z, texture2D(Texture_ShadowMap2D, shadowmaptc.xy*ShadowMap_TextureScale).r);\n"
+"#    endif\n"
+"#  endif\n"
+"    return f;\n"
+"}\n"
+"# endif\n"
+"\n"
+"# ifdef USESHADOWMAPCUBE\n"
+"float ShadowMapCompare(vec3 dir)\n"
+"{\n"
+"    // apply depth texture cubemap as light filter\n"
+"    vec4 shadowmaptc = GetShadowMapTCCube(dir);\n"
+"    float f;\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"    f = shadowCube(Texture_ShadowMapCube, shadowmaptc).r;\n"
+"#  else\n"
+"    f = step(shadowmaptc.w, textureCube(Texture_ShadowMapCube, shadowmaptc.xyz).r);\n"
+"#  endif\n"
+"    return f;\n"
+"}\n"
+"# endif\n"
+"#endif\n"
+"\n"
 "#ifdef MODE_WATER\n"
 "\n"
 "// water pass\n"
@@ -817,7 +1116,23 @@ static const char *builtinshaderstring =
 "\n"
 "      vec4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
 "      //vec4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      vec4 ScreenTexCoord = ModelViewProjectionPosition.xyxy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect + vec2(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xyxy * DistortScaleRefractReflect;\n"
+"      vec4 SafeScreenTexCoord = ModelViewProjectionPosition.xyxy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
+"      vec4 ScreenTexCoord = SafeScreenTexCoord + vec2(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xyxy * DistortScaleRefractReflect;\n"
+"      // FIXME temporary hack to detect the case that the reflection\n"
+"      // gets blackened at edges due to leaving the area that contains actual\n"
+"      // content.\n"
+"      // Remove this 'ack once we have a better way to stop this thing from\n"
+"      // 'appening.\n"
+"      float f = min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(0.01, -0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
+"      ScreenTexCoord.xy = mix(SafeScreenTexCoord.xy, ScreenTexCoord.xy, f);\n"
+"      f       = min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(0.01, -0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
+"      ScreenTexCoord.zw = mix(SafeScreenTexCoord.zw, ScreenTexCoord.zw, f);\n"
 "      float Fresnel = pow(min(1.0, 1.0 - float(normalize(EyeVector).z)), 2.0) * ReflectFactor + ReflectOffset;\n"
 "      gl_FragColor = mix(texture2D(Texture_Refraction, ScreenTexCoord.xy) * RefractColor, texture2D(Texture_Reflection, ScreenTexCoord.zw) * ReflectColor, Fresnel);\n"
 "}\n"
@@ -836,7 +1151,18 @@ static const char *builtinshaderstring =
 "\n"
 "      vec2 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect.xy * (1.0 / ModelViewProjectionPosition.w);\n"
 "      //vec2 ScreenTexCoord = (ModelViewProjectionPosition.xy + normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5)).xy * DistortScaleRefractReflect.xy * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      vec2 ScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy + vec2(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xy * DistortScaleRefractReflect.xy;\n"
+"      vec2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
+"      vec2 ScreenTexCoord = SafeScreenTexCoord + vec2(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xy * DistortScaleRefractReflect.xy;\n"
+"      // FIXME temporary hack to detect the case that the reflection\n"
+"      // gets blackened at edges due to leaving the area that contains actual\n"
+"      // content.\n"
+"      // Remove this 'ack once we have a better way to stop this thing from\n"
+"      // 'appening.\n"
+"      float f = min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(0.01, -0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
+"      ScreenTexCoord = mix(SafeScreenTexCoord, ScreenTexCoord, f);\n"
 "      gl_FragColor = texture2D(Texture_Refraction, ScreenTexCoord) * RefractColor;\n"
 "}\n"
 "\n"
@@ -858,7 +1184,7 @@ static const char *builtinshaderstring =
 "      myhalf terrainblend = clamp(myhalf(gl_Color.a) * color.a * 2.0 - 0.5, myhalf(0.0), myhalf(1.0));\n"
 "      //myhalf terrainblend = min(myhalf(gl_Color.a) * color.a * 2.0, myhalf(1.0));\n"
 "      //myhalf terrainblend = myhalf(gl_Color.a) * color.a > 0.5;\n"
-"      color.rgb = mix(myhalf3(texture2D(Texture_SecondaryColor, TexCoord)), color.rgb, terrainblend);\n"
+"      color.rgb = mix(myhalf3(texture2D(Texture_SecondaryColor, TexCoord2)), color.rgb, terrainblend);\n"
 "      color.a = 1.0;\n"
 "      //color = mix(myhalf4(1, 0, 0, 1), color, terrainblend);\n"
 "#endif\n"
@@ -866,9 +1192,9 @@ static const char *builtinshaderstring =
 "#ifdef USEDIFFUSE\n"
 "      // get the surface normal and the gloss color\n"
 "# ifdef USEVERTEXTEXTUREBLEND\n"
-"      myhalf3 surfacenormal = normalize(mix(myhalf3(texture2D(Texture_SecondaryNormal, TexCoord)), myhalf3(texture2D(Texture_Normal, TexCoord)), terrainblend) - myhalf3(0.5, 0.5, 0.5));\n"
+"      myhalf3 surfacenormal = normalize(mix(myhalf3(texture2D(Texture_SecondaryNormal, TexCoord2)), myhalf3(texture2D(Texture_Normal, TexCoord)), terrainblend) - myhalf3(0.5, 0.5, 0.5));\n"
 "#  ifdef USESPECULAR\n"
-"      myhalf3 glosscolor = mix(myhalf3(texture2D(Texture_SecondaryGloss, TexCoord)), myhalf3(texture2D(Texture_Gloss, TexCoord)), terrainblend);\n"
+"      myhalf3 glosscolor = mix(myhalf3(texture2D(Texture_SecondaryGloss, TexCoord2)), myhalf3(texture2D(Texture_Gloss, TexCoord)), terrainblend);\n"
 "#  endif\n"
 "# else\n"
 "      myhalf3 surfacenormal = normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5, 0.5, 0.5));\n"
@@ -912,6 +1238,12 @@ static const char *builtinshaderstring =
 "#  endif\n"
 "# endif\n"
 "\n"
+"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAPCUBE) || defined(USESHADOWMAP2D)\n"
+"#if !showshadowmap\n"
+"    color.rgb *= ShadowMapCompare(CubeVector);\n"
+"#endif\n"
+"#endif\n"
+"\n"
 "# ifdef USECUBEFILTER\n"
 "      // apply light cubemap filter\n"
 "      //color.rgb *= normalize(CubeVector) * 0.5 + 0.5;//vec3(textureCube(Texture_Cube, CubeVector));\n"
@@ -973,7 +1305,7 @@ static const char *builtinshaderstring =
 "              // 0.25 supports up to 75.5 degrees normal/deluxe angle\n"
 "# ifdef USESPECULAR\n"
 "#  ifdef USEEXACTSPECULARMATH\n"
-"      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(reflect(diffusenormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower);\n"
+"      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(reflect(normalize(diffusenormal), surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower);\n"
 "#  else\n"
 "      myhalf3 specularnormal = myhalf3(normalize(diffusenormal + myhalf3(normalize(EyeVector))));\n"
 "      tempcolor += myhalf3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
@@ -1038,11 +1370,11 @@ static const char *builtinshaderstring =
 "      color *= TintColor;\n"
 "\n"
 "#ifdef USEGLOW\n"
+"#ifdef USEVERTEXTEXTUREBLEND\n"
+"      color.rgb += mix(myhalf3(texture2D(Texture_SecondaryGlow, TexCoord2)), myhalf3(texture2D(Texture_Glow, TexCoord)), terrainblend);\n"
+"#else\n"
 "      color.rgb += myhalf3(texture2D(Texture_Glow, TexCoord)) * GlowScale;\n"
 "#endif\n"
-"\n"
-"#ifdef USECONTRASTBOOST\n"
-"      color.rgb = color.rgb / (ContrastBoostCoeff * color.rgb + myhalf3(1, 1, 1));\n"
 "#endif\n"
 "\n"
 "      color.rgb *= SceneBrightness;\n"
@@ -1056,11 +1388,47 @@ static const char *builtinshaderstring =
 "#ifdef USEREFLECTION\n"
 "      vec4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
 "      //vec4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      vec4 ScreenTexCoord = ModelViewProjectionPosition.xyxy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect + vec3(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xyxy * DistortScaleRefractReflect;\n"
-"      color.rgb = mix(color.rgb, myhalf3(texture2D(Texture_Reflection, ScreenTexCoord.zw)) * ReflectColor.rgb, ReflectColor.a);\n"
+"      vec2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW.zw + ScreenCenterRefractReflect.zw;\n"
+"      vec2 ScreenTexCoord = SafeScreenTexCoord + vec3(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xy * DistortScaleRefractReflect.zw;\n"
+"      // FIXME temporary hack to detect the case that the reflection\n"
+"      // gets blackened at edges due to leaving the area that contains actual\n"
+"      // content.\n"
+"      // Remove this 'ack once we have a better way to stop this thing from\n"
+"      // 'appening.\n"
+"      float f = min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(0.01, -0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
+"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
+"      ScreenTexCoord = mix(SafeScreenTexCoord, ScreenTexCoord, f);\n"
+"      color.rgb = mix(color.rgb, myhalf3(texture2D(Texture_Reflection, ScreenTexCoord)) * ReflectColor.rgb, ReflectColor.a);\n"
 "#endif\n"
 "\n"
 "      gl_FragColor = vec4(color);\n"
+"\n"
+"#if showshadowmap\n"
+"# ifdef USESHADOWMAPRECT\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"      gl_FragColor = shadow2DRect(Texture_ShadowMapRect, GetShadowMapTC2D(CubeVector).xyz);\n"
+"#  else\n"
+"      gl_FragColor = texture2DRect(Texture_ShadowMapRect, GetShadowMapTC2D(CubeVector).xy);\n"
+"#  endif\n"
+"# endif\n"
+"# ifdef USESHADOWMAP2D\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"    gl_FragColor = shadow2D(Texture_ShadowMap2D, GetShadowMapTC2D(CubeVector).xyz);\n"
+"#  else\n"
+"    gl_FragColor = texture2D(Texture_ShadowMap2D, GetShadowMapTC2D(CubeVector).xy);\n"
+"#  endif\n"
+"# endif\n"
+"\n"
+"# ifdef USESHADOWMAPCUBE\n"
+"#  ifdef USESHADOWSAMPLER\n"
+"    gl_FragColor = shadowCube(Texture_ShadowMapCube, GetShadowMapTCCube(CubeVector));\n"
+"#  else\n"
+"    gl_FragColor = textureCube(Texture_ShadowMapCube, GetShadowMapTCCube(CubeVector).xyz);\n"
+"#  endif\n"
+"# endif\n"
+"#endif\n"
 "}\n"
 "#endif // !MODE_REFRACTION\n"
 "#endif // !MODE_WATER\n"
@@ -1069,6 +1437,7 @@ static const char *builtinshaderstring =
 "\n"
 "#endif // !MODE_GENERIC\n"
 "#endif // !MODE_POSTPROCESS\n"
+"#endif // !MODE_SHOWDEPTH\n"
 "#endif // !MODE_DEPTH_OR_SHADOW\n"
 ;
 
@@ -1091,22 +1460,31 @@ shadermodeinfo_t;
 
 typedef enum shaderpermutation_e
 {
-       SHADERPERMUTATION_DIFFUSE = 1<<0, // (lightsource) whether to use directional shading
-       SHADERPERMUTATION_VERTEXTEXTUREBLEND = 1<<1, // indicates this is a two-layer material blend based on vertex alpha (q3bsp)
-       SHADERPERMUTATION_COLORMAPPING = 1<<2, // indicates this is a colormapped skin
-       SHADERPERMUTATION_CONTRASTBOOST = 1<<3, // r_glsl_contrastboost boosts the contrast at low color levels (similar to gamma)
-       SHADERPERMUTATION_FOG = 1<<4, // tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_CUBEFILTER = 1<<5, // (lightsource) use cubemap light filter
-       SHADERPERMUTATION_GLOW = 1<<6, // (lightmap) blend in an additive glow texture
-       SHADERPERMUTATION_SPECULAR = 1<<7, // (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<8, // (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
-       SHADERPERMUTATION_REFLECTION = 1<<9, // normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<10, // adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<11, // adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_GAMMARAMPS = 1<<12, // gamma (postprocessing only)
-       SHADERPERMUTATION_POSTPROCESSING = 1<<13, // user defined postprocessing
-       SHADERPERMUTATION_LIMIT = 1<<14, // size of permutations array
-       SHADERPERMUTATION_COUNT = 14 // size of shaderpermutationinfo array
+       SHADERPERMUTATION_DIFFUSE = 1<<0, ///< (lightsource) whether to use directional shading
+       SHADERPERMUTATION_VERTEXTEXTUREBLEND = 1<<1, ///< indicates this is a two-layer material blend based on vertex alpha (q3bsp)
+       SHADERPERMUTATION_VIEWTINT = 1<<1, ///< view tint (postprocessing only)
+       SHADERPERMUTATION_COLORMAPPING = 1<<2, ///< indicates this is a colormapped skin
+       SHADERPERMUTATION_SATURATION = 1<<2, ///< saturation (postprocessing only)
+       SHADERPERMUTATION_FOG = 1<<3, ///< tint the color by fog color or black if using additive blend mode
+       SHADERPERMUTATION_GAMMARAMPS = 1<<3, ///< gamma (postprocessing only)
+       SHADERPERMUTATION_CUBEFILTER = 1<<4, ///< (lightsource) use cubemap light filter
+       SHADERPERMUTATION_GLOW = 1<<5, ///< (lightmap) blend in an additive glow texture
+       SHADERPERMUTATION_BLOOM = 1<<5, ///< bloom (postprocessing only)
+       SHADERPERMUTATION_SPECULAR = 1<<6, ///< (lightsource or deluxemapping) render specular effects
+       SHADERPERMUTATION_POSTPROCESSING = 1<<6, ///< user defined postprocessing (postprocessing only)
+       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<7, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
+       SHADERPERMUTATION_REFLECTION = 1<<8, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
+       SHADERPERMUTATION_OFFSETMAPPING = 1<<9, ///< adjust texcoords to roughly simulate a displacement mapped surface
+       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<10, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+       SHADERPERMUTATION_SHADOWMAPRECT = 1<<11, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<12, ///< (lightsource) use shadowmap cubemap texture as light filter
+       SHADERPERMUTATION_SHADOWMAP2D = 1<<13, ///< (lightsource) use shadowmap rectangle texture as light filter
+       SHADERPERMUTATION_SHADOWMAPPCF = 1<<14, //< (lightsource) use percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<15, //< (lightsource) use higher quality percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWSAMPLER = 1<<16, //< (lightsource) use hardware shadowmap test
+       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<17, //< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+       SHADERPERMUTATION_LIMIT = 1<<18, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 18 ///< size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -1114,36 +1492,41 @@ shaderpermutation_t;
 shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
 {
        {"#define USEDIFFUSE\n", " diffuse"},
-       {"#define USEVERTEXTEXTUREBLEND\n", " vertextextureblend"},
-       {"#define USECOLORMAPPING\n", " colormapping"},
-       {"#define USECONTRASTBOOST\n", " contrastboost"},
-       {"#define USEFOG\n", " fog"},
+       {"#define USEVERTEXTEXTUREBLEND\n#define USEVIEWTINT\n", " vertextextureblend/tint"},
+       {"#define USECOLORMAPPING\n#define USESATURATION\n", " colormapping/saturation"},
+       {"#define USEFOG\n#define USEGAMMARAMPS\n", " fog/gammaramps"},
        {"#define USECUBEFILTER\n", " cubefilter"},
-       {"#define USEGLOW\n", " glow"},
-       {"#define USESPECULAR\n", " specular"},
+       {"#define USEGLOW\n#define USEBLOOM\n", " glow/bloom"},
+       {"#define USESPECULAR\n#define USEPOSTPROCESSING", " specular/postprocessing"},
        {"#define USEEXACTSPECULARMATH\n", " exactspecularmath"},
        {"#define USEREFLECTION\n", " reflection"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
        {"#define USEOFFSETMAPPING_RELIEFMAPPING\n", " reliefmapping"},
-       {"#define USEGAMMARAMPS\n", " gammaramps"},
-       {"#define USEPOSTPROCESSING\n", " postprocessing"},
+       {"#define USESHADOWMAPRECT\n", " shadowmaprect"},
+       {"#define USESHADOWMAPCUBE\n", " shadowmapcube"},
+       {"#define USESHADOWMAP2D\n", " shadowmap2d"},
+       {"#define USESHADOWMAPPCF 1\n", " shadowmappcf"},
+       {"#define USESHADOWMAPPCF 2\n", " shadowmappcf2"},
+       {"#define USESHADOWSAMPLER\n", " shadowsampler"},
+       {"#define USESHADOWMAPVSDCT\n", " shadowmapvsdct"},
 };
 
-// this enum is multiplied by SHADERPERMUTATION_MODEBASE
+/// this enum is multiplied by SHADERPERMUTATION_MODEBASE
 typedef enum shadermode_e
 {
-       SHADERMODE_GENERIC, // (particles/HUD/etc) vertex color, optionally multiplied by one texture
-       SHADERMODE_POSTPROCESS, // postprocessing shader (r_glsl_postprocess)
-       SHADERMODE_DEPTH_OR_SHADOW, // (depthfirst/shadows) vertex shader only
-       SHADERMODE_FLATCOLOR, // (lightmap) modulate texture by uniform color (q1bsp, q3bsp)
-       SHADERMODE_VERTEXCOLOR, // (lightmap) modulate texture by vertex colors (q3bsp)
-       SHADERMODE_LIGHTMAP, // (lightmap) modulate texture by lightmap texture (q1bsp, q3bsp)
-       SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE, // (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
-       SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE, // (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
-       SHADERMODE_LIGHTDIRECTION, // (lightmap) use directional pixel shading from fixed light direction (q3bsp)
-       SHADERMODE_LIGHTSOURCE, // (lightsource) use directional pixel shading from light source (rtlight)
-       SHADERMODE_REFRACTION, // refract background (the material is rendered normally after this pass)
-       SHADERMODE_WATER, // refract background and reflection (the material is rendered normally after this pass)
+       SHADERMODE_GENERIC, ///< (particles/HUD/etc) vertex color, optionally multiplied by one texture
+       SHADERMODE_POSTPROCESS, ///< postprocessing shader (r_glsl_postprocess)
+       SHADERMODE_DEPTH_OR_SHADOW, ///< (depthfirst/shadows) vertex shader only
+       SHADERMODE_FLATCOLOR, ///< (lightmap) modulate texture by uniform color (q1bsp, q3bsp)
+       SHADERMODE_VERTEXCOLOR, ///< (lightmap) modulate texture by vertex colors (q3bsp)
+       SHADERMODE_LIGHTMAP, ///< (lightmap) modulate texture by lightmap texture (q1bsp, q3bsp)
+       SHADERMODE_LIGHTDIRECTIONMAP_MODELSPACE, ///< (lightmap) use directional pixel shading from texture containing modelspace light directions (q3bsp deluxemap)
+       SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE, ///< (lightmap) use directional pixel shading from texture containing tangentspace light directions (q1bsp deluxemap)
+       SHADERMODE_LIGHTDIRECTION, ///< (lightmap) use directional pixel shading from fixed light direction (q3bsp)
+       SHADERMODE_LIGHTSOURCE, ///< (lightsource) use directional pixel shading from light source (rtlight)
+       SHADERMODE_REFRACTION, ///< refract background (the material is rendered normally after this pass)
+       SHADERMODE_WATER, ///< refract background and reflection (the material is rendered normally after this pass)
+       SHADERMODE_SHOWDEPTH, ///< (debugging) renders depth as color
        SHADERMODE_COUNT
 }
 shadermode_t;
@@ -1163,15 +1546,22 @@ shadermodeinfo_t shadermodeinfo[SHADERMODE_COUNT] =
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTSOURCE\n", " lightsource"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_REFRACTION\n", " refraction"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_WATER\n", " water"},
+       {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_SHOWDEPTH\n", " showdepth"},
 };
 
+struct r_glsl_permutation_s;
 typedef struct r_glsl_permutation_s
 {
-       // indicates if we have tried compiling this permutation already
+       /// hash lookup data
+       struct r_glsl_permutation_s *hashnext;
+       unsigned int mode;
+       unsigned int permutation;
+
+       /// indicates if we have tried compiling this permutation already
        qboolean compiled;
-       // 0 if compilation failed
+       /// 0 if compilation failed
        int program;
-       // locations of detected uniforms in program object, or -1 if not found
+       /// locations of detected uniforms in program object, or -1 if not found
        int loc_Texture_First;
        int loc_Texture_Second;
        int loc_Texture_GammaRamps;
@@ -1192,6 +1582,10 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_Cube;
        int loc_Texture_Refraction;
        int loc_Texture_Reflection;
+       int loc_Texture_ShadowMapRect;
+       int loc_Texture_ShadowMapCube;
+       int loc_Texture_ShadowMap2D;
+       int loc_Texture_CubeProjection;
        int loc_FogColor;
        int loc_LightPosition;
        int loc_EyePosition;
@@ -1210,8 +1604,8 @@ typedef struct r_glsl_permutation_s
        int loc_DiffuseColor;
        int loc_SpecularColor;
        int loc_LightDir;
-       int loc_ContrastBoostCoeff; // 1 - 1/ContrastBoost
-       int loc_GammaCoeff; // 1 / gamma
+       int loc_ContrastBoostCoeff; ///< 1 - 1/ContrastBoost
+       int loc_GammaCoeff; ///< 1 / gamma
        int loc_DistortScaleRefractReflect;
        int loc_ScreenScaleRefractReflect;
        int loc_ScreenCenterRefractReflect;
@@ -1225,13 +1619,45 @@ typedef struct r_glsl_permutation_s
        int loc_UserVec4;
        int loc_ClientTime;
        int loc_PixelSize;
+       int loc_Saturation;
+       int loc_ShadowMap_TextureScale;
+       int loc_ShadowMap_Parameters;
 }
 r_glsl_permutation_t;
 
-// information about each possible shader permutation
-r_glsl_permutation_t r_glsl_permutations[SHADERMODE_COUNT][SHADERPERMUTATION_LIMIT];
-// currently selected permutation
+#define SHADERPERMUTATION_HASHSIZE 4096
+
+/// information about each possible shader permutation
+r_glsl_permutation_t *r_glsl_permutationhash[SHADERMODE_COUNT][SHADERPERMUTATION_HASHSIZE];
+/// currently selected permutation
 r_glsl_permutation_t *r_glsl_permutation;
+/// storage for permutations linked in the hash table
+memexpandablearray_t r_glsl_permutationarray;
+
+static r_glsl_permutation_t *R_GLSL_FindPermutation(unsigned int mode, unsigned int permutation)
+{
+       //unsigned int hashdepth = 0;
+       unsigned int hashindex = (permutation * 0x1021) & (SHADERPERMUTATION_HASHSIZE - 1);
+       r_glsl_permutation_t *p;
+       for (p = r_glsl_permutationhash[mode][hashindex];p;p = p->hashnext)
+       {
+               if (p->mode == mode && p->permutation == permutation)
+               {
+                       //if (hashdepth > 10)
+                       //      Con_Printf("R_GLSL_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
+                       return p;
+               }
+               //hashdepth++;
+       }
+       p = (r_glsl_permutation_t*)Mem_ExpandableArray_AllocRecord(&r_glsl_permutationarray);
+       p->mode = mode;
+       p->permutation = permutation;
+       p->hashnext = r_glsl_permutationhash[mode][hashindex];
+       r_glsl_permutationhash[mode][hashindex] = p;
+       //if (hashdepth > 10)
+       //      Con_Printf("R_GLSL_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
+       return p;
+}
 
 static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
 {
@@ -1253,11 +1679,10 @@ static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
        return shaderstring;
 }
 
-static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutation)
+static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode, unsigned int permutation)
 {
        int i;
        shadermodeinfo_t *modeinfo = shadermodeinfo + mode;
-       r_glsl_permutation_t *p = &r_glsl_permutations[mode][permutation];
        int vertstrings_count = 0;
        int geomstrings_count = 0;
        int fragstrings_count = 0;
@@ -1352,6 +1777,10 @@ static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutatio
                p->loc_Texture_Reflection         = qglGetUniformLocationARB(p->program, "Texture_Reflection");
                p->loc_Texture_Attenuation        = qglGetUniformLocationARB(p->program, "Texture_Attenuation");
                p->loc_Texture_Cube               = qglGetUniformLocationARB(p->program, "Texture_Cube");
+               p->loc_Texture_ShadowMapRect      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapRect");
+               p->loc_Texture_ShadowMapCube      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapCube");
+               p->loc_Texture_ShadowMap2D        = qglGetUniformLocationARB(p->program, "Texture_ShadowMap2D");
+               p->loc_Texture_CubeProjection     = qglGetUniformLocationARB(p->program, "Texture_CubeProjection");  
                p->loc_FogColor                   = qglGetUniformLocationARB(p->program, "FogColor");
                p->loc_LightPosition              = qglGetUniformLocationARB(p->program, "LightPosition");
                p->loc_EyePosition                = qglGetUniformLocationARB(p->program, "EyePosition");
@@ -1385,6 +1814,9 @@ static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutatio
                p->loc_UserVec4                   = qglGetUniformLocationARB(p->program, "UserVec4");
                p->loc_ClientTime                 = qglGetUniformLocationARB(p->program, "ClientTime");
                p->loc_PixelSize                  = qglGetUniformLocationARB(p->program, "PixelSize");
+               p->loc_Saturation                 = qglGetUniformLocationARB(p->program, "Saturation");
+               p->loc_ShadowMap_TextureScale     = qglGetUniformLocationARB(p->program, "ShadowMap_TextureScale");
+               p->loc_ShadowMap_Parameters       = qglGetUniformLocationARB(p->program, "ShadowMap_Parameters");
                // initialize the samplers to refer to the texture units we use
                if (p->loc_Texture_First           >= 0) qglUniform1iARB(p->loc_Texture_First          , GL20TU_FIRST);
                if (p->loc_Texture_Second          >= 0) qglUniform1iARB(p->loc_Texture_Second         , GL20TU_SECOND);
@@ -1406,6 +1838,10 @@ static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutatio
                if (p->loc_Texture_Cube            >= 0) qglUniform1iARB(p->loc_Texture_Cube           , GL20TU_CUBE);
                if (p->loc_Texture_Refraction      >= 0) qglUniform1iARB(p->loc_Texture_Refraction     , GL20TU_REFRACTION);
                if (p->loc_Texture_Reflection      >= 0) qglUniform1iARB(p->loc_Texture_Reflection     , GL20TU_REFLECTION);
+               if (p->loc_Texture_ShadowMapRect   >= 0) qglUniform1iARB(p->loc_Texture_ShadowMapRect  , GL20TU_SHADOWMAPRECT);
+               if (p->loc_Texture_ShadowMapCube   >= 0) qglUniform1iARB(p->loc_Texture_ShadowMapCube  , GL20TU_SHADOWMAPCUBE);
+               if (p->loc_Texture_ShadowMap2D     >= 0) qglUniform1iARB(p->loc_Texture_ShadowMap2D    , GL20TU_SHADOWMAP2D);
+               if (p->loc_Texture_CubeProjection  >= 0) qglUniform1iARB(p->loc_Texture_CubeProjection , GL20TU_CUBEPROJECTION);
                CHECKGLERROR
                if (developer.integer)
                        Con_Printf("GLSL shader %s compiled.\n", permutationname);
@@ -1424,13 +1860,18 @@ static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutatio
 
 void R_GLSL_Restart_f(void)
 {
-       unsigned int mode;
-       unsigned int permutation;
-       for (mode = 0;mode < SHADERMODE_COUNT;mode++)
-               for (permutation = 0;permutation < SHADERPERMUTATION_LIMIT;permutation++)
-                       if (r_glsl_permutations[mode][permutation].program)
-                               GL_Backend_FreeProgram(r_glsl_permutations[mode][permutation].program);
-       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
+       unsigned int i, limit;
+       r_glsl_permutation_t *p;
+       limit = Mem_ExpandableArray_IndexRange(&r_glsl_permutationarray);
+       for (i = 0;i < limit;i++)
+       {
+               if ((p = (r_glsl_permutation_t*)Mem_ExpandableArray_RecordAtIndex(&r_glsl_permutationarray, i)))
+               {
+                       GL_Backend_FreeProgram(p->program);
+                       Mem_ExpandableArray_FreeRecord(&r_glsl_permutationarray, (void*)p);
+               }
+       }
+       memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
 }
 
 void R_GLSL_DumpShader_f(void)
@@ -1444,13 +1885,13 @@ void R_GLSL_DumpShader_f(void)
                return;
        }
 
-       FS_Print(file, "// The engine may define the following macros:\n");
-       FS_Print(file, "// #define VERTEX_SHADER\n// #define GEOMETRY_SHADER\n// #define FRAGMENT_SHADER\n");
+       FS_Print(file, "/* The engine may define the following macros:\n");
+       FS_Print(file, "#define VERTEX_SHADER\n#define GEOMETRY_SHADER\n#define FRAGMENT_SHADER\n");
        for (i = 0;i < SHADERMODE_COUNT;i++)
-               FS_Printf(file, "// %s", shadermodeinfo[i].pretext);
+               FS_Print(file, shadermodeinfo[i].pretext);
        for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               FS_Printf(file, "// %s", shaderpermutationinfo[i].pretext);
-       FS_Print(file, "\n");
+               FS_Print(file, shaderpermutationinfo[i].pretext);
+       FS_Print(file, "*/\n");
        FS_Print(file, builtinshaderstring);
        FS_Close(file);
 
@@ -1459,14 +1900,14 @@ void R_GLSL_DumpShader_f(void)
 
 void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
 {
-       r_glsl_permutation_t *perm = &r_glsl_permutations[mode][permutation];
+       r_glsl_permutation_t *perm = R_GLSL_FindPermutation(mode, permutation);
        if (r_glsl_permutation != perm)
        {
                r_glsl_permutation = perm;
                if (!r_glsl_permutation->program)
                {
                        if (!r_glsl_permutation->compiled)
-                               R_GLSL_CompilePermutation(mode, permutation);
+                               R_GLSL_CompilePermutation(perm, mode, permutation);
                        if (!r_glsl_permutation->program)
                        {
                                // remove features until we find a valid permutation
@@ -1478,9 +1919,9 @@ void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
                                        if (!(permutation & j))
                                                continue;
                                        permutation -= j;
-                                       r_glsl_permutation = &r_glsl_permutations[mode][permutation];
+                                       r_glsl_permutation = R_GLSL_FindPermutation(mode, permutation);
                                        if (!r_glsl_permutation->compiled)
-                                               R_GLSL_CompilePermutation(mode, permutation);
+                                               R_GLSL_CompilePermutation(perm, mode, permutation);
                                        if (r_glsl_permutation->program)
                                                break;
                                }
@@ -1546,9 +1987,31 @@ void R_SetupDepthOrShadowShader(void)
        }
 }
 
+void R_SetupShowDepthShader(void)
+{
+       if (gl_support_fragment_shader)
+       {
+               if (r_glsl.integer && r_glsl_usegeneric.integer)
+                       R_SetupShader_SetPermutation(SHADERMODE_SHOWDEPTH, 0);
+               else if (r_glsl_permutation)
+               {
+                       r_glsl_permutation = NULL;
+                       qglUseProgramObjectARB(0);CHECKGLERROR
+               }
+       }
+}
+
 extern rtexture_t *r_shadow_attenuationgradienttexture;
 extern rtexture_t *r_shadow_attenuation2dtexture;
 extern rtexture_t *r_shadow_attenuation3dtexture;
+extern qboolean r_shadow_usingshadowmaprect;
+extern qboolean r_shadow_usingshadowmapcube;
+extern qboolean r_shadow_usingshadowmap2d;
+extern float r_shadow_shadowmap_texturescale[2];
+extern float r_shadow_shadowmap_parameters[4];
+extern qboolean r_shadow_shadowmapvsdct;
+extern qboolean r_shadow_shadowmapsampler;
+extern int r_shadow_shadowmappcf;
 void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass)
 {
        // select a permutation of the lighting shader appropriate to this
@@ -1588,8 +2051,24 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
-                       permutation |= SHADERPERMUTATION_CONTRASTBOOST;
+               if (r_shadow_usingshadowmaprect || r_shadow_usingshadowmap2d || r_shadow_usingshadowmapcube)
+               {
+                       if (r_shadow_usingshadowmaprect)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPRECT;
+                       if (r_shadow_usingshadowmap2d)
+                               permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+                       if (r_shadow_usingshadowmapcube)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPCUBE;
+                       else if(r_shadow_shadowmapvsdct)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPVSDCT;
+
+                       if (r_shadow_shadowmapsampler)
+                               permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
+                       if (r_shadow_shadowmappcf > 1)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
+                       else if (r_shadow_shadowmappcf)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
+               }
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
        {
@@ -1609,8 +2088,6 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        if (r_glsl_offsetmapping_reliefmapping.integer)
                                permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
                }
-               if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
-                       permutation |= SHADERPERMUTATION_CONTRASTBOOST;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
        }
@@ -1629,8 +2106,6 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
-                       permutation |= SHADERPERMUTATION_CONTRASTBOOST;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
        }
@@ -1646,8 +2121,6 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
-                       permutation |= SHADERPERMUTATION_CONTRASTBOOST;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
        }
@@ -1691,8 +2164,6 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if(r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)
-                       permutation |= SHADERPERMUTATION_CONTRASTBOOST;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
        }
@@ -1721,6 +2192,8 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                // additive passes are only darkened by fog, not tinted
                if (r_glsl_permutation->loc_FogColor >= 0)
                        qglUniform3fARB(r_glsl_permutation->loc_FogColor, 0, 0, 0);
+               if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform2fARB(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4fARB(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
        }
        else
        {
@@ -1755,19 +2228,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                if (r_glsl_permutation->loc_ReflectFactor >= 0) qglUniform1fARB(r_glsl_permutation->loc_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
                if (r_glsl_permutation->loc_ReflectOffset >= 0) qglUniform1fARB(r_glsl_permutation->loc_ReflectOffset, rsurface.texture->reflectmin);
        }
-       if (r_glsl_permutation->loc_ContrastBoostCoeff >= 0)
-       {
-               // The formula used is actually:
-               //   color.rgb *= ContrastBoost / ((ContrastBoost - 1) * color.rgb + 1);
-               //   color.rgb *= SceneBrightness;
-               // simplified:
-               //   color.rgb = [[SceneBrightness * ContrastBoost]] * color.rgb / ([[ContrastBoost - 1]] * color.rgb + 1);
-               // and do [[calculations]] here in the engine
-               qglUniform1fARB(r_glsl_permutation->loc_ContrastBoostCoeff, r_glsl_contrastboost.value - 1);
-               if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_refdef.view.colorscale * r_glsl_contrastboost.value);
-       }
-       else
-               if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_refdef.view.colorscale);
+       if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_refdef.view.colorscale);
        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)
        {
@@ -2275,12 +2736,14 @@ void gl_main_start(void)
        //r_texture_fogintensity = NULL;
        memset(&r_bloomstate, 0, sizeof(r_bloomstate));
        memset(&r_waterstate, 0, sizeof(r_waterstate));
-       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
+       memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
+       Mem_ExpandableArray_NewArray(&r_glsl_permutationarray, r_main_mempool, sizeof(r_glsl_permutation_t), 256);
        memset(&r_svbsp, 0, sizeof (r_svbsp));
 
        r_refdef.fogmasktable_density = 0;
 }
 
+extern rtexture_t *loadingscreentexture;
 void gl_main_shutdown(void)
 {
        if (r_maxqueries)
@@ -2301,6 +2764,7 @@ void gl_main_shutdown(void)
                Mem_Free(r_svbsp.nodes);
        memset(&r_svbsp, 0, sizeof (r_svbsp));
        R_FreeTexturePool(&r_main_texturepool);
+       loadingscreentexture = NULL;
        r_texture_blanknormalmap = NULL;
        r_texture_white = NULL;
        r_texture_grey128 = NULL;
@@ -2358,6 +2822,15 @@ void GL_Main_Init(void)
                Cvar_RegisterVariable (&gl_fogend);
                Cvar_RegisterVariable (&gl_skyclip);
        }
+       Cvar_RegisterVariable(&r_motionblur);
+       Cvar_RegisterVariable(&r_motionblur_maxblur);
+       Cvar_RegisterVariable(&r_motionblur_bmin);
+       Cvar_RegisterVariable(&r_motionblur_vmin);
+       Cvar_RegisterVariable(&r_motionblur_vmax);
+       Cvar_RegisterVariable(&r_motionblur_vcoeff);
+       Cvar_RegisterVariable(&r_motionblur_randomize);
+       Cvar_RegisterVariable(&r_damageblur);
+       Cvar_RegisterVariable(&r_animcache);
        Cvar_RegisterVariable(&r_depthfirst);
        Cvar_RegisterVariable(&r_useinfinitefarclip);
        Cvar_RegisterVariable(&r_nearclip);
@@ -2384,7 +2857,11 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_dynamic);
        Cvar_RegisterVariable(&r_fullbright);
        Cvar_RegisterVariable(&r_shadows);
+       Cvar_RegisterVariable(&r_shadows_darken);
+       Cvar_RegisterVariable(&r_shadows_drawafterrtlighting);
+       Cvar_RegisterVariable(&r_shadows_castfrombmodels);
        Cvar_RegisterVariable(&r_shadows_throwdistance);
+       Cvar_RegisterVariable(&r_shadows_throwdirection);
        Cvar_RegisterVariable(&r_q1bsp_skymasking);
        Cvar_RegisterVariable(&r_polygonoffset_submodel_factor);
        Cvar_RegisterVariable(&r_polygonoffset_submodel_offset);
@@ -2392,7 +2869,6 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_drawfog);
        Cvar_RegisterVariable(&r_textureunits);
        Cvar_RegisterVariable(&r_glsl);
-       Cvar_RegisterVariable(&r_glsl_contrastboost);
        Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping);
@@ -2428,6 +2904,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&gl_lightmaps);
        Cvar_RegisterVariable(&r_test);
        Cvar_RegisterVariable(&r_batchmode);
+       Cvar_RegisterVariable(&r_glsl_saturation);
        if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
                Cvar_SetValue("r_fullbrights", 0);
        R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap);
@@ -2601,6 +3078,197 @@ int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, c
 
 //==================================================================================
 
+// LordHavoc: animcache written by Echon, refactored and reformatted by me
+
+/**
+ * Animation cache helps save re-animating a player mesh if it's re-rendered again in a given frame
+ * (reflections, lighting, etc). All animation cache becomes invalid on the next frame and is flushed
+ * (well, over-wrote). The memory for each cache is kept around to save on allocation thrashing.
+ */
+
+typedef struct r_animcache_entity_s
+{
+       float *vertex3f;
+       float *normal3f;
+       float *svector3f;
+       float *tvector3f;
+       int maxvertices;
+       qboolean wantnormals;
+       qboolean wanttangents;
+}
+r_animcache_entity_t;
+
+typedef struct r_animcache_s
+{
+       r_animcache_entity_t entity[MAX_EDICTS*2];
+       int maxindex;
+       int currentindex;
+}
+r_animcache_t;
+
+static r_animcache_t r_animcachestate;
+
+void R_AnimCache_Free(void)
+{
+       int idx;
+       for (idx=0 ; idx<r_animcachestate.maxindex ; idx++)
+       {
+               r_animcachestate.entity[idx].maxvertices = 0;
+               Mem_Free(r_animcachestate.entity[idx].vertex3f);
+               r_animcachestate.entity[idx].vertex3f = NULL;
+               r_animcachestate.entity[idx].normal3f = NULL;
+               r_animcachestate.entity[idx].svector3f = NULL;
+               r_animcachestate.entity[idx].tvector3f = NULL;
+       }
+       r_animcachestate.currentindex = 0;
+       r_animcachestate.maxindex = 0;
+}
+
+void R_AnimCache_ResizeEntityCache(const int cacheIdx, const int numvertices)
+{
+       int arraySize;
+       float *base;
+       r_animcache_entity_t *cache = &r_animcachestate.entity[cacheIdx];
+
+       if (cache->maxvertices >= numvertices)
+               return;
+
+       // Release existing memory
+       if (cache->vertex3f)
+               Mem_Free(cache->vertex3f);
+
+       // Pad by 1024 verts
+       cache->maxvertices = (numvertices + 1023) & ~1023;
+       arraySize = cache->maxvertices * 3;
+
+       // Allocate, even if we don't need this memory in this instance it will get ignored and potentially used later
+       base = (float *)Mem_Alloc(r_main_mempool, arraySize * sizeof(float) * 4);
+       r_animcachestate.entity[cacheIdx].vertex3f = base;
+       r_animcachestate.entity[cacheIdx].normal3f = base + arraySize;
+       r_animcachestate.entity[cacheIdx].svector3f = base + arraySize*2;
+       r_animcachestate.entity[cacheIdx].tvector3f = base + arraySize*3;
+
+//     Con_Printf("allocated cache for %i (%f KB)\n", cacheIdx, (arraySize*sizeof(float)*4)/1024.0f);
+}
+
+void R_AnimCache_NewFrame(void)
+{
+       int i;
+
+       if (r_animcache.integer && r_drawentities.integer)
+               r_animcachestate.maxindex = sizeof(r_animcachestate.entity) / sizeof(r_animcachestate.entity[0]);
+       else if (r_animcachestate.maxindex)
+               R_AnimCache_Free();
+
+       r_animcachestate.currentindex = 0;
+
+       for (i = 0;i < r_refdef.scene.numentities;i++)
+               r_refdef.scene.entities[i]->animcacheindex = -1;
+}
+
+qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
+{
+       dp_model_t *model = ent->model;
+       r_animcache_entity_t *c;
+       // see if it's already cached this frame
+       if (ent->animcacheindex >= 0)
+       {
+               // add normals/tangents if needed
+               c = r_animcachestate.entity + ent->animcacheindex;
+               if (c->wantnormals)
+                       wantnormals = false;
+               if (c->wanttangents)
+                       wanttangents = false;
+               if (wantnormals || wanttangents)
+                       model->AnimateVertices(model, ent->frameblend, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+       }
+       else
+       {
+               // see if this ent is worth caching
+               if (r_animcachestate.maxindex <= r_animcachestate.currentindex)
+                       return false;
+               if (!model || !model->Draw || !model->surfmesh.isanimated || !model->AnimateVertices || (ent->frameblend[0].lerp == 1 && ent->frameblend[0].subframe == 0))
+                       return false;
+               // assign it a cache entry and make sure the arrays are big enough
+               R_AnimCache_ResizeEntityCache(r_animcachestate.currentindex, model->surfmesh.num_vertices);
+               ent->animcacheindex = r_animcachestate.currentindex++;
+               c = r_animcachestate.entity + ent->animcacheindex;
+               c->wantnormals = wantnormals;
+               c->wanttangents = wanttangents;
+               model->AnimateVertices(model, ent->frameblend, c->vertex3f, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+       }
+       return true;
+}
+
+void R_AnimCache_CacheVisibleEntities(void)
+{
+       int i;
+       qboolean wantnormals;
+       qboolean wanttangents;
+
+       if (!r_animcachestate.maxindex)
+               return;
+
+       wantnormals = !r_showsurfaces.integer;
+       wanttangents = !r_showsurfaces.integer && (r_glsl.integer || r_refdef.scene.rtworld || r_refdef.scene.rtdlight);
+
+       // TODO: thread this?
+
+       for (i = 0;i < r_refdef.scene.numentities;i++)
+       {
+               if (!r_refdef.viewcache.entityvisible[i])
+                       continue;
+               R_AnimCache_GetEntity(r_refdef.scene.entities[i], wantnormals, wanttangents);
+       }
+}
+
+//==================================================================================
+
+static void R_View_UpdateEntityLighting (void)
+{
+       int i;
+       entity_render_t *ent;
+       vec3_t tempdiffusenormal;
+
+       for (i = 0;i < r_refdef.scene.numentities;i++)
+       {
+               ent = r_refdef.scene.entities[i];
+
+               // skip unseen models
+               if (!r_refdef.viewcache.entityvisible[i] && r_shadows.integer != 1)
+                       continue;
+
+               // skip bsp models
+               if (ent->model && ent->model->brush.num_leafs)
+               {
+                       // TODO: use modellight for r_ambient settings on world?
+                       VectorSet(ent->modellight_ambient, 0, 0, 0);
+                       VectorSet(ent->modellight_diffuse, 0, 0, 0);
+                       VectorSet(ent->modellight_lightdir, 0, 0, 1);
+                       continue;
+               }
+
+               // fetch the lighting from the worldmodel data
+               VectorSet(ent->modellight_ambient, r_refdef.scene.ambient * (2.0f / 128.0f), r_refdef.scene.ambient * (2.0f / 128.0f), r_refdef.scene.ambient * (2.0f / 128.0f));
+               VectorClear(ent->modellight_diffuse);
+               VectorClear(tempdiffusenormal);
+               if ((ent->flags & RENDER_LIGHT) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
+               {
+                       vec3_t org;
+                       Matrix4x4_OriginFromMatrix(&ent->matrix, org);
+                       r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, org, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+               }
+               else // highly rare
+                       VectorSet(ent->modellight_ambient, 1, 1, 1);
+
+               // move the light direction into modelspace coordinates for lighting code
+               Matrix4x4_Transform3x3(&ent->inversematrix, tempdiffusenormal, ent->modellight_lightdir);
+               if(VectorLength2(ent->modellight_lightdir) == 0)
+                       VectorSet(ent->modellight_lightdir, 0, 0, 1); // have to set SOME valid vector here
+               VectorNormalize(ent->modellight_lightdir);
+       }
+}
+
 static void R_View_UpdateEntityVisible (void)
 {
        int i, renderimask;
@@ -2648,7 +3316,7 @@ static void R_View_UpdateEntityVisible (void)
        }
 }
 
-// only used if skyrendermasked, and normally returns false
+/// only used if skyrendermasked, and normally returns false
 int R_DrawBrushModelsSky (void)
 {
        int i, sky;
@@ -2837,10 +3505,10 @@ static void R_View_SetFrustum(void)
                VectorNormalize(r_refdef.view.frustum[3].normal);
 
                // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * slopex, left, -1024 * slopey, up, r_refdef.view.frustumcorner[0]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * slopex, left, -1024 * slopey, up, r_refdef.view.frustumcorner[1]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * slopex, left,  1024 * slopey, up, r_refdef.view.frustumcorner[2]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * slopex, left,  1024 * slopey, up, r_refdef.view.frustumcorner[3]);
+               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * r_refdef.view.frustum_x, left, -1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[0]);
+               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * r_refdef.view.frustum_x, left, -1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[1]);
+               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * r_refdef.view.frustum_x, left,  1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[2]);
+               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * r_refdef.view.frustum_x, left,  1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[3]);
 
                r_refdef.view.frustum[0].dist = DotProduct (r_refdef.view.origin, r_refdef.view.frustum[0].normal);
                r_refdef.view.frustum[1].dist = DotProduct (r_refdef.view.origin, r_refdef.view.frustum[1].normal);
@@ -2907,19 +3575,13 @@ void R_View_Update(void)
        R_View_SetFrustum();
        R_View_WorldVisibility(r_refdef.view.useclipplane);
        R_View_UpdateEntityVisible();
+       R_View_UpdateEntityLighting();
 }
 
 void R_SetupView(qboolean allowwaterclippingplane)
 {
-       if (!r_refdef.view.useperspective)
-               GL_SetupView_Mode_Ortho(-r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip);
-       else if (gl_stencil && r_useinfinitefarclip.integer)
-               GL_SetupView_Mode_PerspectiveInfiniteFarClip(r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip);
-       else
-               GL_SetupView_Mode_Perspective(r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip);
-
-       GL_SetupView_Orientation_FromEntity(&r_refdef.view.matrix);
-
+       const double *customclipplane = NULL;
+       double plane[4];
        if (r_refdef.view.useclipplane && allowwaterclippingplane)
        {
                // LordHavoc: couldn't figure out how to make this approach the
@@ -2927,18 +3589,31 @@ void R_SetupView(qboolean allowwaterclippingplane)
                vec_t viewdist = DotProduct(r_refdef.view.origin, r_refdef.view.clipplane.normal);
                if (viewdist < r_refdef.view.clipplane.dist + r_water_clippingplanebias.value)
                        dist = r_refdef.view.clipplane.dist;
-               GL_SetupView_ApplyCustomNearClipPlane(r_refdef.view.clipplane.normal[0], r_refdef.view.clipplane.normal[1], r_refdef.view.clipplane.normal[2], dist);
+               plane[0] = r_refdef.view.clipplane.normal[0];
+               plane[1] = r_refdef.view.clipplane.normal[1];
+               plane[2] = r_refdef.view.clipplane.normal[2];
+               plane[3] = dist;
+               customclipplane = plane;
        }
+
+       if (!r_refdef.view.useperspective)
+               R_Viewport_InitOrtho(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, -r_refdef.view.ortho_x, -r_refdef.view.ortho_y, r_refdef.view.ortho_x, r_refdef.view.ortho_y, -r_refdef.farclip, r_refdef.farclip, customclipplane);
+       else if (gl_stencil && r_useinfinitefarclip.integer)
+               R_Viewport_InitPerspectiveInfinite(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, customclipplane);
+       else
+               R_Viewport_InitPerspective(&r_refdef.view.viewport, &r_refdef.view.matrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, r_refdef.view.frustum_x, r_refdef.view.frustum_y, r_refdef.nearclip, r_refdef.farclip, customclipplane);
+       R_SetViewport(&r_refdef.view.viewport);
 }
 
 void R_ResetViewRendering2D(void)
 {
+       r_viewport_t viewport;
        DrawQ_Finish();
 
        // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom
-       qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
-       GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.height - r_refdef.view.y, r_refdef.view.width, r_refdef.view.height, 0, 0, 1, 1, -10, 100, NULL);
+       R_SetViewport(&viewport);
+       GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height);
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
@@ -2964,10 +3639,8 @@ void R_ResetViewRendering3D(void)
 {
        DrawQ_Finish();
 
-       // GL is weird because it's bottom to top, r_refdef.view.y is top to bottom
-       qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
        R_SetupView(true);
-       GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
+       GL_Scissor(r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
@@ -2989,7 +3662,8 @@ void R_ResetViewRendering3D(void)
        R_SetupGenericShader(true);
 }
 
-void R_RenderScene(qboolean addwaterplanes);
+void R_RenderScene(void);
+void R_RenderWaterPlanes(void);
 
 static void R_Water_StartFrame(void)
 {
@@ -2999,8 +3673,8 @@ static void R_Water_StartFrame(void)
 
        // set waterwidth and waterheight to the water resolution that will be
        // used (often less than the screen resolution for faster rendering)
-       waterwidth = (int)bound(1, r_refdef.view.width * r_water_resolutionmultiplier.value, r_refdef.view.width);
-       waterheight = (int)bound(1, r_refdef.view.height * r_water_resolutionmultiplier.value, r_refdef.view.height);
+       waterwidth = (int)bound(1, vid.width * r_water_resolutionmultiplier.value, vid.width);
+       waterheight = (int)bound(1, vid.height * r_water_resolutionmultiplier.value, vid.height);
 
        // calculate desired texture sizes
        // can't use water if the card does not support the texture size
@@ -3018,7 +3692,7 @@ static void R_Water_StartFrame(void)
        }
 
        // allocate textures as needed
-       if (r_waterstate.waterwidth != waterwidth || r_waterstate.waterheight != waterheight || r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
        {
                r_waterstate.maxwaterplanes = MAX_WATERPLANES;
                for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
@@ -3031,28 +3705,30 @@ static void R_Water_StartFrame(void)
                        p->texture_reflection = NULL;
                }
                memset(&r_waterstate, 0, sizeof(r_waterstate));
-               r_waterstate.waterwidth = waterwidth;
-               r_waterstate.waterheight = waterheight;
                r_waterstate.texturewidth = texturewidth;
                r_waterstate.textureheight = textureheight;
        }
 
-       if (r_waterstate.waterwidth)
+       if (r_waterstate.texturewidth)
        {
                r_waterstate.enabled = true;
 
+               // when doing a reduced render (HDR) we want to use a smaller area
+               r_waterstate.waterwidth = (int)bound(1, r_refdef.view.width * r_water_resolutionmultiplier.value, r_refdef.view.width);
+               r_waterstate.waterheight = (int)bound(1, r_refdef.view.height * r_water_resolutionmultiplier.value, r_refdef.view.height);
+
                // set up variables that will be used in shader setup
-               r_waterstate.screenscale[0] = 0.5f * (float)waterwidth / (float)texturewidth;
-               r_waterstate.screenscale[1] = 0.5f * (float)waterheight / (float)textureheight;
-               r_waterstate.screencenter[0] = 0.5f * (float)waterwidth / (float)texturewidth;
-               r_waterstate.screencenter[1] = 0.5f * (float)waterheight / (float)textureheight;
+               r_waterstate.screenscale[0] = 0.5f * (float)r_waterstate.waterwidth / (float)r_waterstate.texturewidth;
+               r_waterstate.screenscale[1] = 0.5f * (float)r_waterstate.waterheight / (float)r_waterstate.textureheight;
+               r_waterstate.screencenter[0] = 0.5f * (float)r_waterstate.waterwidth / (float)r_waterstate.texturewidth;
+               r_waterstate.screencenter[1] = 0.5f * (float)r_waterstate.waterheight / (float)r_waterstate.textureheight;
        }
 
        r_waterstate.maxwaterplanes = MAX_WATERPLANES;
        r_waterstate.numwaterplanes = 0;
 }
 
-static void R_Water_AddWaterPlane(msurface_t *surface)
+void R_Water_AddWaterPlane(msurface_t *surface)
 {
        int triangleindex, planeindex;
        const int *e;
@@ -3061,6 +3737,7 @@ static void R_Water_AddWaterPlane(msurface_t *surface)
        vec3_t center;
        mplane_t plane;
        r_waterstate_waterplane_t *p;
+       texture_t *t = R_GetCurrentTexture(surface->texture);
        // just use the first triangle with a valid normal for any decisions
        VectorClear(normal);
        for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
@@ -3080,7 +3757,7 @@ static void R_Water_AddWaterPlane(msurface_t *surface)
        if (PlaneDiff(r_refdef.view.origin, &plane) < 0)
        {
                // skip backfaces (except if nocullface is set)
-               if (!(surface->texture->currentframe->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
+               if (!(t->currentmaterialflags & MATERIALFLAG_NOCULLFACE))
                        return;
                VectorNegate(plane.normal, plane.normal);
                plane.dist *= -1;
@@ -3106,7 +3783,7 @@ static void R_Water_AddWaterPlane(msurface_t *surface)
                p->pvsvalid = false;
        }
        // merge this surface's materialflags into the waterplane
-       p->materialflags |= surface->texture->currentframe->currentmaterialflags;
+       p->materialflags |= t->currentmaterialflags;
        // merge this surface's PVS into the waterplane
        VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
        if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
@@ -3120,6 +3797,7 @@ static void R_Water_AddWaterPlane(msurface_t *surface)
 static void R_Water_ProcessPlanes(void)
 {
        r_refdef_view_t originalview;
+       r_refdef_view_t myview;
        int planeindex;
        r_waterstate_waterplane_t *p;
 
@@ -3146,34 +3824,40 @@ static void R_Water_ProcessPlanes(void)
        }
 
        // render views
+       r_refdef.view = originalview;
+       r_refdef.view.showdebug = false;
+       r_refdef.view.width = r_waterstate.waterwidth;
+       r_refdef.view.height = r_waterstate.waterheight;
+       r_refdef.view.useclipplane = true;
+       myview = r_refdef.view;
+       r_waterstate.renderingscene = true;
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
        {
-               r_refdef.view.showdebug = false;
-               r_refdef.view.width = r_waterstate.waterwidth;
-               r_refdef.view.height = r_waterstate.waterheight;
-               r_refdef.view.useclipplane = true;
-               r_waterstate.renderingscene = true;
-
                // render the normal view scene and copy into texture
                // (except that a clipping plane should be used to hide everything on one side of the water, and the viewer's weapon model should be omitted)
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                {
+                       r_refdef.view = myview;
                        r_refdef.view.clipplane = p->plane;
                        VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
                        r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
                        PlaneClassify(&r_refdef.view.clipplane);
 
-                       R_RenderScene(false);
+                       R_ResetViewRendering3D();
+                       R_ClearScreen(r_refdef.fogenabled);
+                       R_View_Update();
+                       R_RenderScene();
 
                        // copy view into the screen texture
                        R_Mesh_TexBind(0, R_GetTexture(p->texture_refraction));
                        GL_ActiveTexture(0);
                        CHECKGLERROR
-                       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
+                       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
                }
 
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
                {
+                       r_refdef.view = myview;
                        // render reflected scene and copy into texture
                        Matrix4x4_Reflect(&r_refdef.view.matrix, p->plane.normal[0], p->plane.normal[1], p->plane.normal[2], p->plane.dist, -2);
                        // update the r_refdef.view.origin because otherwise the sky renders at the wrong location (amongst other problems)
@@ -3193,26 +3877,20 @@ static void R_Water_ProcessPlanes(void)
 
                        R_ResetViewRendering3D();
                        R_ClearScreen(r_refdef.fogenabled);
-                       if (r_timereport_active)
-                               R_TimeReport("viewclear");
-
-                       R_RenderScene(false);
+                       R_View_Update();
+                       R_RenderScene();
 
                        R_Mesh_TexBind(0, R_GetTexture(p->texture_reflection));
                        GL_ActiveTexture(0);
                        CHECKGLERROR
-                       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
-
-                       R_ResetViewRendering3D();
-                       R_ClearScreen(r_refdef.fogenabled);
-                       if (r_timereport_active)
-                               R_TimeReport("viewclear");
+                       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
                }
-
-               r_refdef.view = originalview;
-               r_refdef.view.clear = true;
-               r_waterstate.renderingscene = false;
        }
+       r_waterstate.renderingscene = false;
+       r_refdef.view = originalview;
+       R_ResetViewRendering3D();
+       R_ClearScreen(r_refdef.fogenabled);
+       R_View_Update();
        return;
 error:
        r_refdef.view = originalview;
@@ -3228,11 +3906,11 @@ void R_Bloom_StartFrame(void)
 
        // set bloomwidth and bloomheight to the bloom resolution that will be
        // used (often less than the screen resolution for faster rendering)
-       r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.width);
-       r_bloomstate.bloomheight = r_bloomstate.bloomwidth * r_refdef.view.height / r_refdef.view.width;
-       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_refdef.view.height);
-       r_bloomstate.bloomwidth = min(r_bloomstate.bloomwidth, gl_max_texture_size);
-       r_bloomstate.bloomheight = min(r_bloomstate.bloomheight, gl_max_texture_size);
+       r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, vid.height);
+       r_bloomstate.bloomheight = r_bloomstate.bloomwidth * vid.height / vid.width;
+       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, vid.height);
+       r_bloomstate.bloomwidth = bound(1, r_bloomstate.bloomwidth, gl_max_texture_size);
+       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, gl_max_texture_size);
 
        // calculate desired texture sizes
        if (gl_support_arb_texture_non_power_of_two)
@@ -3250,13 +3928,15 @@ void R_Bloom_StartFrame(void)
                for (bloomtextureheight  = 1;bloomtextureheight  < r_bloomstate.bloomheight;bloomtextureheight  *= 2);
        }
 
-       if ((r_hdr.integer || r_bloom.integer) && ((r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512) || r_refdef.view.width > gl_max_texture_size || r_refdef.view.height > gl_max_texture_size))
+       if ((r_hdr.integer || r_bloom.integer || (!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))) && ((r_bloom_resolution.integer < 4 || r_bloom_blur.value < 1 || r_bloom_blur.value >= 512) || r_refdef.view.width > gl_max_texture_size || r_refdef.view.height > gl_max_texture_size))
        {
                Cvar_SetValueQuick(&r_hdr, 0);
                Cvar_SetValueQuick(&r_bloom, 0);
+               Cvar_SetValueQuick(&r_motionblur, 0);
+               Cvar_SetValueQuick(&r_damageblur, 0);
        }
 
-       if (!(r_glsl.integer && (r_glsl_postprocess.integer || (v_glslgamma.integer && !vid_gammatables_trivial) || r_bloom.integer || r_hdr.integer)) && !r_bloom.integer)
+       if (!(r_glsl.integer && (r_glsl_postprocess.integer || (!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) || (v_glslgamma.integer && !vid_gammatables_trivial))) && !r_bloom.integer && !r_hdr.integer && (R_Stereo_Active() || (r_motionblur.value <= 0 && r_damageblur.value <= 0)))
                screentexturewidth = screentextureheight = 0;
        if (!r_hdr.integer && !r_bloom.integer)
                bloomtexturewidth = bloomtextureheight = 0;
@@ -3283,6 +3963,13 @@ void R_Bloom_StartFrame(void)
                        r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
        }
 
+       // when doing a reduced render (HDR) we want to use a smaller area
+       r_bloomstate.bloomwidth = bound(1, r_bloom_resolution.integer, r_refdef.view.height);
+       r_bloomstate.bloomheight = r_bloomstate.bloomwidth * r_refdef.view.height / r_refdef.view.width;
+       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_refdef.view.height);
+       r_bloomstate.bloomwidth = bound(1, r_bloomstate.bloomwidth, r_bloomstate.bloomtexturewidth);
+       r_bloomstate.bloomheight = bound(1, r_bloomstate.bloomheight, r_bloomstate.bloomtextureheight);
+
        // set up a texcoord array for the full resolution screen image
        // (we have to keep this around to copy back during final render)
        r_bloomstate.screentexcoord2f[0] = 0;
@@ -3310,6 +3997,8 @@ void R_Bloom_StartFrame(void)
                r_bloomstate.enabled = true;
                r_bloomstate.hdr = r_hdr.integer != 0;
        }
+
+       R_Viewport_InitOrtho(&r_bloomstate.viewport, &identitymatrix, r_refdef.view.x, vid.height - r_bloomstate.bloomheight - r_refdef.view.y, r_bloomstate.bloomwidth, r_bloomstate.bloomheight, 0, 0, 1, 1, -10, 100, NULL);
 }
 
 void R_Bloom_CopyBloomTexture(float colorscale)
@@ -3318,7 +4007,7 @@ void R_Bloom_CopyBloomTexture(float colorscale)
 
        // scale down screen texture to the bloom texture size
        CHECKGLERROR
-       qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+       R_SetViewport(&r_bloomstate.viewport);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_Color(colorscale, colorscale, colorscale, 1);
        // TODO: optimize with multitexture or GLSL
@@ -3333,8 +4022,8 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
        GL_ActiveTexture(0);
        CHECKGLERROR
-       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
-       r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
 }
 
 void R_Bloom_CopyHDRTexture(void)
@@ -3342,8 +4031,8 @@ void R_Bloom_CopyHDRTexture(void)
        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
        GL_ActiveTexture(0);
        CHECKGLERROR
-       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
-       r_refdef.stats.bloom_copypixels += r_refdef.view.width * r_refdef.view.height;
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
 }
 
 void R_Bloom_MakeTexture(void)
@@ -3360,7 +4049,7 @@ void R_Bloom_MakeTexture(void)
 
        // we have a bloom image in the framebuffer
        CHECKGLERROR
-       qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
+       R_SetViewport(&r_bloomstate.viewport);
 
        for (x = 1;x < min(r_bloom_colorexponent.value, 32);)
        {
@@ -3376,8 +4065,8 @@ void R_Bloom_MakeTexture(void)
                // copy the vertically blurred bloom view to a texture
                GL_ActiveTexture(0);
                CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);CHECKGLERROR
+               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
        }
 
        range = r_bloom_blur.integer * r_bloomstate.bloomwidth / 320;
@@ -3422,8 +4111,8 @@ void R_Bloom_MakeTexture(void)
                // copy the vertically blurred bloom view to a texture
                GL_ActiveTexture(0);
                CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);CHECKGLERROR
+               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
        }
 
        // apply subtract last
@@ -3450,8 +4139,8 @@ void R_Bloom_MakeTexture(void)
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
                GL_ActiveTexture(0);
                CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_bloomstate.bloomheight), r_bloomstate.bloomwidth, r_bloomstate.bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
+               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);CHECKGLERROR
+               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
        }
 }
 
@@ -3468,18 +4157,29 @@ void R_HDR_RenderBloomTexture(void)
 
        // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer?  it might improve SLI performance.
        // TODO: add exposure compensation features
-       // TODO: add fp16 framebuffer support
+       // TODO: add fp16 framebuffer support (using GL_EXT_framebuffer_object)
 
        r_refdef.view.showdebug = false;
        r_refdef.view.colorscale *= r_bloom_colorscale.value / bound(1, r_hdr_range.value, 16);
 
+       R_ResetViewRendering3D();
+
        R_ClearScreen(r_refdef.fogenabled);
        if (r_timereport_active)
                R_TimeReport("HDRclear");
 
+       R_View_Update();
+       if (r_timereport_active)
+               R_TimeReport("visibility");
+
+       // only do secondary renders with HDR if r_hdr is 2 or higher
        r_waterstate.numwaterplanes = 0;
-       R_RenderScene(r_waterstate.enabled);
+       if (r_waterstate.enabled && r_hdr.integer >= 2)
+               R_RenderWaterPlanes();
+
        r_refdef.view.showdebug = true;
+       R_RenderScene();
+       r_waterstate.numwaterplanes = 0;
 
        R_ResetViewRendering2D();
 
@@ -3502,23 +4202,69 @@ static void R_BlendView(void)
 {
        if (r_bloomstate.texture_screen)
        {
-               // copy view into the screen texture
+               // make sure the buffer is available
+               if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
+
                R_ResetViewRendering2D();
                R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
                R_Mesh_ColorPointer(NULL, 0, 0);
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
                GL_ActiveTexture(0);CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_refdef.view.width * r_refdef.view.height;
+
+               if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
+               {  
+                       // declare variables
+                       float speed;
+                       static float avgspeed;
+
+                       speed = VectorLength(cl.movement_velocity);
+
+                       cl.motionbluralpha = bound(0, (cl.time - cl.oldtime) / max(0.001, r_motionblur_vcoeff.value), 1);
+                       avgspeed = avgspeed * (1 - cl.motionbluralpha) + speed * cl.motionbluralpha;
+
+                       speed = (avgspeed - r_motionblur_vmin.value) / max(1, r_motionblur_vmax.value - r_motionblur_vmin.value);
+                       speed = bound(0, speed, 1);
+                       speed = speed * (1 - r_motionblur_bmin.value) + r_motionblur_bmin.value;
+
+                       // calculate values into a standard alpha
+                       cl.motionbluralpha = 1 - exp(-
+                                       (
+                                        (r_motionblur.value * speed / 80)
+                                        +
+                                        (r_damageblur.value * (cl.cshifts[CSHIFT_DAMAGE].percent / 1600))
+                                       )
+                                       /
+                                       max(0.0001, cl.time - cl.oldtime) // fps independent
+                                  );
+
+                       cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
+                       cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
+                       // apply the blur
+                       if (cl.motionbluralpha > 0)
+                       {
+                               R_SetupGenericShader(true);
+                               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                               GL_Color(1, 1, 1, cl.motionbluralpha);
+                               R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
+                               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+                               R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
+                               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
+                       }
+               }
+
+               // copy view into the screen texture
+               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);CHECKGLERROR
+               r_refdef.stats.bloom_copypixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
        }
 
        if (r_glsl.integer && gl_support_fragment_shader && (r_bloomstate.texture_screen || r_bloomstate.texture_bloom))
        {
                unsigned int permutation =
-                         (r_bloomstate.texture_bloom ? SHADERPERMUTATION_GLOW : 0)
-                       | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0)
+                         (r_bloomstate.texture_bloom ? SHADERPERMUTATION_BLOOM : 0)
+                       | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
                        | ((v_glslgamma.value && !vid_gammatables_trivial) ? SHADERPERMUTATION_GAMMARAMPS : 0)
-                       | (r_glsl_postprocess.integer ? SHADERPERMUTATION_POSTPROCESSING : 0);
+                       | (r_glsl_postprocess.integer ? SHADERPERMUTATION_POSTPROCESSING : 0)
+                       | ((!R_Stereo_ColorMasking() && r_glsl_saturation.value != 1) ? SHADERPERMUTATION_SATURATION : 0);
 
                if (r_bloomstate.texture_bloom && !r_bloomstate.hdr)
                {
@@ -3574,8 +4320,10 @@ static void R_BlendView(void)
                        sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &a, &b, &c, &d);
                        qglUniform4fARB(r_glsl_permutation->loc_UserVec4, a, b, c, d);
                }
+               if (r_glsl_permutation->loc_Saturation >= 0)
+                       qglUniform1fARB(r_glsl_permutation->loc_Saturation, r_glsl_saturation.value);
                R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
+               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                return;
        }
 
@@ -3595,7 +4343,7 @@ static void R_BlendView(void)
                R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_bloom));
                R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
                R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
+               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
        }
        else if (r_bloomstate.texture_bloom)
        {
@@ -3624,14 +4372,14 @@ static void R_BlendView(void)
                {
                        R_SetupGenericShader(true);
                        R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
-                       r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
+                       r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                        // now blend on the bloom texture
                        GL_BlendFunc(GL_ONE, GL_ONE);
                        R_Mesh_TexBind(0, R_GetTexture(r_bloomstate.texture_screen));
                        R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
                }
                R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_refdef.view.width * r_refdef.view.height;
+               r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
        }
        if (r_refdef.viewblend[3] >= (1.0f / 256.0f))
        {
@@ -3646,8 +4394,6 @@ static void R_BlendView(void)
        }
 }
 
-void R_RenderScene(qboolean addwaterplanes);
-
 matrix4x4_t r_waterscrollmatrix;
 
 void R_UpdateFogColor(void) // needs to be called before HDR subrender too, as that changes colorscale!
@@ -3661,13 +4407,6 @@ void R_UpdateFogColor(void) // needs to be called before HDR subrender too, as t
                {
                        vec3_t fogvec;
                        VectorCopy(r_refdef.fogcolor, fogvec);
-                       if(r_glsl.integer && (r_glsl_contrastboost.value > 1 || r_glsl_contrastboost.value < 0)) // need to support contrast boost
-                       {
-                               //   color.rgb /= ((ContrastBoost - 1) * color.rgb + 1);
-                               fogvec[0] *= r_glsl_contrastboost.value / ((r_glsl_contrastboost.value - 1) * fogvec[0] + 1);
-                               fogvec[1] *= r_glsl_contrastboost.value / ((r_glsl_contrastboost.value - 1) * fogvec[1] + 1);
-                               fogvec[2] *= r_glsl_contrastboost.value / ((r_glsl_contrastboost.value - 1) * fogvec[2] + 1);
-                       }
                        //   color.rgb *= ContrastBoost * SceneBrightness;
                        VectorScale(fogvec, r_refdef.view.colorscale, fogvec);
                        r_refdef.fogcolor[0] = bound(0.0f, fogvec[0], 1.0f);
@@ -3695,7 +4434,7 @@ void R_UpdateVariables(void)
        r_refdef.shadowpolygonfactor = r_refdef.polygonfactor + r_shadow_polygonfactor.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
        r_refdef.shadowpolygonoffset = r_refdef.polygonoffset + r_shadow_polygonoffset.value * (r_shadow_frontsidecasting.integer ? 1 : -1);
 
-       r_refdef.scene.rtworld = r_shadow_realtime_world.integer;
+       r_refdef.scene.rtworld = r_shadow_realtime_world.integer != 0;
        r_refdef.scene.rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil;
        r_refdef.scene.rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer && r_dynamic.integer;
        r_refdef.scene.rtdlightshadows = r_refdef.scene.rtdlight && r_shadow_realtime_dlight_shadows.integer && gl_stencil;
@@ -3769,7 +4508,7 @@ void R_UpdateVariables(void)
                        // build GLSL gamma texture
 #define RAMPWIDTH 256
                        unsigned short ramp[RAMPWIDTH * 3];
-                       unsigned char ramprgb[RAMPWIDTH][4];
+                       unsigned char rampbgr[RAMPWIDTH][4];
                        int i;
 
                        r_texture_gammaramps_serial = vid_gammatables_serial;
@@ -3777,18 +4516,18 @@ void R_UpdateVariables(void)
                        VID_BuildGammaTables(&ramp[0], RAMPWIDTH);
                        for(i = 0; i < RAMPWIDTH; ++i)
                        {
-                               ramprgb[i][0] = ramp[i] >> 8;
-                               ramprgb[i][1] = ramp[i + RAMPWIDTH] >> 8;
-                               ramprgb[i][2] = ramp[i + 2 * RAMPWIDTH] >> 8;
-                               ramprgb[i][3] = 0;
+                               rampbgr[i][0] = (unsigned char) (ramp[i + 2 * RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
+                               rampbgr[i][1] = (unsigned char) (ramp[i + RAMPWIDTH] * 255.0 / 65535.0 + 0.5);
+                               rampbgr[i][2] = (unsigned char) (ramp[i] * 255.0 / 65535.0 + 0.5);
+                               rampbgr[i][3] = 0;
                        }
                        if (r_texture_gammaramps)
                        {
-                               R_UpdateTexture(r_texture_gammaramps, &ramprgb[0][0], 0, 0, RAMPWIDTH, 1);
+                               R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, RAMPWIDTH, 1);
                        }
                        else
                        {
-                               r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &ramprgb[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+                               r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, NULL);
                        }
                }
        }
@@ -3837,6 +4576,13 @@ R_RenderView
 */
 void R_RenderView(void)
 {
+       if (r_timereport_active)
+               R_TimeReport("start");
+       r_frame++; // used only by R_GetCurrentTexture
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
+
+       R_AnimCache_NewFrame();
+
        if (r_refdef.view.isoverlay)
        {
                // TODO: FIXME: move this into its own backend function maybe? [2/5/2008 Andreas]
@@ -3848,13 +4594,13 @@ void R_RenderView(void)
                r_waterstate.enabled = false;
                r_waterstate.numwaterplanes = 0;
 
-               R_RenderScene(false);
+               R_RenderScene();
 
                CHECKGLERROR
                return;
        }
 
-       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0/* || !r_refdef.scene.worldmodel*/)
+       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0 || !r_renderview.integer/* || !r_refdef.scene.worldmodel*/)
                return; //Host_Error ("R_RenderView: NULL worldmodel");
 
        r_refdef.view.colorscale = r_hdr_scenebrightness.value;
@@ -3886,14 +4632,22 @@ void R_RenderView(void)
        }
        r_refdef.view.clear = true;
 
-       r_refdef.view.showdebug = true;
-
        // this produces a bloom texture to be used in R_BlendView() later
        if (r_hdr.integer)
                R_HDR_RenderBloomTexture();
 
+       r_refdef.view.showdebug = true;
+
+       R_View_Update();
+       if (r_timereport_active)
+               R_TimeReport("visibility");
+
+       r_waterstate.numwaterplanes = 0;
+       if (r_waterstate.enabled)
+               R_RenderWaterPlanes();
+
+       R_RenderScene();
        r_waterstate.numwaterplanes = 0;
-       R_RenderScene(r_waterstate.enabled);
 
        R_BlendView();
        if (r_timereport_active)
@@ -3904,47 +4658,42 @@ void R_RenderView(void)
        CHECKGLERROR
 }
 
-extern void R_DrawLightningBeams (void);
-extern void VM_CL_AddPolygonsToMeshQueue (void);
-extern void R_DrawPortals (void);
-extern cvar_t cl_locs_show;
-static void R_DrawLocs(void);
-static void R_DrawEntityBBoxes(void);
-void R_RenderScene(qboolean addwaterplanes)
+void R_RenderWaterPlanes(void)
 {
-       r_refdef.stats.renders++;
-
-       R_UpdateFogColor();
-
-       if (addwaterplanes)
+       if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawAddWaterPlanes)
        {
-               R_ResetViewRendering3D();
-
-               R_View_Update();
+               r_refdef.scene.worldmodel->DrawAddWaterPlanes(r_refdef.scene.worldentity);
                if (r_timereport_active)
-                       R_TimeReport("watervis");
-
-               if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawAddWaterPlanes)
-               {
-                       r_refdef.scene.worldmodel->DrawAddWaterPlanes(r_refdef.scene.worldentity);
-                       if (r_timereport_active)
-                               R_TimeReport("waterworld");
-               }
+                       R_TimeReport("waterworld");
+       }
 
-               // don't let sound skip if going slow
-               if (r_refdef.scene.extraupdate)
-                       S_ExtraUpdate ();
+       // don't let sound skip if going slow
+       if (r_refdef.scene.extraupdate)
+               S_ExtraUpdate ();
 
-               R_DrawModelsAddWaterPlanes();
-               if (r_timereport_active)
-                       R_TimeReport("watermodels");
+       R_DrawModelsAddWaterPlanes();
+       if (r_timereport_active)
+               R_TimeReport("watermodels");
 
+       if (r_waterstate.numwaterplanes)
+       {
                R_Water_ProcessPlanes();
                if (r_timereport_active)
                        R_TimeReport("waterscenes");
        }
+}
 
-       R_ResetViewRendering3D();
+extern void R_DrawLightningBeams (void);
+extern void VM_CL_AddPolygonsToMeshQueue (void);
+extern void R_DrawPortals (void);
+extern cvar_t cl_locs_show;
+static void R_DrawLocs(void);
+static void R_DrawEntityBBoxes(void);
+void R_RenderScene(void)
+{
+       r_refdef.stats.renders++;
+
+       R_UpdateFogColor();
 
        // don't let sound skip if going slow
        if (r_refdef.scene.extraupdate)
@@ -3954,10 +4703,6 @@ void R_RenderScene(qboolean addwaterplanes)
 
        R_SkyStartFrame();
 
-       R_View_Update();
-       if (r_timereport_active)
-               R_TimeReport("visibility");
-
        Matrix4x4_CreateTranslate(&r_waterscrollmatrix, sin(r_refdef.scene.time) * 0.025 * r_waterscroll.value, sin(r_refdef.scene.time * 0.8f) * 0.025 * r_waterscroll.value, 0);
 
        if (cl.csqc_vidvars.drawworld)
@@ -3977,6 +4722,8 @@ void R_RenderScene(qboolean addwaterplanes)
                        R_TimeReport("bmodelsky");
        }
 
+       R_AnimCache_CacheVisibleEntities();
+
        if (r_depthfirst.integer >= 1 && cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawDepth)
        {
                r_refdef.scene.worldmodel->DrawDepth(r_refdef.scene.worldentity);
@@ -4009,12 +4756,10 @@ void R_RenderScene(qboolean addwaterplanes)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
-       if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0)
+       if (r_shadows.integer > 0 && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadows();
-
                R_ResetViewRendering3D();
-
                // don't let sound skip if going slow
                if (r_refdef.scene.extraupdate)
                        S_ExtraUpdate ();
@@ -4028,6 +4773,15 @@ void R_RenderScene(qboolean addwaterplanes)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
+       if (r_shadows.integer > 0 && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
+       {
+               R_DrawModelShadows();
+               R_ResetViewRendering3D();
+               // don't let sound skip if going slow
+               if (r_refdef.scene.extraupdate)
+                       S_ExtraUpdate ();
+       }
+
        if (cl.csqc_vidvars.drawworld)
        {
                R_DrawLightningBeams();
@@ -4081,7 +4835,7 @@ void R_RenderScene(qboolean addwaterplanes)
 
        R_SetupGenericShader(true);
 
-       if (r_refdef.view.showdebug && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawDebug && (r_showtris.value > 0 || r_shownormals.value > 0 || r_showcollisionbrushes.value > 0))
+       if (r_refdef.view.showdebug && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->DrawDebug && (r_showtris.value > 0 || r_shownormals.value != 0 || r_showcollisionbrushes.value > 0))
        {
                r_refdef.scene.worldmodel->DrawDebug(r_refdef.scene.worldentity);
                if (r_timereport_active)
@@ -4358,6 +5112,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, qboolean depthdisable, qboolean depthshort, 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)
 {
+       // NOTE: this must not call qglDepthFunc (see r_shadow.c, R_BeginCoronaQuery) thanks to ATI
        float fog = 1.0f;
        float vertex3f[12];
 
@@ -4367,14 +5122,6 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        R_Mesh_Matrix(&identitymatrix);
        GL_BlendFunc(blendfunc1, blendfunc2);
 
-       if(v_flipped_state)
-       {
-               scalex1 = -scalex1;
-               scalex2 = -scalex2;
-               GL_CullFace(r_refdef.view.cullface_front);
-       }
-       else
-               GL_CullFace(r_refdef.view.cullface_back);
        GL_CullFace(GL_NONE);
 
        GL_DepthMask(false);
@@ -4562,20 +5309,76 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
        return (float)(parms[0] + parms[1] * f);
 }
 
-void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
+void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags)
 {
        int w, h, idx;
-       int i;
-       dp_model_t *model = ent->model;
        float f;
        float tcmat[12];
-       q3shaderinfo_layer_tcmod_t *tcmod;
-
-       if (t->basematerialflags & MATERIALFLAG_NODRAW)
+       matrix4x4_t matrix, temp;
+       switch(tcmod->tcmod)
        {
-               t->currentmaterialflags = t->basematerialflags & (MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW);
-               return;
+               case Q3TCMOD_COUNT:
+               case Q3TCMOD_NONE:
+                       if (currentmaterialflags & MATERIALFLAG_WATERSCROLL)
+                               matrix = r_waterscrollmatrix;
+                       else
+                               matrix = identitymatrix;
+                       break;
+               case Q3TCMOD_ENTITYTRANSLATE:
+                       // this is used in Q3 to allow the gamecode to control texcoord
+                       // scrolling on the entity, which is not supported in darkplaces yet.
+                       Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
+                       break;
+               case Q3TCMOD_ROTATE:
+                       Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
+                       Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * r_refdef.scene.time, 0, 0, 1);
+                       Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
+                       break;
+               case Q3TCMOD_SCALE:
+                       Matrix4x4_CreateScale3(&matrix, tcmod->parms[0], tcmod->parms[1], 1);
+                       break;
+               case Q3TCMOD_SCROLL:
+                       Matrix4x4_CreateTranslate(&matrix, tcmod->parms[0] * r_refdef.scene.time, tcmod->parms[1] * r_refdef.scene.time, 0);
+                       break;
+               case Q3TCMOD_PAGE: // poor man's animmap (to store animations into a single file, useful for HTTP downloaded textures)
+                       w = (int) tcmod->parms[0];
+                       h = (int) tcmod->parms[1];
+                       f = r_refdef.scene.time / (tcmod->parms[2] * w * h);
+                       f = f - floor(f);
+                       idx = (int) floor(f * w * h);
+                       Matrix4x4_CreateTranslate(&matrix, (idx % w) / tcmod->parms[0], (idx / w) / tcmod->parms[1], 0);
+                       break;
+               case Q3TCMOD_STRETCH:
+                       f = 1.0f / R_EvaluateQ3WaveFunc(tcmod->wavefunc, tcmod->waveparms);
+                       Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
+                       break;
+               case Q3TCMOD_TRANSFORM:
+                       VectorSet(tcmat +  0, tcmod->parms[0], tcmod->parms[1], 0);
+                       VectorSet(tcmat +  3, tcmod->parms[2], tcmod->parms[3], 0);
+                       VectorSet(tcmat +  6, 0                   , 0                , 1);
+                       VectorSet(tcmat +  9, tcmod->parms[4], tcmod->parms[5], 0);
+                       Matrix4x4_FromArray12FloatGL(&matrix, tcmat);
+                       break;
+               case Q3TCMOD_TURBULENT:
+                       // this is handled in the RSurf_PrepareVertices function
+                       matrix = identitymatrix;
+                       break;
        }
+       temp = *texmatrix;
+       Matrix4x4_Concat(texmatrix, &matrix, &temp);
+}
+
+texture_t *R_GetCurrentTexture(texture_t *t)
+{
+       int i;
+       const entity_render_t *ent = rsurface.entity;
+       dp_model_t *model = ent->model;
+       q3shaderinfo_layer_tcmod_t *tcmod;
+
+       if (t->update_lastrenderframe == r_frame && t->update_lastrenderentity == (void *)ent)
+               return t->currentframe;
+       t->update_lastrenderframe = r_frame;
+       t->update_lastrenderentity = (void *)ent;
 
        // switch to an alternate material if this is a q1bsp animated material
        {
@@ -4596,7 +5399,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                {
                        // use an alternate animation if the entity's frame is not 0,
                        // and only if the texture has an alternate animation
-                       if (ent->frame2 != 0 && t->anim_total[1])
+                       if (ent->framegroupblend[0].frame != 0 && t->anim_total[1])
                                t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(r_refdef.scene.time * 5.0f) % t->anim_total[1]) : 0];
                        else
                                t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(r_refdef.scene.time * 5.0f) % t->anim_total[0]) : 0];
@@ -4663,70 +5466,21 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 
        // there is no tcmod
        if (t->currentmaterialflags & MATERIALFLAG_WATERSCROLL)
+       {
                t->currenttexmatrix = r_waterscrollmatrix;
-
-       for (i = 0, tcmod = t->tcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++)
+               t->currentbackgroundtexmatrix = r_waterscrollmatrix;
+       }
+       else
        {
-               matrix4x4_t matrix;
-               switch(tcmod->tcmod)
-               {
-               case Q3TCMOD_COUNT:
-               case Q3TCMOD_NONE:
-                       if (t->currentmaterialflags & MATERIALFLAG_WATERSCROLL)
-                               matrix = r_waterscrollmatrix;
-                       else
-                               matrix = identitymatrix;
-                       break;
-               case Q3TCMOD_ENTITYTRANSLATE:
-                       // this is used in Q3 to allow the gamecode to control texcoord
-                       // scrolling on the entity, which is not supported in darkplaces yet.
-                       Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
-                       break;
-               case Q3TCMOD_ROTATE:
-                       Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
-                       Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * r_refdef.scene.time, 0, 0, 1);
-                       Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
-                       break;
-               case Q3TCMOD_SCALE:
-                       Matrix4x4_CreateScale3(&matrix, tcmod->parms[0], tcmod->parms[1], 1);
-                       break;
-               case Q3TCMOD_SCROLL:
-                       Matrix4x4_CreateTranslate(&matrix, tcmod->parms[0] * r_refdef.scene.time, tcmod->parms[1] * r_refdef.scene.time, 0);
-                       break;
-               case Q3TCMOD_PAGE: // poor man's animmap (to store animations into a single file, useful for HTTP downloaded textures)
-                       w = (int) tcmod->parms[0];
-                       h = (int) tcmod->parms[1];
-                       f = r_refdef.scene.time / (tcmod->parms[2] * w * h);
-                       f = f - floor(f);
-                       idx = (int) floor(f * w * h);
-                       Matrix4x4_CreateTranslate(&matrix, (idx % w) / tcmod->parms[0], (idx / w) / tcmod->parms[1], 0);
-                       break;
-               case Q3TCMOD_STRETCH:
-                       f = 1.0f / R_EvaluateQ3WaveFunc(tcmod->wavefunc, tcmod->waveparms);
-                       Matrix4x4_CreateFromQuakeEntity(&matrix, 0.5f * (1 - f), 0.5 * (1 - f), 0, 0, 0, 0, f);
-                       break;
-               case Q3TCMOD_TRANSFORM:
-                       VectorSet(tcmat +  0, tcmod->parms[0], tcmod->parms[1], 0);
-                       VectorSet(tcmat +  3, tcmod->parms[2], tcmod->parms[3], 0);
-                       VectorSet(tcmat +  6, 0                   , 0                , 1);
-                       VectorSet(tcmat +  9, tcmod->parms[4], tcmod->parms[5], 0);
-                       Matrix4x4_FromArray12FloatGL(&matrix, tcmat);
-                       break;
-               case Q3TCMOD_TURBULENT:
-                       // this is handled in the RSurf_PrepareVertices function
-                       matrix = identitymatrix;
-                       break;
-               }
-               // either replace or concatenate the transformation
-               if (i < 1)
-                       t->currenttexmatrix = matrix;
-               else
-               {
-                       matrix4x4_t temp = t->currenttexmatrix;
-                       Matrix4x4_Concat(&t->currenttexmatrix, &matrix, &temp);
-               }
+               Matrix4x4_CreateIdentity(&t->currenttexmatrix);
+               Matrix4x4_CreateIdentity(&t->currentbackgroundtexmatrix);
        }
 
+       for (i = 0, tcmod = t->tcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++)
+               R_tcMod_ApplyToMatrix(&t->currenttexmatrix, tcmod, t->currentmaterialflags);
+       for (i = 0, tcmod = t->backgroundtcmods;i < Q3MAXTCMODS && tcmod->tcmod;i++, tcmod++)
+               R_tcMod_ApplyToMatrix(&t->currentbackgroundtexmatrix, tcmod, t->currentmaterialflags);
+
        t->colormapping = VectorLength2(ent->colormap_pantscolor) + VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
        t->basetexture = (!t->colormapping && t->currentskinframe->merged) ? t->currentskinframe->merged : t->currentskinframe->base;
        t->glosstexture = r_texture_black;
@@ -4771,7 +5525,8 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
        if (t->currentmaterialflags & MATERIALFLAG_WALL)
        {
                int layerflags = 0;
-               int blendfunc1, blendfunc2, depthmask;
+               int blendfunc1, blendfunc2;
+               qboolean depthmask;
                if (t->currentmaterialflags & MATERIALFLAG_ADD)
                {
                        blendfunc1 = GL_SRC_ALPHA;
@@ -4855,14 +5610,8 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                        R_Texture_AddLayer(t, false, GL_SRC_ALPHA, (t->currentmaterialflags & MATERIALFLAG_BLENDED) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA, TEXTURELAYERTYPE_FOG, t->currentskinframe->fog, &identitymatrix, r_refdef.fogcolor[0] / r_refdef.view.colorscale, r_refdef.fogcolor[1] / r_refdef.view.colorscale, r_refdef.fogcolor[2] / r_refdef.view.colorscale, t->lightmapcolor[3]);
                }
        }
-}
 
-void R_UpdateAllTextureInfo(entity_render_t *ent)
-{
-       int i;
-       if (ent->model)
-               for (i = 0;i < ent->model->num_texturesperskin;i++)
-                       R_UpdateTextureInfo(ent, ent->model->data_textures + i);
+       return t->currentframe;
 }
 
 rsurfacestate_t rsurface;
@@ -4892,6 +5641,9 @@ void R_Mesh_ResizeArrays(int newvertices)
 void RSurf_ActiveWorldEntity(void)
 {
        dp_model_t *model = r_refdef.scene.worldmodel;
+       //if (rsurface.entity == r_refdef.scene.worldentity)
+       //      return;
+       rsurface.entity = r_refdef.scene.worldentity;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = identitymatrix;
@@ -4903,14 +5655,8 @@ void RSurf_ActiveWorldEntity(void)
        VectorSet(rsurface.modellight_lightdir, 0, 0, 1);
        VectorSet(rsurface.colormap_pantscolor, 0, 0, 0);
        VectorSet(rsurface.colormap_shirtcolor, 0, 0, 0);
-       rsurface.frameblend[0].frame = 0;
+       memset(rsurface.frameblend, 0, sizeof(rsurface.frameblend));
        rsurface.frameblend[0].lerp = 1;
-       rsurface.frameblend[1].frame = 0;
-       rsurface.frameblend[1].lerp = 0;
-       rsurface.frameblend[2].frame = 0;
-       rsurface.frameblend[2].lerp = 0;
-       rsurface.frameblend[3].frame = 0;
-       rsurface.frameblend[3].lerp = 0;
        rsurface.basepolygonfactor = r_refdef.polygonfactor;
        rsurface.basepolygonoffset = r_refdef.polygonoffset;
        rsurface.modelvertex3f  = model->surfmesh.data_vertex3f;
@@ -4961,6 +5707,9 @@ void RSurf_ActiveWorldEntity(void)
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents)
 {
        dp_model_t *model = ent->model;
+       //if (rsurface.entity == ent && (!model->surfmesh.isanimated || (!wantnormals && !wanttangents)))
+       //      return;
+       rsurface.entity = (entity_render_t *)ent;
        if (rsurface.array_size < model->surfmesh.num_vertices)
                R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = ent->matrix;
@@ -4977,10 +5726,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        VectorCopy(ent->modellight_lightdir, rsurface.modellight_lightdir);
        VectorCopy(ent->colormap_pantscolor, rsurface.colormap_pantscolor);
        VectorCopy(ent->colormap_shirtcolor, rsurface.colormap_shirtcolor);
-       rsurface.frameblend[0] = ent->frameblend[0];
-       rsurface.frameblend[1] = ent->frameblend[1];
-       rsurface.frameblend[2] = ent->frameblend[2];
-       rsurface.frameblend[3] = ent->frameblend[3];
+       memcpy(rsurface.frameblend, ent->frameblend, sizeof(ent->frameblend));
        rsurface.basepolygonfactor = r_refdef.polygonfactor;
        rsurface.basepolygonoffset = r_refdef.polygonoffset;
        if (ent->model->brush.submodel)
@@ -4988,9 +5734,16 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                rsurface.basepolygonfactor += r_polygonoffset_submodel_factor.value;
                rsurface.basepolygonoffset += r_polygonoffset_submodel_offset.value;
        }
-       if (model->surfmesh.isanimated && model->AnimateVertices && (rsurface.frameblend[0].lerp != 1 || rsurface.frameblend[0].frame != 0))
+       if (model->surfmesh.isanimated && model->AnimateVertices && (rsurface.frameblend[0].lerp != 1 || rsurface.frameblend[0].subframe != 0))
        {
-               if (wanttangents)
+               if (R_AnimCache_GetEntity((entity_render_t *)ent, wantnormals, wanttangents))
+               {
+                       rsurface.modelvertex3f = r_animcachestate.entity[ent->animcacheindex].vertex3f;
+                       rsurface.modelsvector3f = wanttangents ? r_animcachestate.entity[ent->animcacheindex].svector3f : NULL;
+                       rsurface.modeltvector3f = wanttangents ? r_animcachestate.entity[ent->animcacheindex].tvector3f : NULL;
+                       rsurface.modelnormal3f = wantnormals ? r_animcachestate.entity[ent->animcacheindex].normal3f : NULL;
+               }
+               else if (wanttangents)
                {
                        rsurface.modelvertex3f = rsurface.array_modelvertex3f;
                        rsurface.modelsvector3f = rsurface.array_modelsvector3f;
@@ -5106,7 +5859,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        rsurface.normal3f = rsurface.modelnormal3f = rsurface.array_modelnormal3f;
                        rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject = 0;
                        rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset = 0;
-                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer);
+                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
                }
                if (generatetangents && !rsurface.modelsvector3f)
                {
@@ -5116,7 +5869,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        rsurface.tvector3f = rsurface.modeltvector3f = rsurface.array_modeltvector3f;
                        rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject = 0;
                        rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset = 0;
-                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer);
+                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
                }
        }
        rsurface.vertex3f  = rsurface.modelvertex3f;
@@ -5179,8 +5932,8 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                                VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
                                        }
                                }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
+                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
                        rsurface.vertex3f = rsurface.array_deformedvertex3f;
                        rsurface.vertex3f_bufferobject = 0;
@@ -5271,8 +6024,8 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        VectorSubtract(end, start, up);
                                        VectorNormalize(up);
                                        // calculate a forward vector to use instead of the original plane normal (this is how we get a new right vector)
-                                       //VectorSubtract(rsurface.modelorg, center, forward);
-                                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, forward);
+                                       VectorSubtract(rsurface.modelorg, center, forward);
+                                       //Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, forward);
                                        VectorNegate(forward, forward);
                                        VectorReflect(forward, 0, up, forward);
                                        VectorNormalize(forward);
@@ -5310,8 +6063,8 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                                VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
                                        }
                                }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
+                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
                        rsurface.vertex3f = rsurface.array_deformedvertex3f;
                        rsurface.vertex3f_bufferobject = 0;
@@ -5342,7 +6095,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        normal[2] += deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
                                        VectorNormalize(normal);
                                }
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
+                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
                        rsurface.svector3f = rsurface.array_deformedsvector3f;
                        rsurface.svector3f_bufferobject = 0;
@@ -5452,12 +6205,28 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        float *out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;
                        for (j = 0;j < surface->num_vertices;j++, vertex += 3, normal += 3, out_tc += 2)
                        {
-                               float l, d, eyedir[3];
-                               VectorSubtract(rsurface.modelorg, vertex, eyedir);
-                               l = 0.5f / VectorLength(eyedir);
-                               d = DotProduct(normal, eyedir)*2;
-                               out_tc[0] = 0.5f + (normal[1]*d - eyedir[1])*l;
-                               out_tc[1] = 0.5f - (normal[2]*d - eyedir[2])*l;
+                               // identical to Q3A's method, but executed in worldspace so
+                               // carried models can be shiny too
+
+                               float viewer[3], d, reflected[3], worldreflected[3];
+
+                               VectorSubtract(rsurface.modelorg, vertex, viewer);
+                               // VectorNormalize(viewer);
+
+                               d = DotProduct(normal, viewer);
+
+                               reflected[0] = normal[0]*2*d - viewer[0];
+                               reflected[1] = normal[1]*2*d - viewer[1];
+                               reflected[2] = normal[2]*2*d - viewer[2];
+                               // note: this is proportinal to viewer, so we can normalize later
+
+                               Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
+                               VectorNormalize(worldreflected);
+
+                               // note: this sphere map only uses world x and z!
+                               // so positive and negative y will LOOK THE SAME.
+                               out_tc[0] = 0.5 + 0.5 * worldreflected[1];
+                               out_tc[1] = 0.5 - 0.5 * worldreflected[2];
                        }
                }
                rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
@@ -6119,6 +6888,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                return;
 
        R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
+       R_Mesh_TexMatrix(1, &rsurface.texture->currentbackgroundtexmatrix);
        R_Mesh_TexBind(GL20TU_NORMAL, R_GetTexture(rsurface.texture->currentskinframe->nmap));
        R_Mesh_TexBind(GL20TU_COLOR, R_GetTexture(rsurface.texture->basetexture));
        R_Mesh_TexBind(GL20TU_GLOSS, R_GetTexture(rsurface.texture->glosstexture));
@@ -6151,7 +6921,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                GL_Color(1, 1, 1, 1);
                R_Mesh_ColorPointer(NULL, 0, 0);
 
-               R_SetupSurfaceShader(vec3_origin, rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND);
+               R_SetupSurfaceShader(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND);
                if (r_glsl_permutation)
                {
                        RSurf_PrepareVerticesForBatch(true, true, texturenumsurfaces, texturesurfacelist);
@@ -6174,7 +6944,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                R_Mesh_TexBind(GL20TU_REFLECTION, R_GetTexture(r_texture_white)); // changed per surface
        }
 
-       R_SetupSurfaceShader(vec3_origin, rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
+       R_SetupSurfaceShader(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
        if (!r_glsl_permutation)
                return;
 
@@ -6476,7 +7246,10 @@ static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, msurf
        R_SetupGenericShader(false);
 
        if(rsurface.texture && rsurface.texture->currentskinframe)
+       {
                memcpy(c, rsurface.texture->currentskinframe->avgcolor, sizeof(c));
+               c[3] *= rsurface.texture->currentalpha;
+       }
        else
        {
                c[0] = 1;
@@ -6568,7 +7341,22 @@ static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, msurf
        RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
 }
 
-static void R_DrawTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
+{
+       CHECKGLERROR
+       RSurf_SetupDepthAndCulling();
+       if (r_showsurfaces.integer == 3)
+               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+       else if (r_glsl.integer && gl_support_fragment_shader)
+               R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth);
+       else if (gl_combine.integer && r_textureunits.integer >= 2)
+               R_DrawTextureSurfaceList_GL13(texturenumsurfaces, texturesurfacelist, writedepth);
+       else
+               R_DrawTextureSurfaceList_GL11(texturenumsurfaces, texturesurfacelist, writedepth);
+       CHECKGLERROR
+}
+
+static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
@@ -6596,7 +7384,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        // to a model, knowing that they are meaningless otherwise
        if (ent == r_refdef.scene.worldentity)
                RSurf_ActiveWorldEntity();
-       else if ((ent->effects & EF_FULLBRIGHT) || (r_showsurfaces.integer && r_showsurfaces.integer != 3) || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
                RSurf_ActiveModelEntity(ent, false, false);
        else
                RSurf_ActiveModelEntity(ent, true, r_glsl.integer && gl_support_fragment_shader);
@@ -6606,8 +7394,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                j = i + 1;
                surface = rsurface.modelsurfaces + surfacelist[i];
                texture = surface->texture;
-               R_UpdateTextureInfo(ent, texture);
-               rsurface.texture = texture->currentframe;
+               rsurface.texture = R_GetCurrentTexture(texture);
                rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
                // scan ahead until we find a different texture
                endsurface = min(i + 1024, numsurfaces);
@@ -6621,13 +7408,18 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        texturesurfacelist[texturenumsurfaces++] = surface;
                }
                // render the range of surfaces
-               R_DrawTextureSurfaceList(texturenumsurfaces, texturesurfacelist, false);
+               if (ent == r_refdef.scene.worldentity)
+                       R_DrawWorldTextureSurfaceList(texturenumsurfaces, texturesurfacelist, false);
+               else
+                       R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, false);
        }
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
        GL_AlphaTest(false);
 }
 
-static void R_ProcessTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, const entity_render_t *queueentity)
+static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly)
 {
+       const entity_render_t *queueentity = r_refdef.scene.worldentity;
        CHECKGLERROR
        if (depthonly)
        {
@@ -6689,23 +7481,113 @@ static void R_ProcessTextureSurfaceList(int texturenumsurfaces, msurface_t **tex
        else
        {
                // the alphatest check is to make sure we write depth for anything we skipped on the depth-only pass earlier
-               R_DrawTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth || (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST));
+               R_DrawWorldTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth || (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST));
        }
        CHECKGLERROR
 }
 
-void R_QueueSurfaceList(entity_render_t *ent, int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly, qboolean addwaterplanes)
+void R_QueueWorldSurfaceList(int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
 {
        int i, j;
        texture_t *texture;
-       // if we're rendering water textures (extra scene renders), use a separate loop to avoid burdening the main one
-       if (addwaterplanes)
+       // break the surface list down into batches by texture and use of lightmapping
+       for (i = 0;i < numsurfaces;i = j)
        {
-               for (i = 0;i < numsurfaces;i++)
-                       if (surfacelist[i]->texture->currentframe->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
-                               R_Water_AddWaterPlane(surfacelist[i]);
+               j = i + 1;
+               // texture is the base texture pointer, rsurface.texture is the
+               // current frame/skin the texture is directing us to use (for example
+               // if a model has 2 skins and it is on skin 1, then skin 0 tells us to
+               // use skin 1 instead)
+               texture = surfacelist[i]->texture;
+               rsurface.texture = R_GetCurrentTexture(texture);
+               rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL;
+               if (!(rsurface.texture->currentmaterialflags & flagsmask) || (rsurface.texture->currentmaterialflags & MATERIALFLAG_NODRAW))
+               {
+                       // if this texture is not the kind we want, skip ahead to the next one
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture;j++)
+                               ;
+                       continue;
+               }
+               // simply scan ahead until we find a different texture or lightmap state
+               for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
+                       ;
+               // render the range of surfaces
+               R_ProcessWorldTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly);
+       }
+}
+
+static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, const entity_render_t *queueentity)
+{
+       CHECKGLERROR
+       if (depthonly)
+       {
+               if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
+                       return;
+               if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
+                       return;
+               RSurf_SetupDepthAndCulling();
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       }
+       else if (r_showsurfaces.integer && !r_refdef.view.showdebug)
+       {
+               RSurf_SetupDepthAndCulling();
+               GL_AlphaTest(false);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ResetTextureState();
+               R_SetupGenericShader(false);
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               GL_DepthMask(true);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_Color(0, 0, 0, 1);
+               GL_DepthTest(writedepth);
+               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       }
+       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
+       {
+               RSurf_SetupDepthAndCulling();
+               GL_AlphaTest(false);
+               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ResetTextureState();
+               R_SetupGenericShader(false);
+               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               GL_DepthMask(true);
+               GL_BlendFunc(GL_ONE, GL_ZERO);
+               GL_DepthTest(true);
+               RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
+       }
+       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY)
+               R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
+       else if (!rsurface.texture->currentnumlayers)
                return;
+       else if (((rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED) || (r_showsurfaces.integer == 3 && (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST))) && queueentity)
+       {
+               // transparent surfaces get pushed off into the transparent queue
+               int surfacelistindex;
+               const msurface_t *surface;
+               vec3_t tempcenter, center;
+               for (surfacelistindex = 0;surfacelistindex < texturenumsurfaces;surfacelistindex++)
+               {
+                       surface = texturesurfacelist[surfacelistindex];
+                       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(&rsurface.matrix, tempcenter, center);
+                       R_MeshQueue_AddTransparent(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST ? r_refdef.view.origin : center, R_DrawSurface_TransparentCallback, queueentity, surface - rsurface.modelsurfaces, rsurface.rtlight);
+               }
+       }
+       else
+       {
+               // the alphatest check is to make sure we write depth for anything we skipped on the depth-only pass earlier
+               R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth || (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST));
        }
+       CHECKGLERROR
+}
+
+void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, msurface_t **surfacelist, int flagsmask, qboolean writedepth, qboolean depthonly)
+{
+       int i, j;
+       texture_t *texture;
        // break the surface list down into batches by texture and use of lightmapping
        for (i = 0;i < numsurfaces;i = j)
        {
@@ -6715,7 +7597,7 @@ void R_QueueSurfaceList(entity_render_t *ent, int numsurfaces, msurface_t **surf
                // if a model has 2 skins and it is on skin 1, then skin 0 tells us to
                // use skin 1 instead)
                texture = surfacelist[i]->texture;
-               rsurface.texture = texture->currentframe;
+               rsurface.texture = R_GetCurrentTexture(texture);
                rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL;
                if (!(rsurface.texture->currentmaterialflags & flagsmask) || (rsurface.texture->currentmaterialflags & MATERIALFLAG_NODRAW))
                {
@@ -6728,7 +7610,7 @@ void R_QueueSurfaceList(entity_render_t *ent, int numsurfaces, msurface_t **surf
                for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
                        ;
                // render the range of surfaces
-               R_ProcessTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, ent);
+               R_ProcessModelTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, ent);
        }
 }
 
@@ -6869,7 +7751,7 @@ void R_DrawDebugModel(entity_render_t *ent)
                {
                        if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j])
                                continue;
-                       rsurface.texture = surface->texture->currentframe;
+                       rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
                                RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
@@ -6882,14 +7764,26 @@ void R_DrawDebugModel(entity_render_t *ent)
                                        else
                                                GL_Color(0, r_refdef.view.colorscale, 0, r_showtris.value);
                                        elements = (ent->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
+                                       R_Mesh_VertexPointer(rsurface.vertex3f, 0, 0);
+                                       R_Mesh_ColorPointer(NULL, 0, 0);
+                                       R_Mesh_TexCoordPointer(0, 0, NULL, 0, 0);
+                                       qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+                                       //R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, ent->model->surfmesh.data_element3i, NULL, 0, 0);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
+                                       qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                                        CHECKGLERROR
+                               }
+                               if (r_shownormals.value < 0)
+                               {
                                        qglBegin(GL_LINES);
-                                       for (k = 0;k < surface->num_triangles;k++, elements += 3)
+                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
-#define GLVERTEXELEMENT(n) qglVertex3f(rsurface.vertex3f[elements[n]*3+0], rsurface.vertex3f[elements[n]*3+1], rsurface.vertex3f[elements[n]*3+2])
-                                               GLVERTEXELEMENT(0);GLVERTEXELEMENT(1);
-                                               GLVERTEXELEMENT(1);GLVERTEXELEMENT(2);
-                                               GLVERTEXELEMENT(2);GLVERTEXELEMENT(0);
+                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, 0, 0, 1);
+                                               qglVertex3f(v[0], v[1], v[2]);
+                                               VectorMA(v, -r_shownormals.value, rsurface.svector3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, 1, 1, 1);
+                                               qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
                                        CHECKGLERROR
@@ -6942,7 +7836,7 @@ void R_DrawDebugModel(entity_render_t *ent)
 extern void R_BuildLightMap(const entity_render_t *ent, msurface_t *surface);
 int r_maxsurfacelist = 0;
 msurface_t **r_surfacelist = NULL;
-void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean addwaterplanes, qboolean debug)
+void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug)
 {
        int i, j, endj, f, flagsmask;
        texture_t *t;
@@ -6967,7 +7861,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
        update = model->brushq1.lightmapupdateflags;
 
        // update light styles on this submodel
-       if (!skysurfaces && !depthonly && !addwaterplanes && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
+       if (!skysurfaces && !depthonly && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
        {
                model_brush_lightstyleinfo_t *style;
                for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++)
@@ -6982,12 +7876,12 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                }
        }
 
-       R_UpdateAllTextureInfo(r_refdef.scene.worldentity);
-       flagsmask = addwaterplanes ? (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) : (skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL);
+       flagsmask = skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL;
 
        if (debug)
        {
                R_DrawDebugModel(r_refdef.scene.worldentity);
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
        }
 
@@ -6998,41 +7892,38 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
        rsurface.rtlight = NULL;
        numsurfacelist = 0;
        // add visible surfaces to draw list
-       j = model->firstmodelsurface;
-       endj = j + model->nummodelsurfaces;
-       if (update)
+       for (i = 0;i < model->nummodelsurfaces;i++)
        {
-               for (;j < endj;j++)
-               {
+               j = model->sortedmodelsurfaces[i];
+               if (r_refdef.viewcache.world_surfacevisible[j])
+                       r_surfacelist[numsurfacelist++] = surfaces + j;
+       }
+       // update lightmaps if needed
+       if (update)
+               for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
                        if (r_refdef.viewcache.world_surfacevisible[j])
-                       {
-                               r_surfacelist[numsurfacelist++] = surfaces + j;
-                               // update lightmap if needed
                                if (update[j])
                                        R_BuildLightMap(r_refdef.scene.worldentity, surfaces + j);
-                       }
-               }
-       }
-       else
-               for (;j < endj;j++)
-                       if (r_refdef.viewcache.world_surfacevisible[j])
-                               r_surfacelist[numsurfacelist++] = surfaces + j;
        // don't do anything if there were no surfaces
        if (!numsurfacelist)
+       {
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
-       R_QueueSurfaceList(r_refdef.scene.worldentity, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
+       }
+       R_QueueWorldSurfaceList(numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly);
        GL_AlphaTest(false);
 
        // add to stats if desired
-       if (r_speeds.integer && !skysurfaces && !depthonly && !addwaterplanes)
+       if (r_speeds.integer && !skysurfaces && !depthonly)
        {
                r_refdef.stats.world_surfaces += numsurfacelist;
                for (j = 0;j < numsurfacelist;j++)
                        r_refdef.stats.world_triangles += r_surfacelist[j]->num_triangles;
        }
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }
 
-void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean addwaterplanes, qboolean debug)
+void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug)
 {
        int i, j, endj, f, flagsmask;
        texture_t *t;
@@ -7056,7 +7947,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        // to a model, knowing that they are meaningless otherwise
        if (ent == r_refdef.scene.worldentity)
                RSurf_ActiveWorldEntity();
-       else if ((ent->effects & EF_FULLBRIGHT) || (r_showsurfaces.integer && r_showsurfaces.integer != 3) || VectorLength2(ent->modellight_diffuse) < (1.0f / 256.0f))
+       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
                RSurf_ActiveModelEntity(ent, false, false);
        else
                RSurf_ActiveModelEntity(ent, true, r_glsl.integer && gl_support_fragment_shader && !depthonly);
@@ -7065,7 +7956,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        update = model->brushq1.lightmapupdateflags;
 
        // update light styles
-       if (!skysurfaces && !depthonly && !addwaterplanes && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
+       if (!skysurfaces && !depthonly && model->brushq1.num_lightstyles && r_refdef.lightmapintensity > 0)
        {
                model_brush_lightstyleinfo_t *style;
                for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++)
@@ -7080,12 +7971,12 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                }
        }
 
-       R_UpdateAllTextureInfo(ent);
-       flagsmask = addwaterplanes ? (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) : (skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL);
+       flagsmask = skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL;
 
        if (debug)
        {
                R_DrawDebugModel(ent);
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
        }
 
@@ -7096,27 +7987,28 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        rsurface.rtlight = NULL;
        numsurfacelist = 0;
        // add visible surfaces to draw list
-       j = model->firstmodelsurface;
-       endj = j + model->nummodelsurfaces;
-       for (;j < endj;j++)
-               r_surfacelist[numsurfacelist++] = surfaces + j;
+       for (i = 0;i < model->nummodelsurfaces;i++)
+               r_surfacelist[numsurfacelist++] = surfaces + model->sortedmodelsurfaces[i];
        // don't do anything if there were no surfaces
        if (!numsurfacelist)
+       {
+               rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
                return;
+       }
        // update lightmaps if needed
        if (update)
-               for (j = model->firstmodelsurface;j < endj;j++)
+               for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++)
                        if (update[j])
                                R_BuildLightMap(ent, surfaces + j);
-       R_QueueSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, addwaterplanes);
+       R_QueueModelSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly);
        GL_AlphaTest(false);
 
        // add to stats if desired
-       if (r_speeds.integer && !skysurfaces && !depthonly && !addwaterplanes)
+       if (r_speeds.integer && !skysurfaces && !depthonly)
        {
-               r_refdef.stats.entities++;
                r_refdef.stats.entities_surfaces += numsurfacelist;
                for (j = 0;j < numsurfacelist;j++)
                        r_refdef.stats.entities_triangles += r_surfacelist[j]->num_triangles;
        }
+       rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 }