]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
implemented r_transparent_alphatocoverage 2 which promotes alphablend if
[xonotic/darkplaces.git] / gl_rmain.c
index 354160da4166f594ee9e76c55591b359d6facd62..35b11c2a139c67585f3e6f10e2540c97a22683a6 100644 (file)
@@ -72,7 +72,7 @@ cvar_t r_farclip_world = {0, "r_farclip_world", "2", "adds map size to farclip m
 cvar_t r_nearclip = {0, "r_nearclip", "1", "distance from camera of nearclip plane" };
 cvar_t r_deformvertexes = {0, "r_deformvertexes", "1", "allows use of deformvertexes in shader files (can be turned off to check performance impact)"};
 cvar_t r_transparent = {0, "r_transparent", "1", "allows use of transparent surfaces (can be turned off to check performance impact)"};
-cvar_t r_transparent_alphatocoverage = {0, "r_transparent_alphatocoverage", "0", "enables alpha-to-coverage antialiasing technique on alphatest surfaces, this is not yet finished as multisampling is not used"};
+cvar_t r_transparent_alphatocoverage = {0, "r_transparent_alphatocoverage", "1", "enables GL_ALPHA_TO_COVERAGE antialiasing technique on alphablend and alphatest surfaces when using vid_samples 2 or higher"};
 cvar_t r_showoverdraw = {0, "r_showoverdraw", "0", "shows overlapping geometry"};
 cvar_t r_showbboxes = {0, "r_showbboxes", "0", "shows bounding boxes of server entities, value controls opacity scaling (1 = 10%,  10 = 100%)"};
 cvar_t r_showsurfaces = {0, "r_showsurfaces", "0", "1 shows surfaces as different colors, or a value of 2 shows triangle draw order (for analyzing whether meshes are optimized for vertex cache)"};
@@ -173,6 +173,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_water_scissormode = {0, "r_water_scissormode", "3", "scissor (1) or cull (2) or both (3) water renders"};
+cvar_t r_water_lowquality = {0, "r_water_lowquality", "0", "special option to accelerate water rendering, 1 disables shadows and particles, 2 disables all dynamic lights"};
 
 cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "0", "enables animation smoothing on sprites"};
 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
@@ -205,14 +206,6 @@ cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces
 cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture (for level designers)"};
 
 cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
-cvar_t r_track_sprites = {CVAR_SAVE, "r_track_sprites", "1", "track SPR_LABEL* sprites by putting them as indicator at the screen border to rotate to"};
-cvar_t r_track_sprites_flags = {CVAR_SAVE, "r_track_sprites_flags", "1", "1: Rotate sprites accordingly, 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_overheadsprites_perspective = {CVAR_SAVE, "r_overheadsprites_perspective", "5", "fake perspective effect for SPR_OVERHEAD sprites"};
-cvar_t r_overheadsprites_pushback = {CVAR_SAVE, "r_overheadsprites_pushback", "15", "how far to pull the SPR_OVERHEAD sprites toward the eye (used to avoid intersections with 3D models)"};
-cvar_t r_overheadsprites_scalex = {CVAR_SAVE, "r_overheadsprites_scalex", "1", "additional scale for overhead sprites for x axis"};
-cvar_t r_overheadsprites_scaley = {CVAR_SAVE, "r_overheadsprites_scaley", "1", "additional scale for overhead sprites for y axis"};
 
 cvar_t r_glsl_saturation = {CVAR_SAVE, "r_glsl_saturation", "1", "saturation multiplier (only working in glsl!)"};
 cvar_t r_glsl_saturation_redcompensate = {CVAR_SAVE, "r_glsl_saturation_redcompensate", "0", "a 'vampire sight' addition to desaturation effect, does compensation for red color, r_glsl_restart is required"};
@@ -282,7 +275,7 @@ typedef struct cubemapinfo_s
 cubemapinfo_t;
 
 int r_texture_numcubemaps;
-cubemapinfo_t r_texture_cubemaps[MAX_CUBEMAPS];
+cubemapinfo_t *r_texture_cubemaps[MAX_CUBEMAPS];
 
 unsigned int r_queries[MAX_OCCLUSION_QUERIES];
 unsigned int r_numqueries;
@@ -677,6 +670,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"},
        {"#define USEBOUNCEGRID\n", " bouncegrid"},
        {"#define USEBOUNCEGRIDDIRECTIONAL\n", " bouncegriddirectional"},
+       {"#define USETRIPPY\n", " trippy"},
 };
 
 // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS!
@@ -1899,15 +1893,31 @@ void R_GLSL_DumpShader_f(void)
                Con_Printf("failed to write to hlsl/default.hlsl\n");
 }
 
