]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
fix errors and warnings in MSVC when compiling as C++
[xonotic/darkplaces.git] / gl_rmain.c
index e41026c6446a1464fd546c8df71de00daca047db..6463c80599199826f3f7f4d6c84fa74e9ad2f227 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" };
@@ -96,7 +109,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 +139,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 +153,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;
@@ -153,7 +167,7 @@ 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;
@@ -168,10 +182,14 @@ rtexture_t *r_texture_gammaramps;
 unsigned int r_texture_gammaramps_serial;
 //rtexture_t *r_texture_fogintensity;
 
+unsigned int r_queries[R_MAX_OCCLUSION_QUERIES];
+unsigned int r_numqueries;
+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
+/// vertex coordinates for a quad that covers the screen exactly
 const static float r_screenvertex3f[12] =
 {
        0, 0, 0,
@@ -471,6 +489,9 @@ static const char *builtinshaderstring =
 "#ifdef USEGAMMARAMPS\n"
 "uniform sampler2D Texture_GammaRamps;\n"
 "#endif\n"
+"#ifdef USESATURATION\n"
+"uniform float Saturation;\n"
+"#endif\n"
 "#ifdef USEVERTEXTEXTUREBLEND\n"
 "uniform vec4 TintColor;\n"
 "#endif\n"
@@ -496,13 +517,20 @@ static const char *builtinshaderstring =
 "\n"
 "#ifdef USEPOSTPROCESSING\n"
 "// do r_glsl_dumpshader, edit glsl/default.glsl, and replace this by your own postprocessing if you want\n"
-"// this code does a blur with the radius specified in the first component of r_glsl_postprocess_uservec1\n"
-"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.987688,  0.156434));\n"
-"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.156434,  0.891007));\n"
-"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.891007,  0.453990));\n"
-"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.707107, -0.707107));\n"
-"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.453990, -0.891007));\n"
-"      gl_FragColor /= 6;\n"
+"// this code does a blur with the radius specified in the first component of r_glsl_postprocess_uservec1 and blends it using the second component\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.987688, -0.156434)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.156434, -0.891007)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.891007, -0.453990)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2( 0.707107,  0.707107)) * UserVec1.y;\n"
+"      gl_FragColor += texture2D(Texture_First, gl_TexCoord[0].xy + PixelSize*UserVec1.x*vec2(-0.453990,  0.891007)) * UserVec1.y;\n"
+"      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" // TODO: test this on ATI
 "#endif\n"
 "\n"
 "#ifdef USEGAMMARAMPS\n"
@@ -563,6 +591,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"
@@ -613,6 +644,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"
@@ -854,7 +888,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"
@@ -862,9 +896,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"
@@ -969,7 +1003,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"
@@ -1034,8 +1068,12 @@ 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"
+"#endif\n"
 "\n"
 "#ifdef USECONTRASTBOOST\n"
 "      color.rgb = color.rgb / (ContrastBoostCoeff * color.rgb + myhalf3(1, 1, 1));\n"
@@ -1087,22 +1125,23 @@ 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_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_SATURATION = 1<<14, ///< user defined postprocessing
+       SHADERPERMUTATION_LIMIT = 1<<15, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 15 ///< size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -1123,23 +1162,24 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USEOFFSETMAPPING_RELIEFMAPPING\n", " reliefmapping"},
        {"#define USEGAMMARAMPS\n", " gammaramps"},
        {"#define USEPOSTPROCESSING\n", " postprocessing"},
+       {"#define USESATURATION\n", " saturation"},
 };
 
-// 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_COUNT
 }
 shadermode_t;
@@ -1163,11 +1203,11 @@ shadermodeinfo_t shadermodeinfo[SHADERMODE_COUNT] =
 
 typedef struct r_glsl_permutation_s
 {
-       // indicates if we have tried compiling this permutation already
+       /// 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;
@@ -1206,8 +1246,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;
@@ -1221,12 +1261,13 @@ typedef struct r_glsl_permutation_s
        int loc_UserVec4;
        int loc_ClientTime;
        int loc_PixelSize;
+       int loc_Saturation;
 }
 r_glsl_permutation_t;
 
-// information about each possible shader permutation
+/// information about each possible shader permutation
 r_glsl_permutation_t r_glsl_permutations[SHADERMODE_COUNT][SHADERPERMUTATION_LIMIT];
-// currently selected permutation
+/// currently selected permutation
 r_glsl_permutation_t *r_glsl_permutation;
 
 static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
@@ -1243,13 +1284,13 @@ static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
        }
        else if (!strcmp(filename, "glsl/default.glsl"))
        {
-               shaderstring = Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
+               shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
                memcpy(shaderstring, builtinshaderstring, strlen(builtinshaderstring) + 1);
        }
        return shaderstring;
 }
 