-void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale)
+void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale, qboolean notrippy)
 {
+       unsigned int permutation = 0;
+       if (r_trippy.integer && !notrippy)
+               permutation |= SHADERPERMUTATION_TRIPPY;
+       permutation |= SHADERPERMUTATION_VIEWTINT;
+       if (first)
+               permutation |= SHADERPERMUTATION_DIFFUSE;
+       if (second)
+               permutation |= SHADERPERMUTATION_SPECULAR;
+       if (texturemode == GL_MODULATE)
+               permutation |= SHADERPERMUTATION_COLORMAPPING;
+       else if (texturemode == GL_ADD)
+               permutation |= SHADERPERMUTATION_GLOW;
+       else if (texturemode == GL_DECAL)
+               permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
        if (!second)
                texturemode = GL_MODULATE;
+       if (vid.allowalphatocoverage)
+               GL_AlphaToCoverage(false);
        switch (vid.renderpath)
        {
        case RENDERPATH_D3D9:
 #ifdef SUPPORTD3D
-               R_SetupShader_SetPermutationHLSL(SHADERMODE_GENERIC, SHADERPERMUTATION_VIEWTINT | (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+               R_SetupShader_SetPermutationHLSL(SHADERMODE_GENERIC, permutation);
                R_Mesh_TexBind(GL20TU_FIRST , first );
                R_Mesh_TexBind(GL20TU_SECOND, second);
 #endif
@@ -1920,7 +1930,7 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                break;
        case RENDERPATH_GL20:
        case RENDERPATH_GLES2:
-               R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, SHADERPERMUTATION_VIEWTINT | (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+               R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, permutation);
                R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , first );
                R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second, second);
                break;
@@ -1936,20 +1946,25 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                R_Mesh_TexBind(0, first );
                break;
        case RENDERPATH_SOFT:
-               R_SetupShader_SetPermutationSoft(SHADERMODE_GENERIC, SHADERPERMUTATION_VIEWTINT | (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
+               R_SetupShader_SetPermutationSoft(SHADERMODE_GENERIC, permutation);
                R_Mesh_TexBind(GL20TU_FIRST , first );
                R_Mesh_TexBind(GL20TU_SECOND, second);
                break;
        }
 }
 
-void R_SetupShader_DepthOrShadow(void)
+void R_SetupShader_DepthOrShadow(qboolean notrippy)
 {
+       unsigned int permutation = 0;
+       if (r_trippy.integer && !notrippy)
+               permutation |= SHADERPERMUTATION_TRIPPY;
+       if (vid.allowalphatocoverage)
+               GL_AlphaToCoverage(false);
        switch (vid.renderpath)
        {
        case RENDERPATH_D3D9:
 #ifdef SUPPORTD3D
-               R_SetupShader_SetPermutationHLSL(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               R_SetupShader_SetPermutationHLSL(SHADERMODE_DEPTH_OR_SHADOW, permutation);
 #endif
                break;
        case RENDERPATH_D3D10:
@@ -1960,7 +1975,7 @@ void R_SetupShader_DepthOrShadow(void)
                break;
        case RENDERPATH_GL20:
        case RENDERPATH_GLES2:
-               R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, permutation);
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GLES1:
@@ -1971,18 +1986,25 @@ void R_SetupShader_DepthOrShadow(void)
                R_Mesh_TexBind(0, 0);
                break;
        case RENDERPATH_SOFT:
-               R_SetupShader_SetPermutationSoft(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               R_SetupShader_SetPermutationSoft(SHADERMODE_DEPTH_OR_SHADOW, permutation);
                break;
        }
 }
 
-void R_SetupShader_ShowDepth(void)
+void R_SetupShader_ShowDepth(qboolean notrippy)
 {
+       int permutation = 0;
+       if (r_trippy.integer && !notrippy)
+               permutation |= SHADERPERMUTATION_TRIPPY;
+       if (r_trippy.integer)
+               permutation |= SHADERPERMUTATION_TRIPPY;
+       if (vid.allowalphatocoverage)
+               GL_AlphaToCoverage(false);
        switch (vid.renderpath)
        {
        case RENDERPATH_D3D9:
 #ifdef SUPPORTHLSL
-               R_SetupShader_SetPermutationHLSL(SHADERMODE_SHOWDEPTH, 0);
+               R_SetupShader_SetPermutationHLSL(SHADERMODE_SHOWDEPTH, permutation);
 #endif
                break;
        case RENDERPATH_D3D10:
@@ -1993,7 +2015,7 @@ void R_SetupShader_ShowDepth(void)
                break;
        case RENDERPATH_GL20:
        case RENDERPATH_GLES2:
-               R_SetupShader_SetPermutationGLSL(SHADERMODE_SHOWDEPTH, 0);
+               R_SetupShader_SetPermutationGLSL(SHADERMODE_SHOWDEPTH, permutation);
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GLES1:
@@ -2001,7 +2023,7 @@ void R_SetupShader_ShowDepth(void)
        case RENDERPATH_GL11:
                break;
        case RENDERPATH_SOFT:
-               R_SetupShader_SetPermutationSoft(SHADERMODE_SHOWDEPTH, 0);
+               R_SetupShader_SetPermutationSoft(SHADERMODE_SHOWDEPTH, permutation);
                break;
        }
 }
@@ -2077,7 +2099,7 @@ static int R_BlendFuncFlags(int src, int dst)
        return r;
 }
 
-void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane)
+void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane, qboolean notrippy)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
@@ -2091,6 +2113,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        float m16f[16];
        matrix4x4_t tempmatrix;
        r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane;
+       if (r_trippy.integer && !notrippy)
+               permutation |= SHADERPERMUTATION_TRIPPY;
        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
                permutation |= SHADERPERMUTATION_ALPHAKILL;
        if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1])
@@ -2127,6 +2151,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        GL_BlendFunc(GL_ONE, GL_ZERO);
                        blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO);
                }
+               if (vid.allowalphatocoverage)
+                       GL_AlphaToCoverage(false);
        }
        else if (rsurfacepass == RSURFPASS_DEFERREDGEOMETRY)
        {
@@ -2146,6 +2172,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                mode = SHADERMODE_DEFERREDGEOMETRY;
                GL_BlendFunc(GL_ONE, GL_ZERO);
                blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO);
+               if (vid.allowalphatocoverage)
+                       GL_AlphaToCoverage(false);
        }
        else if (rsurfacepass == RSURFPASS_RTLIGHT)
        {
@@ -2190,6 +2218,8 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
                blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE);