-static void R_GLSL_CompilePermutation(shadermode_t mode, shaderpermutation_t permutation)
+static void R_GLSL_CompilePermutation(unsigned int mode, unsigned int permutation)
 {
        int i;
        shadermodeinfo_t *modeinfo = shadermodeinfo + mode;
@@ -1381,6 +1422,7 @@ static void R_GLSL_CompilePermutation(shadermode_t mode, shaderpermutation_t per
                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");
                // 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);
@@ -1420,8 +1462,8 @@ static void R_GLSL_CompilePermutation(shadermode_t mode, shaderpermutation_t per
 
 void R_GLSL_Restart_f(void)
 {
-       shadermode_t mode;
-       shaderpermutation_t permutation;
+       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)
@@ -1453,7 +1495,7 @@ void R_GLSL_DumpShader_f(void)
        Con_Printf("glsl/default.glsl written\n");
 }
 
-void R_SetupShader_SetPermutation(shadermode_t mode, unsigned int permutation)
+void R_SetupShader_SetPermutation(unsigned int mode, unsigned int permutation)
 {
        r_glsl_permutation_t *perm = &r_glsl_permutations[mode][permutation];
        if (r_glsl_permutation != perm)
@@ -1552,7 +1594,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
        // minimum features necessary to avoid wasting rendering time in the
        // fragment shader on features that are not being used
        unsigned int permutation = 0;
-       shadermode_t mode = 0;
+       unsigned int mode = 0;
        // TODO: implement geometry-shader based shadow volumes someday
        if (r_glsl_offsetmapping.integer)
        {
@@ -1734,7 +1776,7 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
                        if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, r_refdef.lightmapintensity * specularscale);
                }
                if (r_glsl_permutation->loc_TintColor >= 0) qglUniform4fARB(r_glsl_permutation->loc_TintColor, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2], rsurface.texture->lightmapcolor[3]);
-               if (r_glsl_permutation->loc_GlowScale     >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
+               if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
                // additive passes are only darkened by fog, not tinted
                if (r_glsl_permutation->loc_FogColor >= 0)
                {
@@ -1794,13 +1836,14 @@ void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, f
 
 #define SKINFRAME_HASH 1024
 
-struct
+typedef struct
 {
        int loadsequence; // incremented each level change
        memexpandablearray_t array;
        skinframe_t *hash[SKINFRAME_HASH];
 }
-r_skinframe;
+r_skinframe_t;
+r_skinframe_t r_skinframe;
 
 void R_SkinFrame_PrepareForPurge(void)
 {
@@ -2224,7 +2267,7 @@ skinframe_t *R_SkinFrame_LoadMissing(void)
        if (cls.state == ca_dedicated)
                return NULL;
 
-       skinframe = R_SkinFrame_Find("missing", TEXF_PRECACHE, 0, 0, 0, true);
+       skinframe = R_SkinFrame_Find("missing", TEXF_PRECACHE | TEXF_FORCENEAREST, 0, 0, 0, true);
        skinframe->stain = NULL;
        skinframe->merged = NULL;
        skinframe->base = r_texture_notexture;
@@ -2245,6 +2288,10 @@ skinframe_t *R_SkinFrame_LoadMissing(void)
 
 void gl_main_start(void)
 {
+       r_numqueries = 0;
+       r_maxqueries = 0;
+       memset(r_queries, 0, sizeof(r_queries));
+
        memset(r_qwskincache, 0, sizeof(r_qwskincache));
        memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
 
@@ -2272,8 +2319,16 @@ void gl_main_start(void)
        r_refdef.fogmasktable_density = 0;
 }
 
+extern rtexture_t *loadingscreentexture;
 void gl_main_shutdown(void)
 {
+       if (r_maxqueries)
+               qglDeleteQueriesARB(r_maxqueries, r_queries);
+
+       r_numqueries = 0;
+       r_maxqueries = 0;
+       memset(r_queries, 0, sizeof(r_queries));
+
        memset(r_qwskincache, 0, sizeof(r_qwskincache));
        memset(r_qwskincache_skinframe, 0, sizeof(r_qwskincache_skinframe));
 
@@ -2285,6 +2340,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;
@@ -2342,6 +2398,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);
@@ -2412,6 +2477,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);
@@ -2472,8 +2538,8 @@ void GL_Init (void)
        Con_Printf("GL_VENDOR: %s\n", gl_vendor);
        Con_Printf("GL_RENDERER: %s\n", gl_renderer);
        Con_Printf("GL_VERSION: %s\n", gl_version);
-       Con_Printf("GL_EXTENSIONS: %s\n", gl_extensions);
-       Con_Printf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions);
+       Con_DPrintf("GL_EXTENSIONS: %s\n", gl_extensions);
+       Con_DPrintf("%s_EXTENSIONS: %s\n", gl_platform, gl_platformextensions);
 
        VID_CheckExtensions();
 
@@ -2585,6 +2651,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;
@@ -2632,7 +2889,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;
@@ -2891,6 +3148,7 @@ 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)
@@ -2973,7 +3231,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)
 {
@@ -3036,7 +3295,7 @@ static void R_Water_StartFrame(void)
        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;
@@ -3045,6 +3304,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)
@@ -3064,7 +3324,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;
@@ -3090,7 +3350,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
@@ -3104,6 +3364,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;
 
@@ -3130,24 +3391,29 @@ 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));
@@ -3158,6 +3424,7 @@ static void R_Water_ProcessPlanes(void)
 
                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)
@@ -3177,26 +3444,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");
                }
-
-               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;
@@ -3234,13 +3495,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;
@@ -3452,18 +3715,28 @@ 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");
+
        r_waterstate.numwaterplanes = 0;
-       R_RenderScene(r_waterstate.enabled);
+       if (r_waterstate.enabled)
+               R_RenderWaterPlanes();
+
        r_refdef.view.showdebug = true;
+       R_RenderScene();
+       r_waterstate.numwaterplanes = 0;
 
        R_ResetViewRendering2D();
 
@@ -3486,12 +3759,57 @@ 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
+
+               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.width * r_refdef.view.height;
+                       }
+               }
+
+               // copy view into the screen texture
                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;
        }
@@ -3502,7 +3820,8 @@ static void R_BlendView(void)
                          (r_bloomstate.texture_bloom ? SHADERPERMUTATION_GLOW : 0)
                        | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 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)
                {
@@ -3558,6 +3877,8 @@ 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;
                return;
@@ -3630,8 +3951,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!
@@ -3679,7 +3998,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;
@@ -3753,7 +4072,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;
@@ -3761,18 +4080,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);
                        }
                }
        }
@@ -3821,6 +4140,11 @@ R_RenderView
 */
 void R_RenderView(void)
 {
+       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]
@@ -3832,7 +4156,7 @@ void R_RenderView(void)
                r_waterstate.enabled = false;
                r_waterstate.numwaterplanes = 0;
 
-               R_RenderScene(false);
+               R_RenderScene();
 
                CHECKGLERROR
                return;
@@ -3870,14 +4194,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)
@@ -3888,47 +4220,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)
@@ -3938,10 +4265,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)
@@ -3961,6 +4284,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);
@@ -4065,7 +4390,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)
@@ -4342,6 +4667,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];
 
@@ -4351,14 +4677,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);
@@ -4546,20 +4864,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 = MATERIALFLAG_NODRAW;
-               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
        {
@@ -4580,7 +4954,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];
@@ -4600,12 +4974,12 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                }
                t->currentskinframe = r_qwskincache_skinframe[i];
                if (t->currentskinframe == NULL)
-                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
+                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->shadertime)) % t->numskinframes];
        }
        else if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
+               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->shadertime)) % t->numskinframes];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes];
+               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->shadertime)) % t->backgroundnumskinframes];
 
        t->currentmaterialflags = t->basematerialflags;
        t->currentalpha = ent->alpha;
@@ -4647,70 +5021,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 = tcmod->parms[0];
-                       h = tcmod->parms[1];
-                       f = r_refdef.scene.time / (tcmod->parms[2] * w * h);
-                       f = f - floor(f);
-                       idx = 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;
@@ -4755,7 +5080,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;
@@ -4839,14 +5165,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;
@@ -4876,6 +5196,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;
@@ -4887,14 +5210,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;
@@ -4945,6 +5262,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;
@@ -4961,10 +5281,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)
@@ -4972,9 +5289,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;
@@ -5090,7 +5414,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)
                {
@@ -5100,7 +5424,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;
@@ -5163,8 +5487,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;
@@ -5255,8 +5579,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);
@@ -5294,8 +5618,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;
@@ -5326,7 +5650,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;
@@ -5436,12 +5760,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;
@@ -6103,6 +6443,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));
@@ -6135,7 +6476,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);
@@ -6158,7 +6499,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;
 
@@ -6460,7 +6801,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;
@@ -6552,7 +6896,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();
@@ -6580,7 +6939,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);
@@ -6590,8 +6949,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);
@@ -6605,13 +6963,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)
        {
@@ -6673,23 +7036,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)
        {
@@ -6699,7 +7152,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))
                {
@@ -6712,7 +7165,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);
        }
 }
 
@@ -6853,7 +7306,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);
@@ -6866,14 +7319,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
@@ -6926,7 +7391,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;
@@ -6942,7 +7407,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                r_maxsurfacelist = model->num_surfaces;
                if (r_surfacelist)
                        Mem_Free(r_surfacelist);
-               r_surfacelist = Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
+               r_surfacelist = (msurface_t **) Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
        }
 
        RSurf_ActiveWorldEntity();
@@ -6951,7 +7416,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++)
@@ -6966,12 +7431,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;
        }
 
@@ -6982,41 +7447,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;
@@ -7032,7 +7494,7 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                r_maxsurfacelist = model->num_surfaces;
                if (r_surfacelist)
                        Mem_Free(r_surfacelist);
-               r_surfacelist = Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
+               r_surfacelist = (msurface_t **) Mem_Alloc(r_main_mempool, r_maxsurfacelist * sizeof(*r_surfacelist));
        }
 
        // if the model is static it doesn't matter what value we give for
@@ -7040,7 +7502,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);
@@ -7049,7 +7511,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++)
@@ -7064,12 +7526,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;
        }
 
@@ -7080,27 +7542,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
 }