+               if (vid.allowalphatocoverage)
+                       GL_AlphaToCoverage(false);
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
        {
@@ -2232,6 +2262,17 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
+               // when using alphatocoverage, we don't need alphakill
+               if (vid.allowalphatocoverage)
+               {
+                       if (r_transparent_alphatocoverage.integer)
+                       {
+                               GL_AlphaToCoverage((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+                               permutation &= ~SHADERPERMUTATION_ALPHAKILL;
+                       }
+                       else
+                               GL_AlphaToCoverage(false);
+               }
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT_DIRECTIONAL)
        {
@@ -2284,6 +2325,17 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
+               // when using alphatocoverage, we don't need alphakill
+               if (vid.allowalphatocoverage)
+               {
+                       if (r_transparent_alphatocoverage.integer)
+                       {
+                               GL_AlphaToCoverage((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+                               permutation &= ~SHADERPERMUTATION_ALPHAKILL;
+                       }
+                       else
+                               GL_AlphaToCoverage(false);
+               }
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
        {
@@ -2333,6 +2385,17 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
+               // when using alphatocoverage, we don't need alphakill
+               if (vid.allowalphatocoverage)
+               {
+                       if (r_transparent_alphatocoverage.integer)
+                       {
+                               GL_AlphaToCoverage((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+                               permutation &= ~SHADERPERMUTATION_ALPHAKILL;
+                       }
+                       else
+                               GL_AlphaToCoverage(false);
+               }
        }
        else
        {
@@ -2418,6 +2481,17 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
                blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
+               // when using alphatocoverage, we don't need alphakill
+               if (vid.allowalphatocoverage)
+               {
+                       if (r_transparent_alphatocoverage.integer)
+                       {
+                               GL_AlphaToCoverage((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+                               permutation &= ~SHADERPERMUTATION_ALPHAKILL;
+                       }
+                       else
+                               GL_AlphaToCoverage(false);
+               }
        }
        if(!(blendfuncflags & BLENDFUNC_ALLOWS_COLORMOD))
                colormod = dummy_colormod;
@@ -2918,6 +2992,8 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                else if (r_shadow_shadowmappcf)
                        permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
        }
+       if (vid.allowalphatocoverage)
+               GL_AlphaToCoverage(false);
        Matrix4x4_Transform(&r_refdef.view.viewport.viewmatrix, rtlight->shadoworigin, viewlightorigin);
        Matrix4x4_Concat(&lighttoview, &r_refdef.view.viewport.viewmatrix, &rtlight->matrix_lighttoworld);
        Matrix4x4_Invert_Simple(&viewtolight, &lighttoview);
@@ -3741,14 +3817,36 @@ rtexture_t *R_GetCubemap(const char *basename)
 {
        int i;
        for (i = 0;i < r_texture_numcubemaps;i++)
-               if (!strcasecmp(r_texture_cubemaps[i].basename, basename))
-                       return r_texture_cubemaps[i].texture ? r_texture_cubemaps[i].texture : r_texture_whitecube;
-       if (i >= MAX_CUBEMAPS)
+               if (r_texture_cubemaps[i] != NULL)
+                       if (!strcasecmp(r_texture_cubemaps[i]->basename, basename))
+                               return r_texture_cubemaps[i]->texture ? r_texture_cubemaps[i]->texture : r_texture_whitecube;
+       if (i >= MAX_CUBEMAPS || !r_main_mempool)
                return r_texture_whitecube;
        r_texture_numcubemaps++;
-       strlcpy(r_texture_cubemaps[i].basename, basename, sizeof(r_texture_cubemaps[i].basename));
-       r_texture_cubemaps[i].texture = R_LoadCubemap(r_texture_cubemaps[i].basename);
-       return r_texture_cubemaps[i].texture;
+       r_texture_cubemaps[i] = (cubemapinfo_t *)Mem_Alloc(r_main_mempool, sizeof(cubemapinfo_t));
+       strlcpy(r_texture_cubemaps[i]->basename, basename, sizeof(r_texture_cubemaps[i]->basename));
+       r_texture_cubemaps[i]->texture = R_LoadCubemap(r_texture_cubemaps[i]->basename);
+       return r_texture_cubemaps[i]->texture;
+}
+
+void R_FreeCubemap(const char *basename)
+{
+       int i;
+
+       for (i = 0;i < r_texture_numcubemaps;i++)
+       {
+               if (r_texture_cubemaps[i] != NULL)
+               {
+                       if (r_texture_cubemaps[i]->texture)
+                       {
+                               if (developer_loading.integer)
+                                       Con_DPrintf("unloading cubemap \"%s\"\n", r_texture_cubemaps[i]->basename);
+                               R_FreeTexture(r_texture_cubemaps[i]->texture);
+                               Mem_Free(r_texture_cubemaps[i]);
+                               r_texture_cubemaps[i] = NULL;
+                       }
+               }
+       }
 }
 
 void R_FreeCubemaps(void)
@@ -3757,9 +3855,13 @@ void R_FreeCubemaps(void)
        for (i = 0;i < r_texture_numcubemaps;i++)
        {
                if (developer_loading.integer)
-                       Con_DPrintf("unloading cubemap \"%s\"\n", r_texture_cubemaps[i].basename);
-               if (r_texture_cubemaps[i].texture)
-                       R_FreeTexture(r_texture_cubemaps[i].texture);
+                       Con_DPrintf("unloading cubemap \"%s\"\n", r_texture_cubemaps[i]->basename);
+               if (r_texture_cubemaps[i] != NULL)
+               {
+                       if (r_texture_cubemaps[i]->texture)
+                               R_FreeTexture(r_texture_cubemaps[i]->texture);
+                       Mem_Free(r_texture_cubemaps[i]);
+               }
        }
        r_texture_numcubemaps = 0;
 }
@@ -3911,6 +4013,9 @@ void gl_main_start(void)
        hlslshaderstring = NULL;
        memset(&r_svbsp, 0, sizeof (r_svbsp));
 
+       memset(r_texture_cubemaps, 0, sizeof(r_texture_cubemaps));
+       r_texture_numcubemaps = 0;
+
        r_refdef.fogmasktable_density = 0;
 }
 
@@ -4135,6 +4240,8 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_water_refractdistort);
        Cvar_RegisterVariable(&r_water_reflectdistort);
        Cvar_RegisterVariable(&r_water_scissormode);
+       Cvar_RegisterVariable(&r_water_lowquality);
+
        Cvar_RegisterVariable(&r_lerpsprites);
        Cvar_RegisterVariable(&r_lerpmodels);
        Cvar_RegisterVariable(&r_lerplightstyles);
@@ -4167,15 +4274,6 @@ void GL_Main_Init(void)
        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, NULL, NULL);
-
-       Cvar_RegisterVariable(&r_track_sprites);
-       Cvar_RegisterVariable(&r_track_sprites_flags);
-       Cvar_RegisterVariable(&r_track_sprites_scalew);
-       Cvar_RegisterVariable(&r_track_sprites_scaleh);
-       Cvar_RegisterVariable(&r_overheadsprites_perspective);
-       Cvar_RegisterVariable(&r_overheadsprites_pushback);
-       Cvar_RegisterVariable(&r_overheadsprites_scalex);
-       Cvar_RegisterVariable(&r_overheadsprites_scaley);
 }
 
 extern void R_Textures_Init(void);
@@ -4246,6 +4344,8 @@ int R_CullBox(const vec3_t mins, const vec3_t maxs)
 {
        int i;
        mplane_t *p;
+       if (r_trippy.integer)
+               return false;
        for (i = 0;i < r_refdef.view.numfrustumplanes;i++)
        {
                // skip nearclip plane, it often culls portals when you are very close, and is almost never useful
@@ -4296,6 +4396,8 @@ int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, c
 {
        int i;
        const mplane_t *p;
+       if (r_trippy.integer)
+               return false;
        for (i = 0;i < numplanes;i++)
        {
                p = planes + i;
@@ -4595,6 +4697,8 @@ void R_AnimCache_CacheVisibleEntities(void)
 
 //==================================================================================
 
+extern cvar_t r_overheadsprites_pushback;
+
 static void R_View_UpdateEntityLighting (void)
 {
        int i;
@@ -4777,7 +4881,7 @@ static void R_View_UpdateEntityVisible (void)
                        r_refdef.viewcache.entityvisible[i] = !(ent->flags & renderimask) && ((ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)) || !R_CullBox(ent->mins, ent->maxs));
                }
        }
-       if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane)
+       if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane && !r_trippy.integer)
                // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling
        {
                for (i = 0;i < r_refdef.scene.numentities;i++)
@@ -5516,17 +5620,44 @@ void R_Water_AddWaterPlane(msurface_t *surface, int entno)
        }
 }
 
+extern cvar_t r_drawparticles;
+extern cvar_t r_drawdecals;
+
 static void R_Water_ProcessPlanes(void)
 {
        int myscissor[4];
        r_refdef_view_t originalview;
        r_refdef_view_t myview;
-       int planeindex;
+       int planeindex, qualityreduction = 0, old_r_dynamic = 0, old_r_shadows = 0, old_r_worldrtlight = 0, old_r_dlight = 0, old_r_particles = 0, old_r_decals = 0;
        r_waterstate_waterplane_t *p;
        vec3_t visorigin;
 
        originalview = r_refdef.view;
 
+       // lowquality hack, temporarily shut down some cvars and restore afterwards
+       qualityreduction = r_water_lowquality.integer;
+       if (qualityreduction > 0)
+       {
+               if (qualityreduction >= 1)
+               {
+                       old_r_shadows = r_shadows.integer;
+                       old_r_worldrtlight = r_shadow_realtime_world.integer;
+                       old_r_dlight = r_shadow_realtime_dlight.integer;
+                       Cvar_SetValueQuick(&r_shadows, 0);
+                       Cvar_SetValueQuick(&r_shadow_realtime_world, 0);
+                       Cvar_SetValueQuick(&r_shadow_realtime_dlight, 0);
+               }
+               if (qualityreduction >= 2)
+               {
+                       old_r_dynamic = r_dynamic.integer;
+                       old_r_particles = r_drawparticles.integer;
+                       old_r_decals = r_drawdecals.integer;
+                       Cvar_SetValueQuick(&r_dynamic, 0);
+                       Cvar_SetValueQuick(&r_drawparticles, 0);
+                       Cvar_SetValueQuick(&r_drawdecals, 0);
+               }
+       }
+
        // make sure enough textures are allocated
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
        {
@@ -5579,7 +5710,6 @@ static void R_Water_ProcessPlanes(void)
                        // update the r_refdef.view.origin because otherwise the sky renders at the wrong location (amongst other problems)
                        Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, r_refdef.view.origin);
                        r_refdef.view.clipplane = p->plane;
-
                        // reverse the cullface settings for this render
                        r_refdef.view.cullface_front = GL_FRONT;
                        r_refdef.view.cullface_back = GL_BACK;
@@ -5706,13 +5836,29 @@ static void R_Water_ProcessPlanes(void)
        R_ResetViewRendering3D();
        R_ClearScreen(r_refdef.fogenabled);
        R_View_Update();
-       return;
+       goto finish;
 error:
        r_refdef.view = originalview;
        r_waterstate.renderingscene = false;
        Cvar_SetValueQuick(&r_water, 0);
        Con_Printf("R_Water_ProcessPlanes: Error: texture creation failed!  Turned off r_water.\n");
-       return;
+finish:
+       // lowquality hack, restore cvars
+       if (qualityreduction > 0)
+       {
+               if (qualityreduction >= 1)
+               {
+                       Cvar_SetValueQuick(&r_shadows, old_r_shadows);
+                       Cvar_SetValueQuick(&r_shadow_realtime_world, old_r_worldrtlight);
+                       Cvar_SetValueQuick(&r_shadow_realtime_dlight, old_r_dlight);
+               }
+               if (qualityreduction >= 2)
+               {
+                       Cvar_SetValueQuick(&r_dynamic, old_r_dynamic);
+                       Cvar_SetValueQuick(&r_drawparticles, old_r_particles);
+                       Cvar_SetValueQuick(&r_drawdecals, old_r_decals);
+               }
+       }
 }
 
 void R_Bloom_StartFrame(void)
@@ -5960,7 +6106,7 @@ void R_Bloom_CopyBloomTexture(float colorscale)
                break;
        }
        // TODO: do boxfilter scale-down in shader?
-       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1, true);
        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
@@ -5996,7 +6142,7 @@ void R_Bloom_MakeTexture(void)
                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
                GL_Color(r,r,r,1);
                R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
-               R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
+               R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1, true);
                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
@@ -6012,7 +6158,7 @@ void R_Bloom_MakeTexture(void)
        brighten = sqrt(brighten);
        if(range >= 1)
                brighten *= (3 * range) / (2 * range - 1); // compensate for the "dot particle"
-       R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1, true);
 
        for (dir = 0;dir < 2;dir++)
        {
@@ -6193,7 +6339,7 @@ static void R_BlendView(void)
                                                R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_bloomstate.screentexcoord2f);
                                                break;
                                        }
-                                       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1);
+                                       R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1, true);
                                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                                        r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                                }
@@ -6212,7 +6358,7 @@ static void R_BlendView(void)
                                R_ResetViewRendering2D();
                                GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
                                R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
-                               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+                               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
                                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                                R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        }
@@ -6320,7 +6466,7 @@ static void R_BlendView(void)
                        R_ResetViewRendering2D();
                        GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
                        R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
-                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                        R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                }
@@ -6972,7 +7118,7 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
        }
        R_Mesh_PrepareVertices_Generic_Arrays(8, vertex3f, color4f, NULL);
        R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
        R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
@@ -6988,7 +7134,7 @@ static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtligh
                return;
 
        GL_CullFace(GL_NONE);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
 
        prog = 0;
        SV_VM_Begin();
@@ -7138,7 +7284,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
                }
        }
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
        R_Mesh_PrepareVertices_Generic_Arrays(6, nomodelvertex3f, color4f, NULL);
        R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, NULL, 0, nomodelelement3s, NULL, 0);
 }
@@ -7326,7 +7472,7 @@ static qboolean R_TestQ3WaveFunc(q3wavefunc_t func, const float *parms)
 static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
 {
        double index, f;
-       index = parms[2] + r_refdef.scene.time * parms[3];
+       index = parms[2] + rsurface.shadertime * parms[3];
        index -= floor(index);
        switch (func & ((1 << Q3WAVEFUNC_USER_SHIFT) - 1))
        {
@@ -7362,7 +7508,8 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
 void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags)
 {
        int w, h, idx;
-       float f;
+       double f;
+       double offsetd[2];
        float tcmat[12];
        matrix4x4_t matrix, temp;
        switch(tcmod->tcmod)
@@ -7381,19 +7528,22 @@ void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *t
                        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_ConcatRotate(&matrix, tcmod->parms[0] * rsurface.shadertime, 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);
+                       // extra care is needed because of precision breakdown with large values of time
+                       offsetd[0] = tcmod->parms[0] * rsurface.shadertime;
+                       offsetd[1] = tcmod->parms[1] * rsurface.shadertime;
+                       Matrix4x4_CreateTranslate(&matrix, offsetd[0] - floor(offsetd[0]), offsetd[1] - floor(offsetd[1]), 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 = rsurface.shadertime / (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);
@@ -7471,7 +7621,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                if (model->skinscenes)
                {
                        if (model->skinscenes[s].framecount > 1)
-                               s = model->skinscenes[s].firstframe + (unsigned int) (r_refdef.scene.time * model->skinscenes[s].framerate) % model->skinscenes[s].framecount;
+                               s = model->skinscenes[s].firstframe + (unsigned int) (rsurface.shadertime * model->skinscenes[s].framerate) % model->skinscenes[s].framecount;
                        else
                                s = model->skinscenes[s].firstframe;
                }
@@ -7482,9 +7632,9 @@ texture_t *R_GetCurrentTexture(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 (rsurface.ent_alttextures && 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];
+                               t = t->anim_frames[1][(t->anim_total[1] >= 2) ? ((int)(rsurface.shadertime * 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];
+                               t = t->anim_frames[0][(t->anim_total[0] >= 2) ? ((int)(rsurface.shadertime * 5.0f) % t->anim_total[0]) : 0];
                }
                texture->currentframe = t;
        }
@@ -7504,16 +7654,16 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                        R_LoadQWSkin(&r_qwskincache[i], cl.scores[i].qw_skin);
                t->currentskinframe = r_qwskincache[i].skinframe;
                if (t->currentskinframe == NULL)
-                       t->currentskinframe = t->skinframes[(unsigned int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
+                       t->currentskinframe = t->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->skinframerate, t->numskinframes)];
        }
        else if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes[(unsigned int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
+               t->currentskinframe = t->skinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->skinframerate, t->numskinframes)];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes[(unsigned int)(t->backgroundskinframerate * (cl.time - rsurface.ent_shadertime)) % t->backgroundnumskinframes];
+               t->backgroundcurrentskinframe = t->backgroundskinframes[LoopingFrameNumberFromDouble(rsurface.shadertime * t->backgroundskinframerate, t->backgroundnumskinframes)];
 
        t->currentmaterialflags = t->basematerialflags;
        t->currentalpha = rsurface.colormod[3];
-       if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer))
+       if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer || r_trippy.integer))
                t->currentalpha *= r_wateralpha.value;
        if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
                t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW; // we apply wateralpha later
@@ -7550,6 +7700,11 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        }
        else
                t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA);
+       if (vid.allowalphatocoverage && r_transparent_alphatocoverage.integer >= 2 && ((t->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHA | MATERIALFLAG_ADD | MATERIALFLAG_CUSTOMBLEND)) == (MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHA)))
+       {
+               // promote alphablend to alphatocoverage (a type of alphatest) if antialiasing is on
+               t->currentmaterialflags = (t->currentmaterialflags & ~(MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHA)) | MATERIALFLAG_ALPHATEST;
+       }
        if ((t->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)) == MATERIALFLAG_BLENDED && r_transparentdepthmasking.integer && !(t->basematerialflags & MATERIALFLAG_BLENDED))
                t->currentmaterialflags |= MATERIALFLAG_TRANSDEPTH;
 
@@ -7754,8 +7909,8 @@ void RSurf_ActiveWorldEntity(void)
        memset(rsurface.userwavefunc_param, 0, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
-       rsurface.ent_shadertime = 0;
        rsurface.ent_flags = r_refdef.scene.worldentity->flags;
+       rsurface.shadertime = r_refdef.scene.time;
        rsurface.matrix = identitymatrix;
        rsurface.inversematrix = identitymatrix;
        rsurface.matrixscale = 1;
@@ -7865,8 +8020,8 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        memcpy(rsurface.userwavefunc_param, ent->userwavefunc_param, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = ent->skinnum;
        rsurface.ent_qwskin = (ent->entitynumber <= cl.maxclients && ent->entitynumber >= 1 && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[ent->entitynumber - 1].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl")) ? (ent->entitynumber - 1) : -1;
-       rsurface.ent_shadertime = ent->shadertime;
        rsurface.ent_flags = ent->flags;
+       rsurface.shadertime = r_refdef.scene.time - ent->shadertime;
        rsurface.matrix = ent->matrix;
        rsurface.inversematrix = ent->inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -8034,8 +8189,8 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
        rsurface.skeleton = NULL;
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
-       rsurface.ent_shadertime = shadertime;
        rsurface.ent_flags = entflags;
+       rsurface.shadertime = r_refdef.scene.time - shadertime;
        rsurface.modelnumvertices = numvertices;
        rsurface.modelnumtriangles = numtriangles;
        rsurface.matrix = *matrix;
@@ -8547,20 +8702,53 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        if (batchneed & (BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_ARRAY_LIGHTMAP))
                        {
                                if (batchneed & BATCHNEED_ARRAY_VERTEX)
-                                       memcpy(rsurface.batchvertex3f + 3*numvertices, rsurface.modelvertex3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
-                               if ((batchneed & BATCHNEED_ARRAY_NORMAL) && rsurface.modelnormal3f)
-                                       memcpy(rsurface.batchnormal3f + 3*numvertices, rsurface.modelnormal3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
-                               if ((batchneed & BATCHNEED_ARRAY_VECTOR) && rsurface.modelsvector3f)
                                {
-                                       memcpy(rsurface.batchsvector3f + 3*numvertices, rsurface.modelsvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
-                                       memcpy(rsurface.batchtvector3f + 3*numvertices, rsurface.modeltvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                       if (rsurface.batchvertex3f)
+                                               memcpy(rsurface.batchvertex3f + 3*numvertices, rsurface.modelvertex3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                       else
+                                               memset(rsurface.batchvertex3f + 3*numvertices, 0, surfacenumvertices * sizeof(float[3]));
+                               }
+                               if (batchneed & BATCHNEED_ARRAY_NORMAL)
+                               {
+                                       if (rsurface.modelnormal3f)
+                                               memcpy(rsurface.batchnormal3f + 3*numvertices, rsurface.modelnormal3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                       else
+                                               memset(rsurface.batchnormal3f + 3*numvertices, 0, surfacenumvertices * sizeof(float[3]));
+                               }
+                               if (batchneed & BATCHNEED_ARRAY_VECTOR)
+                               {
+                                       if (rsurface.modelsvector3f)
+                                       {
+                                               memcpy(rsurface.batchsvector3f + 3*numvertices, rsurface.modelsvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                               memcpy(rsurface.batchtvector3f + 3*numvertices, rsurface.modeltvector3f + 3*surfacefirstvertex, surfacenumvertices * sizeof(float[3]));
+                                       }
+                                       else
+                                       {
+                                               memset(rsurface.batchsvector3f + 3*numvertices, 0, surfacenumvertices * sizeof(float[3]));
+                                               memset(rsurface.batchtvector3f + 3*numvertices, 0, surfacenumvertices * sizeof(float[3]));
+                                       }
+                               }
+                               if (batchneed & BATCHNEED_ARRAY_VERTEXCOLOR)
+                               {
+                                       if (rsurface.modellightmapcolor4f)
+                                               memcpy(rsurface.batchlightmapcolor4f + 4*numvertices, rsurface.modellightmapcolor4f + 4*surfacefirstvertex, surfacenumvertices * sizeof(float[4]));
+                                       else
+                                               memset(rsurface.batchlightmapcolor4f + 4*numvertices, 0, surfacenumvertices * sizeof(float[4]));
+                               }
+                               if (batchneed & BATCHNEED_ARRAY_TEXCOORD)
+                               {
+                                       if (rsurface.modeltexcoordtexture2f)
+                                               memcpy(rsurface.batchtexcoordtexture2f + 2*numvertices, rsurface.modeltexcoordtexture2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
+                                       else
+                                               memset(rsurface.batchtexcoordtexture2f + 2*numvertices, 0, surfacenumvertices * sizeof(float[2]));
+                               }
+                               if (batchneed & BATCHNEED_ARRAY_LIGHTMAP)
+                               {
+                                       if (rsurface.modeltexcoordlightmap2f)
+                                               memcpy(rsurface.batchtexcoordlightmap2f + 2*numvertices, rsurface.modeltexcoordlightmap2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
+                                       else
+                                               memset(rsurface.batchtexcoordlightmap2f + 2*numvertices, 0, surfacenumvertices * sizeof(float[2]));
                                }
-                               if ((batchneed & BATCHNEED_ARRAY_VERTEXCOLOR) && rsurface.modellightmapcolor4f)
-                                       memcpy(rsurface.batchlightmapcolor4f + 4*numvertices, rsurface.modellightmapcolor4f + 4*surfacefirstvertex, surfacenumvertices * sizeof(float[4]));
-                               if ((batchneed & BATCHNEED_ARRAY_TEXCOORD) && rsurface.modeltexcoordtexture2f)
-                                       memcpy(rsurface.batchtexcoordtexture2f + 2*numvertices, rsurface.modeltexcoordtexture2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
-                               if ((batchneed & BATCHNEED_ARRAY_LIGHTMAP) && rsurface.modeltexcoordlightmap2f)
-                                       memcpy(rsurface.batchtexcoordlightmap2f + 2*numvertices, rsurface.modeltexcoordlightmap2f + 2*surfacefirstvertex, surfacenumvertices * sizeof(float[2]));
                        }
                        RSurf_RenumberElements(rsurface.modelelement3i + 3*surfacefirsttriangle, rsurface.batchelement3i + 3*numtriangles, 3*surfacenumtriangles, numvertices - surfacefirstvertex);
                        numvertices += surfacenumvertices;
@@ -8684,6 +8872,11 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
 //                     rsurface.batchnormal3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f);
 //                     rsurface.batchnormal3f_vertexbuffer = NULL;
 //                     rsurface.batchnormal3f_bufferoffset = 0;
+                       // sometimes we're on a renderpath that does not use vectors (GL11/GL13/GLES1)
+                       if (!VectorLength2(rsurface.batchnormal3f + 3*rsurface.batchfirstvertex))
+                               Mod_BuildNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       if (!VectorLength2(rsurface.batchsvector3f + 3*rsurface.batchfirstvertex))
+                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchsvector3f, rsurface.batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        // a single autosprite surface can contain multiple sprites...
                        for (j = 0;j < batchnumvertices - 3;j += 4)
                        {
@@ -8820,9 +9013,9 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                                float vertex[3];
                                float *normal = rsurface.batchnormal3f + 3*j;
                                VectorScale(rsurface.batchvertex3f + 3*j, 0.98f, vertex);
-                               normal[0] = rsurface.batchnormal3f[j*3+0] + deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                               normal[1] = rsurface.batchnormal3f[j*3+1] + deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                               normal[2] = rsurface.batchnormal3f[j*3+2] + deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               normal[0] = rsurface.batchnormal3f[j*3+0] + deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], rsurface.shadertime * deform->parms[1]);
+                               normal[1] = rsurface.batchnormal3f[j*3+1] + deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], rsurface.shadertime * deform->parms[1]);
+                               normal[2] = rsurface.batchnormal3f[j*3+2] + deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], rsurface.shadertime * deform->parms[1]);
                                VectorNormalize(normal);
                        }
                        if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
@@ -8886,7 +9079,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
 //                     rsurface.batchnormal3f_bufferoffset = 0;
                        for (j = 0;j < batchnumvertices;j++)
                        {
-                               scale = sin(rsurface.batchtexcoordtexture2f[j*2+0] * deform->parms[0] + r_refdef.scene.time * deform->parms[2]) * deform->parms[1];
+                               scale = sin(rsurface.batchtexcoordtexture2f[j*2+0] * deform->parms[0] + rsurface.shadertime * deform->parms[2]) * deform->parms[1];
                                VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.batchvertex3f + 3*j);
                        }
                        // if we get here, BATCHNEED_ARRAY_NORMAL is in batchneed, so no need to check
@@ -8980,7 +9173,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
        if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
        {
                amplitude = rsurface.texture->tcmods[0].parms[1];
-               animpos = rsurface.texture->tcmods[0].parms[2] + r_refdef.scene.time * rsurface.texture->tcmods[0].parms[3];
+               animpos = rsurface.texture->tcmods[0].parms[2] + rsurface.shadertime * rsurface.texture->tcmods[0].parms[3];
 //             rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
 //             rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
 //             rsurface.batchtexcoordtexture2f_bufferoffset = 0;
@@ -9400,7 +9593,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        // transparent sky would be ridiculous
        if (rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED)
                return;
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
        skyrenderlater = true;
        RSurf_SetupDepthAndCulling();
        GL_DepthMask(true);
@@ -9410,12 +9603,12 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        // in Quake3 maps as it causes problems with q3map2 sky tricks,
        // and skymasking also looks very bad when noclipping outside the
        // level, so don't use it then either.
-       if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis)
+       if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer)
        {
                R_Mesh_ResetTextureState();
                if (skyrendermasked)
                {
-                       R_SetupShader_DepthOrShadow();
+                       R_SetupShader_DepthOrShadow(false);
                        // depth-only (masking)
                        GL_ColorMask(0,0,0,0);
                        // just to make sure that braindead drivers don't draw
@@ -9429,7 +9622,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                }
                else
                {
-                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+                       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
                        // fog sky
                        GL_BlendFunc(GL_ONE, GL_ZERO);
                        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
@@ -9454,7 +9647,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
        {
                // render screenspace normalmap to texture
                GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL, false);
                RSurf_DrawBatch();
        }
 
@@ -9481,18 +9674,18 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
                        {
                                // render water or distortion background
                                GL_DepthMask(true);
-                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex));
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex), false);
                                RSurf_DrawBatch();
                                // blend surface on top
                                GL_DepthMask(false);
-                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL);
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL, false);
                                RSurf_DrawBatch();
                        }
                        else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION))
                        {
                                // render surface with reflection texture as input
                                GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex));
+                               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_waterstate.waterplanes + startplaneindex), false);
                                RSurf_DrawBatch();
                        }
                }
@@ -9501,12 +9694,8 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
 
        // render surface batch normally
        GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-       R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL);
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
-               GL_AlphaTest(true);
+       R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) != 0);
        RSurf_DrawBatch();
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
-               GL_AlphaTest(false);
 }
 
 static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
@@ -9728,7 +9917,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        float c[4];
 
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
 
        if(rsurface.texture && rsurface.texture->currentskinframe)
        {
@@ -9834,7 +10023,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
                RSurf_DrawBatch_GL11_ClampColor();
 
                R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.passcolor4f, NULL);
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
                RSurf_DrawBatch();
        }
        else if (!r_refdef.view.showdebug)
@@ -10036,7 +10225,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_BlendFunc(GL_ONE, GL_ZERO);
                                GL_DepthMask(true);
 //                             R_Mesh_ResetTextureState();
-                               R_SetupShader_DepthOrShadow();
+                               R_SetupShader_DepthOrShadow(false);
                        }
                        RSurf_SetupDepthAndCulling();
                        RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
@@ -10349,7 +10538,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
                        vertex3f[i] = mins[j] + size[j] * locboxvertex3f[i];
 
        R_Mesh_PrepareVertices_Generic_Arrays(6*4, vertex3f, NULL, NULL);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
        R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, NULL, 0, locboxelements, NULL, 0);
 }
 
@@ -10803,10 +10992,10 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent)
        lifetime = cl_decals_time.value + cl_decals_fadetime.value;
 
        if (decalsystem->lastupdatetime)
-               frametime = (cl.time - decalsystem->lastupdatetime);
+               frametime = (r_refdef.scene.time - decalsystem->lastupdatetime);
        else
                frametime = 0;
-       decalsystem->lastupdatetime = cl.time;
+       decalsystem->lastupdatetime = r_refdef.scene.time;
        decal = decalsystem->decals;
        numdecals = decalsystem->numdecals;
 
@@ -10883,7 +11072,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
        else
                RSurf_ActiveModelEntity(ent, false, false, false);
 
-       decalsystem->lastupdatetime = cl.time;
+       decalsystem->lastupdatetime = r_refdef.scene.time;
        decal = decalsystem->decals;
 
        faderate = 1.0f / max(0.001f, cl_decals_fadetime.value);
@@ -10967,7 +11156,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
 
                // now render the decals all at once
                // (this assumes they all use one particle font texture!)
-               RSurf_ActiveCustomEntity(&rsurface.matrix, &rsurface.inversematrix, rsurface.ent_flags, rsurface.ent_shadertime, 1, 1, 1, 1, numdecals*3, decalsystem->vertex3f, decalsystem->texcoord2f, NULL, NULL, NULL, decalsystem->color4f, numtris, decalsystem->element3i, decalsystem->element3s, false, false);
+               RSurf_ActiveCustomEntity(&rsurface.matrix, &rsurface.inversematrix, rsurface.ent_flags, ent->shadertime, 1, 1, 1, 1, numdecals*3, decalsystem->vertex3f, decalsystem->texcoord2f, NULL, NULL, NULL, decalsystem->color4f, numtris, decalsystem->element3i, decalsystem->element3s, false, false);
 //             R_Mesh_ResetTextureState();
                R_Mesh_PrepareVertices_Generic_Arrays(numtris * 3, decalsystem->vertex3f, decalsystem->color4f, decalsystem->texcoord2f);
                GL_DepthMask(false);
@@ -10976,7 +11165,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                GL_DepthTest(true);
                GL_CullFace(GL_NONE);
                GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
-               R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1);
+               R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1, false);
                R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, NULL, 0, decalsystem->element3s, NULL, 0);
        }
 }
@@ -11033,7 +11222,7 @@ void R_DrawDebugModel(void)
        {
                float c = r_refdef.view.colorscale * r_showoverdraw.value * 0.125f;
                flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
                GL_DepthTest(false);
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
@@ -11063,7 +11252,7 @@ void R_DrawDebugModel(void)
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
 
 //     R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, false);
        GL_DepthRange(0, 1);
        GL_DepthTest(!r_showdisabledepthtest.integer);
        GL_DepthMask(false);
@@ -11175,7 +11364,7 @@ void R_DrawDebugModel(void)
                        {
                                RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_NOGAPS, 1, &surface);
                                qglBegin(GL_LINES);
-                               if (r_shownormals.value < 0)
+                               if (r_shownormals.value < 0 && rsurface.batchnormal3f)
                                {
                                        for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
@@ -11198,6 +11387,9 @@ void R_DrawDebugModel(void)
                                                GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
+                               }
+                               if (r_shownormals.value > 0 && rsurface.batchtvector3f)
+                               {
                                        for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
                                                VectorCopy(rsurface.batchvertex3f + l * 3, v);
@@ -11207,6 +11399,9 @@ void R_DrawDebugModel(void)
                                                GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
+                               }
+                               if (r_shownormals.value > 0 && rsurface.batchnormal3f)
+                               {
                                        for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
                                                VectorCopy(rsurface.batchvertex3f + l * 3, v);